EpiServer - get absolute friendly url for given culture for page - episerver

I've a following setup in my Manage Websites panel
General Url is set to alloy.com
alloy.no is set for no culture
alloy.se is set for sv culture
alloy.com is set for en culture
In my code, i want to get the friendly external url for given language for given page.
So for Search page I want to get absolute friendly url in all languages.
I use following code to get friendly url for page (found on Anders G. Nordby blog):
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
var pageAddress = urlResolver.GetUrl(reference, language);
var builder = new UrlBuilder(pageAddress);
Global.UrlRewriteProvider.ConvertToExternal(builder, null, Encoding.UTF8);
var friendlyUrl = builder.Uri.IsAbsoluteUri
? builder.ToString()
: UriSupport.AbsoluteUrlBySettings(builder.ToString());
return friendlyUrl;
It is simple if I will use the alloy.com webpage and in my custom code generate friendly url.
no - alloy.no/søk
se - alloy.se/sök
en - alloy.com/search
But when I use alloy.no to enter edit mode and I will try to generate address for no i get alloy.com/søk when it should be alloy.no/søk.
I found that if I use alloy.no to go to Edit Mode, code :
urlResolver.GetUrl(reference, language)
returns only /søk and code
UriSupport.AbsoluteUrlBySettings(builder.ToString())
add the General URL (alloy.com) instead of alloy.no.
How can I improve this code to take correct host name for page in different culture?

The GetUrl method of the UrlResolver will return a URL to a page that is relative or absolute depending on the current request context. A URL will be relative if the page is located in the current site and absolute if in another site or if the call is made outside a request.
If you are using EPiServer.CMS.Core version 8.0 or later there is also support for identifying one site as the primary site. This update also made it possible to explicitly request that the URL should be to the primary site by setting the ForceCanonical flag on the VirtualPathArguments parameter. If not the flag is not set, it will prefer the current site URL over the primary (given the requested content is located on the current site).
So, with this in mind, you can assume that the returned URL, if not absolute, will be relative to the currently requested site and safely combine it with the currently requested URL such as:
private static string ExternalUrl(this ContentReference contentLink, CultureInfo language)
{
var urlString = UrlResolver.Current.GetUrl(contentLink, language.Name, new VirtualPathArguments { ForceCanonical = true });
if (string.IsNullOrEmpty(urlString) || HttpContext.Current == null) return urlString;
var uri = new Uri(urlString, UriKind.RelativeOrAbsolute);
if (uri.IsAbsoluteUri) return urlString;
return new Uri(HttpContext.Current.Request.Url, uri).ToString();
}
In most cases I would probably prefer not to use HttpContext.Current directly and instead pass in the current request URL. But in this case I have opted to use it directly to keep the example more contained.

We have a support case regarding this. We want the url resolver to always return an absolute url when requesting the canonical url. This is our current solution:
public static string ExternalUrl(this PageData p, bool absoluteUrl, string languageBranch)
{
var result = ServiceLocator.Current.GetInstance<UrlResolver>().GetUrl(
p.ContentLink,
languageBranch,
new VirtualPathArguments
{
ContextMode = ContextMode.Default,
ForceCanonical = absoluteUrl
});
// HACK: Temprorary fix until GetUrl and ForceCanonical works as expected,
// i.e returning an absolute URL even if there is a HTTP context that matches the page's
// site definition and host.
if (absoluteUrl)
{
Uri relativeUri;
if (Uri.TryCreate(result, UriKind.RelativeOrAbsolute, out relativeUri))
{
if (!relativeUri.IsAbsoluteUri)
{
var siteDefinitionResolver = ServiceLocator.Current.GetInstance<SiteDefinitionResolver>();
var siteDefinition = siteDefinitionResolver.GetDefinitionForContent(p.ContentLink, true, true);
var hosts = siteDefinition.GetHosts(p.Language, true);
var host = hosts.FirstOrDefault(h => h.Type == HostDefinitionType.Primary) ?? hosts.FirstOrDefault(h => h.Type == HostDefinitionType.Undefined);
var basetUri = siteDefinition.SiteUrl;
if (host != null)
{
// Try to create a new base URI from the host with the site's URI scheme. Name should be a valid
// authority, i.e. have a port number if it differs from the URI scheme's default port number.
Uri.TryCreate(siteDefinition.SiteUrl.Scheme + "://" + host.Name, UriKind.Absolute, out basetUri);
}
var absoluteUri = new Uri(basetUri, relativeUri);
return absoluteUri.AbsoluteUri;
}
}
}
return result;
}

