Christoph Rabel 1 Month Ago - Edited I fully agree that Objects are great and very useful. So far, we have used the webcontent approach alot since it was very convenient for simple structured data. Objects are often far better suited for this, but not always. Btw.: That multilingual objects were not possible was also a showstopper for us in some cases. We practically never use the mapping feature, what we usually do is: a) Add a template contributor that prepares the data for the Freemarker template in a nice way. The contributor creates a dto for the frontend developers and enriches it with extra information (often categories or other related information). b) Add a rest call returning such a dto and we render everything in Javascript. Which approach we use depends a bit on the usecase. Note: You could, of course, use the Liferay API directly from Freemarker/Javascript, but in my experience, it is more convenient to just prepare everything in a Java class. In the early days (with serviceLocator) we did practically everything in Freemarker and it was painful and horrible. About the mapping feature: I did not test it in the most current Liferay version, but we were never able to get the mappings to work conveniently according to our requirements. Sure, there might be some simple contents, where it works, but these were usually replaced by Fragments + direct editing anyway. Freemarker performance: Maybe this is outdated, it was a while ago, but I once tried to improve the performance of pages with (usually) 10 webcontents or so on them. My initial assumption was that Freemarker must be the problem. But when I measured it, Freemarker wasn't the bottleneck, it was really neglible. Again, this could be outdated, but in the early days of Fragments, somebody (I think Pavel S.) measured fragments with/without Freemarker once and found a 7% performance difference. So, while we prefer the Javascript + Rest Service approach nowadays, Freemarker isn't that bad. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Christoph Rabel 1 Month Ago - Edited Hi Christoph, Long time no see :) I hope you're coming to the Devcon in November? What you wrote here is almost exactly how we approach the development in 7.4. We've developed a template contributor to provide enriched data to Fragments or used the old Journal Templates for rendering specific sections of the Display Page (e.g., those with repeatable fields). Then, we map these to regular paragraph fragments. Liferay's provided mappings are effective only for straightforward scenarios, anything more complex requires additional coding. What do you use as a REST service, Liferay Headless API or you build your custom? I find the built-in Headless API (either GraphQL or REST) still quite difficult to use and not fit for public websites. It's not available for unauthenticated users, which makes sense as the visitors should not be able to browse all the article metadata. I also agree that doing anything in freemarker is painful and horrible! Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Hi Krzysztof, I am the Product Manager for the Headless Infrastructure and I'd like to know if you could comment on the difficulties you face so we can improve their capabilities. About APIs not being available for guest users, that's just part of our policy of being secure by default, so we do not make it easy for unwanted users to attack your solution. However, it is possible to provide access for Guest users. For that you need to configure Service Access Policies, so they only have access to the APIs you want to. You can find information about how to configure it here: https://learn.liferay.com/w/dxp/headless-delivery/consuming-apis/making-unauthenticated-requests Please sign in to reply. Reply as... Cancel Christoph Rabel Pablo Agulla 1 Month Ago - Edited Hi Pablo, First of all: the documentation. It's short: https://learn.liferay.com/w/dxp/headless-delivery/apis-with-rest-builder/producing-and-implementing-apis-with-rest-builder And there's a lot of things I don't know. How do I get the User in my rest service? Or the request? It's probably in the thread local, but I'd rather write something like this: public Response doSomething(MyDTO dat, @Context User user, @Context Company company) and have it directly available. (I can do this, I have implemented custom contexts). Also: How can I return multiple response codes from the implementation? With a custom implementation I can send 200 and 204 and whatever I want from the same method: return Response.status(someCalculatedResponseStatus).build(); Just throwing an exception and 200 OK does not cut it. Maybe this is all a documentation issue, but ... - Public/Protected: Pretty much all our APIs need to be accessible public and private. I don't want to configure Service Access Policies. I don't want to click through several environments to allow access to a new method. Using annotations and osgi properties + custom code is far more convenient. Also: I really rarely need an openapi specification, most calls are just used in the project and not shared with external implementors. So, it doesn't save me anything. It is simply easier to implement this stuff directly. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hi Pablo, Thank you for joining our discussion, it's great to be able to discuss the issues directly with the author! I played with the API again today (on 2023.Q4.2 hotfix-134) and I tried to open /o/headless-delivery/v1.0/structured-contents/{articleId} to the public as we often need access to web content fields. The major issue is that it still reveals too much information, like author, all the web content technical details but also internal categories (from which I revoked Guest permission!). So it is definitely not fit for public use for now. What I noticed as well is that it's not very consistent in terms of how it uses the underlying Liferay data, for example the GraphQL API uses siteKey (group key) to identify the site and the REST API uses the old groupId (called siteId, which also creates some confusion). For retrieving web content, some methods use articleId and others use resourcePrimKey from what I remember (both named in a different, non-liferay way). What's more, many of these values are difficult to obtain on Display Pages or within Collections, where they could potentially be used. What I usually need to implement is to retrieve the Web Content data from request (either as JournalArticle or as InfoItem within the Collection), get the appropriate key, and save it somewhere on the page so the front-end developer can call the API from JS. I'll keep experimenting with the API and let you know if I have any new findings. I'll also mention you on slack discussion with David, about how much data gets revealed by the API. Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Thanks for your comment, I'll take a look. For example, about "The major issue is that it still reveals too much information, like author, all the web content technical details" -> we try to expose no more information that it's already available in the UI (like here I'm seeing the author of your comment), or for the web content technical details, the needed information to be able to render/consume the content. I'm referring to field Types, contentStructureId, etc. About the internal categories, I'm reviewing it internally. About "for example the GraphQL API uses siteKey (group key) to identify the site and the REST API uses the old groupId (called siteId, which also creates some confusion)" -> as we started with rest, we went for siteId, but then realized that it's not easy to use, so for GraphQL we did a better job with siteKey. For not breaking compatibility, we kept in REST the name of the parameter to siteId, but actually, it can work with the siteName as well. "For retrieving web content, some methods use articleId and others use resourcePrimKey from what I remember" -> we expanded the possibilities, so now we support many parameters to retrieve the content. "(both named in a different, non-liferay way)" -> we consciouly decoupled the API datamodel from the db data model, so in case we decide to change the db data model, we can keep the API working I hope my answers at least gives you context about why our APIs work the way they do. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Thank you for the clarifications, they shed light on the design decisions behind the current API architecture. In general, the API is very useful and it really accelerates the development. It's easier to use it to retrieve the data from the backend, much better than using traditional local services. However, I have a suggestion regarding the OpenAPI schema documentation. For instance, the getStructuredContent method simply states: "Retrieves the structured content via its ID". This doesn't say much and a potential developer could go to the Web Content in Liferay, open an article, copy the "Article ID", paste it in the API and... receive 404. A brief note clarifying that the required parameter is the resourcePrimKey from the JournalArticle would help and save a lot of time. Moreover, it could say that this method fetches the most recent approved version of the article (btw, for some reason there is no info on the article version in the reply). Otherwise, newcomers to Liferay will very quickly get frustrated with the product as they won't be able to get the simple things done. Not because of the API itself, but because of the lack of knowledge and documentation. Another area of concern is the data exposure. The current approach to privacy is well-suited for Intranets or Internal Knowledge Bases, where each user is a known and trusted entity. For public, corporate websites though, the rules are different. The unauthenticated visitors should only access content as it is presented within fragments or rendered by Freemarker templates. They don't have access to Liferay's UI. Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed. Because of the above, for now, we can't expose the current API to the public. Fortunately, we discovered the great restClient which already helps us to skip all the terrible Freemarker development on top of journalArticle's XML and local services. Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Hi Krzysztof, About the IDs, we wanted to "decouple" the API world from the internal world, so the ID you need to use to retrieve a single content is the one provided when asking for the collection: "http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents". If at some point we decide (I know, 99% chance of not happening) change Ids from resourcePrimKey to ArticleId, we will not break the API contract. " it could say that this method fetches the most recent approved version of the article" -> yes, it fetches the last version only of the approved articles. This was decided taking into account your comment about data exposure, in delivery we do not have information about version. To retrieve all articles in different status and with version information, you can use the headless admin content API: http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents Regarding your comment about data exposure: "Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed." I understand your concern, however that depends on the use case. We have customers who use web content for blogging, or to have a section like "news" in their public websites where information about the author, or publication dates are needed, so our reasoning was to not expose more information that we are exposing for example for blogs or information you can expose in the UI. Having said this, we are working on a feature, currently in Beta, that allows you to define custom endpoints for Objects, exposing only the information you are interested in. It's still in beta: https://learn.liferay.com/w/dxp/headless-delivery/api-builder As we expand the Object use cases, you will be able to apply it to more places. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hello Pablo, I understand the concept of separating the API world from the internal world, but my concern is the documentation. I have been working with Liferay for many years, so I can identify the Liferay entities that each API artifact represents (even by going through the sources). However, If I give this API to a front-end developer not familiar with Liferay, he'll be completely lost. All the entity and parameter names are different than in Liferay (both UI and the database) and the only documentation that says about it (https://help.liferay.com/hc/en-us/articles/360028727072-OpenAPI-Profiles) is very limited and actually is outdated. For example, it mentions ContentSet (in DXP 2023.Q4.2 it seems to be renamed to ContentSetElement), which represents the internal AssetListEntry (a term known only to Liferay experts) and there is no mention of Collections at all. Other labels are similar, API introduces a third layer of labeling for Liferay artifacts (after UI and database/services) so it brings even more confusion to the platform that is already very complex. That brings out a problem that Liferay has had for many years. It is a really feature-rich and versatile platform, but due to the lack of documentation and common best practices (caused by some architectural inconsistencies), a lot of Liferay projects are poorly implemented, not fully leveraging the Liferay powers and with lots of custom coding that could have been avoided. In terms of the used keys, it makes total sense if the application is built entirely on top of the API (so the keys come from collections). However, in a Liferay world, there are lots of "hybrid" apps, that utilize Collection Fragments or Display Pages from where we need to call the API to reach certain data that are not retrievable by standard fragment mappings. I also tried the API builder some time ago and it looked very promising. From what I understand though, it works only with Objects, so we can't use it until we migrate away from Web Content. Thank you for mentioning the headless-admin-content API, I haven't looked into that yet, so I'll check it in the next couple of days. Thanks again for your comments and your input to this discussion! Please sign in to reply. Reply as... Cancel Christoph Rabel Krzysztof Gołębiowski 1 Month Ago - Edited Hi Krzysztof, yes, it's a while. I will most likely go to the Devcon, especially since it's basically "nearby" in Budapest. The virtual thing was not my piece of cake, but a real Devcon ... We usually use the contributors directly from the fragment or execute macros (we deploy them and use <#include "/<bundle>_SERVLET_CONTEXT_/path/xy.ftl" /> to load them, but your solution is pretty nice too. I really like the mapping to paragraphs idea. Since we currently deploy the fragments using the fragment toolkit, it didn't matter to me, but since the toolkit was deprecated with 2024.Q1, I was already pondering how to do it in the future. We also build our custom REST services with the OSGI whiteboard. More convenient. Please sign in to reply. Reply as... Cancel David H Nebinger Christoph Rabel 1 Month Ago - Edited The fragments toolkit is deprecated, yes, but not going away soon. Team is working on another mechanism for fragments export/import, and the toolkit will not go away before that mechanism is launched and tested. In the interim, you are safe to use the toolkit... Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Christoph Rabel 1 Month Ago - Edited Another thing I rediscovered a couple of days ago (I've heard about it on the last Devcon for the first time) was the restClient tool which allows you to call every headless-delivery or custom objects API from the ftl template (check https://help.liferay.com/hc/en-us/articles/14840864106765-Accessing-to-Objects-custom-fields-in-an-Aplication-Diplay-Template). Then, getting a full article rendered with a specific template is just: <#assign documentCard = restClient.get("/headless-delivery/v1.0/structured-contents/${resourcePrimKey}/rendered-content/SOME_TEMPLATE") /> That works really nice and I start replacing my custom contributors written in java with these calls. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited With all things FreeMarker, you should always do performance tests on things like this, preferrably using an Asset Publisher with 20+ instances rendered on a page, then look at your page rendering performance. It's not going to tell you how the site will operate when thousands of users are trying to invoke FM at the same time for all of this rendering, but it can tell you if perhaps you're adding overhead that you might not otherwise see until under load. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski David H Nebinger 1 Month Ago - Edited That's a good point, thanks! I'll look closer at the performance of these templates. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited Since it is in a FreeMarker template, the template is making the rest call (so server side synchronous processing), retrieving the result, parsing the json, keeping part of it around for the render needs, discarding the response when done (so a GC activity), ... Just because restClient() is in there won't mean there isn't a cost involved with using it... Please sign in to reply. Reply as... Cancel Christoph Rabel David H Nebinger 1 Month Ago - Edited Regarding performance: The recent addition of "builtin" glowroot is really awesome. We are using it for several years now and it helped us uncover and fix lots of performance issues. The small cost of instrumentation was always quickly offset by the metrics you get. https://learn.liferay.com/w/dxp/system-administration/using-glowroot-with-liferay (Note: If you use an older Liferay version, glowroot can be added "manually". There's a guide here in the blog somewhere) Please sign in to reply. Reply as... Cancel David H Nebinger Christoph Rabel 1 Month Ago - Edited The best part about the Glowroot integration is the inclusion of a custom plugin which monitors FreeMarker rendering times, created by our very own Fabian Bouché... The plugin can help visualize FreeMarker-based performance issues. Please sign in to reply. Reply as... Cancel
Krzysztof Gołębiowski Christoph Rabel 1 Month Ago - Edited Hi Christoph, Long time no see :) I hope you're coming to the Devcon in November? What you wrote here is almost exactly how we approach the development in 7.4. We've developed a template contributor to provide enriched data to Fragments or used the old Journal Templates for rendering specific sections of the Display Page (e.g., those with repeatable fields). Then, we map these to regular paragraph fragments. Liferay's provided mappings are effective only for straightforward scenarios, anything more complex requires additional coding. What do you use as a REST service, Liferay Headless API or you build your custom? I find the built-in Headless API (either GraphQL or REST) still quite difficult to use and not fit for public websites. It's not available for unauthenticated users, which makes sense as the visitors should not be able to browse all the article metadata. I also agree that doing anything in freemarker is painful and horrible! Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Hi Krzysztof, I am the Product Manager for the Headless Infrastructure and I'd like to know if you could comment on the difficulties you face so we can improve their capabilities. About APIs not being available for guest users, that's just part of our policy of being secure by default, so we do not make it easy for unwanted users to attack your solution. However, it is possible to provide access for Guest users. For that you need to configure Service Access Policies, so they only have access to the APIs you want to. You can find information about how to configure it here: https://learn.liferay.com/w/dxp/headless-delivery/consuming-apis/making-unauthenticated-requests Please sign in to reply. Reply as... Cancel Christoph Rabel Pablo Agulla 1 Month Ago - Edited Hi Pablo, First of all: the documentation. It's short: https://learn.liferay.com/w/dxp/headless-delivery/apis-with-rest-builder/producing-and-implementing-apis-with-rest-builder And there's a lot of things I don't know. How do I get the User in my rest service? Or the request? It's probably in the thread local, but I'd rather write something like this: public Response doSomething(MyDTO dat, @Context User user, @Context Company company) and have it directly available. (I can do this, I have implemented custom contexts). Also: How can I return multiple response codes from the implementation? With a custom implementation I can send 200 and 204 and whatever I want from the same method: return Response.status(someCalculatedResponseStatus).build(); Just throwing an exception and 200 OK does not cut it. Maybe this is all a documentation issue, but ... - Public/Protected: Pretty much all our APIs need to be accessible public and private. I don't want to configure Service Access Policies. I don't want to click through several environments to allow access to a new method. Using annotations and osgi properties + custom code is far more convenient. Also: I really rarely need an openapi specification, most calls are just used in the project and not shared with external implementors. So, it doesn't save me anything. It is simply easier to implement this stuff directly. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hi Pablo, Thank you for joining our discussion, it's great to be able to discuss the issues directly with the author! I played with the API again today (on 2023.Q4.2 hotfix-134) and I tried to open /o/headless-delivery/v1.0/structured-contents/{articleId} to the public as we often need access to web content fields. The major issue is that it still reveals too much information, like author, all the web content technical details but also internal categories (from which I revoked Guest permission!). So it is definitely not fit for public use for now. What I noticed as well is that it's not very consistent in terms of how it uses the underlying Liferay data, for example the GraphQL API uses siteKey (group key) to identify the site and the REST API uses the old groupId (called siteId, which also creates some confusion). For retrieving web content, some methods use articleId and others use resourcePrimKey from what I remember (both named in a different, non-liferay way). What's more, many of these values are difficult to obtain on Display Pages or within Collections, where they could potentially be used. What I usually need to implement is to retrieve the Web Content data from request (either as JournalArticle or as InfoItem within the Collection), get the appropriate key, and save it somewhere on the page so the front-end developer can call the API from JS. I'll keep experimenting with the API and let you know if I have any new findings. I'll also mention you on slack discussion with David, about how much data gets revealed by the API. Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Thanks for your comment, I'll take a look. For example, about "The major issue is that it still reveals too much information, like author, all the web content technical details" -> we try to expose no more information that it's already available in the UI (like here I'm seeing the author of your comment), or for the web content technical details, the needed information to be able to render/consume the content. I'm referring to field Types, contentStructureId, etc. About the internal categories, I'm reviewing it internally. About "for example the GraphQL API uses siteKey (group key) to identify the site and the REST API uses the old groupId (called siteId, which also creates some confusion)" -> as we started with rest, we went for siteId, but then realized that it's not easy to use, so for GraphQL we did a better job with siteKey. For not breaking compatibility, we kept in REST the name of the parameter to siteId, but actually, it can work with the siteName as well. "For retrieving web content, some methods use articleId and others use resourcePrimKey from what I remember" -> we expanded the possibilities, so now we support many parameters to retrieve the content. "(both named in a different, non-liferay way)" -> we consciouly decoupled the API datamodel from the db data model, so in case we decide to change the db data model, we can keep the API working I hope my answers at least gives you context about why our APIs work the way they do. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Thank you for the clarifications, they shed light on the design decisions behind the current API architecture. In general, the API is very useful and it really accelerates the development. It's easier to use it to retrieve the data from the backend, much better than using traditional local services. However, I have a suggestion regarding the OpenAPI schema documentation. For instance, the getStructuredContent method simply states: "Retrieves the structured content via its ID". This doesn't say much and a potential developer could go to the Web Content in Liferay, open an article, copy the "Article ID", paste it in the API and... receive 404. A brief note clarifying that the required parameter is the resourcePrimKey from the JournalArticle would help and save a lot of time. Moreover, it could say that this method fetches the most recent approved version of the article (btw, for some reason there is no info on the article version in the reply). Otherwise, newcomers to Liferay will very quickly get frustrated with the product as they won't be able to get the simple things done. Not because of the API itself, but because of the lack of knowledge and documentation. Another area of concern is the data exposure. The current approach to privacy is well-suited for Intranets or Internal Knowledge Bases, where each user is a known and trusted entity. For public, corporate websites though, the rules are different. The unauthenticated visitors should only access content as it is presented within fragments or rendered by Freemarker templates. They don't have access to Liferay's UI. Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed. Because of the above, for now, we can't expose the current API to the public. Fortunately, we discovered the great restClient which already helps us to skip all the terrible Freemarker development on top of journalArticle's XML and local services. Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Hi Krzysztof, About the IDs, we wanted to "decouple" the API world from the internal world, so the ID you need to use to retrieve a single content is the one provided when asking for the collection: "http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents". If at some point we decide (I know, 99% chance of not happening) change Ids from resourcePrimKey to ArticleId, we will not break the API contract. " it could say that this method fetches the most recent approved version of the article" -> yes, it fetches the last version only of the approved articles. This was decided taking into account your comment about data exposure, in delivery we do not have information about version. To retrieve all articles in different status and with version information, you can use the headless admin content API: http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents Regarding your comment about data exposure: "Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed." I understand your concern, however that depends on the use case. We have customers who use web content for blogging, or to have a section like "news" in their public websites where information about the author, or publication dates are needed, so our reasoning was to not expose more information that we are exposing for example for blogs or information you can expose in the UI. Having said this, we are working on a feature, currently in Beta, that allows you to define custom endpoints for Objects, exposing only the information you are interested in. It's still in beta: https://learn.liferay.com/w/dxp/headless-delivery/api-builder As we expand the Object use cases, you will be able to apply it to more places. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hello Pablo, I understand the concept of separating the API world from the internal world, but my concern is the documentation. I have been working with Liferay for many years, so I can identify the Liferay entities that each API artifact represents (even by going through the sources). However, If I give this API to a front-end developer not familiar with Liferay, he'll be completely lost. All the entity and parameter names are different than in Liferay (both UI and the database) and the only documentation that says about it (https://help.liferay.com/hc/en-us/articles/360028727072-OpenAPI-Profiles) is very limited and actually is outdated. For example, it mentions ContentSet (in DXP 2023.Q4.2 it seems to be renamed to ContentSetElement), which represents the internal AssetListEntry (a term known only to Liferay experts) and there is no mention of Collections at all. Other labels are similar, API introduces a third layer of labeling for Liferay artifacts (after UI and database/services) so it brings even more confusion to the platform that is already very complex. That brings out a problem that Liferay has had for many years. It is a really feature-rich and versatile platform, but due to the lack of documentation and common best practices (caused by some architectural inconsistencies), a lot of Liferay projects are poorly implemented, not fully leveraging the Liferay powers and with lots of custom coding that could have been avoided. In terms of the used keys, it makes total sense if the application is built entirely on top of the API (so the keys come from collections). However, in a Liferay world, there are lots of "hybrid" apps, that utilize Collection Fragments or Display Pages from where we need to call the API to reach certain data that are not retrievable by standard fragment mappings. I also tried the API builder some time ago and it looked very promising. From what I understand though, it works only with Objects, so we can't use it until we migrate away from Web Content. Thank you for mentioning the headless-admin-content API, I haven't looked into that yet, so I'll check it in the next couple of days. Thanks again for your comments and your input to this discussion! Please sign in to reply. Reply as... Cancel Christoph Rabel Krzysztof Gołębiowski 1 Month Ago - Edited Hi Krzysztof, yes, it's a while. I will most likely go to the Devcon, especially since it's basically "nearby" in Budapest. The virtual thing was not my piece of cake, but a real Devcon ... We usually use the contributors directly from the fragment or execute macros (we deploy them and use <#include "/<bundle>_SERVLET_CONTEXT_/path/xy.ftl" /> to load them, but your solution is pretty nice too. I really like the mapping to paragraphs idea. Since we currently deploy the fragments using the fragment toolkit, it didn't matter to me, but since the toolkit was deprecated with 2024.Q1, I was already pondering how to do it in the future. We also build our custom REST services with the OSGI whiteboard. More convenient. Please sign in to reply. Reply as... Cancel David H Nebinger Christoph Rabel 1 Month Ago - Edited The fragments toolkit is deprecated, yes, but not going away soon. Team is working on another mechanism for fragments export/import, and the toolkit will not go away before that mechanism is launched and tested. In the interim, you are safe to use the toolkit... Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Christoph Rabel 1 Month Ago - Edited Another thing I rediscovered a couple of days ago (I've heard about it on the last Devcon for the first time) was the restClient tool which allows you to call every headless-delivery or custom objects API from the ftl template (check https://help.liferay.com/hc/en-us/articles/14840864106765-Accessing-to-Objects-custom-fields-in-an-Aplication-Diplay-Template). Then, getting a full article rendered with a specific template is just: <#assign documentCard = restClient.get("/headless-delivery/v1.0/structured-contents/${resourcePrimKey}/rendered-content/SOME_TEMPLATE") /> That works really nice and I start replacing my custom contributors written in java with these calls. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited With all things FreeMarker, you should always do performance tests on things like this, preferrably using an Asset Publisher with 20+ instances rendered on a page, then look at your page rendering performance. It's not going to tell you how the site will operate when thousands of users are trying to invoke FM at the same time for all of this rendering, but it can tell you if perhaps you're adding overhead that you might not otherwise see until under load. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski David H Nebinger 1 Month Ago - Edited That's a good point, thanks! I'll look closer at the performance of these templates. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited Since it is in a FreeMarker template, the template is making the rest call (so server side synchronous processing), retrieving the result, parsing the json, keeping part of it around for the render needs, discarding the response when done (so a GC activity), ... Just because restClient() is in there won't mean there isn't a cost involved with using it... Please sign in to reply. Reply as... Cancel Christoph Rabel David H Nebinger 1 Month Ago - Edited Regarding performance: The recent addition of "builtin" glowroot is really awesome. We are using it for several years now and it helped us uncover and fix lots of performance issues. The small cost of instrumentation was always quickly offset by the metrics you get. https://learn.liferay.com/w/dxp/system-administration/using-glowroot-with-liferay (Note: If you use an older Liferay version, glowroot can be added "manually". There's a guide here in the blog somewhere) Please sign in to reply. Reply as... Cancel David H Nebinger Christoph Rabel 1 Month Ago - Edited The best part about the Glowroot integration is the inclusion of a custom plugin which monitors FreeMarker rendering times, created by our very own Fabian Bouché... The plugin can help visualize FreeMarker-based performance issues. Please sign in to reply. Reply as... Cancel
Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Hi Krzysztof, I am the Product Manager for the Headless Infrastructure and I'd like to know if you could comment on the difficulties you face so we can improve their capabilities. About APIs not being available for guest users, that's just part of our policy of being secure by default, so we do not make it easy for unwanted users to attack your solution. However, it is possible to provide access for Guest users. For that you need to configure Service Access Policies, so they only have access to the APIs you want to. You can find information about how to configure it here: https://learn.liferay.com/w/dxp/headless-delivery/consuming-apis/making-unauthenticated-requests Please sign in to reply. Reply as... Cancel Christoph Rabel Pablo Agulla 1 Month Ago - Edited Hi Pablo, First of all: the documentation. It's short: https://learn.liferay.com/w/dxp/headless-delivery/apis-with-rest-builder/producing-and-implementing-apis-with-rest-builder And there's a lot of things I don't know. How do I get the User in my rest service? Or the request? It's probably in the thread local, but I'd rather write something like this: public Response doSomething(MyDTO dat, @Context User user, @Context Company company) and have it directly available. (I can do this, I have implemented custom contexts). Also: How can I return multiple response codes from the implementation? With a custom implementation I can send 200 and 204 and whatever I want from the same method: return Response.status(someCalculatedResponseStatus).build(); Just throwing an exception and 200 OK does not cut it. Maybe this is all a documentation issue, but ... - Public/Protected: Pretty much all our APIs need to be accessible public and private. I don't want to configure Service Access Policies. I don't want to click through several environments to allow access to a new method. Using annotations and osgi properties + custom code is far more convenient. Also: I really rarely need an openapi specification, most calls are just used in the project and not shared with external implementors. So, it doesn't save me anything. It is simply easier to implement this stuff directly. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hi Pablo, Thank you for joining our discussion, it's great to be able to discuss the issues directly with the author! I played with the API again today (on 2023.Q4.2 hotfix-134) and I tried to open /o/headless-delivery/v1.0/structured-contents/{articleId} to the public as we often need access to web content fields. The major issue is that it still reveals too much information, like author, all the web content technical details but also internal categories (from which I revoked Guest permission!). So it is definitely not fit for public use for now. What I noticed as well is that it's not very consistent in terms of how it uses the underlying Liferay data, for example the GraphQL API uses siteKey (group key) to identify the site and the REST API uses the old groupId (called siteId, which also creates some confusion). For retrieving web content, some methods use articleId and others use resourcePrimKey from what I remember (both named in a different, non-liferay way). What's more, many of these values are difficult to obtain on Display Pages or within Collections, where they could potentially be used. What I usually need to implement is to retrieve the Web Content data from request (either as JournalArticle or as InfoItem within the Collection), get the appropriate key, and save it somewhere on the page so the front-end developer can call the API from JS. I'll keep experimenting with the API and let you know if I have any new findings. I'll also mention you on slack discussion with David, about how much data gets revealed by the API. Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Thanks for your comment, I'll take a look. For example, about "The major issue is that it still reveals too much information, like author, all the web content technical details" -> we try to expose no more information that it's already available in the UI (like here I'm seeing the author of your comment), or for the web content technical details, the needed information to be able to render/consume the content. I'm referring to field Types, contentStructureId, etc. About the internal categories, I'm reviewing it internally. About "for example the GraphQL API uses siteKey (group key) to identify the site and the REST API uses the old groupId (called siteId, which also creates some confusion)" -> as we started with rest, we went for siteId, but then realized that it's not easy to use, so for GraphQL we did a better job with siteKey. For not breaking compatibility, we kept in REST the name of the parameter to siteId, but actually, it can work with the siteName as well. "For retrieving web content, some methods use articleId and others use resourcePrimKey from what I remember" -> we expanded the possibilities, so now we support many parameters to retrieve the content. "(both named in a different, non-liferay way)" -> we consciouly decoupled the API datamodel from the db data model, so in case we decide to change the db data model, we can keep the API working I hope my answers at least gives you context about why our APIs work the way they do. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Thank you for the clarifications, they shed light on the design decisions behind the current API architecture. In general, the API is very useful and it really accelerates the development. It's easier to use it to retrieve the data from the backend, much better than using traditional local services. However, I have a suggestion regarding the OpenAPI schema documentation. For instance, the getStructuredContent method simply states: "Retrieves the structured content via its ID". This doesn't say much and a potential developer could go to the Web Content in Liferay, open an article, copy the "Article ID", paste it in the API and... receive 404. A brief note clarifying that the required parameter is the resourcePrimKey from the JournalArticle would help and save a lot of time. Moreover, it could say that this method fetches the most recent approved version of the article (btw, for some reason there is no info on the article version in the reply). Otherwise, newcomers to Liferay will very quickly get frustrated with the product as they won't be able to get the simple things done. Not because of the API itself, but because of the lack of knowledge and documentation. Another area of concern is the data exposure. The current approach to privacy is well-suited for Intranets or Internal Knowledge Bases, where each user is a known and trusted entity. For public, corporate websites though, the rules are different. The unauthenticated visitors should only access content as it is presented within fragments or rendered by Freemarker templates. They don't have access to Liferay's UI. Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed. Because of the above, for now, we can't expose the current API to the public. Fortunately, we discovered the great restClient which already helps us to skip all the terrible Freemarker development on top of journalArticle's XML and local services. Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Hi Krzysztof, About the IDs, we wanted to "decouple" the API world from the internal world, so the ID you need to use to retrieve a single content is the one provided when asking for the collection: "http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents". If at some point we decide (I know, 99% chance of not happening) change Ids from resourcePrimKey to ArticleId, we will not break the API contract. " it could say that this method fetches the most recent approved version of the article" -> yes, it fetches the last version only of the approved articles. This was decided taking into account your comment about data exposure, in delivery we do not have information about version. To retrieve all articles in different status and with version information, you can use the headless admin content API: http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents Regarding your comment about data exposure: "Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed." I understand your concern, however that depends on the use case. We have customers who use web content for blogging, or to have a section like "news" in their public websites where information about the author, or publication dates are needed, so our reasoning was to not expose more information that we are exposing for example for blogs or information you can expose in the UI. Having said this, we are working on a feature, currently in Beta, that allows you to define custom endpoints for Objects, exposing only the information you are interested in. It's still in beta: https://learn.liferay.com/w/dxp/headless-delivery/api-builder As we expand the Object use cases, you will be able to apply it to more places. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hello Pablo, I understand the concept of separating the API world from the internal world, but my concern is the documentation. I have been working with Liferay for many years, so I can identify the Liferay entities that each API artifact represents (even by going through the sources). However, If I give this API to a front-end developer not familiar with Liferay, he'll be completely lost. All the entity and parameter names are different than in Liferay (both UI and the database) and the only documentation that says about it (https://help.liferay.com/hc/en-us/articles/360028727072-OpenAPI-Profiles) is very limited and actually is outdated. For example, it mentions ContentSet (in DXP 2023.Q4.2 it seems to be renamed to ContentSetElement), which represents the internal AssetListEntry (a term known only to Liferay experts) and there is no mention of Collections at all. Other labels are similar, API introduces a third layer of labeling for Liferay artifacts (after UI and database/services) so it brings even more confusion to the platform that is already very complex. That brings out a problem that Liferay has had for many years. It is a really feature-rich and versatile platform, but due to the lack of documentation and common best practices (caused by some architectural inconsistencies), a lot of Liferay projects are poorly implemented, not fully leveraging the Liferay powers and with lots of custom coding that could have been avoided. In terms of the used keys, it makes total sense if the application is built entirely on top of the API (so the keys come from collections). However, in a Liferay world, there are lots of "hybrid" apps, that utilize Collection Fragments or Display Pages from where we need to call the API to reach certain data that are not retrievable by standard fragment mappings. I also tried the API builder some time ago and it looked very promising. From what I understand though, it works only with Objects, so we can't use it until we migrate away from Web Content. Thank you for mentioning the headless-admin-content API, I haven't looked into that yet, so I'll check it in the next couple of days. Thanks again for your comments and your input to this discussion! Please sign in to reply. Reply as... Cancel
Christoph Rabel Pablo Agulla 1 Month Ago - Edited Hi Pablo, First of all: the documentation. It's short: https://learn.liferay.com/w/dxp/headless-delivery/apis-with-rest-builder/producing-and-implementing-apis-with-rest-builder And there's a lot of things I don't know. How do I get the User in my rest service? Or the request? It's probably in the thread local, but I'd rather write something like this: public Response doSomething(MyDTO dat, @Context User user, @Context Company company) and have it directly available. (I can do this, I have implemented custom contexts). Also: How can I return multiple response codes from the implementation? With a custom implementation I can send 200 and 204 and whatever I want from the same method: return Response.status(someCalculatedResponseStatus).build(); Just throwing an exception and 200 OK does not cut it. Maybe this is all a documentation issue, but ... - Public/Protected: Pretty much all our APIs need to be accessible public and private. I don't want to configure Service Access Policies. I don't want to click through several environments to allow access to a new method. Using annotations and osgi properties + custom code is far more convenient. Also: I really rarely need an openapi specification, most calls are just used in the project and not shared with external implementors. So, it doesn't save me anything. It is simply easier to implement this stuff directly. Please sign in to reply. Reply as... Cancel
Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hi Pablo, Thank you for joining our discussion, it's great to be able to discuss the issues directly with the author! I played with the API again today (on 2023.Q4.2 hotfix-134) and I tried to open /o/headless-delivery/v1.0/structured-contents/{articleId} to the public as we often need access to web content fields. The major issue is that it still reveals too much information, like author, all the web content technical details but also internal categories (from which I revoked Guest permission!). So it is definitely not fit for public use for now. What I noticed as well is that it's not very consistent in terms of how it uses the underlying Liferay data, for example the GraphQL API uses siteKey (group key) to identify the site and the REST API uses the old groupId (called siteId, which also creates some confusion). For retrieving web content, some methods use articleId and others use resourcePrimKey from what I remember (both named in a different, non-liferay way). What's more, many of these values are difficult to obtain on Display Pages or within Collections, where they could potentially be used. What I usually need to implement is to retrieve the Web Content data from request (either as JournalArticle or as InfoItem within the Collection), get the appropriate key, and save it somewhere on the page so the front-end developer can call the API from JS. I'll keep experimenting with the API and let you know if I have any new findings. I'll also mention you on slack discussion with David, about how much data gets revealed by the API. Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Thanks for your comment, I'll take a look. For example, about "The major issue is that it still reveals too much information, like author, all the web content technical details" -> we try to expose no more information that it's already available in the UI (like here I'm seeing the author of your comment), or for the web content technical details, the needed information to be able to render/consume the content. I'm referring to field Types, contentStructureId, etc. About the internal categories, I'm reviewing it internally. About "for example the GraphQL API uses siteKey (group key) to identify the site and the REST API uses the old groupId (called siteId, which also creates some confusion)" -> as we started with rest, we went for siteId, but then realized that it's not easy to use, so for GraphQL we did a better job with siteKey. For not breaking compatibility, we kept in REST the name of the parameter to siteId, but actually, it can work with the siteName as well. "For retrieving web content, some methods use articleId and others use resourcePrimKey from what I remember" -> we expanded the possibilities, so now we support many parameters to retrieve the content. "(both named in a different, non-liferay way)" -> we consciouly decoupled the API datamodel from the db data model, so in case we decide to change the db data model, we can keep the API working I hope my answers at least gives you context about why our APIs work the way they do. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Thank you for the clarifications, they shed light on the design decisions behind the current API architecture. In general, the API is very useful and it really accelerates the development. It's easier to use it to retrieve the data from the backend, much better than using traditional local services. However, I have a suggestion regarding the OpenAPI schema documentation. For instance, the getStructuredContent method simply states: "Retrieves the structured content via its ID". This doesn't say much and a potential developer could go to the Web Content in Liferay, open an article, copy the "Article ID", paste it in the API and... receive 404. A brief note clarifying that the required parameter is the resourcePrimKey from the JournalArticle would help and save a lot of time. Moreover, it could say that this method fetches the most recent approved version of the article (btw, for some reason there is no info on the article version in the reply). Otherwise, newcomers to Liferay will very quickly get frustrated with the product as they won't be able to get the simple things done. Not because of the API itself, but because of the lack of knowledge and documentation. Another area of concern is the data exposure. The current approach to privacy is well-suited for Intranets or Internal Knowledge Bases, where each user is a known and trusted entity. For public, corporate websites though, the rules are different. The unauthenticated visitors should only access content as it is presented within fragments or rendered by Freemarker templates. They don't have access to Liferay's UI. Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed. Because of the above, for now, we can't expose the current API to the public. Fortunately, we discovered the great restClient which already helps us to skip all the terrible Freemarker development on top of journalArticle's XML and local services. Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Hi Krzysztof, About the IDs, we wanted to "decouple" the API world from the internal world, so the ID you need to use to retrieve a single content is the one provided when asking for the collection: "http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents". If at some point we decide (I know, 99% chance of not happening) change Ids from resourcePrimKey to ArticleId, we will not break the API contract. " it could say that this method fetches the most recent approved version of the article" -> yes, it fetches the last version only of the approved articles. This was decided taking into account your comment about data exposure, in delivery we do not have information about version. To retrieve all articles in different status and with version information, you can use the headless admin content API: http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents Regarding your comment about data exposure: "Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed." I understand your concern, however that depends on the use case. We have customers who use web content for blogging, or to have a section like "news" in their public websites where information about the author, or publication dates are needed, so our reasoning was to not expose more information that we are exposing for example for blogs or information you can expose in the UI. Having said this, we are working on a feature, currently in Beta, that allows you to define custom endpoints for Objects, exposing only the information you are interested in. It's still in beta: https://learn.liferay.com/w/dxp/headless-delivery/api-builder As we expand the Object use cases, you will be able to apply it to more places. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hello Pablo, I understand the concept of separating the API world from the internal world, but my concern is the documentation. I have been working with Liferay for many years, so I can identify the Liferay entities that each API artifact represents (even by going through the sources). However, If I give this API to a front-end developer not familiar with Liferay, he'll be completely lost. All the entity and parameter names are different than in Liferay (both UI and the database) and the only documentation that says about it (https://help.liferay.com/hc/en-us/articles/360028727072-OpenAPI-Profiles) is very limited and actually is outdated. For example, it mentions ContentSet (in DXP 2023.Q4.2 it seems to be renamed to ContentSetElement), which represents the internal AssetListEntry (a term known only to Liferay experts) and there is no mention of Collections at all. Other labels are similar, API introduces a third layer of labeling for Liferay artifacts (after UI and database/services) so it brings even more confusion to the platform that is already very complex. That brings out a problem that Liferay has had for many years. It is a really feature-rich and versatile platform, but due to the lack of documentation and common best practices (caused by some architectural inconsistencies), a lot of Liferay projects are poorly implemented, not fully leveraging the Liferay powers and with lots of custom coding that could have been avoided. In terms of the used keys, it makes total sense if the application is built entirely on top of the API (so the keys come from collections). However, in a Liferay world, there are lots of "hybrid" apps, that utilize Collection Fragments or Display Pages from where we need to call the API to reach certain data that are not retrievable by standard fragment mappings. I also tried the API builder some time ago and it looked very promising. From what I understand though, it works only with Objects, so we can't use it until we migrate away from Web Content. Thank you for mentioning the headless-admin-content API, I haven't looked into that yet, so I'll check it in the next couple of days. Thanks again for your comments and your input to this discussion! Please sign in to reply. Reply as... Cancel
Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Thanks for your comment, I'll take a look. For example, about "The major issue is that it still reveals too much information, like author, all the web content technical details" -> we try to expose no more information that it's already available in the UI (like here I'm seeing the author of your comment), or for the web content technical details, the needed information to be able to render/consume the content. I'm referring to field Types, contentStructureId, etc. About the internal categories, I'm reviewing it internally. About "for example the GraphQL API uses siteKey (group key) to identify the site and the REST API uses the old groupId (called siteId, which also creates some confusion)" -> as we started with rest, we went for siteId, but then realized that it's not easy to use, so for GraphQL we did a better job with siteKey. For not breaking compatibility, we kept in REST the name of the parameter to siteId, but actually, it can work with the siteName as well. "For retrieving web content, some methods use articleId and others use resourcePrimKey from what I remember" -> we expanded the possibilities, so now we support many parameters to retrieve the content. "(both named in a different, non-liferay way)" -> we consciouly decoupled the API datamodel from the db data model, so in case we decide to change the db data model, we can keep the API working I hope my answers at least gives you context about why our APIs work the way they do. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Thank you for the clarifications, they shed light on the design decisions behind the current API architecture. In general, the API is very useful and it really accelerates the development. It's easier to use it to retrieve the data from the backend, much better than using traditional local services. However, I have a suggestion regarding the OpenAPI schema documentation. For instance, the getStructuredContent method simply states: "Retrieves the structured content via its ID". This doesn't say much and a potential developer could go to the Web Content in Liferay, open an article, copy the "Article ID", paste it in the API and... receive 404. A brief note clarifying that the required parameter is the resourcePrimKey from the JournalArticle would help and save a lot of time. Moreover, it could say that this method fetches the most recent approved version of the article (btw, for some reason there is no info on the article version in the reply). Otherwise, newcomers to Liferay will very quickly get frustrated with the product as they won't be able to get the simple things done. Not because of the API itself, but because of the lack of knowledge and documentation. Another area of concern is the data exposure. The current approach to privacy is well-suited for Intranets or Internal Knowledge Bases, where each user is a known and trusted entity. For public, corporate websites though, the rules are different. The unauthenticated visitors should only access content as it is presented within fragments or rendered by Freemarker templates. They don't have access to Liferay's UI. Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed. Because of the above, for now, we can't expose the current API to the public. Fortunately, we discovered the great restClient which already helps us to skip all the terrible Freemarker development on top of journalArticle's XML and local services. Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Hi Krzysztof, About the IDs, we wanted to "decouple" the API world from the internal world, so the ID you need to use to retrieve a single content is the one provided when asking for the collection: "http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents". If at some point we decide (I know, 99% chance of not happening) change Ids from resourcePrimKey to ArticleId, we will not break the API contract. " it could say that this method fetches the most recent approved version of the article" -> yes, it fetches the last version only of the approved articles. This was decided taking into account your comment about data exposure, in delivery we do not have information about version. To retrieve all articles in different status and with version information, you can use the headless admin content API: http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents Regarding your comment about data exposure: "Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed." I understand your concern, however that depends on the use case. We have customers who use web content for blogging, or to have a section like "news" in their public websites where information about the author, or publication dates are needed, so our reasoning was to not expose more information that we are exposing for example for blogs or information you can expose in the UI. Having said this, we are working on a feature, currently in Beta, that allows you to define custom endpoints for Objects, exposing only the information you are interested in. It's still in beta: https://learn.liferay.com/w/dxp/headless-delivery/api-builder As we expand the Object use cases, you will be able to apply it to more places. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hello Pablo, I understand the concept of separating the API world from the internal world, but my concern is the documentation. I have been working with Liferay for many years, so I can identify the Liferay entities that each API artifact represents (even by going through the sources). However, If I give this API to a front-end developer not familiar with Liferay, he'll be completely lost. All the entity and parameter names are different than in Liferay (both UI and the database) and the only documentation that says about it (https://help.liferay.com/hc/en-us/articles/360028727072-OpenAPI-Profiles) is very limited and actually is outdated. For example, it mentions ContentSet (in DXP 2023.Q4.2 it seems to be renamed to ContentSetElement), which represents the internal AssetListEntry (a term known only to Liferay experts) and there is no mention of Collections at all. Other labels are similar, API introduces a third layer of labeling for Liferay artifacts (after UI and database/services) so it brings even more confusion to the platform that is already very complex. That brings out a problem that Liferay has had for many years. It is a really feature-rich and versatile platform, but due to the lack of documentation and common best practices (caused by some architectural inconsistencies), a lot of Liferay projects are poorly implemented, not fully leveraging the Liferay powers and with lots of custom coding that could have been avoided. In terms of the used keys, it makes total sense if the application is built entirely on top of the API (so the keys come from collections). However, in a Liferay world, there are lots of "hybrid" apps, that utilize Collection Fragments or Display Pages from where we need to call the API to reach certain data that are not retrievable by standard fragment mappings. I also tried the API builder some time ago and it looked very promising. From what I understand though, it works only with Objects, so we can't use it until we migrate away from Web Content. Thank you for mentioning the headless-admin-content API, I haven't looked into that yet, so I'll check it in the next couple of days. Thanks again for your comments and your input to this discussion! Please sign in to reply. Reply as... Cancel
Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Thank you for the clarifications, they shed light on the design decisions behind the current API architecture. In general, the API is very useful and it really accelerates the development. It's easier to use it to retrieve the data from the backend, much better than using traditional local services. However, I have a suggestion regarding the OpenAPI schema documentation. For instance, the getStructuredContent method simply states: "Retrieves the structured content via its ID". This doesn't say much and a potential developer could go to the Web Content in Liferay, open an article, copy the "Article ID", paste it in the API and... receive 404. A brief note clarifying that the required parameter is the resourcePrimKey from the JournalArticle would help and save a lot of time. Moreover, it could say that this method fetches the most recent approved version of the article (btw, for some reason there is no info on the article version in the reply). Otherwise, newcomers to Liferay will very quickly get frustrated with the product as they won't be able to get the simple things done. Not because of the API itself, but because of the lack of knowledge and documentation. Another area of concern is the data exposure. The current approach to privacy is well-suited for Intranets or Internal Knowledge Bases, where each user is a known and trusted entity. For public, corporate websites though, the rules are different. The unauthenticated visitors should only access content as it is presented within fragments or rendered by Freemarker templates. They don't have access to Liferay's UI. Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed. Because of the above, for now, we can't expose the current API to the public. Fortunately, we discovered the great restClient which already helps us to skip all the terrible Freemarker development on top of journalArticle's XML and local services. Please sign in to reply. Reply as... Cancel Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Hi Krzysztof, About the IDs, we wanted to "decouple" the API world from the internal world, so the ID you need to use to retrieve a single content is the one provided when asking for the collection: "http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents". If at some point we decide (I know, 99% chance of not happening) change Ids from resourcePrimKey to ArticleId, we will not break the API contract. " it could say that this method fetches the most recent approved version of the article" -> yes, it fetches the last version only of the approved articles. This was decided taking into account your comment about data exposure, in delivery we do not have information about version. To retrieve all articles in different status and with version information, you can use the headless admin content API: http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents Regarding your comment about data exposure: "Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed." I understand your concern, however that depends on the use case. We have customers who use web content for blogging, or to have a section like "news" in their public websites where information about the author, or publication dates are needed, so our reasoning was to not expose more information that we are exposing for example for blogs or information you can expose in the UI. Having said this, we are working on a feature, currently in Beta, that allows you to define custom endpoints for Objects, exposing only the information you are interested in. It's still in beta: https://learn.liferay.com/w/dxp/headless-delivery/api-builder As we expand the Object use cases, you will be able to apply it to more places. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hello Pablo, I understand the concept of separating the API world from the internal world, but my concern is the documentation. I have been working with Liferay for many years, so I can identify the Liferay entities that each API artifact represents (even by going through the sources). However, If I give this API to a front-end developer not familiar with Liferay, he'll be completely lost. All the entity and parameter names are different than in Liferay (both UI and the database) and the only documentation that says about it (https://help.liferay.com/hc/en-us/articles/360028727072-OpenAPI-Profiles) is very limited and actually is outdated. For example, it mentions ContentSet (in DXP 2023.Q4.2 it seems to be renamed to ContentSetElement), which represents the internal AssetListEntry (a term known only to Liferay experts) and there is no mention of Collections at all. Other labels are similar, API introduces a third layer of labeling for Liferay artifacts (after UI and database/services) so it brings even more confusion to the platform that is already very complex. That brings out a problem that Liferay has had for many years. It is a really feature-rich and versatile platform, but due to the lack of documentation and common best practices (caused by some architectural inconsistencies), a lot of Liferay projects are poorly implemented, not fully leveraging the Liferay powers and with lots of custom coding that could have been avoided. In terms of the used keys, it makes total sense if the application is built entirely on top of the API (so the keys come from collections). However, in a Liferay world, there are lots of "hybrid" apps, that utilize Collection Fragments or Display Pages from where we need to call the API to reach certain data that are not retrievable by standard fragment mappings. I also tried the API builder some time ago and it looked very promising. From what I understand though, it works only with Objects, so we can't use it until we migrate away from Web Content. Thank you for mentioning the headless-admin-content API, I haven't looked into that yet, so I'll check it in the next couple of days. Thanks again for your comments and your input to this discussion! Please sign in to reply. Reply as... Cancel
Pablo Agulla Krzysztof Gołębiowski 1 Month Ago Hi Krzysztof, About the IDs, we wanted to "decouple" the API world from the internal world, so the ID you need to use to retrieve a single content is the one provided when asking for the collection: "http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents". If at some point we decide (I know, 99% chance of not happening) change Ids from resourcePrimKey to ArticleId, we will not break the API contract. " it could say that this method fetches the most recent approved version of the article" -> yes, it fetches the last version only of the approved articles. This was decided taking into account your comment about data exposure, in delivery we do not have information about version. To retrieve all articles in different status and with version information, you can use the headless admin content API: http://localhost:8080/o/headless-admin-content/v1.0/sites/{siteId}/structured-contents Regarding your comment about data exposure: "Disclosing the identity of an employee who published the article is undesirable and will not be allowed by any security audit. Also, all the other metadata like creation or publication dates are very often considered internal and should not be revealed." I understand your concern, however that depends on the use case. We have customers who use web content for blogging, or to have a section like "news" in their public websites where information about the author, or publication dates are needed, so our reasoning was to not expose more information that we are exposing for example for blogs or information you can expose in the UI. Having said this, we are working on a feature, currently in Beta, that allows you to define custom endpoints for Objects, exposing only the information you are interested in. It's still in beta: https://learn.liferay.com/w/dxp/headless-delivery/api-builder As we expand the Object use cases, you will be able to apply it to more places. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hello Pablo, I understand the concept of separating the API world from the internal world, but my concern is the documentation. I have been working with Liferay for many years, so I can identify the Liferay entities that each API artifact represents (even by going through the sources). However, If I give this API to a front-end developer not familiar with Liferay, he'll be completely lost. All the entity and parameter names are different than in Liferay (both UI and the database) and the only documentation that says about it (https://help.liferay.com/hc/en-us/articles/360028727072-OpenAPI-Profiles) is very limited and actually is outdated. For example, it mentions ContentSet (in DXP 2023.Q4.2 it seems to be renamed to ContentSetElement), which represents the internal AssetListEntry (a term known only to Liferay experts) and there is no mention of Collections at all. Other labels are similar, API introduces a third layer of labeling for Liferay artifacts (after UI and database/services) so it brings even more confusion to the platform that is already very complex. That brings out a problem that Liferay has had for many years. It is a really feature-rich and versatile platform, but due to the lack of documentation and common best practices (caused by some architectural inconsistencies), a lot of Liferay projects are poorly implemented, not fully leveraging the Liferay powers and with lots of custom coding that could have been avoided. In terms of the used keys, it makes total sense if the application is built entirely on top of the API (so the keys come from collections). However, in a Liferay world, there are lots of "hybrid" apps, that utilize Collection Fragments or Display Pages from where we need to call the API to reach certain data that are not retrievable by standard fragment mappings. I also tried the API builder some time ago and it looked very promising. From what I understand though, it works only with Objects, so we can't use it until we migrate away from Web Content. Thank you for mentioning the headless-admin-content API, I haven't looked into that yet, so I'll check it in the next couple of days. Thanks again for your comments and your input to this discussion! Please sign in to reply. Reply as... Cancel
Krzysztof Gołębiowski Pablo Agulla 1 Month Ago - Edited Hello Pablo, I understand the concept of separating the API world from the internal world, but my concern is the documentation. I have been working with Liferay for many years, so I can identify the Liferay entities that each API artifact represents (even by going through the sources). However, If I give this API to a front-end developer not familiar with Liferay, he'll be completely lost. All the entity and parameter names are different than in Liferay (both UI and the database) and the only documentation that says about it (https://help.liferay.com/hc/en-us/articles/360028727072-OpenAPI-Profiles) is very limited and actually is outdated. For example, it mentions ContentSet (in DXP 2023.Q4.2 it seems to be renamed to ContentSetElement), which represents the internal AssetListEntry (a term known only to Liferay experts) and there is no mention of Collections at all. Other labels are similar, API introduces a third layer of labeling for Liferay artifacts (after UI and database/services) so it brings even more confusion to the platform that is already very complex. That brings out a problem that Liferay has had for many years. It is a really feature-rich and versatile platform, but due to the lack of documentation and common best practices (caused by some architectural inconsistencies), a lot of Liferay projects are poorly implemented, not fully leveraging the Liferay powers and with lots of custom coding that could have been avoided. In terms of the used keys, it makes total sense if the application is built entirely on top of the API (so the keys come from collections). However, in a Liferay world, there are lots of "hybrid" apps, that utilize Collection Fragments or Display Pages from where we need to call the API to reach certain data that are not retrievable by standard fragment mappings. I also tried the API builder some time ago and it looked very promising. From what I understand though, it works only with Objects, so we can't use it until we migrate away from Web Content. Thank you for mentioning the headless-admin-content API, I haven't looked into that yet, so I'll check it in the next couple of days. Thanks again for your comments and your input to this discussion! Please sign in to reply. Reply as... Cancel
Christoph Rabel Krzysztof Gołębiowski 1 Month Ago - Edited Hi Krzysztof, yes, it's a while. I will most likely go to the Devcon, especially since it's basically "nearby" in Budapest. The virtual thing was not my piece of cake, but a real Devcon ... We usually use the contributors directly from the fragment or execute macros (we deploy them and use <#include "/<bundle>_SERVLET_CONTEXT_/path/xy.ftl" /> to load them, but your solution is pretty nice too. I really like the mapping to paragraphs idea. Since we currently deploy the fragments using the fragment toolkit, it didn't matter to me, but since the toolkit was deprecated with 2024.Q1, I was already pondering how to do it in the future. We also build our custom REST services with the OSGI whiteboard. More convenient. Please sign in to reply. Reply as... Cancel David H Nebinger Christoph Rabel 1 Month Ago - Edited The fragments toolkit is deprecated, yes, but not going away soon. Team is working on another mechanism for fragments export/import, and the toolkit will not go away before that mechanism is launched and tested. In the interim, you are safe to use the toolkit... Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Christoph Rabel 1 Month Ago - Edited Another thing I rediscovered a couple of days ago (I've heard about it on the last Devcon for the first time) was the restClient tool which allows you to call every headless-delivery or custom objects API from the ftl template (check https://help.liferay.com/hc/en-us/articles/14840864106765-Accessing-to-Objects-custom-fields-in-an-Aplication-Diplay-Template). Then, getting a full article rendered with a specific template is just: <#assign documentCard = restClient.get("/headless-delivery/v1.0/structured-contents/${resourcePrimKey}/rendered-content/SOME_TEMPLATE") /> That works really nice and I start replacing my custom contributors written in java with these calls. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited With all things FreeMarker, you should always do performance tests on things like this, preferrably using an Asset Publisher with 20+ instances rendered on a page, then look at your page rendering performance. It's not going to tell you how the site will operate when thousands of users are trying to invoke FM at the same time for all of this rendering, but it can tell you if perhaps you're adding overhead that you might not otherwise see until under load. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski David H Nebinger 1 Month Ago - Edited That's a good point, thanks! I'll look closer at the performance of these templates. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited Since it is in a FreeMarker template, the template is making the rest call (so server side synchronous processing), retrieving the result, parsing the json, keeping part of it around for the render needs, discarding the response when done (so a GC activity), ... Just because restClient() is in there won't mean there isn't a cost involved with using it... Please sign in to reply. Reply as... Cancel Christoph Rabel David H Nebinger 1 Month Ago - Edited Regarding performance: The recent addition of "builtin" glowroot is really awesome. We are using it for several years now and it helped us uncover and fix lots of performance issues. The small cost of instrumentation was always quickly offset by the metrics you get. https://learn.liferay.com/w/dxp/system-administration/using-glowroot-with-liferay (Note: If you use an older Liferay version, glowroot can be added "manually". There's a guide here in the blog somewhere) Please sign in to reply. Reply as... Cancel David H Nebinger Christoph Rabel 1 Month Ago - Edited The best part about the Glowroot integration is the inclusion of a custom plugin which monitors FreeMarker rendering times, created by our very own Fabian Bouché... The plugin can help visualize FreeMarker-based performance issues. Please sign in to reply. Reply as... Cancel
David H Nebinger Christoph Rabel 1 Month Ago - Edited The fragments toolkit is deprecated, yes, but not going away soon. Team is working on another mechanism for fragments export/import, and the toolkit will not go away before that mechanism is launched and tested. In the interim, you are safe to use the toolkit... Please sign in to reply. Reply as... Cancel
Krzysztof Gołębiowski Christoph Rabel 1 Month Ago - Edited Another thing I rediscovered a couple of days ago (I've heard about it on the last Devcon for the first time) was the restClient tool which allows you to call every headless-delivery or custom objects API from the ftl template (check https://help.liferay.com/hc/en-us/articles/14840864106765-Accessing-to-Objects-custom-fields-in-an-Aplication-Diplay-Template). Then, getting a full article rendered with a specific template is just: <#assign documentCard = restClient.get("/headless-delivery/v1.0/structured-contents/${resourcePrimKey}/rendered-content/SOME_TEMPLATE") /> That works really nice and I start replacing my custom contributors written in java with these calls. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited With all things FreeMarker, you should always do performance tests on things like this, preferrably using an Asset Publisher with 20+ instances rendered on a page, then look at your page rendering performance. It's not going to tell you how the site will operate when thousands of users are trying to invoke FM at the same time for all of this rendering, but it can tell you if perhaps you're adding overhead that you might not otherwise see until under load. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski David H Nebinger 1 Month Ago - Edited That's a good point, thanks! I'll look closer at the performance of these templates. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited Since it is in a FreeMarker template, the template is making the rest call (so server side synchronous processing), retrieving the result, parsing the json, keeping part of it around for the render needs, discarding the response when done (so a GC activity), ... Just because restClient() is in there won't mean there isn't a cost involved with using it... Please sign in to reply. Reply as... Cancel Christoph Rabel David H Nebinger 1 Month Ago - Edited Regarding performance: The recent addition of "builtin" glowroot is really awesome. We are using it for several years now and it helped us uncover and fix lots of performance issues. The small cost of instrumentation was always quickly offset by the metrics you get. https://learn.liferay.com/w/dxp/system-administration/using-glowroot-with-liferay (Note: If you use an older Liferay version, glowroot can be added "manually". There's a guide here in the blog somewhere) Please sign in to reply. Reply as... Cancel David H Nebinger Christoph Rabel 1 Month Ago - Edited The best part about the Glowroot integration is the inclusion of a custom plugin which monitors FreeMarker rendering times, created by our very own Fabian Bouché... The plugin can help visualize FreeMarker-based performance issues. Please sign in to reply. Reply as... Cancel
David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited With all things FreeMarker, you should always do performance tests on things like this, preferrably using an Asset Publisher with 20+ instances rendered on a page, then look at your page rendering performance. It's not going to tell you how the site will operate when thousands of users are trying to invoke FM at the same time for all of this rendering, but it can tell you if perhaps you're adding overhead that you might not otherwise see until under load. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski David H Nebinger 1 Month Ago - Edited That's a good point, thanks! I'll look closer at the performance of these templates. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited Since it is in a FreeMarker template, the template is making the rest call (so server side synchronous processing), retrieving the result, parsing the json, keeping part of it around for the render needs, discarding the response when done (so a GC activity), ... Just because restClient() is in there won't mean there isn't a cost involved with using it... Please sign in to reply. Reply as... Cancel Christoph Rabel David H Nebinger 1 Month Ago - Edited Regarding performance: The recent addition of "builtin" glowroot is really awesome. We are using it for several years now and it helped us uncover and fix lots of performance issues. The small cost of instrumentation was always quickly offset by the metrics you get. https://learn.liferay.com/w/dxp/system-administration/using-glowroot-with-liferay (Note: If you use an older Liferay version, glowroot can be added "manually". There's a guide here in the blog somewhere) Please sign in to reply. Reply as... Cancel David H Nebinger Christoph Rabel 1 Month Ago - Edited The best part about the Glowroot integration is the inclusion of a custom plugin which monitors FreeMarker rendering times, created by our very own Fabian Bouché... The plugin can help visualize FreeMarker-based performance issues. Please sign in to reply. Reply as... Cancel
Krzysztof Gołębiowski David H Nebinger 1 Month Ago - Edited That's a good point, thanks! I'll look closer at the performance of these templates. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited Since it is in a FreeMarker template, the template is making the rest call (so server side synchronous processing), retrieving the result, parsing the json, keeping part of it around for the render needs, discarding the response when done (so a GC activity), ... Just because restClient() is in there won't mean there isn't a cost involved with using it... Please sign in to reply. Reply as... Cancel Christoph Rabel David H Nebinger 1 Month Ago - Edited Regarding performance: The recent addition of "builtin" glowroot is really awesome. We are using it for several years now and it helped us uncover and fix lots of performance issues. The small cost of instrumentation was always quickly offset by the metrics you get. https://learn.liferay.com/w/dxp/system-administration/using-glowroot-with-liferay (Note: If you use an older Liferay version, glowroot can be added "manually". There's a guide here in the blog somewhere) Please sign in to reply. Reply as... Cancel David H Nebinger Christoph Rabel 1 Month Ago - Edited The best part about the Glowroot integration is the inclusion of a custom plugin which monitors FreeMarker rendering times, created by our very own Fabian Bouché... The plugin can help visualize FreeMarker-based performance issues. Please sign in to reply. Reply as... Cancel
David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited Since it is in a FreeMarker template, the template is making the rest call (so server side synchronous processing), retrieving the result, parsing the json, keeping part of it around for the render needs, discarding the response when done (so a GC activity), ... Just because restClient() is in there won't mean there isn't a cost involved with using it... Please sign in to reply. Reply as... Cancel Christoph Rabel David H Nebinger 1 Month Ago - Edited Regarding performance: The recent addition of "builtin" glowroot is really awesome. We are using it for several years now and it helped us uncover and fix lots of performance issues. The small cost of instrumentation was always quickly offset by the metrics you get. https://learn.liferay.com/w/dxp/system-administration/using-glowroot-with-liferay (Note: If you use an older Liferay version, glowroot can be added "manually". There's a guide here in the blog somewhere) Please sign in to reply. Reply as... Cancel David H Nebinger Christoph Rabel 1 Month Ago - Edited The best part about the Glowroot integration is the inclusion of a custom plugin which monitors FreeMarker rendering times, created by our very own Fabian Bouché... The plugin can help visualize FreeMarker-based performance issues. Please sign in to reply. Reply as... Cancel
Christoph Rabel David H Nebinger 1 Month Ago - Edited Regarding performance: The recent addition of "builtin" glowroot is really awesome. We are using it for several years now and it helped us uncover and fix lots of performance issues. The small cost of instrumentation was always quickly offset by the metrics you get. https://learn.liferay.com/w/dxp/system-administration/using-glowroot-with-liferay (Note: If you use an older Liferay version, glowroot can be added "manually". There's a guide here in the blog somewhere) Please sign in to reply. Reply as... Cancel David H Nebinger Christoph Rabel 1 Month Ago - Edited The best part about the Glowroot integration is the inclusion of a custom plugin which monitors FreeMarker rendering times, created by our very own Fabian Bouché... The plugin can help visualize FreeMarker-based performance issues. Please sign in to reply. Reply as... Cancel
David H Nebinger Christoph Rabel 1 Month Ago - Edited The best part about the Glowroot integration is the inclusion of a custom plugin which monitors FreeMarker rendering times, created by our very own Fabian Bouché... The plugin can help visualize FreeMarker-based performance issues. Please sign in to reply. Reply as... Cancel
Orin Fink 1 Month Ago - Edited This is a super walkthrough to help implementation teams consider the pros and cons of the (implementation) design choices that one might make at this inflection point of LR Objects. For many of our implementations, the defect related to Localization support would be a show stopper, so hopefully that is resolved soon enough. Please sign in to reply. Reply as... Cancel
David H Nebinger 1 Month Ago - Edited The good news for both of you is that localization is supported in Objects, it's just a bug in the mapping aspects, and I've opened a ticket on that aspect so I'd expect it to get resolved at some point. The value in mapping, you don't have to worry about parsing web content or extracting field values in any way, with Objects the fields are just there and accessible, especially when using the headless APIs. Any time you're using FM, there's always a risk of FM abuse, even when you use a custom contributor. For example, if you only need 1 field from your structure, even using a contributor, you may need to parse the whole content just to get a single field value. And if you're not careful how you are getting your field values, you could be parsing the data multiple times. Objects doesn't suffer from any of that, it's one of the reasons I think Objects is really the better option for structured data than a web content structure and FM is. Sure there may be gaps and bugs, but those can be fixed. The web content is what it is, and is not going to be "fixable" for many scenarios. Give the fragment mappings another shot, they're getting better with each release and are super convenient whether using Objects or structured web contents. Please sign in to reply. Reply as... Cancel
Danielle Ardon 1 Month Ago - Edited Very interesting use case for objects, thanks! We also have been looking into objects as replacements for webcontent but are running into too much restrictions for this to be possible. However, we do use it for very simple data that just needs to be displayed in lists. I.e. like how you would use dynamic data lists in the past. I hope in the future it would be more flexible, because I love the field validation possibilities of objects, since that is something many customers ask for in webcontent to help editors with their content. I do have to disagree with you on the following: "So here's another benefit for using Objects - you get specific control panels for each one." That is indeed nice if you do not have many objects/ content types. But most of our customers have a lot of content types that would crowd the interface. I think it would be great if it would be possible to choose a category key for objects, just as you would for system settings/portlets so you can group objects in the interface. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski Danielle Ardon 1 Month Ago - Edited "So here's another benefit for using Objects - you get specific control panels for each one." That is unfortunately a problem also for us. We developed one site-scoped app with 5 object types, but for now, it is used only on one site. Currently, all these control panel entries appear on all other sites, even the ones that will never use it. I imagine that if we start creating more of these, the Control Panel will become unmanageable. It would be great to have the following options: 1) Grouping the Objects in some sort of an Application, that would have just one entry in the Control Panel 2) For Site-scoped Applications, enabling or disabling an Application for a specific site. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited Well, you could not use a control panel at all. When you enable the "Show Widget in Page Builder" option, you'll have the same interface but as a widget that you can drop on a page of the site. You can set up a page, drop the widget on it, permission the page for the select group that have access, and leave the panel link undefined. Only use the page on the site(s) that needs it, exclude it from the rest. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski David H Nebinger 1 Month Ago - Edited That would be a good solution as well, but I don't see an option to unlink it from the Control Panel. The list just shows the list of available sections. Please sign in to reply. Reply as... Cancel
Krzysztof Gołębiowski Danielle Ardon 1 Month Ago - Edited "So here's another benefit for using Objects - you get specific control panels for each one." That is unfortunately a problem also for us. We developed one site-scoped app with 5 object types, but for now, it is used only on one site. Currently, all these control panel entries appear on all other sites, even the ones that will never use it. I imagine that if we start creating more of these, the Control Panel will become unmanageable. It would be great to have the following options: 1) Grouping the Objects in some sort of an Application, that would have just one entry in the Control Panel 2) For Site-scoped Applications, enabling or disabling an Application for a specific site. Please sign in to reply. Reply as... Cancel David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited Well, you could not use a control panel at all. When you enable the "Show Widget in Page Builder" option, you'll have the same interface but as a widget that you can drop on a page of the site. You can set up a page, drop the widget on it, permission the page for the select group that have access, and leave the panel link undefined. Only use the page on the site(s) that needs it, exclude it from the rest. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski David H Nebinger 1 Month Ago - Edited That would be a good solution as well, but I don't see an option to unlink it from the Control Panel. The list just shows the list of available sections. Please sign in to reply. Reply as... Cancel
David H Nebinger Krzysztof Gołębiowski 1 Month Ago - Edited Well, you could not use a control panel at all. When you enable the "Show Widget in Page Builder" option, you'll have the same interface but as a widget that you can drop on a page of the site. You can set up a page, drop the widget on it, permission the page for the select group that have access, and leave the panel link undefined. Only use the page on the site(s) that needs it, exclude it from the rest. Please sign in to reply. Reply as... Cancel Krzysztof Gołębiowski David H Nebinger 1 Month Ago - Edited That would be a good solution as well, but I don't see an option to unlink it from the Control Panel. The list just shows the list of available sections. Please sign in to reply. Reply as... Cancel
Krzysztof Gołębiowski David H Nebinger 1 Month Ago - Edited That would be a good solution as well, but I don't see an option to unlink it from the Control Panel. The list just shows the list of available sections. Please sign in to reply. Reply as... Cancel