Limiting the UI language drop-down in the Kentico Pages application - localization

I'm building a multilingual site in Kentico where I want a user to have limited access to the languages that they can create in the Pages application. I can specify which languages the user can edit using the Languages tab in the user properties as shown in the User management page in the Kentico documentation.
The result of this is that if my user tries to view a language that they have not been assigned to and has no page created yet is that they receive a permission error/warning:
You are not allowed to create this culture version.
What I want to do is limit the drop-down list in the Pages application so that I can only select languages that I have permission to change. I can't find any settings that might limit the drop-down to assigned languages only.
Ideally, the user should only be able to see pages that they can edit in the Pages application for my use case as there are in excess of 30 languages.
How can you limit the UI language drop-down to show only languages that the user is allowed to see?

The language drop-down in the pages application is ultimately provided by the LanguageMenu control (which you can find in CMSModules\Content\Controls\LanguageMenu.ascx). You need to make a copy of this and modify the Page_Load method in order to limit the languages base upon the set cultures (if any).
A very quick example is to add the following code before the loop that builds the list items:
var userCultureIDs = new List<Int32>();
var userCulturesSet = false;
if (CurrentUser.Bindings.CollectionNames.Contains("UserCultures") && CurrentUser.Bindings["UserCultures"].Count > 0)
{
foreach (var userCulture in CurrentUser.Bindings["UserCultures"])
{
userCultureIDs.Add((Int32)userCulture["CultureID"]);
userCulturesSet = true;
}
}
and then inside that loop, make the first line:
if (userCulturesSet && !userCultureIDs.Contains(culture.CultureID) && !CurrentUser.CheckPrivilegeLevel(UserPrivilegeLevelEnum.GlobalAdmin)) continue;
This ensures that if you're Global Admin, you get everything and that - if no languages are set - you get the default list.
As is usual with best practice, you're better off making duplicates of these controls before you modify them to make sure that you don't cause any unintended side-effects. Keeping that in mind, be aware of the control chain:
CMSModules\Content\CMSDesk\Default.aspx
CMSModules\Content\Controls\ContentNavigation.aspx
CMSModules\Content\Controls\TreeLanguageMenu.ascx
CMSModules\Content\Controls\LanguageMenu.ascx

Related

How to translate non-page content in Kentico

We recently upgraded our Kentico 8.2 instance to 9.0 and are now focusing on localizing our site into two additional cultures. The textual content of our site is primarily stored in one of three places:
Editable regions
Text fields in custom page types
Custom tables
We're using the Kentico EMS license and would like to leverage the built-in Translation Services app in order to translate content in each of these locations, however it appears that it only supports with content within editable regions. I know Kentico has an input control for translating text boxes but it forces the content editor to create a distinct resource key for it; we simply have so much content I'm concerned this will get out of control for editors.
Additionally, we had considered migrating some of our content into pages but they don't logically fit there, and there are some complex relationships that would cause duplicate content so I'd really prefer to keep the content where it is. The other thought I had was to build either a custom module or custom form control to do some dirty work under the covers but didn't want to reinvent the wheel if there was already a known approach.
Is there a Kentico recommended approach, or workaround, to managing content translations within custom tables and page type fields?
You can use Translation in Kentico to translate page type fields and send them for translation. Check this documentation page for an example. It can be more complex at first, but it should do the trick for you. I have just tested creating a simple translation request for my page type and the resulting XLF file contained all page type fields.
For custom tables I will have to dissapoint you because custom tables in Kentico are not culture specific. If performance is important for you the best way to create translations is to add CultureCode field to your table in order to specify which culture the item is for.
For example you can have table with columns: ItemID, Text, CultureCode
And then the data would look like:
1, Dog, en-US
2, Hund, ge-GE
3, Pes, cs-CZ
If you would want to go even step further I would recommend to create this not as a custom table, but as a Custom class which will sit inside a custom module for which you can create your own interface which would allow editors to easily create all culture versions for the items.
For the custom page types and custom tables, you can change those text fields to localized text fields at the field definition and it will allow you to enter different values per language without a resource key. This is the best route IMHO for page types and custom table translation. Although this still does not allow for "automatic" or built-in translation.
I am not sure what do you mean by content translation in page type fields - there is support for translating page type fields. You can find example on sample site in Kentico installation. (The site is called DancingGoat - see image example for page type field localization in english and spanish language).

Multiple Adobe Analytic tools in Adobe DTM - how do I only have one fire at a time?