I also ran into this issue when writing a scheduled job running on a multilanguage, multihostname site. In order to get an absolute url with the right hostname in a HttpContext-less situation I had to combine Henrik's code with Dejan's code from here: https://www.dcaric.com/blog/episerver-how-to-get-external-page-url.
This is what I came up with:
public static string ExternalUrl(this ContentReference contentLink, CultureInfo language)
{
var urlString = UrlResolver.Current.GetUrl(contentLink, language.Name, new VirtualPathArguments { ForceCanonical = true });
var uri = new Uri(urlString, UriKind.RelativeOrAbsolute);
if (uri.IsAbsoluteUri) return urlString;
string externalUrl = HttpContext.Current == null
? UriSupport.AbsoluteUrlBySettings(urlString)
: HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) + urlString;
return externalUrl;
}

I've manage to write something base on the Johan & Henry answers:
public static string GetExternalAbsoluteFriendlyUrl(
this ContentReference reference,
string language)
{
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
var friendlyUrl = urlResolver.GetUrl(reference, language);
if (string.IsNullOrEmpty(friendlyUrl))
return friendlyUrl;
var uri = new Uri(friendlyUrl, UriKind.RelativeOrAbsolute);
if (uri.IsAbsoluteUri)
return friendlyUrl;
if (HttpContext.Current != null)
return new Uri(HttpContext.Current.Request.Url, uri).ToString();
var siteDefinitionResolver =
ServiceLocator.Current.GetInstance<SiteDefinitionResolver>();
var siteDefinition =
siteDefinitionResolver.GetDefinitionForContent(reference, true, true);
return new Uri(siteDefinition.SiteUrl, friendlyUrl).ToString();
}

Related

How to update value of a "usercert" property in LDAP AD

I have a requirement where I need to update the value saved in the property ("usercert") of a computer present in active directory.
// Retrieving properties value from AD
DirectoryEntry entry = new DirectoryEntry(LDAPPath, LDAPUser, DecryptPwd(LDAPPwd, LDAPKey));
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = string.Format("(&(objectCategory=computer)(Name=" + MachineName + "))");
result = searcher.FindOne();
byte[] text= (byte[])result.GetDirectoryEntry().Properties["usercert"].Value;
// Updateing new value to AD string updatedText= "New Text";
if (result.GetDirectoryEntry().Properties["usercert"] != null &&
result.GetDirectoryEntry().Properties["usercert"].Value != null)
{
byte[] updatedTextByte = Encoding.ASCII.GetBytes(updatedText);
result.GetDirectoryEntry().InvokeSet("usercert", updatedPassByte);
//(result.GetDirectoryEntry().Properties["usercert"]).Value = Encoding.ASCII.GetBytes(updatedText);
//result.GetDirectoryEntry().Properties["usercert"].Add(Encoding.ASCII.GetBytes(updatedText));
//result.GetDirectoryEntry().Properties["usercert"][0] = Encoding.ASCII.GetBytes(updatedText);
result.GetDirectoryEntry().CommitChanges();
}
I tried with all of the above commented code but nothing works for me. Can you please help me to solve this issue.
Calling GetDirectoryEntry() creates a new DirectoryEntry object each time you call it, which you can see in the source code here.
So when you do this:
result.GetDirectoryEntry().CommitChanges();
It's creating a brand new DirectoryEntry object and calling CommitChanges() on that. So nothing is changed.
You will need to call GetDirectoryEntry() only once and make changes to that object. For example:
var resultDe = result.GetDirectoryEntry();
resultDe.Properties["usercert"]).Value = whatever;
resuleDe.CommitChanges();

