Moving AEM Touch UI pages based on path on page properties - javascript

We are given a requirement such that the page should be moved to a location based on the path provided at the page properties.
How to implement that in Touch UI?
In Classic UI we can use edit config and may use listeners and write respective JS code on that.Correct me if I am wrong.

Your question alludes to wanting to use JavaScript to move the page. I've put together an example using the Touch UI dialog. It works, but would require polish to validate user input and prevent string manipulation errors.
In this example, I'm using the dialog-success event that triggers after a dialog is saved. See Touch UI Events. I check to see if the textfield with the CSS selector is populated, and if it is, I post back to the Sling Post Servlet using the #MoveFrom suffix to move the node and its children (the page and the jcr:content, etc...). If that operation is successful, navigate the user to the new page.
In your dialog, add a textfield and give it a unique class name:
<movePage
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldLabel="Move page to:"
class="move-page"/>
Then add this JavaScript to a ClientLib used only in authoring mode such as cq.authoring.editor:
(function ($, $document) {
'use strict';
$document.on("dialog-success", function(e) {
var newPath,
lastSlash,
moveFromSuffix,
newDirectory,
currentPath,
data;
e.stopImmediatePropagation();
newPath = $('.move-page').val();
if (newPath) {
lastSlash = newPath.lastIndexOf('/');
moveFromSuffix = newPath.substring(lastSlash + 1) + Granite.Sling.MOVE_SUFFIX;
newDirectory = newPath.substring(0, lastSlash);
currentPath = Granite.HTTP.getPath().replace('/editor.html', '');
data = {};
data[moveFromSuffix] = currentPath;
$.post(newDirectory, data)
.done(function(){
window.location = '/editor.html' + newPath + '.html';
})
.fail(function(){
$(window).adaptTo('foundation-ui').alert('Alert', 'Could not move page');
});
}
});
})($, $(document));
However, another option would be to do it server side by implementing a custom Sling Post Processor.

Related

Using Ezoic (or Adsense) with VueJS v2

