Our company has multiple brands and each brand has its own host name, but they are all part of the same site. We can let customers share baskets and other session information when they switch between brands via a redirect link using URLUtils.sessionRedirect.
But URLUtils is not available in content assets. Is it possible to form a session redirect link in content asset keeping all the session information?
Thanks in advance.
You can include dynamic content in Content Assets with the $include('Controller-Name', 'name1', 'value1', 'name2', 'value2', ...)$ syntax. See the MarkupText Class Documentation for more info on that syntax. The 'name1' and 'value1' parameters are mapped as query string attributes eg: Controller-Name?name1=value1&name2=value2
Create a controller that outputs the session redirect link you need, and call it via that syntax like: $include(Util-RenderSessionLink, 'siteID', 'foo')$
The controller would need to use a response Content-Type header of text/plain or something like that so that nothing is injected into the response. (eg: Storefront toolkit or tracking tags) For example:
response.setContentType('text/plain');
Alternatively, you could process the content asset for some sorts of keys that you perform find & replace operations on. For example, the following code does a find & replace on a Content Asset's body content for the key: '%%SessionLink%%'.
var ContentMgr = require('dw/content/ContentMgr');
var URLUtils = require('dw/web/URLUtils');
if (!empty(content) {
var content = ContentMgr.getContent('my-content-id');
var contentOut = "";
var viewData = {};
contentOut = content.custom.body.getMarkup()
.replace('%%SessionLink%%', URLUtils.sessionRedirect(...));
viewData.content = contentOut;
// then output your `pdict.content` key within a template with the appropriate encoding
}
If anybody else is running into this, we added a bit of client-side javascript that pickups up all outbound links and if it's one of our domains it sends them through a session redirect. This way we don't need content managers to fix very link between domains:
var domains = ["domaina.com", "domainb.com", "domainc.com"]
var sessionRedirectBase = '/s/Our-Site/dw/shared_session_redirect';
$(document).on("click.crossSite", "a", (e) => {
const href = $(e.currentTarget).attr("href");
if (href) { //does the link have a href
if (href.match(/^(http(s)?:)?\/\//)) { //is href an absolute url
const url = new URL(href);
if (url.hostname != window.location.hostname && domains.indexOf(url.hostname) > -1) { //is hostname not the current one and part of the domains array
e.preventDefault();
const sessionRedirect = `${sessionRedirectBase}?url=${encodeURIComponent(href)}`
window.location = sessionRedirect;
}
}
}
});
Related
I am trying to creating a app which can send posts to tumblr using tumblr.js api.
I could send single image using the createPhotoPost method, but I have to send multiple image in a single post via this method.
From the documentation it says that createPhotoPost method has a "data" parameter which can be an array with "URL-encoded binary contents"
When ever I try to send something in the data Array it returns "[Error: form-data: Arrays are not supported.]".
Can someone please help to solve this issue and please explain what should we pass in the array (Really I am not getting what is URL-encoded binary contents)?
Thanks in advance
There is an error in tumblr.js
https://tumblr.github.io/tumblr.js/tumblr.js.html#line504
The documentation at https://www.tumblr.com/docs/en/api/v2#posting is correct.
Basically the issue is that its not supposed to be
data = [ one , two ] its litterally params['data[0]'] = one ; params['data[1]'] = two; (?A PHP Convention)
var request = require('request')// already a tumblr.js dependency
var currentRequest = request({
url:'https://api.tumblr.com/v2/blog/someblog.tumblr.com/post',
oauth:keys,
},function(err,response,body){
currentRequest//
debugger
cb()
})
var params = {
'type' :'photo',
'caption':'Click To Verify You Are A Robot',
}
var params_images = [
fs.createReadStream('image'),
fs.createReadStream('image')
]
// Sign it with the non-data parameters
currentRequest.form(params)
currentRequest.oauth(keys);
// Clear the side effects from form(param)
delete currentRequest.headers['content-type'];
delete currentRequest.body;
// And then add the full body
var form = currentRequest.form();
//###add params_images to params keys 'data[0]' 'data[1]' 'data[2]' ....
params_images.forEach(function(image,index){
params['data['+index+']']
})
for (var key in params) {
form.append(key, params[key]);
}
// Add the form header back
var form_headers = form.getHeaders()
for(var key in form_headers){
currentRequest[key] = form_headers[key]
}
I'm trying to send a dynamic url using a template. I want to direct users to a specific link, but I'm not sure how to do that dynamically? I can just hardcode the url, but would prefer to use dynamic urls in case I make changes in the future.
here's what I have, I realize that trying to store a url variable like this
url: "{{pathFor 'welcome'}}"
is pretty dumb, but I can't figure out how to do this. Any help is appreciated! This is what I have:
var dataContext = {
message: "To set up your profile follow the link",
url: "{{pathFor 'welcome'}}",
hyperlinkText: "Get Started",
title: "Welcome to site!"
};
var html = Blaze.toHTMLWithData(Template.emailTemplate, dataContext);
var from = "myemail#mysite.com";
var to = currentUser.emails[0].address;
var subject = "Welcome to site";
Meteor.call("sendEmail", to, from, subject, html);
In javascript just evaluate Router.path(pathName) to get the path. You can get the base path with Meteor.absoluteUrl(). Note that the former will include a leading / while the latter includes a trailing / so you'll need to remove one. Ex:
function dynamicPath(pathName){
return Meteor.absoluteUrl() + Router.path(pathName).substr(1);
}
Getting blank values for title and description in serveResource method.Is this the right way to send the parameters from io request?
After inserting blank values in database I have to reload the page to see the inserted values?So io-request is not ajax request?
<aui:script use="aui-base">
A.one('#<portlet:namespace/>save').on('click', function(event) {
var A = AUI();
var title=A.one('#<portlet:namespace/>title').val();
alert(title);
var description=A.one('#<portlet:namespace/>description');
var url = '<%= newJob.toString() %>';
A.io.request(
url,
{
method:'POST',
data: {
<portlet:namespace />title: title,
<portlet:namespace />description: description,
},
}
['aui-io-deprecated']
);
Liferay.Util.getOpener().<portlet:namespace/>closePopup('<portlet:namespace/>dialog');
});
AUI's io request is ajax request only.
You can get parameters in serveResource method using code below:
ParamUtil.get(resourceRequest, "NAMEOFPARAMETER");
Modify your javascript function and provide data attribute as below:
data: {
'<portlet:namespace />title': title,
'<portlet:namespace />description': description,
}
I assume both title and description are textfields. If so, description is missing a .val() call, or more appropriately, .get('value'). I didn't use a dialog/modal in my source, but the overall approach should be the same.
<script>
AUI().use('aui-base', 'aui-io-request', function(A){
A.one('#<portlet:namespace />save').on('click', function(event) {
var title= A.one('#<portlet:namespace />title').get('value');
var description=A.one('#<portlet:namespace />description').get('value');
var url = '<%=myResourceURL.toString()%>';
A.io.request(url,
{
method:'POST',
data: {
title: title,
description: description,
},
});
});
});
</script>
I'm still relatively new to Liferay and have had trouble with this as well. I've noticed that the data parameters are not in the parametersMap of the default ResourceRequest, as you have stated. Out of curiosity, I decided to use
UploadPortletRequest req = PortalUtil.getUploadPortletRequest(resourceRequest);
in the serveResource method and check it's parametersMap. The title and description parameters are available therein. I'm still learning where and how to access data from Liferay objects, but it would seem that for the UploadPortletRequest to have the data, it would be plucked from somewhere within the default ResourceRequest ... where still remains elusive to me.
After inserting blank values in database I have to reload the page to see the inserted values?
You have to reload the page because a resource action does not trigger a page refresh. If you are manipulating data that you want reflected in some other "view" you'll need to configure the appropriate communication or use one of the other available url types that does trigger the doView method of your other "view".
On the system I am working on right now, I have had to try to tame ember-data a bit about how it does its REST request. The way that ember-data by default figures the URL for a certain request for a model is just not gonna cut it with the backend I am using.
What I need is, to get ember-data to use the same URL that ember is loading, but with a '?json' suffix. That is, if ember switches page to my band page, and the url is /bands, I want ember-data to request /bands?json for the data it needs, not whatever it figures from the name of the model. One could say, that I wanted the URL to be calculated from the path of the loading route, instead of from the name of the model being used.
I have tried by subclassing DS.RESTAdapter{} and see if I could get the buildURL method to do this, but I can't figure out how to get the URL ember is gonna load. The buildURL method is called before ember changes the location, so I can't use document.location.href or something. I can imagine I will need a way to ask ember what it is now loading, and what the URL is.
Any ideas of how to do this?
UPDATE
There hasn't been any satisfying solutions, so I decided to just do it the dirty way. This is it:
App.RouterSignature = [
['index', '/', '/index_models'],
['bands', '/bands', '/band_models'],
['band', '/band/:band_slug', '/band_model']
];
App.Router.map(function() {
for (var i = 0; i < App.RouterSignature.length; i++) {
var route = App.RouterSignature[i];
this.resource(route[0], {path: route[1]});
}
});
App.CustomAdapter = DS.RESTAdapter.extend({
buildURL: function(record, suffix) {
var url,
suffix = '?json',
needle = this._super(record);
for (var i = 0; i < App.RouterSignature.length && !url; i++) {
var route = App.RouterSignature[i];
if (route[2] == needle)
url = route[1];
}
return url + suffix;
}
});
Now App.Routes and DS.RESTAdapter.buildURL are based off the same data. The first two values in the App.RouterSignature list is just the name of the route, the path of the route. The third value is what DS.RESTAdapter.buildURL by default guesses should be the url. My custom adapter then takes that guess, matches it with one of the items in the App.RouterSignature list and then takes the second value from that item - the routes path.
Now the requests that ember-data makes is to the same url as the routes path.
You can try to setup your Adapter like so:
App.Adapter = DS.RESTAdapter.extend({
...
buildURL: function(record, suffix){
return this._super(record, suffix) + "?json";
}
});
App.Store = DS.Store.extend({
...
adapter: App.Adapter.create();
...
});
See here for more info on the RESTAdapter buildURL method.
Hope it helps
I am building a REST API for my project. The API for getting a given user's INFO is:
api.com/users/[USER-ID]
I would like to also allow the client to pass in a list of user IDs. How can I construct the API so that it is RESTful and takes in a list of user ID's?
If you are passing all your parameters on the URL, then probably comma separated values would be the best choice. Then you would have an URL template like the following:
api.com/users?id=id1,id2,id3,id4,id5
api.com/users?id=id1,id2,id3,id4,id5
api.com/users?ids[]=id1&ids[]=id2&ids[]=id3&ids[]=id4&ids[]=id5
IMO, above calls does not looks RESTful, however these are quick and efficient workaround (y). But length of the URL is limited by webserver, eg tomcat.
RESTful attempt:
POST http://example.com/api/batchtask
[
{
method : "GET",
headers : [..],
url : "/users/id1"
},
{
method : "GET",
headers : [..],
url : "/users/id2"
}
]
Server will reply URI of newly created batchtask resource.
201 Created
Location: "http://example.com/api/batchtask/1254"
Now client can fetch batch response or task progress by polling
GET http://example.com/api/batchtask/1254
This is how others attempted to solve this issue:
Google Drive
Facebook
Microsoft
Subbu Allamaraju
I find another way of doing the same thing by using #PathParam. Here is the code sample.
#GET
#Path("data/xml/{Ids}")
#Produces("application/xml")
public Object getData(#PathParam("zrssIds") String Ids)
{
System.out.println("zrssIds = " + Ids);
//Here you need to use String tokenizer to make the array from the string.
}
Call the service by using following url.
http://localhost:8080/MyServices/resources/cm/data/xml/12,13,56,76
where
http://localhost:8080/[War File Name]/[Servlet Mapping]/[Class Path]/data/xml/12,13,56,76
As much as I prefer this approach:-
api.com/users?id=id1,id2,id3,id4,id5
The correct way is
api.com/users?ids[]=id1&ids[]=id2&ids[]=id3&ids[]=id4&ids[]=id5
or
api.com/users?ids=id1&ids=id2&ids=id3&ids=id4&ids=id5
This is how rack does it. This is how php does it. This is how node does it as well...
There seems to be a few ways to achieve this. I'd like to offer how I solve it:
GET /users/<id>[,id,...]
It does have limitation on the amount of ids that can be specified because of URI-length limits - which I find a good thing as to avoid abuse of the endpoint.
I prefer to use path parameters for IDs and keep querystring params dedicated to filters. It maintains RESTful-ness by ensuring the document responding at the URI can still be considered a resource and could still be cached (although there are some hoops to jump to cache it effectively).
I'm interested in comments in my hunt for the ideal solution to this form :)
You can build a Rest API or a restful project using ASP.NET MVC and return data as a JSON.
An example controller function would be:
public JsonpResult GetUsers(string userIds)
{
var values = JsonConvert.DeserializeObject<List<int>>(userIds);
var users = _userRepository.GetAllUsersByIds(userIds);
var collection = users.Select(user => new { id = user.Id, fullname = user.FirstName +" "+ user.LastName });
var result = new { users = collection };
return this.Jsonp(result);
}
public IQueryable<User> GetAllUsersByIds(List<int> ids)
{
return _db.Users.Where(c=> ids.Contains(c.Id));
}
Then you just call the GetUsers function via a regular AJAX function supplying the array of Ids(in this case I am using jQuery stringify to send the array as string and dematerialize it back in the controller but you can just send the array of ints and receive it as an array of int's in the controller). I've build an entire Restful API using ASP.NET MVC that returns the data as cross domain json and that can be used from any app. That of course if you can use ASP.NET MVC.
function GetUsers()
{
var link = '<%= ResolveUrl("~")%>users?callback=?';
var userIds = [];
$('#multiselect :selected').each(function (i, selected) {
userIds[i] = $(selected).val();
});
$.ajax({
url: link,
traditional: true,
data: { 'userIds': JSON.stringify(userIds) },
dataType: "jsonp",
jsonpCallback: "refreshUsers"
});
}