Rename language after item is created - sitecore

I'm using sitecore 6.5 with two languages installed, en (default) and fr-CA. There are items in the tree with content in both en and fr-CA.
The problem is that the French url has 'fr-CA' in it and we want that to be 'fr', for example:
http://website.com/fr/page.aspx instead of http://website.com/fr-CA/page.aspx
I tried renaming the language from 'fr-CA' to 'fr' and that fixed the url but the content still points to the old language 'fr-CA', so the item shows three languages: en, fr and fr-CA. It's not recognizing the name change.
Any suggestions are much appreciated.
Thanks,
Tarek

The problem is you have created fr-CA versions of your items which cannot be fixed by renaming the language .. you can now make a fr version but, like you are seeing, this means there are now 3 possible versions.
One suggestion is to leave the languages in Sitecore alone and alter how links are served and processed instead.
You would probably need to look at adding your own method into the httpRequestBegin pipeline in Sitecore. This would follow the LanguageResolver entry. You can then parse the RawUrl and set Sitecore.Context.Langauge' to French if the first element in it matched/fr/`.
Extremely quick & dirty example:
public class MyLanguageResolver : HttpRequestProcessor
{
public override void Process(HttpRequestArgs args)
{
string languageText = WebUtil.ExtractLanguageName(args.Context.Request.RawUrl);
if(languageText == "fr")
{
Sitecore.Context.Language = LanguageManager.GetLanguage("fr-CA");
}
}
}
You would probably also have to override the LinkProvider in the <linkManager> section of the web.config to format your URLs when they are resolved by Sitecore.
Another extremely quick & dirty example:
public class MyLinkProvider : LinkProvider
{
public override string GetItemUrl(Sitecore.Data.Items.Item item, UrlOptions options)
{
var url = base.GetItemUrl(item, options);
url = url.Replace("/fr-CA/", "/fr/");
return url;
}
}
Another way (slightly more long-winded as it will need to be executed via a script) is to copy the data from the fr-CA version to the fr version and then delete the fr-CA version of each item.
Rough helper method that encompasses what you're trying to do
private void CopyLanguage(ID id, Language sourceLanguage, Language destinationLanguage)
{
var master = Database.GetDatabase("master");
var sourceLanguageItem = master.GetItem(id, sourceLanguage);
var destinationLanguageItem = master.GetItem(id, destinationLanguage);
using (new SecurityDisabler())
{
destinationLanguageItem.Editing.BeginEdit();
//for each field in source, create in destination if it does not exist
foreach (Field sf in sourceLanguageItem.Fields)
{
if (sf.Name.Contains("_")) continue;
destinationLanguageItem.Fields[sf.Name].Value = sf.Value;
}
destinationLanguageItem.Editing.AcceptChanges();
////Remove the source language version
ItemManager.RemoveVersions(sourceLanguageItem,sourceLanguage, SecurityCheck.Disable);
}
}
Another way to update the languages on your content items is:
Export the fr-CA language to a .xml file (Using the Control Panel)
In the .xml file replace all and tags with the and
Rename fr-CA language in the master database to the fr
Import language from the .xml file
Run Clean Up Databases task (from the Control Panel)
Also you can create a sql script that will change fr-CA language with the fr for all records in the UnversionedFields and VersionedFields tables.
If you need any more information or examples please let me know. :)

I had a similar requirement to rename a language while retaining the content. I decided to migrate content from one language to another by using Unicorn:
1: Create a predicate telling Unicorn to track all of your content. In my case:
<include name="site content" database="master" path="/sitecore/content/mySite" />
Reserialize the content, writing it to disk as YML files
Using a tool that can perform a find & replace in multiple files at once, such as Notepad++, replace all instances of "Language: fr-CA" with "Language: fr" in your yml files.
Run a Unicorn Sync
You will find that all of your content is now associated with the "fr" language instead of "fr-CA".

Related

Direct link to language in OpenCart