I have a new version of my site which uses VueJS v2 (the previous one didn't). The main code is placed inside <div id="app"></div> and Vue is initiated. The issue is that I partner with an advertising company called Ezoic that injects ads through using AI onto the page, but these ads aren't displaying properly. I believe it is related to these errors:
https://pagead2.googlesyndication.com/pagead/show_ads.js
show_ads.js:53 Failed to execute 'write' on 'Document': It isn't
possible to write into a document from an asynchronously-loaded
external script unless it is explicitly opened.
Ezoic works with Google Ad Exchange, so I believe it is the above line that's related to the issue.
I'm wondering, is there any way in which I could make my application compatible with Ezoic/Adsense? I thought about having Vue on the page only where needed, rather than the entire page (<div id="app"></div> goes from the start of body to the end of body), but this would mean I need multiple Vue instances running as I have components at the top (search box) and also throughout the pages.
I have no access to the code that Ezoic inject onto the page as this is done on their end (my site uses their DNS and they modify the response before sending to the visitor, to include the ad code). Ezoic team is also having a look into this issue presently but any information I could pass along could be helpful!
At the request of Dynamic Remo I am submitting an answer for Ezoic's standalone implementation that is compatible with Vue.
I will however preface this with they absolutely hate it when you install it this way and essentially refuse to support it. - with that said you have way more control over placement
The Solution:
First add the following script tag somewhere outside or your custom defined root element.
<script type="text/javascript" src="//www.ezojs.com/ezoic/sa.min.js" async=""></script>
In your vue component you will need to create all your placeholder elements with a id of "ezoic-pub-ad-placeholder-xx" where xx is replaced by the actual id found in your enzoic dashboard. Dynamic creation does work as long as there is a matching id in ezoic so you have two options:
Dynamic:
<div v-for="placeholderId in ezoicArray" :id="'ezoic-pub-ad-placeholder-' + placeholderId" class="ezoic"></div>
Standard:
<div id="ezoic-pub-ad-placeholder-102" class="ezoic"></div>
To actually display ads in your placeholders you can use this function I wrote. Just call it when the component is mounted.
ezoic(placeholderList) {
if (window.ezstandalone !== undefined) {
window.ezoicPlaceholderArray = window.ezoicPlaceholderArray || [];
// Add Placeholders to Array
placeholderList.forEach((placeholder) => {
this.addPlaceholderOnce(window.ezoicPlaceholderArray, placeholder);
});
// Enable Once - Refresh on Change
window.ezoicRefreshed = false;
window.ezoicEnabled = window.ezoicEnabled || false;
// Next Tick Ensures All Enzoic Blocks Are Loaded
this.$nextTick(() => {
// On First Load We Must Enable
if (!window.ezoicEnabled) {
window.ezstandalone.define(window.ezoicPlaceholderArray);
window.ezoicPlaceholderArray = null;
console.log('ezoic defined and array reset');
window.ezstandalone.enable();
console.log('ezoic enabled');
window.ezstandalone.display();
console.log('ezoic displayed');
window.ezoicEnabled = true;
window.ezoicRefreshed = true;
}
// On Refresh We Have To Destroy & Refresh
if (!window.ezoicRefreshed) {
window.ezstandalone.destroy();
console.log('ezoic destroyed');
window.ezstandalone.define(window.ezoicPlaceholderArray);
window.ezoicPlaceholderArray = null;
console.log('ezoic refresh defined and array reset');
window.ezstandalone.refresh();
console.log('ezoic refreshed');
window.ezoicRefreshed = true;
}
});
} else {
console.log('Error: Missing Ezoic Standalone');
}
},
addPlaceholderOnce(array, placeholder) {
if (!array.includes(placeholder)) {
array.push(parseInt(placeholder));
console.log('ezoic-pub-ad-placeholder-' + placeholder + ' - Created');
}
},
If you need to change your placeholder setup for different reasons as I do simply add window.ezoicRefreshed = false; to the appropriate lifecycle hook. In my case I have it in beforeUnmount because each route gets a custom placeholder list.
Hopefully this helps!

Disable view picker DIV is not accessible in MS CRM

I am trying to disable view picker on account lookup in 2016.
The code works fine I mean I am not getting any error but the code not able to find the particular DIV of "parentaccountid".
Where as I can see it in Elements:
But it's returns {null} when the code try to get the elements of DIV - document.getElementByid("parentaccountid");
My code runs on load of opportunity page.
Even at this step I can see the particular DIV element- id: parentaccountid
Where as I still get {null} value:
Where as in 2013 I have seen it working pretty fine.
Sorry I don't have data to share here but it works fine.
Here is the code below:
function DisablePick()
{
VULoader();
//call this function from OnLoad handler
function VULoader(){
var myLookup1;
alert("Hello..I am Here");
var fetch = "<fetch mapping='logical'>"
+"<entity name='account'>"
+"<attribute name='name'/>"
+"<filter type='and'>"
+"<condition attribute='name' operator='eq' value='Blue Yonder Airlines (sample)' />"
+"</filter>"
+"</link-entity>"
+"</entity>"
+"</fetch>";
myLookup1 = new XrmLookupField("parentaccountid");
myLookup1.AddLockedView(
//sViewId
myLookup1.NewGuid() ,
//sEntityName
"account",
//sViewDisplayName
"My Locked Custom View",
//sFilterXml
fetch,
//sFilterLayout
layout(1, "name", "accountid")
.column("name", 200)
.toString()
);
}
function XrmLookupField(sId) {
var xlf = this;
//control instance
xlf.Ctl = Xrm.Page.getControl(sId);
//dom instance
xlf.Dom = document.getElementById(sId);
//jquery instance
xlf.$ = $(xlf.Dom);
/* 2013 addition --- Inline Control instance --- */
xlf.$i = $("#" + sId + "_i");
//use that to disable the view picker
xlf.DisableViewPicker = function () {
/* 2013 addition --- The attribute capitalization changed */
xlf.SetParameter("disableviewpicker", "1");
}
//use that to enable the view picker
xlf.EnableViewPicker = function () {
/* 2013 addition --- The attribute capitalization changed */
xlf.SetParameter("disableviewpicker", "0");
}
//set undocumented attributes
xlf.SetParameter = function (sName, vValue) {
xlf.$.attr(sName, vValue);
/* 2013 addition --- Also change the inline contorl value */
xlf.$i.attr(sName, vValue);
}
//add locked view
xlf.AddLockedView = function (sViewId, sEntityName, sViewDisplayName, sFilterXml, sFilterLayout) {
//first enable the view picker
xlf.EnableViewPicker();
//add the custom view (last parameter set the view as default)
xlf.Ctl.addCustomView(sViewId, sEntityName, sViewDisplayName, sFilterXml, sFilterLayout, true);
//lock the view picker
xlf.DisableViewPicker();
}
//create new guid
xlf.NewGuid = function () {
var d = new Date().getTime();
var guid = '{xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx}'.replace(/[xy]/g, function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16);
});
return guid;
}
}
}
Few points to mention:
I am calling DisablePicker method on load of an opportunity page.
Yes, I have already checked whether any duplicate id is there or not but I didn't find any.
I tried to run this method on load of Account field as well but nope no response.
If I change the property disableviewpicker value from 0 to 1 in DIV itself manually from browser after page gets loaded & open the lookup then it will take the effect of the changed value.
Really I am getting no clue why it's behaving this manner, I just need to know where exactly I am going wrong or it's a product bug but I don't think it is. As its a very basic behavior.
PS: For my non MS CRM friends I cannot change the DIV or anything except my JavaScript code.
Your form code executes in a frame that is separate from the actual CRM form; it runs in the ClientApiWrapper.aspx page. Therefore, to access the form element you want to modify from your form script, you form code should do parent.document.getElementById instead of document.getElementById.
When you use your browser's dev tools, the default frame that the console executes against is also not the frame that has your form elements, and it's also not the frame that has your form's code. The simplest way to execute against the correct frame is to use the function in your dev tools to switch frames. In Firefox, that button looks like this:
It's easiest to set your frame to the ClientApiWrapper.aspx one, as it provides access to both the libraries loaded on your form as well as the CRM client-side API.

