DotCMS page API does not return "layout" field - dotcms

I want to use the Layout-as-a-Service (LaaS) feature of DotCMS. This approach is documented in
https://dotcms.com/blog/post/more-than-a-headless-cms-layout-as-a-service-in-dotcms and also
in https://github.com/fmontes/dotcms-page.
Both articles suggest, that the DotCMS page API should return a field called "layout" in the response, e.g. to http://localhost:8080/api/v1/page/json/test-page
test-page is a page, which is using a standard template. By standard template, I mean template created with "Template Designer", with a 20% sidebar on the left, one 100% width column, both containing "Blank container".
No matter what I try, the "layout" field is never part of the response. All I get is this:
{
"errors": [],
"entity": {
"canCreateTemplate": true,
"containers": ...,
"numberContents": 2,
"page": ...,
"site": ...,
"template": ...,
"viewAs": ... },
"messages": [],
"i18nMessagesMap": {},
"permissions": []
}
I tried DotCMS version 5.2.0 and also 5.2.3.
Is this perhaps a feature of the Enterprise edition only?
Edit: What I expect:
{
"errors": [],
"entity": {
"canCreateTemplate": true,
"containers": ...,
"layout": {
"width": "responsive",
"title": "mytemplate1",
"header": true,
"footer": true,
"body": {
"rows": [
{
"columns": [
{
"containers": [
{
"identifier": "b5ea1513-7653-4602-a729-97cd8dd099b6",
"uuid": "1582123997023"
}
],
"widthPercent": 100,
"leftOffset": 1,
"styleClass": null,
"preview": false,
"width": 12,
"left": 0
}
],
"styleClass": null
}
]
},
"sidebar": {
"containers": [
{
"identifier": "b5ea1513-7653-4602-a729-97cd8dd099b6",
"uuid": "1582123991866"
}
],
"location": "left",
"width": "small",
"widthPercent": 20,
"preview": false
}
}
...

Nevermind, I found the answer myself.
I checked the source code. In com.dotmarketing.portlets.htmlpageasset.business.render.page.HTMLPageAssetRenderedBuilder#build there is following piece of code:
final TemplateLayout layout = template != null && template.isDrawed() && !LicenseManager.getInstance().isCommunity()
? DotTemplateTool.themeLayout(template.getInode()) : null;
So the answer is, that you can use LaaS promoted by DotCMS only if you have a non-community licence.

Your page needs to use a "layout template" in order to retrieve the layout from dotCMS - using an advanced template won't work.
Also, layouts are part of the enterprise edition. Make sure you have a license in order to use the layout manager.

Related

How to interpret user search query (in Elasticsearch)

I would like to serve my visitors the best results possible when they use our search feature.
To achieve this I would like to interpret the search query.
For example a user searches for 'red beds for kids 120cm'
I would like to interpret it as following:
Category-Filter is "beds" AND "children"
Color-filter is red
Size-filter is 120cm
Are there ready to go tools for Elasticsearch?
Will I need NLP in front of Elasticsearch?
Elasticsearch is pretty powerful on its own and is very much capable of returning the most relevant results to full-text search queries, provided that data is indexed and queried adequately.
Under the hood it always performs text analysis for full-text searches (for fields of type text). A text analyzer consists of a character filter, tokenizer and a token filter.
For instance, synonym token filter can replace kids with children in the user query.
Above that search queries on modern websites are often facilitated via category selectors in the UI, which can easily be implemented with querying keyword fields of Elasticsearch.
It might be enough to model your data correctly and tune its indexing to implement the search you need - and if that is not enough, you can always add some extra layer of NLP-like logic on the client side, like #2ps suggested.
Now let me show a toy example of what you can achieve with a synonym token filter and copy_to feature.
Let's define the mapping
Let's pretend that our products are characterized by the following properties: Category, Color, and Size.LengthCM.
The mapping will look something like:
PUT /my_index
{
"mappings": {
"properties": {
"Category": {
"type": "keyword",
"copy_to": "DescriptionAuto"
},
"Color": {
"type": "keyword",
"copy_to": "DescriptionAuto"
},
"Size": {
"properties": {
"LengthCM": {
"type": "integer",
"copy_to": "DescriptionAuto"
}
}
},
"DescriptionAuto": {
"type": "text",
"analyzer": "MySynonymAnalyzer"
}
}
},
"settings": {
"index": {
"analysis": {
"analyzer": {
"MySynonymAnalyzer": {
"tokenizer": "standard",
"filter": [
"MySynonymFilter"
]
}
},
"filter": {
"MySynonymFilter": {
"type": "synonym",
"lenient": true,
"synonyms": [
"kid, kids => children"
]
}
}
}
}
}
}
Notice that we selected type keyword for the fields Category and Color.
Now, what about these copy_to and synonym?
What will copy_to do?
Every time we send an object for indexing into our index, value of the keyword field Category will be copied to a full-text field DescritpionAuto. This is what copy_to does.
What will synonym do?
To enable synonym we need to define a custom analyzer, see MySynonymAnalyzer which we defined under "settings" above.
Roughly, it will replace every token that matches something on the left of => with the token on the right.
How will the documents look like?
Let's insert a few example documents:
POST /my_index/_doc
{
"Category": [
"beds",
"adult"
],
"Color": "red",
"Size": {
"LengthCM": 150
}
}
POST /my_index/_doc
{
"Category": [
"beds",
"children"
],
"Color": "red",
"Size": {
"LengthCM": 120
}
}
POST /my_index/_doc
{
"Category": [
"couches",
"adult",
"family"
],
"Color": "blue",
"Size": {
"LengthCM": 200
}
}
POST /my_index/_doc
{
"Category": [
"couches",
"adult",
"family"
],
"Color": "red",
"Size": {
"LengthCM": 200
}
}
As you can see, DescriptionAuto is not present in the original documents - though due to copy_to we will be able to query it.
Let's see how.
Performing the search!
Now we can try out our index with a simple query_string query:
POST /my_index/_doc/_search
{
"query": {
"query_string": {
"query": "red beds for kids 120cm",
"default_field": "DescriptionAuto"
}
}
}
The results will look something like the following:
"hits": {
...
"max_score": 2.3611186,
"hits": [
{
...
"_score": 2.3611186,
"_source": {
"Category": [
"beds",
"children"
],
"Color": "red",
"Size": {
"LengthCM": 120
}
}
},
{
...
"_score": 1.0998137,
"_source": {
"Category": [
"beds",
"adult"
],
"Color": "red",
"Size": {
"LengthCM": 150
}
}
},
{
...
"_score": 0.34116736,
"_source": {
"Category": [
"couches",
"adult",
"family"
],
"Color": "red",
"Size": {
"LengthCM": 200
}
}
}
]
}
The document with categories beds and children and color red is on top. And its relevance score is twice bigger than of its follow-up!
How can I check how Elasticsearch interpreted the user's query?
It is easy to do via analyze API:
POST /my_index/_analyze
{
"text": "red bed for kids 120cm",
"analyzer": "MySynonymAnalyzer"
}
{
"tokens": [
{
"token": "red",
"start_offset": 0,
"end_offset": 3,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "bed",
"start_offset": 4,
"end_offset": 7,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "for",
"start_offset": 8,
"end_offset": 11,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "children",
"start_offset": 12,
"end_offset": 16,
"type": "SYNONYM",
"position": 3
},
{
"token": "120cm",
"start_offset": 17,
"end_offset": 22,
"type": "<ALPHANUM>",
"position": 4
}
]
}
As you can see, there is no token kids, but there is token children.
On a side note, in this example Elasticsearch wasn't able, though, to parse the size of the bed: token 120cm didn't match to anything, since all sizes are integers, like 120, 150, etc. Another layer of tweaking will be needed to extract 120 from 120cm token.
I hope this gives an idea of what can be achieved with Elasticsearch's built-in text analysis capabilities!

Loopback include filter within scope works for GET but fails for POST request

I have this scope defined in my order.json which has relation with branch and customer along with other properties.
"name": "Order",
"properties":{...},
"relations": {...},
"acls": {...},
"scope": {
"include": [
{"relation": "branch", "scope": { "fields": "BranchName" } },
{"relation": "customer", "scope": { "fields": "CustomerName" } }
]
}
This works well as expected in all GET requests with following results
[
{
"OrderDate": "2018-01-12T17:52:21.000Z",
"CustomerId": 39,
"BranchId": 5,
"CustomerRef": "Order by Phone",
...
"CreatedBy": 1,
"id": 1,
"branch": {
"BranchName": "aaaa",
"id": 5
},
"customer": {
"CustomerName": "xxxx",
"id": 39
}
}
]
I was expecting a similar result, however, the response array received after a successful POST request does not include BranchName and CustomerName info from the related models.
Am I doing it correctly? or is there any other way to get back information from related models after a Create/Update operation. I am just trying to avoid another GET request immediately after Create/Update.
You can use the Operation hook after save.
Order.observe('after save', function(ctx, next) {
if (ctx.instance) {
ctx.instance.relatedmodel = someFunctionToGetRelatedModel();
}
next();
});
Whatever is inside the ctx.instance should be included in loopbacks responses.
You just have to figure out how to seamlessly pull the related model details, you want to include.

How do you handle large relationship data attributes and compound documents?

If an article has several comments (think thousands over time). Should data.relationships.comments return with a limit?
{
"data": [
{
"type": "articles",
"id": 1,
"attributes": {
"title": "Some title",
},
"relationships": {
"comments": {
"links": {
"related": "https://www.foo.com/api/v1/articles/1/comments"
},
"data": [
{ "type": "comment", "id": "1" }
...
{ "type": "comment", "id": "2000" }
]
}
}
}
],
"included": [
{
"type": "comments",
"id": 1,
"attributes": {
"body": "Lorem ipusm",
}
},
.....
{
"type": "comments",
"id": 2000,
"attributes": {
"body": "Lorem ipusm",
}
},
]
}
This starts to feel concerning, when you think of compound documents (http://jsonapi.org/format/#document-compound-documents). Which means, the included section will list all comments as well, making the JSON payload quite large.
If you want to limit the number of records you get at a time from a long list use pagination (JSON API spec).
I would load the comments separately with store.query (ember docs), like so -
store.query('comments', { author_id: <author_id>, page: 3 });
which will return the relevant subset of comments.
If you don't initially want to make two requests per author, you could include the first 'page' in the authors request as you're doing now.
You may also want to look into an addon like Ember Infinity (untested), which will provide an infinite scrolling list and automatically make pagination requests.

Unknown Generics

I am attempting to create a database system modeling SQL, where each table is stored in a JSON file. Really not focusing on speed at the moment as this is more an exercise in communication. I came up with the following markup,
{
"table": {
"auto_increment": 3,
"last_edited": 1423163442
},
"definitions": [
{
"name": "id",
"kind": "INT",
"length": 8,
"default": "NONE",
"index": "PRIMARY",
"auto_increment": true
},
{
"name": "username",
"kind": "VARCHAR",
"length": 25,
"default": "NONE",
"index": "UNIQUE",
"auto_increment": false
},
{
"name": "name_last",
"kind": "VARCHAR",
"length": 25,
"default": "NONE",
"index": "NONE",
"auto_increment": false
},
{
"name": "name_first",
"kind": "VARCHAR",
"length": 25,
"default": "NONE",
"index": "NONE",
"auto_increment": false
}
],
"data": [
{
"id": 0,
"username": "Neat-O",
"name_first": "Kenny",
"name_last": "Hill"
},
{
"id": 1,
"username": "12thAggie",
"name_first": "Reville",
"name_last": "18"
},
{
"id": 2,
"username": "I_Like_Cheese",
"name_first": "Shh",
"name_last": "Secret"
}
]
}
With this file I need to be able to read in and essentially create an object in C++ representing this table, or any table in SQL for that matter. It is a limited implementation of SQL, so let's just consider this markup to hover around the edge of the intended functionality. In my current design I am thinking it would be best to define abstract column objects, that can essentially be stacked together to form a whole table. Operations can be performed on all the columns as though they are a whole table, and then saved back to disk. Again, I am not concerned with performance at the moment mostly readability and implementation. So my question to you C++ gurus is this, how would you interpret this data and manipulate it once instantiated as a C++ object? I have been exploring generics but I keep getting hung up on the "selectable" data types, INTS, VARCHAR, etc... My current class "kind" (for a column has the following members:
template <class T>
class Kind
{
private:
int length;
string kind;
string default_value;
string index;
int auto_increment;
vector<T> data;
}
However, when trying to create the table I would need to somehow create a type that allows for multiple types of generics.
vector<Kind<?>> table;
Where allows for VARCHAR, INT, etc... Any help with this would be appreciated! Thank you.

What is the "likes" field from an FB Open Graph GET og.likes request?

I've implemented functionality to Like a non-FB URL in a cross-platform mobile app (Phonegap) I'm developing, and part of functionality is that I need to find out if a user has liked a URL before, hence a GET on the og.likes object. In the result of this request there's a field in the og.likes data that I'm unsure about.
My request:
GET me/og.likes?access_token={AccessToken}&object={EncodedExternalURL}
The response:
{
"data": [
{
"id": "_____",
"from": {
"id": "______",
"name": "______"
},
"start_time": "2015-01-12T06:17:24+0000",
"end_time": "2015-01-12T06:17:24+0000",
"publish_time": "2015-01-12T06:17:24+0000",
"application": {
"name": "______",
"namespace": "______",
"id": "______"
},
"data": {
"object": {
"id": "____",
"url": "____",
"type": "website",
"title": "____"
}
},
"type": "og.likes",
"no_feed_story": false,
"likes": { // <-- this guy here and its properties
"count": 0,
"can_like": true,
"user_likes": false
},
"comments": {
"count": 0,
"can_comment": true,
"comment_order": "chronological"
}
}
],
"paging": {
"next": "____"
}
}
What is the likes field? And the sub properties of count, can_like, user_likes? Is it that other users can like this Like?
likes field is for manage the likes that this like has. Users can like or comment a like.