I have multiple instances of Adobe Analytics in the same Adobe DTM web property. They all go to different report suites. I'd like to create a page load rule so that the data is only reported to one report suite for any given page. So far the page load rules that I have created are causing the AA data to report to all of the report suites. Does anyone have any idea to get them to only fire to one at a time?
Thanks,
Mike
It is possible to set DTM to suppress the call for an event based rule or direct call rule but currently it is not possible to do it with a page load rule within the DTM built-in interface.
Some things you can do:
1) Combine your Adobe Analytics instances into a single instance. Is there a particular reason you are using multiple instances of Adobe Analytics? I know you mentioned they are for separate report suites, but are they also for separate Adobe Analytics instances altogether? If not, is it because they are completely separate implementations with different variables and logic assigned for them? If the only thing different between them is report suite id, then you should combine them and write some logic to pop relevant report suite id(s) for s.account.
2) Make use of s.abort to suppress the calls. If your code version is H25.3+ or you are using AppMeasurement then you can set s.abort to true to make the next Adobe Analytics request (s.t or s.tl call) be cancelled. Note that this may only be feasible if your instances are all using separate namespaces.. if you are just outputting several instances of code all under the same namespace, you will have issues. Also, if you are doing that, then you may also have issues as far as variables carrying over to other instances. But in any case, basically within your page load rule you'd add a rule condition where criteria is Data:Custom and then you'd write js logic to determine which one(s) you don't want to pop and set [namespace].abort=true; for each of them. Then return true at the end to make sure the condition is always true.
Or, you can set s.abort=true; within the Custom Page Code section for each Adobe Analytics instance you want to suppress in the page load rule. There are various other methods for popping s.abort but hopefully you get the picture. Which one is best just depends on how all your stuff is actually setup.
3) Pop the Adobe Analytics tags as a regular tag instead of a Tool. Remove them as a tool and put them as a Javascript / Third Pary Tag within the page load rule. This includes the core lib code and anything else you may have in the Tool config section. This also means outputting the "trigger" (s.t call). Make a separate one for each one and then you can make conditions for them to pop.
To add to Crayon's answer, you can also choose a custom code implementation in the Library Management section of the tool configuration settings. You will have to paste in the s_code contents and check the box that says "Set report suites using custom code below"
Then, you can use whatever logic you want to set a variable in the code that directs data to the report suites.
if(document.location.href == "http://www.mypage1.com"){
s_account = "rsid_1";
}else{...}
To add to Brett's answer - with rare exception, I let Adobe manage the analytics file. However, you can use the customize page code section within the Adobe Analytics tool configuration to deploy plugins or set the report suite ID dynamically. Here's an example I commonly use:
var suiteid;
var enviro;
var getURL = window.location.href
// Set RSID based on URL
if (getURL.indexOf('myURL.com') != -1)
{
suiteid = 'rsidproduction';
s.linkInternalFilters = 'javascript:,mailto:,tel:,' + window.location.host;
enviro = 'prod';
}
else
{
suiteid = 'rsidstaging';
s.linkInternalFilters = 'javascript:,mailto:,tel:,' + window.location.host;
enviro = 'dev';
}
s.account = suiteid;
This is an easy way to dynamically change the RSID and keep the analytics file managed by Adobe. I also blogged about this here: Managing your RISD Dynamically

Where should I put data like testimonials and addresses in Umbraco?