Dojo Toolkit create dynamic widget at runtime

I'm pretty new to Dojo and working version 1.10.
I'm looking for a solution to create a widget at runtime, based on data requested from a server.
The application has a tree. If you click on a item in the tree a new tab should be created and a script should be executed to get the data from the server and create the widget. (In most cases its a form, the data from the server describes the types of inputs). The location of the script is stored in the tree node.
At the moment in my application I can click on the tree node -> a contentpane is created and added as a tab. In the contentpane the href-attribute is set to static .html-site like this:
dynWidget.html?scriptlocation=abc
In the .html file I tried to read the parameters from the URL via the location attribute. This, of course, does not work, because the location attribute contains the URL of the complete site not the URL attached in the content pane.
Is there a possibility to get the href-attribute from the contentpane?
Is there a completely diffrent solution for this problem?
Any help appreciated!
Thank you very much!
Your question could do with more detail, but maybe you need:
var node = dojo.byId("contentpane");
var value = domAttr.get(node, "href-attribute");
Save data about tree items in dedicated store
Use Object Pool Pattern for saving opened tabs list
Use require and Widget.addChild()
// Custom name for click event handler. Replace to own.
onItemClick: function(item) {
// Getting data from store. Any item have full path to widget.
var dataItem = store.get(item.id),
requirePath = dataItem.requirePath; // Full path to widget
// Load widget via require function
require([ requirePath ], function(LoadedWidget){
var newTab = new ContentPane({}),
newWidget = new LoadedWidget({});
// Append tab in the global tabs store
desktop.tabs.add(newTab);
// Place new widget to tab
newTab.addChild(newWidget);
// Run new widget:
newWidget.startup();
});
}
Also example for creating widget in runtime:
define([
"dojo/dom-construct",
"dojo/_base/declare",
"dijit/_WidgetBase"
], function(
domConstruct,
declare,
_WidgetBase
){
return declare(
"My.Widget.Name",
_WidgetBase,
{
buildRendering: function(){
this.inherited(arguments); // Call parent method
this.domNode = domConstruct.create("div", {
// Detail properties of DOM element
});
this._button = domConstruct.create("button", {
label: "OKAY"
});
},
_okBtnHandler: function(event) {
// Handler for click by OKAY button
console.log(this); // Instance of widget, not button DOM node
},
startup: function(){
this.inherited(arguments);
// Connect handlers to widget dom elements
// Also, "this" for handler now is My.Widget.Name instance, not DOM Button element
this.connect(this._button, "onClick", "_okBtnHandler");
}
}
);
});

What is the best practice for maintain a menu after a page reload? I have this custom code which feels wrong