Get velocity template from database in Springboot

I have to get velocity templates from database in Spring boot and Spring data project.I have not tried any code yet as I am new to all technologies(Velocity, Spring boot and data) and not finding anything on google. Does anybody tried here to get template from db, please suggest me some links or anything else which i can refer?
Update: I have binding classes in db (in grails) and I have to access process method from java .In db class is ,
class bindingSubject {
def log
def process = { pub,listForMail ->
def mapBinding = [:]
def fund
def perimeters = pub.sub.entities
perimeters.each(){ entity ->
if (fu == null){
if (entity instanceof S)
fu = entity.fu
if (entity instanceof Fund)
fu = fu
}
}mapBinding.entity = fu.name return mapBinding
}
}
and java code written to
-> Load script
mapScriptClass = new HashMap<String, Object>();
if (script != null) {
if (mapScriptClass.get(name) == null) {
GroovyCodeSource groovySource = new GroovyCodeSource(script,name,"");
GroovyClassLoader classLoader = new GroovyClassLoader(this.getClass().getClassLoader());
// Load string as Groovy script class.
Class scriptClass = classLoader.parseClass(groovySource);
try {
Object classInstance = scriptClass.newInstance();
ApplicationContext ctx = (ApplicationContext)ServletContextHolder
.getServletContext().getAttribute(ApplicationAttributes.APPLICATION_CONTEXT);
ctx.getAutowireCapableBeanFactory().autowireBeanProperties(classInstance, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false);
mapScriptClass.put(name, classInstance);
return classInstance;
To call process method from db(in grails this works , how to do it in java?)
Object scriptClass = loadScriptService.getScriptClass("scriptBindingSubject"+templateMail.getId(),
templateMail.getScriptBindingSubject());
if (scriptClass != null) {
try{
bindingSubject = scriptClass.process(pub,subMail);
}
now i am not sure how to call process method from java(to db) to bind properties
Thanks.
As you write, the templates are stored in the database.
So you need to read them (using JDBC or JPA) and depending on how they are stored, you will get a String, char[] or byte[].
You can convert all of them into a java.io.Reader like
CharArrayReader, StringReader,
how this can be done for a byte[] you can see in this tutorial
SimpleTemplateEngine has a method
createTemplate(Reader reader)
that finally creates the template for the reader.
Hope that helps.

XPSDocumentWriter - Printing Specific Pages to Specific Trays

I'm currently working on a printing application. This app has the requirement that certain pages need to come from specific trays on the printer. Here's the guts of what I've got so far:
foreach (var dto in dispensersToPrint)
{
var documents = FilterDocumentSections(DispenserDocumentsToPrint.RetrieveByDispenserId(dto.DispenserId));
var groupedDocs = documents.GroupBy(t => t.DocumentTypeId);
var queueName = Properties.Settings.Default.PrinterName;
var queue = RawPrinterHelper.GetPrintQueue(queueName);
var seq = new FixedDocumentSequence();
var xpsWriter = PrintQueue.CreateXpsDocumentWriter(queue);
foreach (var docGroup in groupedDocs)
{
var printTicket = queue.DefaultPrintTicket.Clone();
var printTray = MapPrintTray((DocumentSectionType)docGroup.Key);
if (!printTray.IsNullOrEmpty())
{
printTicket = RawPrinterHelper.ModifyPrintTicket(printTicket, "psk:JobInputBin", printTray);
}
var fixedDoc = new FixedDocument();
fixedDoc.PrintTicket = printTicket;
foreach (var doc in docGroup)
{
var pageContent = new PageContent();
var fixedPage = new FixedPage();
var localFileName = string.Empty;
var unzippedFileName = string.Empty;
//copy files locally
localFileName = CopyFileToLocalMachine(doc.FileName);
//unzip file
unzippedFileName = EmfPrintingHelper.UnzipEmfFile(localFileName);
var itemToPrint = new PrintableEmfImage
{
DataContext = new EmfImageViewModel { FileName = unzippedFileName }
};
fixedPage.Children.Add(itemToPrint);
pageContent.Child = fixedPage;
fixedDoc.Pages.Add(pageContent);
}
var docRef = new DocumentReference();
docRef.SetDocument(fixedDoc);
seq.References.Add(docRef);
}
xpsWriter.Write(seq);
}
At a real high level:
For each Dispenser (Work Order) i need to print; i first start by grouping by the DocumentType (i.e. Print type A to tray 1)
I then create a new FixedDocumentSequence
For each DocumentType; I then create a fixed document. I then modify the print ticket to look at the appropriate tray.
I then build each individual page for each document type; and add them to the FixedDocument
Once the building of the FixedDocument is complete; I append it to the DocumentSequence.
I then send the FixedDocumentSequence to the xpsWriter.
But for some reason; these settings aren't being honored. I get all the documents printing out of the same tray.
Here are some of my observations so far:
The modifying of the print ticket does work; I've verified this by sending a modified printTicket into the xpsWriter; but this applies the settings to the entire job; which is a no go for me.
When querying my print capabilities; i noticed that i only have JobInputBin. I don't quite think this means this printer doesn't support the functionality; as multi-tray printing works from a similar WindowsForms app (which uses PageSettings.PaperSource)
Any ideas on what I could try next? Has anyone been successful doing something like this before?
I'll start off by saying, I don't have access to a printer with trays, so I am unfortunately not capable of testing this solution. That said, I'll direct your attention to an MSDN forum post, here, where the original poster was in pursuit of the same tray-per-page behavior.
Based on your posted code, you may have already seen some of what's in this post, judging by your posted code having at least some implementation of ModifyPrintTicket().
In the post, there are several different users, each citing a solution for their specific version of the problem. However, the one that seems most relevant in this case is the solution regarding namespaces not being correctly accounted for in ModifyPrintTicket() (as posted by
Jo0815). I say 'most relevant' because the poster speaks of the print tray being disregarded. They (wittersworld) provide an alternate implementation to correct the issue. In the post on MSDN, the link to the complete source is broken, but can be located here.
The gist is, on ModifyPrintTicket(), they add a namespaceUri parameter, then withing changed this:
if (node != null)
{
node.Attributes["name"].Value = newValue;
}
to this:
if (node != null)
{
if (newValue.StartsWith("ns0000"))
{
// add namespace to xml doc
XmlAttribute namespaceAttribute = xmlDoc.CreateAttribute("xmlns:ns0000");
namespaceAttribute.Value = namespaceUri;
xmlDoc.DocumentElement.Attributes.Append(namespaceAttribute);
}
node.Attributes["name"].Value = newValue;
}
allowing the user to specify the printer-specific namespace used.
I hope this is helpful.

Check if List of Users are valid against adfs in C#

I have a requirement to check if the users in my application are active users in active directory.
I need to send a notification when one of the user alias becomes invalid.
In most of the examples I see validating only one user at a time against ADFS using LDAP which is going to take a very long time large number of users.
Is there any way by which I can validate by sending a list of users and validate, so that it will be faster?
Thanks.
Out the box in ADFS, no.
This sounds like something you should call from your app. using the AD C# API's.
Refer Howto: (Almost) Everything In Active Directory via C#.
Or (in some cases) Everything in Active Directory via C#.NET 3.5 (Using System.DirectoryServices.AccountManagement)
Starting with .Net 3.5 there's System.DirectoryServices.AccountManagement
I'd code something like
public List<string> InvalidUsernames (List<string> usernames)
{
var result = new List<string>();
var domainName = "OkieDokie";
var ldapContext = new PrincipalContext(ContextType.Domain, domainName);
foreach (var username in usernames)
{
var user = UserPrincipal.FindByIdentity(ldapContext, username);
if (user == null) //null means it couldn't be found
{
result.Add(username);
}
}
return result;
}
But it all depends on what you consider active/invalid. In the if you could check for the user.AccountExpirationDate (?date) or user.Enabled (?bool).
Or if you do have a common group for all of them, you could replace the previous foreach and use:
var usersGroup = UsernamesInGroup("theONEgroup");
foreach (var username in usernames)
{
var user = UserPrincipal.FindByIdentity(ldapContext, username);
if (user == null) //null means it couldn't be found
{
result.Add(username);
}
}
public List<string> UsernamesInGroup(string groupName)
{
GroupPrincipal grupo = GroupPrincipal.FindByIdentity(MainOU, groupName);
return UsernamesInGroup(group);
}
public List<string> UsernamesInGroup(GroupPrincipal gp)
{
List<string> userNames = new List<string>();
var principalsInGroup = gp.GetMembers(true);
foreach (Principal principal in principalsInGroup)
{
if (principal.StructuralObjectClass == "user")
{
userNames.Add(principal.SamAccountName);
}
}
return userNames;
}

Invalid Credentials with Bing Maps on WP7, WPF, VS2010

I've got a key from http://www.bingmapsportal.com and I've added the following code to my project, as demonstrated all over the web.
In the .xaml file:
my:Map Height="320" HorizontalAlignment="Stretch" Name="map1" VerticalAlignment="Top" CredentialsProvider="fa0bb238-62bb-41b9-a1e6-459a5e9564a6"/>
(Key has been slightly edited to avoid abuse)
In the .xaml.cs file:
map1.CredentialsProvider = new ApplicationIdCredentialsProvider("fa0bb238-62bb-41b9-a1e6-459a5e9564a6");
GeocodeRequest gReq = new GeocodeRequest();
GeocodeServiceClient gSrvc = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
gReq.Credentials = new Credentials();
gReq.Credentials.ApplicationId = "fa0bb238-62bb-41b9-a1e6-459a5e9564a6".ToUpper();
gReq.Query = address;
FilterBase[] filters = new FilterBase[2];
filters[0] = new ConfidenceFilter() { MinimumConfidence = Confidence.High };
GeocodeOptions gOpt = new GeocodeOptions();
gOpt.Filters = filters;
gReq.Options = gOpt;
gSrvc.GeocodeCompleted += new EventHandler<GeocodeCompletedEventArgs>(gSrvc_GeocodeCompleted);
gSrvc.GeocodeAsync(gReq);
But I can't get it to work, I'm getting an invalid credentials message on the map itself, and an Invalid Credentials exception with the server's response on the GeocodeRequest.
I've visited around 20 forum topics (including WP7 Bing Maps 'Invalid Credentials' Error) and I seem to have done everything they're talking about or have posted as a solution.
Any other ideas?
Here is how i initialize a GeocodeRequest in WP7:
GeocodeService.GeocodeRequest request = new GeocodeService.GeocodeRequest
{
Culture = CultureInfo.CurrentUICulture.ToString(),
Credentials = new GeocodeService.Credentials { ApplicationId = applicationId },
UserProfile = new GeocodeService.UserProfile { DeviceType = GeocodeService.DeviceType.Mobile },
Options = new GeocodeService.GeocodeOptions { Count = 1 },
Query = address,
};
As you can see i don't do ToUpper() on the ApplicationId string, but i'm setting the UserProfile (and also the Culture) property. Perhaps the UserProfile.DeviceType = Mobile setting must correspond to the type of your Bing Map API key, which is certainly Mobile, too.
Maybe this is somehow helpful.

Resources