I am fairly new to Umbraco and I am wondering where I should put pieces of random data.
I have a lot of experience with Sitecore, used it for several years, I am certified etc etc. But now I have to build something with Umbraco. At a first glance Umbraco looks inferior to Sitecore in every way, but I might be wrong about that.
So what I am wondering is, where should I put non-browsable pieces of data that are visible on several places throughout the website? I'd like to create data items in Umbraco for things like Testimonials, Offices? I'd like to have a centralized place in Umbraco where they can be maintained and reference them from a page node. But the way it looks now is that everything has to be on the page node. Which is sort ok, for your average family webpage.
Could someone please shed some light on this?
You could create another node under the man content and call it site settings and store them there that way all pages under the home page are just visible pages on the front end and all data nodes are in a separate area.
There is one property in umbraco that you can add to document types and name it "umbracoNaviHide" (for alias, the name can be anything). This allows wires in automatically to the .IsVisible() method.
var children = Model.Content.Children.Where(x => x.IsVisible());
I find this situation to be very frequent, think of slideshows. When I make an Umbraco website, under my root node I normalle havea Slideshow document type (that contains slides) and I programmatically traverse those to build the slideshow on the home page for example. This Slideshow document has that "umbracoNaviHide" property and I skip it from my menus either using the .IsVisible() method or by manually skipping specific document types.
var menuItems = Model.Content.Children.Where(x => x.DocumentTypeAlias != "Slideshow" && x.DocumentTypeAlias != "Search");
On the other hand, if you are looking for "labels", you can look at "Dictionnary" items under the "Settings" tab.
To directly answer your questions, I reccomend putting non-browsable pieces of data as children of the relevant browsable content node. (But there are other valid ways to do this, it really is up to you and what's best for your content editors.)
Hope this helps.

How to handle front-end content in order to display menu items based on the current user?

I am using Ruby on Rails 3.2.2 and I would like to "handle" / "generate" the proper front-end content so to follow a "common" way of managing menu items mostly based on the current user "equality". That is, I would like to display different menu items based on
the current browsed page;
the current user that is accessing that page (the page could be related to a user that is not the current user).
Where (for example, in controller, view or model files) and how (for example, stating checks related to the current user in controller, view or model files) I should "state" / "put" those "conditional statements"?
At this time I think (mostly for matters relating to the current browsed page) I could handle this matter directly in controllers by using helper methods...
I tend to put this into the View files with simple or static cases coded into the View file and more complex cases which would require more than just one line of coding into the helpers.
But in order not to bloat the view file, I suggest to use partials to offload blocks of code from the view file.
One of the reasons why I use the views is that I will do certain user based formatting anyways, e.g. if I list all the orders, then the normal user will see certain columns displayed, whereas if the admin logs in, he will see a couple of additional columns. This is typically done with something like
<%if #curreny_user_type == 'admin'%><td> ...... </td><%end%>
Thus before I now start splitting up some of the user based logic into the controller and others into the views, I'ld rather have them always at the same place.

How to link a google apps script in a google site

I'm creating a google site for my company and I'm utilizing google apps scripts to do a little extra on the site. I would really like to link a script to a drop-down menu that I made. However, I can't figure out how to link the script. I know how to link a script just as a google gadget and as a stand alone link, but I would really like to have the script run when I click on an item from my drop-down menu.
For security reasons, Google don't let you put javascript in Google Sites.
They provide Apps Scripts instead, but as they work on an isolated world (on the server rather than the browser), its very tricky and has its ways.
Because its very different to standard page's javascript, you have to rethink your goal in terms of what Apps Scripts lets you do.
Google Apps Scripts lets you build an User Interface (using its yet experimental UI API) that can be visualized as a standalone script in a full page or inserted in a iframe in Sites. This means you won't have a dropdown menu overlaping your site: you need an static space to visualize your script's UI.
There is another more primitive way to "embed" your scripts commands in your site: use links. A link that fires a script, even with your own parameters, only to run de command, but without any UI. You can make a menu with options, each of them fires a script. But I'm not talking about dropdown menu.
About Google Apps Scripts User Interfaces
https://developers.google.com/apps-script/guide_user_interfaces
https://developers.google.com/apps-script/guide_gui_builder
https://developers.google.com/apps-script/service_ui
Not sure what you mean by "link the script", do you have code someplace else? By "link" it sounds like you mean to "Call" the code, with an event handler. I'll show you how to call a function with a ServerHandler triggered by either a GUI ListBox Change event or from a Button Click event.
In Google Apps Scrips (GAS) there are three methods to do GUI.
HTML Service - Much like plain HTML, you could insert HTML form and input tags.
UI Service - Much like java (as far as layout managers), see below.
GUI Builder - I suggest doing it manually first to better understand layout.
In Google Sites you can add most HTML directly without a script. The UI Service and GUI Builder will generate HTML form tags for you, and since there's rarely any reason to insert GUI elements unless you are executing some code you probably want to start with using these.
Here is a Drop-Down list examplewith some changes to show how a handler function can be called from multiple UI elements (which they call Widgets sometimes) and how to use the parameter:
function doGet(e) { // use doGet() & UiApp to make a canvas.
var app = UiApp.createApplication();
var doEvent = app.createServerHandler('doEvent').setId('doEvent');
var myList = app.createListBox().setId('myList').setName('myList');
myList.addItem('one'); // add items, I use single quote strings.
myList.addItem('two').addItem('three') // I know it looks weird.
// Scripts let you do this, by returning self for your convenience.
.addChangeHandler(
app.createServerHandler('doEvent')
);
app.add(myList); // Add element to GUI.
doEvent.addCallbackElement(myList); // Add to Event Handler.
app.add(app.createButton('Click Me').setId('myButton')
.addClickHandler(doEvent));
return app;
} // Simple DropDown by Jason K.
function doEvent(e) { // use split() if isMultipleSelect is true
var app = UiApp.getActiveApplication();
app.add(app.createLabel(
'List Value is ' + e.parameter.myList
+ ' from ' + e.parameter.source));
return app;
}
As far as troubleshooting, remember to add each element with app.add() and return app; at the end of doGet and each handler function.
Handlers execute a function like JavaScript onClick() or onChange() functions, most UI are not useful without handlers. ClientHandler are more efficient but ServerHandler do more, start with ServerHandlers and any simple functions can be converted to ClientHandlers for better performance. You can choose to space out your handlers or cram it all into one line-of-code, really a matter of personal preference however do assign it to a variable if you plan to use it for more than one GUI object. You may want to look up the different layout managers to make more fancy looking applications, or just use the GUI Builder. Also there use to be other create functions like app.createServerClickHandler() but I understand those were useless and are now deprecated so ignore any other references you find like that, however we do still use addChangeHandler() and addClickHandler() to the GUI elements themselves.
The setName() seems to be silly, it is only needed to set the parameter name (I hope they change that) so for now I suggest just setting it the same as the element id. I also made the Handler's variable name = its id = the event function name just to illustrate how they are all related.

Resources