I have a custom Menu which loads a new MVC View for each click as I want.
I load the new View by setting window.location.href. To make it work I have to set the baseURL (the name of the website) each time. To Store the state of the menu I use URL's querystring.
My concerns is in the use of:
'/WebConsole53/' // hardcode baseurl i have to apply each time manually
Setting window.location.href to load the new View from JavaScript // Is this the best way or should I use some URL/Html helpers instead?
I store the state of the selected menuItem in the querystring ("menu") // Is it more common to store that kind in Session/Cookie?
Any thoughts, corrections and suggestions would be much appriciated - thanks.
_Layout.cshtml
var controller = $self.data('webconsole-controller');
var action = $self.data('webconsole-action');
var menu = "?menu=" + $self.attr('id');
var relUrl = controller + "/" + action + menu;
var url = urlHelper.getUrl(relUrl);
window.location.href = url;
UrlHelper.js
var urlHelper = function () {
var getBaseUrl = '/WebConsole53/',
buildUrl = function(relUrl) {
return getBaseUrl + relUrl;
};
var getUrl = function(relUrl) { // relUrl format: 'controller/action'
return buildUrl(relUrl);
};
return {
getUrl: getUrl
};
}();
I Use MVC 5.
You can save this problem using Route. Through the route you know exactly where you are located in you application.
_Layout.cshtml is definetely not the place to have this javascript. Maybe you are missing some MVC concepts, I would recommend you to read a bit more about routes and routelinks
I hope this helps you a bit: RouteLinks in MVC
'/WebConsole53/' // hardcode baseurl I have to apply each time manually
sometimes you need to access your root from javascript where you don't have access to server-side code (eg #Html). While refactoring may be the best option you can get around this by storing the baseurl once, using server-side code, eg in _layout.cshtml:
<head>
<script type="text/javascript">
var basePath = '#Url.Content("~")'; // includes trailing /
</script>
... load other scripts after the above ...
</head>
you can then reference this everywhere and it will always be valid even if you move the base / migrate to test/live.
Setting window.location.href to load the new View from JavaScript // Is this the best way or should I use some URL/Html helpers instead?
Depends on your requirements - you could use $.ajax (or shortcuts $.get or $.load) to load PartialViews into specific areas on your page. There's plenty of examples on SO for this and the jquery api help.
Or just use <a> anchors or #Html.ActionLink as already suggested. Without needing menu= (see next) you don't need to control all your links.
I store the state of the selected menuItem in the querystring ("menu") // Is it more common to store that kind in Session/Cookie?
If you change the page, then you could query the current url to find which menu item points to it and highlight that one (ie set the menu dynamically rather than store it).
This would also cover the case where you user enters the url directly without the menu= part ... or where your forget to add this... not that that would happen :)
Additional: You can specify which layout to use in your view by specifying the Layout at the top of the view, eg:
#{
Layout = "~/Views/Shared/AltLayout.cshtml";
}
(which is also one of the options when you right click Views and Add View in visual studio)
Without this, MVC uses a configuration-by-convention and looks at Views/_ViewStart.cshtml which specifies the default _layout.cshtml.
If you don't want a layout at all, then just return PartialView(); instead

Persist js user controls state

My RIA application has a lot js UI controls (almost all of their is jQuery UI parts like datepicker, dialog, jqgrid). So then user works with some controls on one page and then go to another page and then clicks back all page components has initial state (textboxes are empty, grids are empty and so on). So how can I persist UI controls state and then restoring it between pages? It seems I need some like JS serialization/deserialization methods storing serialized data in user server session. How can I do it with minimal costs? How do you guys it in your projects? Any thougs, links, posts will be very appreciated. Thank you guys in advance!
P.S. My project is ASP .NET MVC3
EDIT
Just now I remember about memnto design pattern. Could anyone of you advice me something related to this idea?
Thanks again!
I wrote this as a comment of your question but I'm putting it as an answer as it might be the only way to do this without having to "hack" it with some sort of side plugins.
The use of Viewstate have nothing to do with you using or not JQuery UI "addins". All you have to do is use server-side controls instead.
Note that for this you have to use the control client-side name, something like:
$('#<%= MyDateTextbox.ClientID %>').datepicker();
This way you can apply JQuery UI on server-side controls and take advantage of the Viewstate restoring the controls value on back-button navigation.
I would do it using Persist-JS (Persist-JS on GitHub), jQuery (which you are using already) and json.js (JSON-js on GitHub) .
Something like this:
var store;
//Restore form data
$(function() {
store = new Persist.Store('My Page Store');
var formDataStr = store.get('formdata');
if (formDataStr !== null) {
var formData = eval('(' + formDataStr + ')');
if (formData.hasData) {
$(':input').each(function() {
if (this.name != undefined && this.name != "") {
if (formData[this.name] != undefined) {
$(this).val(formData[this.name].value);
}
}
});
}
}
});
and...
//Persist form data
$(window).unload(function() {
var formData = {};
$(':input').each(function() {
if (this.name != undefined && this.name != "") {
formData[this.name] = {};
formData[this.name].value = $(this).val();
}
});
formData.hasData = true;
store.set('formdata', JSON.stringify(formData));
});
In other words, loop through all the inputs on the page, store their value ($.val()), create a JSON object and persist it for these values. When restoring the form, simply do the opposite -- loop through the inputs and grab the properties off of the JSON object by name formData[name]. There's some defensive stuff in there (ie: check for persisted object to be null, which is how persist-js works).

Categories