I have a website(built with OpenCart) with multiple languages, e.g. English, German, French.
Users can change language using default functionality of the OpenCart - clicking on language icons on top.
Is it possible to send users automaticaly (so they don't have to click on the flag) from :
Germany to German version of the website
France to French version of the website
(English language is default)
Is there an URL I can use for these languages if the default page is for example http://mystore.com ?
(I noticed that when I click on the language icon the URL is not changing - it's the same for all languages)
Nowadays opencart doesn't support this function, but in the past , older versions of Opencart did have this function.
If you want to include this function in your website you will have to do the following:
Edit this file:
catalog/controller/module/language.php
find this:
class ControllerModuleLanguage extends Controller {
protected function index() {
if (isset($this->request->post['language_code'])) {
before the "if", you will have to include the following :
if (isset($this->request->get['lang'])) {
$this->session->data['language'] = $this->request->get['lang'];
if (isset($_SERVER['HTTP_REFERER']) && (strpos($_SERVER['HTTP_REFERER'], $this->config->get('config_url')) !== false) ) {
$this->redirect($_SERVER['HTTP_REFERER']);
} else {
$this->redirect($this->url->link('common/home'));
}
} else {
The source
An example of website with this code:
http://incomingtospain.com/madrid&lang=de
http://incomingtospain.com/madrid&lang=ru
This website has 8 idioms and you can access by different url, with this variable "lang" &lang=es &lang=en ... &lang=de &lang=ru
I think language is set in session variable
For the functionality you mentioned will be achive in following way:
Use the HTML5 geolocation to detect the location of user
Research in opencart to set the language function
after all done place your code using VQMOD if you want to do it in proper way
or you can also edit your core opencart files( Not recommended)
if the browser doesn't support geolocation or they refuse to share their location just load the default language.
With OpenCart 2.0, you must work on the file index.php (in your website root) and place this code :
if (isset($request->get['lang']) && array_key_exists($request->get['lang'], $languages)) {
$session->data['language'] = $request->get['lang'];
}
between line 155 and line 157
Line 153 to 154 :
foreach ($query->rows as $result) {
$languages[$result['code']] = $result;
}
(you add here the new code)
Line 157 :
if (isset($session->data['language']) && array_key_exists($session->data['language'], $languages)) {
Line 158 :
$code = $session->data['language'];

Language fallback for media library item in sitecore

I have a number of PDFs in English language. I have web pages in English and German lang.
If in a German Page I want to display PDF of English lang, it is not possible as that German version PDF is not available, so I tried to do fallback for media library item, even then no help.
So can someone please tell me any alternative for this.
NOTE: I don't want to upload english document in German version, as there are other languages available and customers cannot upload those many times in all lang.
I need to upload a document in Only English but display in all other Languages irrespective of that document is there in that lang or not.
It's ok even if I need to make changes through code.
Thanks in advance
Which template are you using to upload your PDF? If you are using /sitecore/templates/System/Media/Unversioned/Pdf then this inherits from /sitecore/templates/System/Media/Unversioned/File and the blob field for this is marked as shared anyway:
Shared fields are shared across language versions, so if you upload an English PDF and link to that same media item from a German item then it will link to the original English PDF.
In Sitecore, when adding a field to a template, there's a checkbox called "shared". What's it for?
There are a couple of ways to do this. You can do it in the code for your rendering, or you can use the language fallback module from the Sitecore marketplace.
To do it in code you would need to create a new MediaProvider. Create a class that inherits from Sitecore.Resources.Media.MediaProvider and override the protected virtual MediaData GetMediaData(MediaUri mediaUri) method.
This method gets the sitecore item for the context language or the language in the Uri. So you can implement the fall back here:
public class MediaProviderWithFallback : Sitecore.Resources.Media.MediaProvider
{
protected override Sitecore.Resources.Media.MediaData GetMediaData(Sitecore.Resources.Media.MediaUri mediaUri)
{
Assert.ArgumentNotNull((object)mediaUri, "mediaUri");
Database database = mediaUri.Database;
if (database == null)
{
return null;
}
string mediaPath = mediaUri.MediaPath;
if (string.IsNullOrEmpty(mediaPath))
{
return null;
}
Language language = mediaUri.Language;
if (language == null)
{
language = Context.Language;
}
Sitecore.Data.Version version = mediaUri.Version;
if (version == null)
{
version = Sitecore.Data.Version.Latest;
}
Sitecore.Data.Items.Item mediaItem = database.GetItem(mediaPath, language, version);
if (mediaItem == null)
{
return (MediaData)null;
}
// Check for language fallback
if (mediaItem.Versions.Count == 0)
{
// Workout your language fallback here from config or sitecore settings etc...
language = Language.Parse("en");
// Try and get the media item in the fallback language
mediaItem = database.GetItem(mediaPath, language, version);
if (mediaItem == null)
{
return null;
}
}
return MediaManager.Config.ConstructMediaDataInstance(mediaItem);
}
}
Please note - this is untested code. You should store your fallback in config or modify the language template in sitecore.
Once you have that class you will need to update your web.config to use your provider over Sitecores. So find this section in the web.config and change the type to be your class and assembley:
<!-- MEDIA PATH -->
<mediaPath defaultProvider="default">
<providers>
<clear />
<add name="default" type="Sitecore.Resources.Media.MediaPathProvider, Sitecore.Kernel" />
</providers>
</mediaPath>
It's best practice to try to identify whether media will need to be versioned ahead of time. If you know media is going to need to be versioned based on language, you should make sure to update in your web.config the following attribute:
<!--By default, Media items are not versionable and the below setting is set to false in the web.config.
If you upload an image in one language, it will persist across all language versions.
If you change this to true, then versioning will apply and you would have to set the media item into all language versions,
or enable fallback, but if enforce version presence is turned on and media template guids are included in EnforceVersionPresenceTemplates,
then you'll have to make sure all language versions at least exist-->
<setting name="Media.UploadAsVersionableByDefault">
<patch:attribute name="value">true</patch:attribute>
</setting>
Alex Shyba's Partial Language Fallback module will successfully work with this. I would recommend making sure not to enforce version presence on any media templates (don't want to force admins to have to create blank language versions). Then they can create english versions and then only create a language version when they need to override it.
You will need, in the case of using partial language fallback, to make sure the enable fallback checkboxes are checked on the media versionable template fields.
I also recommend updating the media provider so that it embeds language into the media url so that caching doesn't come into play. EG: if you create a pdf named Directions.pdf and it loads at www.site.com/media/Directions.pdf, when you switch between languages, it very well could cache it. So you would want to update the media provider to encode the media url with the context language.
You can see a demo here:
https://github.com/Verndale-Corp/Sitecore-Fallback-FullDemo
public class CustomMediaProvider : MediaProvider
{
public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
{
Assert.ArgumentNotNull((object)item, "item");
Assert.ArgumentNotNull((object)options, "options");
string result = base.GetMediaUrl(item, options);
// Added by Verndale, check if language should be embedded
UrlOptions urlOptions = UrlOptions.DefaultOptions;
urlOptions = LanguageHelper.CheckOverrideLanguageEmbedding(urlOptions);
if (urlOptions.LanguageEmbedding == LanguageEmbedding.Always && options.UseItemPath)
{
result = "/" + Sitecore.Context.Language.Name.ToLowerInvariant() + Sitecore.StringUtil.EnsurePrefix('/', result);
}
return result;
}
public static UrlOptions CheckOverrideLanguageEmbedding(UrlOptions urlOptions)
{
var thisSite = Sitecore.Context.Site;
if (urlOptions.Site != null)
thisSite = urlOptions.Site;
if (!String.IsNullOrEmpty(thisSite.SiteInfo.Properties["languageEmbedding"]))
{
if (thisSite.SiteInfo.Properties["languageEmbedding"].ToLower() == "never")
urlOptions.LanguageEmbedding = LanguageEmbedding.Never;
else if (thisSite.SiteInfo.Properties["languageEmbedding"].ToLower() == "always")
urlOptions.LanguageEmbedding = LanguageEmbedding.Always;
else if (thisSite.SiteInfo.Properties["languageEmbedding"].ToLower() == "asneeded")
urlOptions.LanguageEmbedding = LanguageEmbedding.AsNeeded;
}
return urlOptions;
}
}

Sitecore Multisite Manager and 'source' field in template builder

Is there any way to parametise the Datasource for the 'source' field in the Template Builder?
We have a multisite setup. As part of this it would save a lot of time and irritation if we could point our Droptrees and Treelists point at the appropriate locations rather than common parents.
For instance:
Content
--Site1
--Data
--Site2
--Data
Instead of having to point our site at the root Content folder I want to point it at the individual data folders, so I want to do something like:
DataSource=/sitecore/content/$sitename/Data
I can't find any articles on this. Is it something that's possible?
Not by default, but you can use this technique to code your datasources:
http://newguid.net/sitecore/2013/coded-field-datasources-in-sitecore/
You could possibly use relative paths if it fits with the rest of your site structure. It could be as simple as:
./Data
But if the fields are on random items all over the tree, that might not be helpul.
Otherwise try looking at:
How to use sitecore query in datasource location? (dynamic datasouce)
You might want to look at using a Querable Datasource Location and plugging into the getRenderingDatasource pipeline.
It's really going to depend on your use cases. The thing I like about this solution is there is no need to create a whole bunch of controls which effectively do he same thing as the default Sitecore ones, and you don't have to individually code up each datasource you require - just set the query you need to get the data. You can also just set the datasource query in the __standard values for the templates.
This is very similar to Holger's suggestion, I just think this code is neater :)
Since Sitecore 7 requires VS 2012 and our company isn't going to upgrade any time soon I was forced to find a Sitecore 6 solution to this.
Drawing on this article and this one I came up with this solution.
public class SCWTreeList : TreeList
{
protected override void OnLoad(EventArgs e)
{
if (!String.IsNullOrEmpty(Source))
this.Source = SourceQuery.Resolve(SContext.ContentDatabase.Items[ItemID], Source);
base.OnLoad(e);
}
}
This creates a custom TreeList control and passes it's Source field through to a class to handle it. All that class needs to do is resolve anything you have in the Source field into a sitecore query path which can then be reassigned to the source field. This will then go on to be handled by Sitecore's own query engine.
So for our multi-site solution it enabled paths such as this:
{A588F1CE-3BB7-46FA-AFF1-3918E8925E09}/$sitename
To resolve to paths such as this:
/sitecore/medialibrary/Product Images/Site2
Our controls will then only show items for the correct site.
This is the method that handles resolving the GUIDs and tokens:
public static string Resolve(Item item, string query)
{
// Resolve tokens
if (query.Contains("$"))
{
MatchCollection matches = Regex.Matches(query, "\\$[a-z]+");
foreach (Match match in matches)
query = query.Replace(match.Value, ResolveToken(item, match.Value));
}
// Resolve GUIDs.
MatchCollection guidMatches = Regex.Matches(query, "^{[a-zA-Z0-9-]+}");
foreach (Match match in guidMatches)
{
Guid guid = Guid.Parse(match.Value);
Item queryItem = SContext.ContentDatabase.GetItem(new ID(guid));
if (item != null)
query = query.Replace(match.Value, queryItem.Paths.FullPath);
}
return query;
}
Token handling below, as you can see it requires that any item using the $siteref token is inside an Site Folder item that we created. That allows us to use a field which contains the name that all of our multi-site content folders must follow - Site Reference. As long at that naming convention is obeyed it allows us to reference folders within the media library or any other shared content within Sitecore.
static string ResolveToken(Item root, string token)
{
switch (token)
{
case "$siteref":
string sRef = string.Empty;
Item siteFolder = root.Axes.GetAncestors().First(x => x.TemplateID.Guid == TemplateKeys.CMS.SiteFolder);
if (siteFolder != null)
sRef = siteFolder.Fields["Site Reference"].Value;
return sRef;
}
throw new Exception("Token '" + token + "' is not recognised. Please disable wishful thinking and try again.");
}
So far this works for TreeLists, DropTrees and DropLists. It would be nice to get it working with DropLinks but this method does not seem to work.
This feels like scratching the surface, I'm sure there's a lot more you could do with this approach.

Sitecore Workbox - Modify "Open" to open up the content editor item in a new browser tab

In the Workbox we have “Open” and “Preview” functionality.
Is it possible to modify the “Open” functionality to open the content editor item in a new browser tab, rather than displaying the “Content Editor” in a pop-up?
That's possible:
First, find out what code is used for the workbox. This can be done by opening the Workbox.xml (located in webroot/sitecore/shell/Applications/Workbox) file.
You'll see something like
<CodeBeside Type="Sitecore.Shell.Applications.Workbox.WorkboxForm,Sitecore.Client"/>
By using Reflector on the WorkboxForm class I can see that the following happens when you click 'Open' in workbox:
webControl["Click"] = string.Concat(new object[] { "Open(\"", item.ID, "\", \"", item.Language, "\", \"", item.Version, "\")" });
So you'll need to create your own version of a WorkboxForm, inheriting Sitecore's WorkboxForm class and override the Open method, something like so:
protected new void Open(string id, string language, string version)
{
// Your code goes here
}
In Workbox.xml, change the CodeBeside to point to your new class.
For more information on custom functionality in the Workbox, I can recommend reading through this article, which has a lot of detail in it, also on other methods in the Workbox.
There's also another useful question on StackOverflow already. The comment on the accepted answer points out you can put your Workbox.xml file into /sitecore/shell/override.
Please note that this is based on Sitecore 6.5 update 5, it might differ a bit in other versions.
In the Appconfig/Commands.config you can find commands and classes related to them.
I have reflected some commands like preview, open and understood what's going on internally.
e.g. name="item:open" type="Sitecore.Shell.Framework.Commands.ContentEditor.OpenItem,Sitecore.Kernel"
And I have overridded the "Open" mwthod in the workbox as follows to open the content editor item in the new tab,
...
UrlString urlString = new UrlString("/sitecore/shell/Applications/Content%20Editor");
urlString.Append("id", id);
urlString.Append("vs", version);
urlString.Append("ro", sectionId);
urlString.Append("la", language);
urlString.Append("fo", id);
SheerResponse.Eval("window.open('" + (object)urlString + "', '_blank')");
...
It works!!!!!

How to generate media item link with id instead of path in Sitecore

Anyone knows how to generate links in sitecore with ID instead of item path?
If you use GetMediaUrl method from the API, I can get this URL:
/~/media/Images/Archive/content/News and Events/News_and_Events_Level2/20070419162739/iwhiz3.jpg
The problem with this approach is that if someone changes the media item name, removes it somewhere or deletes it, the above link will break.
I notice if I insert a media link from rich text editor, I get the link as below:
/~/media/14BDED00E4D64DFD8F74019AED4D74EB.ashx
The second link is better because it's using the item id, so if the actual media item is renamed, removed, or deleted, all related links will be updated too. On top of that, when Sitecore renders the page, it will actually convert the above link and display the item path so it's readable.
I'm using Sitecore 6.5 and currently doing content migration so I need to make sure all internal links are updated properly.
May I know if there is a method to generate the second link by using sitecore API?
Thanks!
The GetMediaItemUrl extension method seems to give you what you want.
public static class ItemExtensions
{
public static string GetMediaItemUrl(this Item item)
{
var mediaUrlOptions = new MediaUrlOptions() { UseItemPath = false, AbsolutePath = true };
return Sitecore.Resources.Media.MediaManager.GetMediaUrl(item, mediaUrlOptions);
}
}
[TestFixture]
public class when_using_items_extensions
{
[Test]
public void a_url_based_on_media_item_id_can_be_generated()
{
// Arrange
Database db = global::Sitecore.Configuration.Factory.GetDatabase("master");
Item item = db.GetItem("/sitecore/media library/Images/MyImage");
// Act
var mediaUrl = item.GetMediaItemUrl();
// Assert
Assert.That(mediaUrl, Is.EqualTo("/~/media/17A1341ABEEC46788F2159843DCEAB03.ashx"));
}
}
These are called dynamic links and you can normally generate them using the LinkManager e.g:
Sitecore.Links.LinkManager.GetDynamicUrl(item)
.. but I'm not sure of the method to do this with Media links (there probably is one but I cant seem to find it and its not on MediaManager) but the basic syntax is:
"/~/media/" + item.ID.ToShortID() + ".ashx"
If you always want to use ID's instead of paths, you can change this setting in webconfig to false (like this):
<setting name="Media.UseItemPaths" value="false"/>`
Here is what the webconfig describes about it:
MEDIA - USE ITEM PATHS FOR URLS
This setting controls if item paths are used for constructing media URLs.
If false, short ids will be used.
Default value: true
Then you can use the default implementation (without additional parameters):
Sitecore.Resources.Media.MediaManager.GetMediaUrl(item);
This is what I use:
var imgField = ((Sitecore.Data.Fields.ImageField)currentItem.Fields["Icon"]);
MediaUrlOptions opt = new MediaUrlOptions();
opt.AlwaysIncludeServerUrl = true;
// Absolute Path works as well. So either use AbsolutePath or AlwaysIncludeServerUrl
opt.AbsolutePath = true;
string mediaUrl = MediaManager.GetMediaUrl(imgField.MediaItem, opt);