Soft29 blogLiferay, Alfrescohttps://soft29.ru/blog/feed/entries/atom2023-05-16T16:50:33+00:00Apache Rollerhttps://soft29.ru/blog/entry/docker-compose-combine-two-applicationsDocker compose: combine two applications under a single nginx gatewayStanislav2022-11-14T18:58:48+00:002023-01-05T20:00:32+00:00<p><font size="4">Docker compose is a really handy tool in the world of DevOps, it allows to combine multiple Docker containers within a single monolithic app. And moreover it’s possible to have a bunch of such apps hosted within a server. It sounds like a cool feature, but what if we would like to share services between some of these apps or even more: have a single gateway that relays all incoming internet communications to the appropriate app? In this post we’ll see how to handle such situation.</font></p><p><font size="4">Docker compose is a really handy tool in the world of DevOps, it allows to combine multiple Docker containers within a single monolithic app. And moreover it’s possible to have a bunch of such apps hosted within a server. It sounds like a cool feature, but what if we would like to share services between some of these apps or even more: have a single gateway that relays all incoming internet communications to the appropriate app? In this post we’ll see how to handle such situation.</font></p><p><font size="4">Let’s assume that we have two docker-compose apps: </font></p><ol><li><font size="4">one of them <a title="mailu" href="https://mailu.io/" target="_blank" rel="nofollow">mailu</a> – an smtp server, consisting of postfix, dovecot, antispam etc having a nginx server on top of them in order to relay incoming request from various mail-clients. Nginx gateway called <strong>front </strong>there: </font></li><p><pre class="yaml" style="font-family: monospace;"><span style="color: rgb(0, 127, 69);">services</span><span style="color: brown; font-weight: bold;">:
</span> <span style="color: blue;"># Core services</span><span style="color: rgb(0, 127, 69);">
front</span>:<span style="color: green;">
image</span><span style="color: brown; font-weight: bold;">: </span>$<span class="br0">{</span>DOCKER_ORG:-mailu<span class="br0">}</span>/$<span class="br0">{</span>DOCKER_PREFIX:-<span class="br0">}</span>nginx:$<span class="br0">{</span>MAILU_VERSION:-1.9<span class="br0">}</span><span style="color: green;">
restart</span><span style="color: brown; font-weight: bold;">: </span>always<span style="color: green;">
env_file</span><span style="color: brown; font-weight: bold;">: </span>mailu.env<span style="color: rgb(0, 127, 69);">
logging</span>:<span style="color: green;">
driver</span><span style="color: brown; font-weight: bold;">: </span>json-file<span style="color: rgb(0, 127, 69);">
ports</span><span style="color: brown; font-weight: bold;">:
</span> - <span style="color: rgb(207, 0, 207);">"11.220.171.213:587:587"</span>
- <span style="color: rgb(207, 0, 207);">"11.220.171.213:143:143"</span><span style="color: rgb(0, 127, 69);">
volumes</span><span style="color: brown; font-weight: bold;">:
</span> - <span style="color: rgb(207, 0, 207);">"/mailu/certs:/certs"</span>
- <span style="color: rgb(207, 0, 207);">"/mailu/overrides/nginx:/overrides:ro"</span><span style="color: rgb(0, 127, 69);">
smtp</span>:<span style="color: green;">
image</span><span style="color: brown; font-weight: bold;">: </span>$<span class="br0">{</span>DOCKER_ORG:-mailu<span class="br0">}</span>/$<span class="br0">{</span>DOCKER_PREFIX:-<span class="br0">}</span>postfix:$<span class="br0">{</span>MAILU_VERSION:-1.9<span class="br0">}</span><span style="color: rgb(0, 127, 69);">
depends_on</span><span style="color: brown; font-weight: bold;">:
</span> - front </pre><li><font size="4">another app is a Tomcat application container. It includes mysql service (<strong>db</strong>), tomcat itself (<strong>tomcat</strong>) and nginx (<strong>nginx</strong>) gateway – we do need our app to be available online through SSL:</font></li><pre class="yaml" style="font-family: monospace;"><span style="color: rgb(0, 127, 69);">services</span>:<span style="color: rgb(0, 127, 69);">
db</span>:<span style="color: green;">
image</span><span style="color: brown; font-weight: bold;">: </span>mariadb<span style="color: rgb(0, 127, 69);">
volumes</span><span style="color: brown; font-weight: bold;">:
</span> - /opt/test:/var/lib/mysql<span style="color: rgb(0, 127, 69);">
ports</span><span style="color: brown; font-weight: bold;">:
</span> - 3306:3306<span style="color: rgb(0, 127, 69);">
tomcat</span>:<span style="color: rgb(0, 127, 69);">
depends_on</span><span style="color: brown; font-weight: bold;">:
</span> - db<span style="color: green;">
image</span><span style="color: brown; font-weight: bold;">: </span>tomcat:7.0.70<span style="color: rgb(0, 127, 69);">
volumes</span><span style="color: brown; font-weight: bold;">:
</span> - ./tomcat/lib/mail.jar:/usr/local/tomcat/lib/mail.jar
- ./tomcat/webapps:/usr/local/tomcat/webapps
- ./tomcat/logs:/usr/local/tomcat/logs
- ./tomcat/mediafiles:/opt/mediafiles<span style="color: rgb(0, 127, 69);">
ports</span><span style="color: brown; font-weight: bold;">:
</span> - '8080:8080'<span style="color: rgb(0, 127, 69);">
nginx</span>:<span style="color: green;">
image</span><span style="color: brown; font-weight: bold;">: </span>nginx<span style="color: rgb(0, 127, 69);">
depends_on</span><span style="color: brown; font-weight: bold;">:
</span> - tomcat<span style="color: rgb(0, 127, 69);">
ports</span><span style="color: brown; font-weight: bold;">:
</span> - <span style="color: rgb(207, 0, 207);">"80:80"</span>
- <span style="color: rgb(207, 0, 207);">"443:443"</span><span style="color: rgb(0, 127, 69);">
volumes</span><span style="color: brown; font-weight: bold;">:
</span> - ./nginx:/etc/nginx
- /mailu/certs:/etc/nginx/certs </pre></ol><p><font size="4">As we see from our yaml compose configs – our apps use different port numbers exposed through SSL. It’s okay to have one nginx server that routes requests to mail ports (e.g., 143, 587) and also another one that routes requests to the web app exposed on port 443 (SSL) and 80. But what if our mailu server has web interface enabled? For example it’s possible to turn on roundcube or admin interface there which should be accessible on 443 and 80 as well, in this case we’ll get a collision. The best way to overcome this issue is to assume that tomcat’s app is the primary net that will pass mailu 80/443 requests to the “mailu” subnet. As a first step we should add tomcat nginx <-> maily nginx mapping to mailu yaml: </font></p><pre class="yaml" style="font-family: monospace;"><span style="color: rgb(0, 127, 69);">ports</span><span style="color: brown; font-weight: bold;">:
</span> - <span style="color: rgb(207, 0, 207);">"127.0.0.1:9080:80"</span>
- <span style="color: rgb(207, 0, 207);">"127.0.0.1:9443:443"</span></pre><p><font size="4">Here we expose inner docker port 80 and 443 to the outside port 9080 and 9443, it allows us to avoid interference with the nginx 80 and 443 port of the main app. </font></p><p><font size="4">Then we need to pass mailu <strong>front</strong> service to the <strong>nginx</strong> of the main net. To make this work, we see that network name of the mailu within the yaml is “default”, persist right at the bottom:</font></p><pre class="yaml" style="font-family: monospace;"><span style="color: rgb(0, 127, 69);">networks</span>:<span style="color: rgb(0, 127, 69);">
default</span>:<span style="color: green;">
driver</span><span style="color: brown; font-weight: bold;">: </span>bridge</pre><p><font size="4"><font size="4">And let’s connect containers from the main network, called “facet”, to the mailu one. Since the mailu app is the name of the folder it’s located in, i.e. <em>mailu</em>, to access its network we should use %APP_NAME%_%NETWORK_NAME% in Tomcat’s yaml:</font></font></p><pre class="yaml" style="font-family: monospace;"><span style="color: rgb(0, 127, 69);">networks</span>:<span style="color: rgb(0, 127, 69);">
mailu_default</span>:<span style="color: green;">
external</span><span style="color: brown; font-weight: bold;">: </span>true
<span style="color: blue;"># current app network name</span><span style="color: rgb(0, 127, 69);">
facet</span>:<span style="color: green;">
driver</span><span style="color: brown; font-weight: bold;">: </span>bridge</pre><p><font size="4"><font size="4">Also need to pass <strong>front</strong> service to the nginx of the main app, it can be done though the <em>network</em> section:</font></font></p><pre class="yaml" style="font-family: monospace;"><span style="color: rgb(0, 127, 69);">nginx</span>:<span style="color: green;">
image</span><span style="color: brown; font-weight: bold;">: </span>nginx<span style="color: rgb(0, 127, 69);">
depends_on</span><span style="color: brown; font-weight: bold;">:
</span> - tomcat<span style="color: rgb(0, 127, 69);">
ports</span><span style="color: brown; font-weight: bold;">:
</span> - <span style="color: rgb(207, 0, 207);">"80:80"</span>
- <span style="color: rgb(207, 0, 207);">"443:443"</span><span style="color: rgb(0, 127, 69);">
volumes</span><span style="color: brown; font-weight: bold;">:
</span> - ./nginx:/etc/nginx
- /mailu/certs:/etc/nginx/certs <span style="color: rgb(0, 127, 69);">
links</span><span style="color: brown; font-weight: bold;">:
</span> - tomcat:tomcat
<span style="color: blue;"># pass "front" to the main nginx</span><span style="color: rgb(0, 127, 69);">
networks</span><span style="color: brown; font-weight: bold;">:
</span> - mailu_default</pre><p><font size="4"><font size="4">Now we can refer to the mailu service in Tomcat’s app, really great, nginx.conf:</font></font></p><pre class="yaml" style="font-family: monospace;">server <span class="br0">{</span>
listen 443 ssl;
server_name soft29.info;
ssl_certificate_key /etc/nginx/certs/key.pem;
ssl_certificate /etc/nginx/certs/cert.pem;
<span style="color: blue;"># note mailu pages: admin, webmail etc</span>
location ~* ^/<span class="br0">(</span>admin|sso|static|webdav|webmail|<span class="br0">(</span>apple\.<span class="br0">)</span>?mobileconfig|<span class="br0">(</span>\.well\-known/autoconfig/<span class="br0">)</span>?mail/|Autodiscover/Autodiscover<span class="br0">)</span> <span class="br0">{</span>
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
<span style="color: blue;"># "front" refers to mailu service</span>
proxy_pass http://front;
<span class="br0">}</span>
<span class="br0">}</span></pre><p><font size="4"><font size="4">Now all SSL requests to mailu subpages will be redirected to mailu nginx. </font></font></p><p><font size="4"><font size="4">Since the <em>network</em> section was added to the compose config, current app’s services are not available to the combined network, they should be added to it. For example, <strong>db</strong> service which is required to tomcat app should be exposed to network, plus the tomcat service itself should be available:</font></font></p><pre class="yaml" style="font-family: monospace;"><span style="color: rgb(0, 127, 69);">db</span>:<span style="color: green;">
image</span><span style="color: brown; font-weight: bold;">: </span>mariadb<span style="color: rgb(0, 127, 69);">
volumes</span><span style="color: brown; font-weight: bold;">:
</span> - /opt/test:/var/lib/mysql<span style="color: rgb(0, 127, 69);">
ports</span><span style="color: brown; font-weight: bold;">:
</span> - 3306:3306
<span style="color: blue;"># expose db to the network</span><span style="color: rgb(0, 127, 69);">
networks</span><span style="color: brown; font-weight: bold;">:
</span> - facet <span style="color: rgb(0, 127, 69);">
tomcat</span>:<span style="color: rgb(0, 127, 69);">
depends_on</span><span style="color: brown; font-weight: bold;">:
</span> - db<span style="color: green;">
image</span><span style="color: brown; font-weight: bold;">: </span>tomcat:7.0.70
<span style="color: blue;"># expose tomcat to the network </span><span style="color: rgb(0, 127, 69);">
networks</span><span style="color: brown; font-weight: bold;">:
</span> - facet </pre><p><font size="4"><font size="4">MySql url is defined within the tomcat’s webapp in this way (<strong>db </strong>is the name of the service there):</font></font></p><pre class="yaml" style="font-family: monospace;">database.jdbc.connectionURL=jdbc:mariadb://db/jroller?autoReconnect=true&useUnicode=true&characterEncoding=utf-8</pre><p><font size="4"><font size="4">And the Tomcat is passed to the main nginx server through this nginx.conf section:</font></font></p><pre class="yaml" style="font-family: monospace;"> location / <span class="br0">{</span>
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
<span style="color: blue;"># "tomcat" is the name of the service</span>
proxy_pass http://tomcat:8080;
<span class="br0">}</span></pre><p><font size="4"><font size="4">In this was all request to the SSL port of the root “/” will be passed to and handled by the Tomcat.</font></font></p><p><font size="4"><font size="4">To summarize: in this article we’ve seen how to implement a very common use case when a couple of docker compose apps should be exposed within a single server and also saw how to handle port routings. I hope you enjoyed it, thanks!<br></font></font></p>https://soft29.ru/blog/entry/odoo-configuration-while-going-liveOdoo configuration while going liveStanislav2022-05-03T10:02:44+00:002023-01-08T11:04:42+00:00<p><font size="4">Setting up Odoo instance on your dev-machine can be really straight forward, but when it comes to real live system, the server will require more specific things to be configured. In current post we’ll see what are those things and how to manage them.</font></p><p><font size="4">Setting up Odoo instance on your dev-machine can be really straight forward, but when it comes to real live system, the server will require more specific things to be configured. In current post we’ll see what are those things and how to manage them.</font></p><p><font size="4">First of all when we switch to production server in most cases it’s built on multicore/thread architecture, so it’s better to use all power of parallel execution. In Odoo config there’s a parameter <strong>workers</strong> which stands for a parallel execution and is calculated by: <em>workers = (CPU cores * 2 + 1)</em>. Assuming we have 4 CPU cores, the workers amount will be 9. So in <em>odoo.conf</em> we specify: </font></p><pre class="properties" style="font-family: monospace;"><span style="color: rgb(0, 0, 128); font-weight: bold;">workers</span> <span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 128, 0); font-weight: bold;"> 9</span>
<span style="color: rgb(0, 0, 128); font-weight: bold;">max_cron_threads</span> <span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 128, 0); font-weight: bold;"> 4</span></pre><p><font size="4">When switched to multicore production architecture, there’s a long polling service starting on a separate port 8072 (in contrast to a dev machine run, where <em>workers</em> = 0 or 1). Long polling service is a chatter implementation that ensures message delivery to Odoo users, useful thing for sure. For reason the most live servers employ proxy servers to pass requests from the outside to the core app, we need to configure these redirections to both the core app and longpolling service within the proxy config (Apache example):</font></p><pre class="properties" style="font-family: monospace;">ProxyPass /longpolling/poll http://127.0.0.1:<span>8072</span>/longpolling/poll/
ProxyPass /longpolling/poll/ http://127.0.0.1:<span>8072</span>/longpolling/poll/
ProxyPassReverse /longpolling/poll/ http://127.0.0.1:<span>8072</span>/longpolling/poll/
ProxyPass / http://127.0.0.1:<span>8069</span>/
ProxyPassReverse / http://127.0.0.1:<span>8069</span>/</pre><p><font size="4">From that Apache config, we see the external port 80 is mapped to the local one 8069 (Odoo instance) and 8072 (chatter long polling instance) depending on the request postfix. </font></p><p><font size="4">One more thing to be considered is that the chatter service is being automatically started on Linux system, though on Windows need to trigger this either manually or by defining a Windows service. For Windows case there’s a CMD command to be run to start longpolling service: </font></p><pre class="properties" style="font-family: monospace;">python odoo-bin gevent</pre><p><font size="4">That’s it, these 3 steps are the most common means that take place when switching to Odoo live. As a further possible step SSL enabling can be considered, this can be done through the same proxy server mentioned in this article. </font></p>https://soft29.ru/blog/entry/implement-rest-endpoint-in-odooImplement REST-endpoint in Odoo and invoke itStanislav2021-04-05T15:16:38+00:002021-04-05T15:17:55+00:00<p><font size="4">REST is an architectural style to build HTTP based webservices. We can claim it is a de-facto standard to create interoperable APIs for multiple app integrations. And it’s pretty often required to integrate Odoo with other 3d-party software. In this article we will figure out how to do it. </font></p><p><font size="4">REST is an architectural style to build HTTP based webservices. We can claim it is a de-facto standard to create interoperable APIs for multiple app integrations. And it’s pretty often required to integrate Odoo with other 3d-party software. In this article we will figure out how to do it. </font></p><p><font size="4">Assume that we need to create an Odoo endpoint that will return a contact (res.partner) by phone number. And then invoke this endpoint by a 3d-party app, i.e. Postman in our case. Here’s how this endpoint will look in Odoo: </font></p><pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);">@http.route(<span style="color: rgb(42, 0, 255);">'/rest_api/public/partner/<string:phone>'</span>, auth=<span style="color: rgb(42, 0, 255);">'user'</span>, <span style="color: rgb(127, 0, 85); font-weight: bold;">type</span>=<span style="color: rgb(42, 0, 255);">'http'</span>, csrf=False)
<span style="color: rgb(127, 0, 85); font-weight: bold;">def</span> get_partner(self, phone, **kw):
res = <span style="color: rgb(127, 0, 85); font-weight: bold;">dict</span>()
partner = request.env[<span style="color: rgb(42, 0, 255);">'res.partner'</span>].search([<span style="color: rgb(42, 0, 255);">'|'</span>, (<span style="color: rgb(42, 0, 255);">'phone'</span>, <span style="color: rgb(42, 0, 255);">'=like'</span>, <span style="color: rgb(42, 0, 255);">'%'</span> + phone), (<span style="color: rgb(42, 0, 255);">'mobile'</span>, <span style="color: rgb(42, 0, 255);">'=like'</span>, <span style="color: rgb(42, 0, 255);">'%'</span> + phone)], limit=1)
<span style="color: rgb(127, 0, 85); font-weight: bold;">if</span> partner:
res[<span style="color: rgb(42, 0, 255);">'name'</span>] = partner.name
res[<span style="color: rgb(42, 0, 255);">'id'</span>] = partner.<span style="color: rgb(127, 0, 85); font-weight: bold;">id</span>
res[<span style="color: rgb(42, 0, 255);">'commercial_company_name'</span>] = partner.commercial_company_name
res[<span style="color: rgb(42, 0, 255);">'email'</span>] = partner.email
res[<span style="color: rgb(42, 0, 255);">'phone'</span>] = partner.phone
res[<span style="color: rgb(42, 0, 255);">'mobile'</span>] = partner.mobile
<span style="color: rgb(127, 0, 85); font-weight: bold;">else</span>:
res[<span style="color: rgb(42, 0, 255);">'search_by_phone'</span>] = phone
res[<span style="color: rgb(42, 0, 255);">'name'</span>] = res[<span style="color: rgb(42, 0, 255);">'commercial_company_name'</span>] \
= res[<span style="color: rgb(42, 0, 255);">'email'</span>] = res[<span style="color: rgb(42, 0, 255);">'phone'</span>] = res[<span style="color: rgb(42, 0, 255);">'mobile'</span>] = False
res[<span style="color: rgb(42, 0, 255);">'id'</span>] = -1
wrapper = []
wrapper.append(res)
<span style="color: rgb(127, 0, 85); font-weight: bold;">return</span> json.dumps(wrapper)
</pre>
<!--Created using ToHtml.com on 2021-04-05 14:18:07 UTC --><p><font size="4">What can we say about this endpoint:</font></p><ol><li><font size="4">Phone number comes as a REST-styled URL parameter, i.e. /<phone></font></li><li><font size="4">CSRF protection is turned off because this is how it should be for any REST-API in Odoo (see a comment in core py of Odoo)</font></li><li><font size="4">In current case the request of type ‘http’ is used (body type == x-www-form-encoded). We could use json request as well, but in that case a request body should be specified as “{}” for empty body, plus header <em>content-type </em> set to ‘application/json’</font></li><li><font size="4">This endpoint can be invoked by either GET or POST method, none of the code should be changed.</font></li></ol><p><font size="4">To call our custom API we also need to provide an authentication for it. First of all we should do an auth within Odoo, this can be done by invocation of Odoo core method <strong>/web/session/authenticate</strong>. This is how it looks in Postman:</font></p><p><a href="https://soft29.ru/blog/mediaresource/17bba507-7550-4990-a05a-72efb972d5c5"><img width="514" height="150" title="image" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/84eb157e-0aa0-46a2-b837-19d840c5ea33" border="0"></a></p><p><font size="4">As a result this method returns a cookie (with a session token) that is stored in Postman for future use. After that we can call our custom endpoint, the auth cookie is already there within a request:</font></p><p><a href="https://soft29.ru/blog/mediaresource/61ee96d6-9a79-4775-bdeb-8613be16ddc3"><img width="647" height="305" title="image" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/cb332b6b-f7b0-4940-a551-dca93bca552d" border="0"></a></p><p><font size="4">Body should be empty for current request and of type <em>form-urlencoded </em>(http-type request). The result looks so:</font></p><p><a href="https://soft29.ru/blog/mediaresource/f4df1be5-4023-45fb-9791-15d9800fafa8"><img width="702" height="291" title="image" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/d878a7ac-6c68-48d0-afd9-30e60cc136b6" border="0"></a></p><p><font size="4">This is how we got the result from Odoo by means of 3d-party software Postman. </font></p><p><font size="4">In this article we’ve found out how to create RESTful API in Odoo to make it possible to communicate to it from the outside. If you have any questions feel free to write a comment. </font></p>https://soft29.ru/blog/entry/using-javascript-in-odoo-snippetUsing Javascript in Odoo snippet optionsStanislav2021-03-08T11:14:51+00:002021-03-08T11:17:53+00:00<p><font size="4">Hi there, this post is dedicated to the theme of Odoo snippet options extension using Javascript. Basically it extends the previous post about snippet options (<a title="snippet options odoo" href="https://soft29.ru/blog/entry/defining-a-snippet-option-in" target="_blank">link</a>), adding a set of Odoo programming techniques that help to solve different tasks related to snippet options using JS. </font></p><p><font size="4">So first of all let’s start with an example of a slider (carousel) snippet defined in Odoo 14. We won’t dig into all round comprehensive code of the slider, but concentrate on main points only. The code of the slider:</font></p><p><font size="4">Hi there, this post is dedicated to the theme of Odoo snippet options extension using Javascript. Basically it extends the previous post about snippet options (<a title="snippet options odoo" href="https://soft29.ru/blog/entry/defining-a-snippet-option-in" target="_blank">link</a>), adding a set of Odoo programming techniques that help to solve different tasks related to snippet options using JS. </font></p><p><font size="4">So first of all let’s start with an example of a slider (carousel) snippet defined in Odoo 14. We won’t dig into all round comprehensive code of the slider, but concentrate on main points only. The code of the slider:</font></p><pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">template</span> <span style="color: rgb(7, 71, 38);">id</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">s_dq_card_carousel</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">name</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">Card Carousel</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">section</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">s_dq_card_carousel</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">div</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">dq-card-carousel</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">div</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">carousel-cell</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">h4</span><span style="color: rgb(0, 0, 128);">></span>Quality and finish<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">h4</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">p</span><span style="color: rgb(0, 0, 128);">></span>
Our market-leading products are designed and manufactured to the highest standards
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">p</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">div</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">div</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">section</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">template</span><span style="color: rgb(0, 0, 128);">></span>
</pre>
<!--Created using ToHtml.com on 2021-03-08 10:06:14 UTC --><p><font size="4">Now let’s say we need to add a ‘new slide’ option and ‘remove all slides’ option. How can we do that? Let’s start with the Odoo designed way. Here’s the snippet option should be defined: </font></p>
<pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">template</span> <span style="color: rgb(7, 71, 38);">id</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">s_dq_card_carousel_opt</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">inherit_id</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">website.snippet_options</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">xpath</span> <span style="color: rgb(7, 71, 38);">expr</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">.</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">position</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">inside</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">div</span> <span style="color: rgb(7, 71, 38);">data-js</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">s_dq_card_carousel_options</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">data-selector</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">.s_dq_card_carousel</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">we-row</span> <span style="color: rgb(7, 71, 38);">string</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">Cards</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">we-button</span> <span style="color: rgb(7, 71, 38);">data-add</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">true</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">data-no-preview</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">true</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>Add<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">we-button</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">we-button</span> <span style="color: rgb(7, 71, 38);">data-remove-all</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">true</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">data-no-preview</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">true</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>Remove all<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">we-button</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">we-row</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">div</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">xpath</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">template</span><span style="color: rgb(0, 0, 128);">></span>
</pre><p><font size="4">We should notice from this excerpt 2 snippet option attributes <strong>data-add</strong> and <strong>data-remove-all</strong>. They both refer to our custom javascript methods where names mapped snake_case –> camel_case: </font></p>
<pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);">options<span style="color: rgb(0, 0, 255);">.</span>registry<span style="color: rgb(0, 0, 255);">.</span>s_dq_card_carousel_options <span style="color: rgb(0, 0, 255);">=</span> options<span style="color: rgb(0, 0, 255);">.</span>Class<span style="color: rgb(0, 0, 255);">.</span>extend<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 255);">{</span>
removeAll<span style="color: rgb(0, 0, 255);">:</span> <span style="color: rgb(0, 0, 255); font-weight: bold;">function</span> <span style="color: rgb(0, 0, 255);">(</span>previewMode<span style="color: rgb(0, 0, 255);">)</span> <span style="color: rgb(0, 0, 255);">{</span>
<span style="color: rgb(0, 0, 255); font-weight: bold;">if</span> <span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 255); font-weight: bold;">confirm</span><span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">Remove all cards from carousel?</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">)</span> <span style="color: rgb(0, 0, 255);">{</span>
<span style="color: rgb(0, 0, 255); font-weight: bold;">this</span><span style="color: rgb(0, 0, 255);">.</span>$target<span style="color: rgb(0, 0, 255);">.</span>find<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">.dq-card-carousel</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">.</span>html<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
<span style="color: rgb(0, 0, 255);">}</span>
<span style="color: rgb(0, 0, 255);">}</span><span style="color: rgb(0, 0, 255);">,</span>
add<span style="color: rgb(0, 0, 255);">:</span> <span style="color: rgb(0, 0, 255); font-weight: bold;">function</span> <span style="color: rgb(0, 0, 255);">(</span>previewMode<span style="color: rgb(0, 0, 255);">)</span> <span style="color: rgb(0, 0, 255);">{</span>
<span style="color: rgb(0, 0, 255); font-weight: bold;">var</span> $container <span style="color: rgb(0, 0, 255);">=</span> <span style="color: rgb(0, 0, 255); font-weight: bold;">this</span><span style="color: rgb(0, 0, 255);">.</span>$target<span style="color: rgb(0, 0, 255);">.</span>find<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">.dq-card-carousel</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
$<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);"><div class="carousel-cell"><h4>Lorem ipsum</h4><p>Dolor sit amet.</p></div></span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">.</span>appendTo<span style="color: rgb(0, 0, 255);">(</span>$container<span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
<span style="color: rgb(0, 0, 255);">}</span><span style="color: rgb(0, 0, 255);">,</span>
<span style="color: rgb(0, 0, 255);">}</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
</pre>
<p><font size="4">In the first method the JS removes all slides from the carousel. In the latter one a new slide is created and appended to the carousel. On snippet start (on first render) a slider-plugin does initialization using just modified html.</font></p><pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">template</span> <span style="color: rgb(7, 71, 38);">id</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">s_proj_carousel_opt</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">inherit_id</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">website.snippet_options</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">xpath</span> <span style="color: rgb(7, 71, 38);">expr</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">//div[@data-js='CoverProperties']</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">position</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">after</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">div</span> <span style="color: rgb(7, 71, 38);">data-js</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">s_project_slide_options</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">data-selector</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">.s_proj_carousel</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">div</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">slide-status</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span><span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">div</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">div</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">btn-group</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">button</span> <span style="color: rgb(7, 71, 38);">type</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">button</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">btn btn-secondary btn-sm btn-remove-slide</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">data-no-preview</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">true</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">i</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">fa fa-trash-o</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span><span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">i</span><span style="color: rgb(0, 0, 128);">></span>Remove Slide
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">button</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">button</span> <span style="color: rgb(7, 71, 38);">type</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">button</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">btn btn-secondary btn-sm btn-add-slide</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">data-no-preview</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">true</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">i</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">fa fa-plus-circle</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span><span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">i</span><span style="color: rgb(0, 0, 128);">></span>Add Slide
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">button</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">div</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">div</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">xpath</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">template</span><span style="color: rgb(0, 0, 128);">></span>
</pre><p><font size="4">And then attach javascript methods on snippet start using JQuery:</font></p>
<pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);">options<span style="color: rgb(0, 0, 255);">.</span>registry<span style="color: rgb(0, 0, 255);">.</span>s_project_slide_options <span style="color: rgb(0, 0, 255);">=</span> options<span style="color: rgb(0, 0, 255);">.</span>Class<span style="color: rgb(0, 0, 255);">.</span>extend<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 255);">{</span>
start<span style="color: rgb(0, 0, 255);">:</span> <span style="color: rgb(0, 0, 255); font-weight: bold;">function</span> <span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 255);">)</span> <span style="color: rgb(0, 0, 255);">{</span>
<span style="color: rgb(0, 0, 255); font-weight: bold;">var</span> self <span style="color: rgb(0, 0, 255);">=</span> <span style="color: rgb(0, 0, 255); font-weight: bold;">this</span><span style="color: rgb(0, 0, 255);">;</span>
<span style="color: rgb(0, 0, 255); font-weight: bold;">this</span><span style="color: rgb(0, 0, 255);">.</span>$el<span style="color: rgb(0, 0, 255);">.</span>find<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">.btn-add-slide</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">.</span>on<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">click</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">,</span> <span style="color: rgb(0, 0, 255); font-weight: bold;">function</span> <span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 255);">)</span> <span style="color: rgb(0, 0, 255);">{</span>
<span style="color: rgb(0, 0, 255); font-weight: bold;">var</span> $slide <span style="color: rgb(0, 0, 255);">=</span> $<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);"><div class="s_project_slide slide" data-name="Project Slide"><img src="/theme/static/src/img/pictures/projects-03.jpg" alt="" /><div class="text"><h1>Lorem ipsum dolor</h1></div><p>Lorem ispum dolor sit emet.</p></div></span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
<span style="color: rgb(0, 0, 255); font-weight: bold;">var</span> $active <span style="color: rgb(0, 0, 255);">=</span> self<span style="color: rgb(0, 0, 255);">.</span>$target<span style="color: rgb(0, 0, 255);">.</span>find<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">.active</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
<span style="color: rgb(0, 0, 255); font-weight: bold;">if</span> <span style="color: rgb(0, 0, 255);">(</span>$active<span style="color: rgb(0, 0, 255);">.</span><span style="color: rgb(0, 0, 255); font-weight: bold;">length</span><span style="color: rgb(0, 0, 255);">)</span> <span style="color: rgb(0, 0, 255);">{</span>
$slide<span style="color: rgb(0, 0, 255);">.</span>insertAfter<span style="color: rgb(0, 0, 255);">(</span>$active<span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
self<span style="color: rgb(0, 0, 255);">.</span>$target<span style="color: rgb(0, 0, 255);">.</span>find<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">.slideshow</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">.</span>trigger<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">next</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
<span style="color: rgb(0, 0, 255);">}</span> <span style="color: rgb(0, 0, 255); font-weight: bold;">else</span> <span style="color: rgb(0, 0, 255);">{</span>
self<span style="color: rgb(0, 0, 255);">.</span>$target<span style="color: rgb(0, 0, 255);">.</span>find<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">.slideshow .slideshow-inner</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">.</span>append<span style="color: rgb(0, 0, 255);">(</span>$slide<span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
self<span style="color: rgb(0, 0, 255);">.</span>$target<span style="color: rgb(0, 0, 255);">.</span>find<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">.slideshow</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">.</span>trigger<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">update</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
<span style="color: rgb(0, 0, 255);">}</span>
<span style="color: rgb(0, 0, 255);">}</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
<span style="color: rgb(0, 0, 255); font-weight: bold;">this</span><span style="color: rgb(0, 0, 255);">.</span>$el<span style="color: rgb(0, 0, 255);">.</span>find<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">.btn-remove-slide</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">.</span>on<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">click</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">,</span> <span style="color: rgb(0, 0, 255); font-weight: bold;">function</span> <span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 255);">)</span> <span style="color: rgb(0, 0, 255);">{</span>
self<span style="color: rgb(0, 0, 255);">.</span>$target<span style="color: rgb(0, 0, 255);">.</span>find<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">.active</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">.</span>remove<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
self<span style="color: rgb(0, 0, 255);">.</span>$target<span style="color: rgb(0, 0, 255);">.</span>find<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">.slideshow</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">.</span>trigger<span style="color: rgb(0, 0, 255);">(</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 230);">prev</span><span style="color: rgb(0, 0, 230);">'</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
<span style="color: rgb(0, 0, 255);">}</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
<span style="color: rgb(0, 0, 255);">}</span>
<span style="color: rgb(0, 0, 255);">}</span><span style="color: rgb(0, 0, 255);">)</span><span style="color: rgb(0, 0, 255);">;</span>
</pre>
<p><font size="4">When you save the modifications on a page, the updated html will be stored and rendered using Slider-plugin.</font></p><p><font size="4">That’s it, in this article we’ve learned how to use Javascript to extend the core Odoo functionality in the area of snippet options. I hope you liked it, enjoy Odoo coding!</font></p><p><font size="4"><br></font></p>https://soft29.ru/blog/entry/defining-a-snippet-option-inDefining a snippet option in OdooStanislav2021-02-27T14:10:36+00:002021-02-27T14:12:47+00:00<p><font face="Calibri" size="4">This post is dedicated to the topic of snippet option creation in Odoo. We all know that a snippet in Odoo is a pretty useful tool to build site pages – just drag and drop it on a page and that’s it - page is ready. Along with the snippets there’re options available to dynamically change snippet look either it’s a custom or out-of-the-box snippet. But what if a snippet has no the option we need, then we can add this option using Odoo configuration. In this post we’ll see how to manage this.</font></p><p><font face="Calibri" size="4">This post is dedicated to the topic of snippet option creation in Odoo. We all know that a snippet in Odoo is a pretty useful tool to build site pages – just drag and drop it on a page and that’s it - page is ready. Along with the snippets there’re options available to dynamically change snippet look either it’s a custom or out-of-the-box snippet. But what if a snippet has no the option we need, then we can add this option using Odoo configuration. In this post we’ll see how to manage this.</font></p><p><font size="4">Assume we have Odoo 14 and our snippet defined so:<font size="4"><br></font></font></p>
<pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">section</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">s-snippet-formula</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">div</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">container</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">div</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">row idea-header</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">div</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">col-md-12</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">span</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">text-center highlight</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>Our offer<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">span</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">div</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">div</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">div</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">section</span><span style="color: rgb(0, 0, 128);">></span>
</pre><p><br></p><p><font size="4">Now we want to add an option to this snippet to make it configurable. To make that happen let’s inject this xml-chunk to a snippet code e.g.:<font size="4"><br></font></font></p>
<pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">template</span> <span style="color: rgb(7, 71, 38);">id</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">s-snippet-formula_opt</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">name</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">Idea Snippet Options</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">inherit_id</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">website.snippet_options</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">xpath</span> <span style="color: rgb(7, 71, 38);">expr</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">//div[@data-js='background']</span><span style="color: rgb(0, 0, 230);">"</span> <span style="color: rgb(7, 71, 38);">position</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">after</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">div</span> <span style="color: rgb(7, 71, 38);">data-selector</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">.s-snippet-formula</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">we-collapse-area</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">we-toggler</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">i</span> <span style="color: rgb(7, 71, 38);">class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">fa fa-fw fa-magic</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">/></span>
Show extra info
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">we-toggler</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">we-collapse</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">we-button</span> <span style="color: rgb(7, 71, 38);">data-select-class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>Yes<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">we-button</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"><</span><span style="color: rgb(0, 0, 128);">we-button</span> <span style="color: rgb(7, 71, 38);">data-select-class</span><span style="color: rgb(0, 0, 255);">=</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 230);">hide_extra_info</span><span style="color: rgb(0, 0, 230);">"</span><span style="color: rgb(0, 0, 128);">></span>No<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">we-button</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">we-collapse</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">we-collapse-area</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">div</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">xpath</span><span style="color: rgb(0, 0, 128);">></span>
<span style="color: rgb(0, 0, 128);"></</span><span style="color: rgb(0, 0, 128);">template</span><span style="color: rgb(0, 0, 128);">></span>
</pre>
<p><font size="4">As a result we’ll see a new configuration toggler called “Show extra info” that includes 2 snippet options “Yes” and “No”:</font></p><p><a href="https://soft29.ru/blog/mediaresource/e2dbd23e-01e4-443e-9d2b-23ae007a19db"><img width="233" height="241" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/bf0c098d-80d2-4083-b704-b0664afe90a5" border="0"></a></p><p><font size="4">There are a few things should be explained regarding the options XML:</font></p><ol><li><font size="4">To make this option appear, we need to inherit the template from <em>website.snippet_options</em> view – a default parent of any snippet option</font></li><li><font size="4">Need to define a selector to bind the option to the snippet - <strong>.s_snippet_formula</strong> in out case</font></li><li><font size="4">To place the option in the Options panel we define: <xpath expr="//div[@data-js='background']" position="after">. In this case our custom option will appear right after Background block.</font></li><li><font size="4">The last important thing here is <em>data-select-class </em>attribute that defines the css-class added to snippet’s html upon our option change. Namely <strong>hide_extra_info</strong> class.</font></li></ol><p><p><font size="4">To apply the option change to the snippet look let’s add some scss that refers to <strong>hide_extra_info</strong> class: </font></p><pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 255);">.</span>s-snippet-formula <span style="color: rgb(0, 0, 255);">{</span>
<span style="color: rgb(0, 0, 136); font-weight: bold;">padding-top</span><span style="color: rgb(0, 0, 255);">:</span> <span style="color: rgb(128, 0, 0);">20</span><span style="color: rgb(128, 0, 0);">px</span><span style="color: rgb(0, 0, 255);">;</span>
&.hide_extra_info {
.row.idea-header {
<span style="color: rgb(0, 0, 136); font-weight: bold;">display</span><span style="color: rgb(0, 0, 255);">:</span> <span style="color: rgb(7, 71, 38);">none</span><span style="color: rgb(0, 0, 255);">;</span>
<span style="color: rgb(0, 0, 255);">}</span>
<span style="color: rgb(0, 0, 255);">}</span>
<span style="color: rgb(0, 0, 255);">}</span>
</pre><p><font size="4">As we see from this scss code the header in snippet either shown or hidden depending on the snippet option.</font></p><p><font size="4">In this article there’s an example of Odoo snippet option presented. As we see this process is pretty straight forward and demonstrates all power of Odoo. Have fun coding!<font size="4"><br></font></font></p>https://soft29.ru/blog/entry/liferay-7-deploy-module-gradleLiferay 7: deploy module using gradle scriptStanislav2020-06-01T10:52:09+00:002020-06-01T12:56:50+00:00<p><font face="Times New Roman" size="4">In Liferay 7 the task of automatic module deployment can be very useful, especially if you continuously develop your code on daily basis. Starting from 7 version of Liferay, project build framework switched mainly from <a href="https://maven.apache.org/" target="_blank" rel="nofollow">Maven</a> to <a href="https://gradle.org/" target="_blank" rel="nofollow">Gradle</a>. Gradle’s build procedure consists of subsequent tasks execution, these tasks are defined and generated during new Liferay module creation. But there’s no ‘deploy’ task – a common task that speeds up the dev-process. In this post we’ll get to know how to solve that.</font></p><p><font face="Times New Roman" size="4">In Liferay 7 the task of automatic module deployment can be very useful, especially if you continuously develop your code on daily basis. Starting from 7 version of Liferay, project build framework switched mainly from <a href="https://maven.apache.org/" target="_blank" rel="nofollow">Maven</a> to <a href="https://gradle.org/" target="_blank" rel="nofollow">Gradle</a>. Gradle’s build procedure consists of subsequent tasks execution, these tasks are defined and generated during new Liferay module creation. But there’s no ‘deploy’ task – a common task that speeds up the dev-process. In this post we’ll get to know how to solve that.</font></p><p><font face="Times New Roman" size="4">First of all we should navigate to <em>build.gradle</em> file – the one that was generated at module creation time. It already includes <em>build </em>task, which designates assembling all outputs and running all checks. We need this task since it generates the resulting jar-file for deployment. Let’s create new <em>hotDeploy</em> task, appending it to <em>build.gradle</em> file:</font></p>
<pre style="background: rgb(246, 248, 255); color: rgb(0, 0, 32);"><span style="color: rgb(32, 0, 128); font-weight: bold;">task</span> <span style="color: rgb(0, 95, 210);">hotDeploy</span><span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(0, 95, 210);">type:</span> <span style="color: rgb(16, 96, 182);">Copy</span><span style="color: rgb(48, 128, 128);">)</span><span style="color: rgb(48, 128, 128);">{</span>
<span style="color: rgb(0, 95, 210);">dependsOn</span> <span style="color: rgb(16, 96, 182);">build</span>
<span style="color: rgb(0, 95, 210);">from</span><span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(0, 95, 210);">file</span><span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(16, 96, 182);">build/libs/tool-1.0.0.jar</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(48, 128, 128);">)</span><span style="color: rgb(48, 128, 128);">)</span>
<span style="color: rgb(0, 95, 210);">into</span><span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(16, 96, 182);">/opt/liferay/deploy</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(48, 128, 128);">)</span>
<span style="color: rgb(48, 128, 128);">}</span>
</pre>
<!--Created using ToHtml.com on 2020-06-01 10:37:45 UTC -->
<p><font face="Times New Roman" size="4">What we can say about this script:</font></p><ol><li><font face="Times New Roman" size="4">It’s executed after <em>build</em> task (see 2 line <em>dependsOn</em>)</font></li><li><font face="Times New Roman" size="4">A module (jar-file) that was built during the <em>build</em> task is taken from build/libs path – a common path for Gradle building process</font></li><li><font face="Times New Roman" size="4">Jar-file is copied in to deploy dir of the Liferay</font></li></ol><p><font face="Times New Roman" size="4">After the <em>hotDeploy</em> task is introduced it can be run from, e.g. IDE:</font></p><p><font face="Times New Roman" size="4"><a href="https://soft29.ru/blog/mediaresource/a9624cdf-d82e-44a1-914c-ed516583669b"><img width="389" height="277" title="gradle" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="gradle" src="https://soft29.ru/blog/mediaresource/1e7b91b6-95e6-4d41-88f1-7183f1b01eb9" border="0"></a></font></p><p><font face="Times New Roman" size="4">After the task is run, the compiled module will be placed to deploy dir and after the moment the module is redeployed. </font></p><p><font face="Times New Roman" size="4">In this post we’ve discussed how the process of module deployment can be enhanced by means of dev-process speed up. The deployment procedure is automated and that is always helpful.</font></p><p><font face="Times New Roman" size="4"><br></font></p>https://soft29.ru/blog/entry/odoo-module-upgrade-on-multipleOdoo module upgrade on multiple databasesStanislav2019-04-28T07:29:46+00:002019-04-28T07:32:32+00:00<p align="left"><font face="Times New Roman" size="4">
This post provides the script that solves the problem of automated module upgrade on multiple databases in Odoo 10. </font></p><p align="left"><font face="Times New Roman" size="4"><p align="left"><font face="Times New Roman" size="4">
This post provides the script that solves the problem of automated module upgrade on multiple databases in Odoo 10. </font></p><p align="left"><font face="Times New Roman" size="4">
If you've worked with Odoo you might know that in order to install some new functionality, you need to install (or update) the appropriate module, i.e. initiate the upgrade process on a database. By default Odoo can deal with this smoothly, just by going to Modules List (having Admin privileges) and pressing "Upgrade" button. The issue here is that this upgrade process takes place on the current database only. And what if there are multiple databases exist, then you have to login to each DB entity via Odoo and navigate to Module list and press that Upgrade button. These steps to be performed manually and if there are, let's say, more than 3 DBs then it may be really annoying to get through each upgrade. There are many inquires on the internet to solve that problem, e.g. <a title="odoo" href="https://www.odoo.com/fr_FR/forum/aide-1/question/how-to-update-all-modules-in-producction-server-with-multiple-database-61431" target="_blank" rel="nofollow">here</a> and <a title="odoo" href="https://www.odoo.com/fr_FR/forum/aide-1/question/how-to-upgrade-nightly-build-3435#3540" target="_blank" rel="nofollow">here</a>, but the solution there is outdated, so in this post you’ll find a workable script for modern Odoo 10 version.</font></p><p align="left"><font face="Times New Roman" size="4">To automate the module upgrade process - a Python script is written that can be invoked on Lynux only (because it involves the usage of pexpect library, which is available on Linux only), no Windows, sorry.</font></p><p align="left"><font face="Times New Roman" size="4">
Firstly we get the list of available databases in Postgres, then we iterate through each of them to perform an upgrade. </font></p><p align="left"><font face="Times New Roman" size="4">
Second important step is to form odoo-bin execution command. Odoo-bin is an entry point to start/upgrade Odoo instance and in this way it allows to initiate the update of some particular module on a database using predefined parameters (e.g. --database, --update).</font></p><p align="left"><font face="Times New Roman" size="4">
Third step is to use pexpect lib to invoke the command on odoo-bin and keep track of Odoo log to catch the message "Modules loaded.", which means that Odoo has finished upgrade and we can switch to the other DB. </font></p><p align="left"><font face="Times New Roman" size="4">The final script looks so:</font></p><p align="left"><pre style="background: rgb(246, 248, 255); color: rgb(0, 0, 32);"><font face="Times New Roman"><font size="4"><span style="color: rgb(89, 89, 121);">#!/usr/bin/env python</span>
<span style="color: rgb(32, 0, 128); font-weight: bold;">from</span> datetime <span style="color: rgb(32, 0, 128); font-weight: bold;">import</span> date
<span style="color: rgb(32, 0, 128); font-weight: bold;">import</span> subprocess<span style="color: rgb(48, 128, 128);">,</span> xmlrpclib<span style="color: rgb(48, 128, 128);">,</span> sys<span style="color: rgb(48, 128, 128);">,</span> os<span style="color: rgb(48, 128, 128);">,</span> psycopg2<span style="color: rgb(48, 128, 128);">,</span> pexpect
<span style="color: rgb(89, 89, 121);"># Database info</span>
db_user <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(16, 96, 182);">'odoo'</span>
db_passwd <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(16, 96, 182);">''</span>
db_host <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(16, 96, 182);">'localhost'</span>
db_port <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(0, 140, 0);">5432</span>
db_name <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(16, 96, 182);">'postgres'</span>
oerp_conf <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(16, 96, 182);">'/opt/odoo/odoo.conf'</span>
oerp_bin <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(16, 96, 182);">"/opt/odoo/odoo-bin"</span>
module_to_upgrade <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(16, 96, 182);">"module_to_upgrade"</span>
today <span style="color: rgb(48, 128, 128);">=</span> date<span style="color: rgb(48, 128, 128);">.</span>today<span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(48, 128, 128);">)</span>
log_date <span style="color: rgb(48, 128, 128);">=</span> today<span style="color: rgb(48, 128, 128);">.</span>strftime<span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(16, 96, 182);">'%Y%m%d'</span><span style="color: rgb(48, 128, 128);">)</span>
log_name <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(16, 96, 182);">"update-{}.log"</span><span style="color: rgb(48, 128, 128);">.</span>format<span style="color: rgb(48, 128, 128);">(</span>log_date<span style="color: rgb(48, 128, 128);">)</span>
base_dir <span style="color: rgb(48, 128, 128);">=</span> os<span style="color: rgb(48, 128, 128);">.</span>getcwd<span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(48, 128, 128);">)</span>
<span style="color: rgb(32, 0, 128); font-weight: bold;">with</span> <span style="color: rgb(64, 0, 0);">open</span><span style="color: rgb(48, 128, 128);">(</span>log_name<span style="color: rgb(48, 128, 128);">,</span> <span style="color: rgb(16, 96, 182);">'a+'</span><span style="color: rgb(48, 128, 128);">)</span> <span style="color: rgb(32, 0, 128); font-weight: bold;">as</span> log_file<span style="color: rgb(48, 128, 128);">:</span>
<span style="color: rgb(89, 89, 121);"># Get list of databases</span>
db <span style="color: rgb(48, 128, 128);">=</span> psycopg2<span style="color: rgb(48, 128, 128);">.</span>connect<span style="color: rgb(48, 128, 128);">(</span>user<span style="color: rgb(48, 128, 128);">=</span> db_user<span style="color: rgb(48, 128, 128);">,</span>
password<span style="color: rgb(48, 128, 128);">=</span> db_passwd<span style="color: rgb(48, 128, 128);">,</span>
host<span style="color: rgb(48, 128, 128);">=</span> db_host<span style="color: rgb(48, 128, 128);">,</span>
port<span style="color: rgb(48, 128, 128);">=</span> db_port<span style="color: rgb(48, 128, 128);">,</span>
database<span style="color: rgb(48, 128, 128);">=</span> db_name<span style="color: rgb(48, 128, 128);">)</span>
cr <span style="color: rgb(48, 128, 128);">=</span> db<span style="color: rgb(48, 128, 128);">.</span>cursor<span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(48, 128, 128);">)</span>
cr<span style="color: rgb(48, 128, 128);">.</span>execute<span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(16, 96, 182);">"select datname from pg_database where datdba=(select usesysid from pg_user where usename='{}') order by datname"</span><span style="color: rgb(48, 128, 128);">.</span>format<span style="color: rgb(48, 128, 128);">(</span>db_user<span style="color: rgb(48, 128, 128);">)</span><span style="color: rgb(48, 128, 128);">)</span>
dblist <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(48, 128, 128);">[</span><span style="color: rgb(64, 0, 0);">str</span><span style="color: rgb(48, 128, 128);">(</span>name<span style="color: rgb(48, 128, 128);">)</span> <span style="color: rgb(32, 0, 128); font-weight: bold;">for</span> <span style="color: rgb(48, 128, 128);">(</span>name<span style="color: rgb(48, 128, 128);">,</span><span style="color: rgb(48, 128, 128);">)</span> <span style="color: rgb(32, 0, 128); font-weight: bold;">in</span> cr<span style="color: rgb(48, 128, 128);">.</span>fetchall<span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(48, 128, 128);">)</span><span style="color: rgb(48, 128, 128);">]</span>
<span style="color: rgb(32, 0, 128); font-weight: bold;">for</span> database <span style="color: rgb(32, 0, 128); font-weight: bold;">in</span> dblist<span style="color: rgb(48, 128, 128);">:</span>
<span style="color: rgb(89, 89, 121);"># Wait for server to upgrade database, then kill it</span>
<span style="color: rgb(32, 0, 128); font-weight: bold;">print</span> <span style="color: rgb(16, 96, 182);">"Upgrading database {}..."</span><span style="color: rgb(48, 128, 128);">.</span>format<span style="color: rgb(48, 128, 128);">(</span>database<span style="color: rgb(48, 128, 128);">)</span>
dbupdate_cmd <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(16, 96, 182);">"{} shell -c {} --database={} --update={}"</span><span style="color: rgb(48, 128, 128);">.</span>format<span style="color: rgb(48, 128, 128);">(</span>oerp_bin<span style="color: rgb(48, 128, 128);">,</span> oerp_conf<span style="color: rgb(48, 128, 128);">,</span> database<span style="color: rgb(48, 128, 128);">,</span> module_to_upgrade<span style="color: rgb(48, 128, 128);">)</span>
output <span style="color: rgb(48, 128, 128);">=</span> pexpect<span style="color: rgb(48, 128, 128);">.</span>spawn<span style="color: rgb(48, 128, 128);">(</span>dbupdate_cmd<span style="color: rgb(48, 128, 128);">)</span>
<span style="color: rgb(32, 0, 128); font-weight: bold;">try</span><span style="color: rgb(48, 128, 128);">:</span>
<span style="color: rgb(89, 89, 121);"># to catch this string loading.py was altered (line 461)</span>
output<span style="color: rgb(48, 128, 128);">.</span>expect<span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(16, 96, 182);">'.*Modules loaded.'</span><span style="color: rgb(48, 128, 128);">,</span> timeout<span style="color: rgb(48, 128, 128);">=</span><span style="color: rgb(0, 140, 0);">600</span><span style="color: rgb(48, 128, 128);">)</span>
<span style="color: rgb(32, 0, 128); font-weight: bold;">except</span> pexpect<span style="color: rgb(48, 128, 128);">.</span>ExceptionPexpect <span style="color: rgb(32, 0, 128); font-weight: bold;">as</span> e<span style="color: rgb(48, 128, 128);">:</span>
<span style="color: rgb(32, 0, 128); font-weight: bold;">print</span> <span style="color: rgb(16, 96, 182);">"Timeout reached while upgrading {}. Try manually upgrading the database with the command '{}'."</span><span style="color: rgb(48, 128, 128);">.</span>format<span style="color: rgb(48, 128, 128);">(</span>database<span style="color: rgb(48, 128, 128);">,</span> dbupdate_cmd<span style="color: rgb(48, 128, 128);">)</span>
output<span style="color: rgb(48, 128, 128);">.</span>kill<span style="color: rgb(48, 128, 128);">(</span><span style="color: rgb(0, 140, 0);">0</span><span style="color: rgb(48, 128, 128);">)</span>
status <span style="color: rgb(48, 128, 128);">=</span> <span style="color: rgb(16, 96, 182);">"Upgraded database {}."</span><span style="color: rgb(48, 128, 128);">.</span>format<span style="color: rgb(48, 128, 128);">(</span>database<span style="color: rgb(48, 128, 128);">)</span>
<span style="color: rgb(32, 0, 128); font-weight: bold;">print</span> status
log_file<span style="color: rgb(48, 128, 128);">.</span>write<span style="color: rgb(48, 128, 128);">(</span>status <span style="color: rgb(68, 170, 221);">+</span> <span style="color: rgb(16, 96, 182);">"</span><span style="color: rgb(15, 105, 255);">\n</span><span style="color: rgb(16, 96, 182);">"</span><span style="color: rgb(48, 128, 128);">)</span>
</font></font></pre><font face="Times New Roman"><font size="4">
<!--Created using ToHtml.com on 2019-04-28 07:28:26 UTC --></font></font><p align="left"><font face="Times New Roman" size="4">This script is also provides logging to console and external file, so you could always check the upgrade progress and results. </font></p><p align="left"><font face="Times New Roman" size="4">In this post the multiple database upgrade script for Odoo 10 is introduced. We use it pretty often and I hope you’ll like it as we do. </font></p>https://soft29.ru/blog/entry/invoke-liferay-service-externallyInvoke Liferay service externallyStanislav2018-08-14T09:01:50+00:002018-11-01T10:05:27+00:00<p><font face="Times New Roman" size="4">Hi there, in this post we’ll take a look at the problem of Liferay 7 service invocation from the external app</font></p><p><font face="Times New Roman" size="4">Hi there, in this post we’ll take a look at the problem of Liferay 7 service invocation from the external app. Liferay provides a set of core services to be invoked using REST calls. That means that one can perform CRUD ops (read/write etc) on some Liferay instance using any client outside. A full set of available out-of-the-box services can be found, e.g. here <em>liferayhost.com/api/jsonws</em>. At this page you’ll see a various number of available Liferay services. For example, we can use the following (and many others) service methods of JournalArticle:</font></p><p><a href="https://soft29.ru/blog/mediaresource/ff7b8db2-a65e-4faa-bdb9-611ee8e57ace"><img width="185" height="439" title="image" style="margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/402ee381-7191-4cbc-a264-31daf3da82e2" border="0"></a></p><p><br></p><p><font face="Times New Roman" size="4">Let’s call the <strong>update-article-translation</strong> from the external app. It has the following method description taken from the same <em>jsonws</em> page:</font></p><p><a href="https://soft29.ru/blog/mediaresource/c9d7f013-0017-4d7a-848f-48c1d5e2f4ce"><img width="373" height="344" title="image" style="margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/f8b48a32-8c1a-4ae5-8816-ff70ab872873" border="0"></a></p><font face="Times New Roman" size="4"><p>To get this method executed we need to find its parameter values. As a starting point, <b>groupId</b> (39931) and <b>articleId</b> (125411) can be retrieved from current article (web content) URL, e.g.:</p><blockquote>http://somesite.dk/web/site/utils?p_p_id=dk_lw_portlet_Utils&p_p_lifecycle=2&p_p_state=normal&p_p_mode =view&p_p_resource_id =getRawArticle&p_p_cacheability=cacheLevelPage&p_p_col_id=column- 1&p_p_col_count=9&_dk_lw_portlet_Utils_<b>groupId=39931 </b>&_dk_lw_portlet_Utils_<b>articleId=125411</b>&_dk_lw_portlet_Utils_portletData =&_dk_lw_portlet_Utils_currentURL=%2Fweb%2Ftest-site%2Futils&_dk_lw_portlet_Utils_doAsUserId =&_dk_lw_portlet_Utils_dataType=JSON&_dk_lw_portlet_Utils_cmd=add</blockquote><p>Also to make calls to Liferay’s web services, need to use Basic Authentication (<a title="liferay" href="https://en.wikipedia.org/wiki/Basic_access_authentication" target="_blank" rel="nofollow">wiki</a>). To make it work we need to encode our login/password using Base64 algorithm. It’s possible to encode our credentials e.g. here (<a href="https://www.blitter.se/utils/basic-authentication-header-generator/">https://www.blitter.se/utils/basic-authentication-header-generator/</a>):</p><p><a href="https://soft29.ru/blog/mediaresource/24970613-9631-4208-8d30-ed28b02c6405"><img width="349" height="266" title="image" style="margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/2cb76566-2503-4ec7-8d57-b7d517165336" border="0"></a></p><p>Now we can use any REST client to make a call to Liferay to get the content of some web content. This endpoint should be used according to <em>jsonws</em> method description: <b>/api/jsonws/journal.journalarticle/get-article</b>. An example is made in ARC (Chrome Extension):<p><a href="https://soft29.ru/blog/mediaresource/67ac41b1-6812-4d92-9eab-d7bfa382e698"><img width="444" height="470" title="image" style="margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/d32f960d-c455-4511-98e8-83a02be79502" border="0"></a><p>We’ll get a JSON response, from which we extract web content’s version and a content actually:<blockquote><p><font color="#4f81bd" size="3">"version": 1,</font><p><font color="#4f81bd" size="3">"content": "<?xml version="1.0"?> <root available-locales="en_US" default-locale="en_US"> <dynamic-element name="content" type="text_area" index-type="keyword" instance-id="sjae"> <dynamic-content language-id="en_US"><![CDATA[<p>some content</p>]]></dynamic-content> </dynamic-element> </root>"</font></p></blockquote><p>Now we should add a new translation to extracted content (note bold text below):<blockquote><p><font size="3"><?xml version="1.0"?> </font><p><font size="3"><root available-locales="en_US, <b>da_DK</b>" default-locale="en_US"> </font><p><font size="3"><dynamic-element name="content" type="text_area" index-type="keyword" instance-id="sjae"> </font><p><font size="3"> <dynamic-content language-id="en_US"> <![CDATA[ <p>some content</p>]]> </dynamic-content> </font><p><b><font size="3"> <dynamic-content language-id="da_DK"> <![CDATA[ <p>some content in Danish</p>]]></dynamic-content > </font></b><p><font size="3"></dynamic-element> </font><p><font size="3"></root></font> </p></blockquote><p>Now we can use ARC client to make a call to Liferay to update the content of the web content. The following fields are required (“-” prefixed are skipped on Liferay side, but anyway we ought to add them to request):<ul><li>groupId (39931)</li><li>articleId (125411)</li><li>version (value = 1, it should be the last version of the web content)</li><li>content (copy from 6.)</li><li>locale (set default locale e.g. «da_DK»), to skip use «-locale»</li><li>-description</li><li>-images</li><li>-title</li></ul><p>Endpoint is, having POST method: <b>/api/jsonws/journal.journalarticle/update-article-translation</b><p>An example made in ARC (Chrome Extension):<p><a href="https://soft29.ru/blog/mediaresource/36484603-fbc7-4b29-a687-7029a2bec5cd"><img width="528" height="459" title="image" style="margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/587e4243-5976-4fa1-afbb-18adbd79a149" border="0"></a><p>In the end the updated translation will be seen in Liferay UI:</p><p><a href="https://soft29.ru/blog/mediaresource/74874fd6-3483-44fe-85b0-fc90ec3df721"><img width="523" height="331" title="image" style="margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/02ee726c-c114-4f81-84ce-e7c58d4c01e1" border="0"></a></p><p>That’s it! In this post we saw how to invoke Liferay service (get article and update article) from any external application using REST interface. I hope it was helpful.</p></font>https://soft29.ru/blog/entry/liferay-7-dxp-saml-singleLiferay 7 DXP + SAML single sign-on (SSO)Stanislav2018-05-01T16:39:43+00:002018-05-01T16:43:33+00:00<p><font face="Times New Roman" size="4">Hi there, this post dedicated to the topic of SSO authentication within Liferay 7 DXP version. Single sign-on is a pretty popular property that allows users to access multiple applications using same credentials without re-login. And SAML is a language that allows cross-party communications to validate and authenticate a user.</font></p><p><font face="Times New Roman" size="4">Hi there, this post dedicated to the topic of SSO authentication within Liferay 7 DXP version. Single sign-on is a pretty popular property that allows users to access multiple applications using same credentials without re-login. And SAML is a language that allows cross-party communications to validate and authenticate a user.</font></p><p><font face="Times New Roman" size="4">To make SSO work our Liferay instance will be configured as an Identity Provider (<a href="https://en.wikipedia.org/wiki/Identity_provider_(SAML)" rel="nofollow">IdP</a> in terms of SAML) and a SimpleSAMLphp <a href="https://simplesamlphp.org/" rel="nofollow">app</a> that will serve as a Service Provider (<a href="https://en.wikipedia.org/wiki/Service_provider_(SAML)" rel="nofollow">SP</a>). </font></p><p><font face="Times New Roman" size="4">1. As a first step after SimpleSAMLphp was installed, we need to configure our custom Authentication Source which is a SP actually.</font></p><blockquote><p><font face="Times New Roman" size="4">An authentication source is responsible for authenticating the user, typically by getting a username and password, and looking it up in some sort of database.</font></p></blockquote><p><font face="Times New Roman" size="4">Our Authentication Source should be added to <em>/simplesamlphp/config/authsources.php</em>:</font></p><pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);"><span style="color: rgb(0, 163, 63);">'lr1dev-sp'</span> => array(
<span style="color: rgb(0, 163, 63);">'saml:SP'</span>,
<span style="color: rgb(145, 145, 145);">// The entity ID of this SP.</span>
<span style="color: rgb(145, 145, 145);">// Can be NULL/unset, in which case an entity ID is generated based on the metadata URL.</span>
<span style="color: rgb(0, 163, 63);">'entityID'</span> => null,
<span style="color: rgb(145, 145, 145);">// The entity ID of the IdP this should SP should contact.</span>
<span style="color: rgb(145, 145, 145);">// Can be NULL/unset, in which case the user will be shown a list of available IdPs.</span>
<span style="color: rgb(0, 163, 63);">'idp'</span> => <span style="color: rgb(0, 163, 63);">'lr1dev-saml-iprovider'</span>,
<span style="color: rgb(145, 145, 145);">// The URL to the discovery service.</span>
<span style="color: rgb(145, 145, 145);">// Can be NULL/unset, in which case a builtin discovery service will be used.</span>
<span style="color: rgb(0, 163, 63);">'discoURL'</span> => null,
<span style="color: rgb(0, 163, 63);">'privatekey'</span> => <span style="color: rgb(0, 163, 63);">'signmessages.pem'</span>,
<span style="color: rgb(0, 163, 63);">'certificate'</span> => <span style="color: rgb(0, 163, 63);">'signmessages.crt'</span>,
),
</pre><p><font face="Times New Roman" size="4">Note: privatekey and certificate are custom-generated keys that are optional, but required for the case if you want to allow user to perform logout using IdP. Otherwise Request not signed exception will be thrown. See <strong>sign.logout</strong> in the next section.</font></p><p><font face="Times New Roman" size="4">2. In this step we should specify Entity Id of the target IdP (Liferay is our case) our SP will talk to. This entity Id is placed in <em>/simplesamlphp/metadata/saml20-idp-remote.php</em>:</font></p><pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);">$metadata[<span style="color: rgb(0, 163, 63);">'lr1dev-saml-iprovider'</span>] = array(
<span style="color: rgb(0, 163, 63);">'SingleSignOnService'</span> => <span style="color: rgb(0, 163, 63);">'https://lr1dev.liferay.com/c/portal/saml/sso'</span>,
<span style="color: rgb(0, 163, 63);">'SingleLogoutService'</span> => <span style="color: rgb(0, 163, 63);">'https://lr1dev.liferay.com/c/portal/saml/slo'</span>,
<span style="color: rgb(0, 163, 63);">'certificate'</span> => <span style="color: rgb(0, 163, 63);">'custom-liferay-saml-iprovider.pem'</span>,
<span style="color: rgb(0, 163, 63);">'sign.logout'</span> => true,
);
</pre><p><font face="Times New Roman" size="4">Here the ‘certificate’ attribute is optional, it may be used in some cases. In short – it’s a certificate for current IdP.</font></p><p><font face="Times New Roman" size="4">On this step, the configuration of SimpleSAMLapp is completed, now we can proceed to Liferay part.</font></p><p><font face="Times New Roman" size="4">3. We assume that Liferay SAML 2.0 Provider plugin is already installed within the Liferay instance. Now we need to configure it to server as an IdP. Navigate to Liferay SAML configuration, General tab, specify the id of the new IdP + generate private key/certificate (the generation of these keys is required): </font></p><p><font face="Times New Roman" size="4"><a href="https://soft29.ru/blog/mediaresource/1df2771a-519c-4e52-bd28-8b17d762bb4c"><img width="345" height="357" title="1" style="border: 0px currentcolor; border-image: none; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="1" src="https://soft29.ru/blog/mediaresource/8975a6d1-dbd2-4597-906f-2fa193b8baab" border="0"></a></font></p><p><font face="Times New Roman" size="4">4. Identity Provider tab should look so:</font></p><p align="center"><a href="https://soft29.ru/blog/mediaresource/5b6fca19-b9ca-41bb-b269-f3c400483ebe"><img width="163" height="328" title="image" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/71f62a97-d147-45a6-8847-adfa1440f9ca" border="0"></a> <a href="https://soft29.ru/blog/mediaresource/428c7cf1-1242-4105-bd41-5083a3ff1d1e"><img width="140" height="329" title="image" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/b682390e-edb6-4764-9b52-8245152f70aa" border="0"></a></p><p><font face="Times New Roman" size="4">If there’s a need to retrieve custom fields (expandos) from IdP, we can add the following to the “Attributes” e.g. “expando:phone-number”.</font></p><p><font face="Times New Roman" size="4">5. Service provider tab. Add our source service provider (<strong>lr1dev-sp</strong>) metadata URL:</font></p><p><a href="https://soft29.ru/blog/mediaresource/d6130c00-e635-4aa4-b878-0f3390a7124e"><img width="690" height="233" title="image" style="border: 0px currentcolor; border-image: none; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="image" src="https://soft29.ru/blog/mediaresource/a2079af5-9f43-4e30-8a7d-a8a132a7ed28" border="0"></a></p><p><font face="Times New Roman" size="4">Alright, we are done with the configuration, it’s time to write a simple PHP page that will use our Service provider to authenticate user against Liferay IdP. Here’s the code:</font></p><pre style="background: rgb(255, 255, 255); color: rgb(0, 0, 0);"><!DOCTYPE html>
<html>
<head>
<title>SAML Login</title>
</head>
<body>
<h1>SAML test login</h1>
<?php
<span style="color: rgb(255, 86, 0);">require_once</span>(<span style="color: rgb(0, 163, 63);">'../../simplesamlphp/lib/_autoload.php'</span>);
<span style="color: rgb(145, 145, 145);">#select our authentication source:</span>
$as <span style="color: rgb(255, 86, 0);">=</span> <span style="color: rgb(255, 86, 0);">new</span> \SimpleSAML\Auth\<span style="color: rgb(165, 53, 174);">Simple</span>(<span style="color: rgb(0, 163, 63);">'lr1dev-sp'</span>);
<span style="color: rgb(145, 145, 145);">#request authentication</span>
$as<span style="color: rgb(255, 86, 0);">-></span>requireAuth();
<span style="color: rgb(145, 145, 145);">#print credentials</span>
$attributes <span style="color: rgb(255, 86, 0);">=</span> $as<span style="color: rgb(255, 86, 0);">-></span>getAttributes();
<span style="color: rgb(145, 145, 145);">//print_r($attributes);</span>
<span style="color: rgb(165, 53, 174);">echo</span> <span style="color: rgb(0, 163, 63);">'<br/>Email address <b>'</span> <span style="color: rgb(255, 86, 0);">.</span> $attributes[<span style="color: rgb(0, 163, 63);">"emailAddress"</span>][0] <span style="color: rgb(255, 86, 0);">.</span> <span style="color: rgb(0, 163, 63);">'</b> successfully authenticated on Liferay and logged back into application server.<br/><br/>'</span>;
?>
</body>
</html>
</pre><p><font face="Times New Roman" size="4">When we launch this app, we’ll be instantly redirected to Liferay login page and after successful login, we are redirected back to the app, where some Liferay user attributes are rendered. </font></p><p><font face="Times New Roman" size="4">That’s it, we configured Liferay 7 DXP coupled with the simpleSAMLphp app. I hope this post was useful. </font></p><p><br><font face="Times New Roman" size="4"></font></p><p><font face="Times New Roman" size="4"></font></p>https://soft29.ru/blog/entry/liferay-7-override-resource-permissionsLiferay 7: override Resource PermissionsStanislav2017-07-22T11:57:52+00:002017-07-22T12:03:45+00:00<p><font size="4" face="Times New Roman">In Liferay 6 when you needed to change default permission set for some resource, then Ext plugin could be used to override such definitions. The reason of Ext using was the point that all permission definitions were stored in <em>portal-impl.jar</em> and Ext was the only way to override it. Starting from Liferay 7 the portal is moving to OSGi architecture where the other rules take place. Since now each OSGi module (e.g. Calendar) has its permissions defined in its own module, and not in the core of the portal. That’s why a new approach should be applied and this post shows how to achieve this. </font></p><p><font size="4" face="Times New Roman">In Liferay 6 when you needed to change default permission set for some resource, then Ext plugin could be used to override such definitions. The reason of Ext using was the point that all permission definitions were stored in <em>portal-impl.jar</em> and Ext was the only way to override it. Starting from Liferay 7 the portal is moving to OSGi architecture where the other rules take place. Since now each OSGi module (e.g. Calendar) has its permissions defined in its own module, and not in the core of the portal. That’s why a new approach should be applied and this post shows how to achieve this. </font></p> <p><font size="4" face="Times New Roman">In OSGi concept, each bundle’s own classpath is of higher priority than the other fragment modules (the ones that supposed to extend the current one). That means that the configuration defined in a fragment module won’t override the target bundle. </font><font size="4" face="Times New Roman">So how should we proceed then? What should be done to be able to change guest defaults, supported and unsupported permissions for site members and others on a resource? Such possibility appeared only since Liferay 7-GA4, where the following should be done (a Calendar took as an example):</font></p> <ol> <li><font size="4" face="Times New Roman">Create a new fragment module that will override Calendar default permissions. It’s <em>bnd.bnd</em> file should include:<pre style="color: #000">Fragment<span style="color: #00f">-</span>Host: com.liferay.calendar.service;bundle<span style="color: #00f">-</span><span style="color: #6782d3">version</span><span style="color: #00f">=</span><span style="color: #093">"2.1.30"</span>
</pre></font>
<li><font size="4" face="Times New Roman">Put <em>portlet-ext.properties</em> into the root of the current module. Its content: <pre style="color: #000">resource.actions.configs=resource-actions/default.xml,resource-actions/default-ext.xml
</pre></font>
<li><font size="4" face="Times New Roman">Create a new file /resource-actions/default-ext.xml that will be a copy of Calendar permissions definition with the custom modification. It will look as usual: <pre style="color: #000"><span style="color: #333"><?<span style="font-weight: 700">xml</span><span style="color: #36c; font-style: italic"> version</span>=<span style="color: #093">"1.0"</span>?></span>
<span style="color: #03c"><!<span style="color: #00f">DOCTYPE</span> resource-action-mapping PUBLIC "-//Liferay//DTD Resource Action Mapping 6.2.0//EN" "http://www.liferay.com/dtd/liferay-resource-action-mapping_6_2_0.dtd"></span>
<span style="color: #03c"><<span style="font-weight: 700">resource-action-mapping</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">model-resource</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">model-name</span>></span>com.liferay.calendar<span style="color: #03c"></<span style="font-weight: 700">model-name</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">portlet-ref</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">portlet-name</span>></span>com_liferay_calendar_web_portlet_CalendarPortlet<span style="color: #03c"></<span style="font-weight: 700">portlet-name</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">portlet-ref</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">root</span>></span>true<span style="color: #03c"></<span style="font-weight: 700">root</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">permissions</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">supports</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">action-key</span>></span>ADD_RESOURCE<span style="color: #03c"></<span style="font-weight: 700">action-key</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">action-key</span>></span>PERMISSIONS<span style="color: #03c"></<span style="font-weight: 700">action-key</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">supports</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">site-member-defaults</span> /></span>
<span style="color: #06f; font-style: italic"><!-- Guest default permission VIEW removed --></span>
<span style="color: #03c"><<span style="font-weight: 700">guest-defaults</span> /></span>
<span style="color: #03c"><<span style="font-weight: 700">guest-unsupported</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">action-key</span>></span>ADD_RESOURCE<span style="color: #03c"></<span style="font-weight: 700">action-key</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">action-key</span>></span>PERMISSIONS<span style="color: #03c"></<span style="font-weight: 700">action-key</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">guest-unsupported</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">permissions</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">model-resource</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">resource-action-mapping</span>></span>
</pre></font>
<li><font size="4" face="Times New Roman">Build the module and deploy it to portal.</font></ol>
<p><font size="4" face="Times New Roman">After deployment your custom permissions will be applied. </font></p>
<p><font size="4" face="Times New Roman">This approach will work only starting from Liferay 7-GA4, but it’s a good beginning that makes Liferay OSGi more flexible and customizable. </font></p>https://soft29.ru/blog/entry/alfresco-solr-enable-search-ofAlfresco Solr: enable search of special charactersStanislav2017-05-01T15:31:58+00:002017-08-01T15:34:14+00:00<p><font size="4" face="Times New Roman">Alfresco’s out of the box search engine Solr is configured in a way that searching of special characters is disabled by default. So, for instance, if you would like to find all documents in a system containing “c++” string then you may get tons (depending on the uploaded documents amount) of results that contain “C”-character without “++”-signs in its name and content: document content <em>Fusce dapibus, tellus ac cursus commodo</em>, search key <em>c++</em>, results <em>“Fusce</em>”, “<em>ac</em>”, “<em>cursus</em>”, “<em>commodo</em>”. In order to configure the correct search behavior please follow down to read current post.</font></p><p><font size="4" face="Times New Roman">Alfresco’s out of the box search engine Solr is configured in a way that searching of special characters is disabled by default. So, for instance, if you would like to find all documents in a system containing “c++” string then you may get tons (depending on the uploaded documents amount) of results that contain “C”-character without “++”-signs in its name and content: document content <em>Fusce dapibus, tellus ac cursus commodo</em>, search key <em>c++</em>, results <em>“Fusce</em>”, “<em>ac</em>”, “<em>cursus</em>”, “<em>commodo</em>”. In order to configure the correct search behavior please follow down to read current post.</font></p> <p><font size="4" face="Times New Roman">Special characters in Solr are: - && || ! ( ) { } [ ] ^ " ~ * ? : \ and we want them to be searched. First of all we need to tweak Solr tokenization settings. Tokenization is a process that parses uploaded or existing documents and extracts tokens from document fields (e.g. content, name) to be added to search index. In Alfresco by default <em>solr.ICUTokenizerFactory</em> tokenizer is used, which excludes special characters from being added to search index. To overcome this, we can replace current tokenizer with the another one which preserve special characters, such tokenizer can be <em>org.apache.solr.analysis.WhitespaceTokenizerFactory</em>, it splits words by whitespaces. To do so please go to %Alfresco%\solr4\workspace-SpacesStore\conf\schema.xml, update <strong>text___</strong> field type tokenization setting:</font></p><pre style="color: #000"><span style="color: #03c"><<span style="font-weight: 700">fieldType</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"text___"</span> <span style="color: #36c; font-style: italic">class</span>=<span style="color: #093">"solr.TextField"</span> <span style="color: #36c; font-style: italic">positionIncrementGap</span>=<span style="color: #093">"100"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">analyzer</span>></span>
<span style="color: #06f; font-style: italic"><!--tokenizer class="solr.ICUTokenizerFactory"/--></span>
<span style="color: #03c"><<span style="font-weight: 700">tokenizer</span> <span style="color: #36c; font-style: italic">class</span>=<span style="color: #093">"org.apache.solr.analysis.WhitespaceTokenizerFactory"</span> /></span>
<span style="color: #03c"></<span style="font-weight: 700">analyzer</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">fieldType</span>></span>
</pre>
<p><font size="4" face="Times New Roman">Here’s field type <strong>text___</strong> is a type that is set to some document fields. At this time we already have <em>WhitespaceTokenizerFactory</em> set to <strong>text___</strong> field type. In this way if we want, for example, a <em>content</em> field of any document to be searchable including special characters – need to replace <em>content</em> field type with the <strong>text___</strong> type (in the same file):</font></p><pre style="color: #000"><span style="color: #03c"><<span style="font-weight: 700">dynamicField</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"content@s____@*"</span> <span style="color: #36c; font-style: italic">type</span>=<span style="color: #093">"identifier"</span> <span style="color: #36c; font-style: italic">indexed</span>=<span style="color: #093">"true"</span> <span style="color: #36c; font-style: italic">omitNorms</span>=<span style="color: #093">"true"</span> <span style="color: #36c; font-style: italic">stored</span>=<span style="color: #093">"false"</span> <span style="color: #36c; font-style: italic">multiValued</span>=<span style="color: #093">"false"</span> <span style="color: #36c; font-style: italic">termPositions</span>=<span style="color: #093">"false"</span> /></span>
<span style="color: #03c"><<span style="font-weight: 700">dynamicField</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"content@s__l_@*"</span> <span style="color: #36c; font-style: italic">type</span>=<span style="color: #093">"alfrescoFieldType"</span> <span style="color: #36c; font-style: italic">indexed</span>=<span style="color: #093">"true"</span> <span style="color: #36c; font-style: italic">omitNorms</span>=<span style="color: #093">"true"</span> <span style="color: #36c; font-style: italic">stored</span>=<span style="color: #093">"false"</span> <span style="color: #36c; font-style: italic">multiValued</span>=<span style="color: #093">"false"</span> <span style="color: #36c; font-style: italic">termPositions</span>=<span style="color: #093">"false"</span> /></span>
<span style="color: #06f; font-style: italic"><!-- commented in the old setting --></span>
<span style="color: #06f; font-style: italic"><!-- dynamicField name="content@s__lt@*" type="<b>alfrescoFieldType</b>" indexed="true" omitNorms="false" stored="false" multiValued="false" /--></span>
<span style="color: #06f; font-style: italic"><!-- new setting appended: --></span>
<span style="color: #03c"><<span style="font-weight: 700">dynamicField</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"content@s__lt@*"</span> <span style="color: #36c; font-style: italic">type</span>=<span style="color: #093">"<b>text___</b>"</span> <span style="color: #36c; font-style: italic">indexed</span>=<span style="color: #093">"true"</span> <span style="color: #36c; font-style: italic">omitNorms</span>=<span style="color: #093">"false"</span> <span style="color: #36c; font-style: italic">stored</span>=<span style="color: #093">"false"</span> <span style="color: #36c; font-style: italic">multiValued</span>=<span style="color: #093">"false"</span> /></span>
<span style="color: #03c"><<span style="font-weight: 700">dynamicField</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"content@s___t@*"</span> <span style="color: #36c; font-style: italic">type</span>=<span style="color: #093">"text___"</span> <span style="color: #36c; font-style: italic">indexed</span>=<span style="color: #093">"true"</span> <span style="color: #36c; font-style: italic">omitNorms</span>=<span style="color: #093">"false"</span> <span style="color: #36c; font-style: italic">stored</span>=<span style="color: #093">"false"</span> <span style="color: #36c; font-style: italic">multiValued</span>=<span style="color: #093">"false"</span> /></span>
</pre>
<p align="left"><font size="4" face="Times New Roman">That’s it, after Solr index if fully <a href="http://docs.alfresco.com/5.1/tasks/solr-reindex.html" rel="nofollow" target="_blank">cleared</a> and recreated, Solr will have index that include special characters, plus search query will also come without special chars losses to perform Solr search.</font></p>https://soft29.ru/blog/entry/how-to-build-liferay-themeHow to build Liferay theme using SDK and AntStanislav2017-03-23T10:54:24+00:002017-05-06T10:27:19+00:00<p><font size="4" face="Times New Roman">Liferay Theming is a very common Liferay technology that widely used to define the look of a portal. It consist of different sets of Javascript, CSS and HTML delivered as a single WAR-file, which should have a special folder structure and configuration files. To create and build such WAR, Liferay SDK can be used that is designed to automate the process of Liferay Theme development. This article describes how to build already existing Theme and customize it using Liferay SDK Ant builder.</font></p> <p><font size="4" face="Times New Roman">Liferay Theming is a very common Liferay technology that widely used to define the look of a portal. It consist of different sets of Javascript, CSS and HTML delivered as a single WAR-file, which should have a special folder structure and configuration files. To create and build such WAR, Liferay SDK can be used that is designed to automate the process of Liferay Theme development. This article describes how to build already existing Theme and customize it using Liferay SDK Ant builder.</font></p> <p><font size="4" face="Times New Roman">Assume that we are on Windows OS and Liferay (e.g., version 6.2-GA6) installed under the <em>C:/Liferay</em> folder:</font></p> <p><font size="4" face="Times New Roman"><a href="https://soft29.ru/blog/mediaresource/5b918e49-d8be-43df-9ad8-97df7ad1b0a1"><img title="Page-1-Image-2" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="Page-1-Image-2" src="https://soft29.ru/blog/mediaresource/de24a651-bfd6-40d9-afe8-2d9040c07875" width="156" height="187"></a></font></p> <p><font size="4" face="Times New Roman">Here are the steps how to build some predefined Liferay theme called “mueller-theme”:</font></p> <p><font size="4" face="Times New Roman">1. Install and configure Apache Ant. This tool will perform theme building (dirs creation, sass compilation, zipping etc.). To check that Ant is correctly installed, run the command<br>in Command Line: <em>ant –version:</em></font></p> <p><font size="4" face="Times New Roman"><a href="https://soft29.ru/blog/mediaresource/eaf93294-1858-4bec-a3a6-410d87d0b2a3"><img title="Page-1-Image-3" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="Page-1-Image-3" src="https://soft29.ru/blog/mediaresource/f093f9dc-4e91-43da-bfce-a7d7598982a0" width="414" height="47"></a></font></p> <p><font size="4" face="Times New Roman">2. Download Liferay 6.2 GA6 Plugins SDK (or other version) using the <a title="liferay" href="https://sourceforge.net/projects/lportal/files/Liferay%20Portal/" rel="nofollow" target="_blank">link</a>: </font></p> <p><a href="https://soft29.ru/blog/mediaresource/48bfc087-eeab-4898-8940-8613cbb45df0"><img title="Page-1-Image-4" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="Page-1-Image-4" src="https://soft29.ru/blog/mediaresource/960699b7-789e-4575-bed2-313e96f39e10" width="508" height="199"></a></p> <p><font size="4" face="Times New Roman">Downloaded Liferay SDK includes Ant scripts to build the theme.</font></p> <p><font size="4" face="Times New Roman">3. Extract SDK archive to some directory (e.g., to c<em>:/SDK</em>). </font></p> <p><font size="4" face="Times New Roman">4. Copy predefined <em>mueller-theme</em> to <em>c:/SDK/themes</em> folder.</font></p> <p><font size="4" face="Times New Roman">5. Create a file "build.[username].properties" in <em>c:/SDK</em> dir. For example, if system user name is “Doe” then properties file name should be “build.Doe.properties”. In this file there is an information about Liferay bundle location (located in c:/Liferay) should be provided:</font></p><pre style="background: #f9f9f9; color: #080808">app.server.type=tomcat
app.server.dir=c:/Liferay/tomcat-7.0.62
app.server.deploy.dir=${app.server.dir}/webapps
app.server.lib.global.dir=${app.server.dir}/lib/ext
app.server.portal.dir=${app.server.dir}/webapps/ROOT
</pre>
<p><font size="4" face="Times New Roman">6. Navigate to location where your theme was just copied (step 4): <em>c:/SDK/themes/mueller-theme</em>.</font></p>
<p><font size="4" face="Times New Roman">7. Make sure that build.xml exists in <em>mueller-theme</em> dir. Run from the Command Line the Ant command to build a theme’s war file: <strong>ant war</strong>. It can take up to 2 minutes to build a theme. If the building completes successfully, the message is shown:</font></p>
<p><a href="https://soft29.ru/blog/mediaresource/752c7d87-0650-4d08-803d-f66ef7d4bfb3"><img title="Page-2-Image-5" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="Page-2-Image-5" src="https://soft29.ru/blog/mediaresource/746f9c8a-f3c7-41f2-94e7-fd2fc111e8a0" width="851" height="264"></a></p>
<p><font size="4" face="Times New Roman">The SDK path in this image is wrong, it should look like <em>c:/SDK</em>, but the overall war building process is presented. </font></p>
<p><font size="4" face="Times New Roman">8. The resulting theme war will be generated in <em>c:/SDK/dist</em>: </font></p>
<p><font size="4" face="Times New Roman"><a href="https://soft29.ru/blog/mediaresource/6258bb75-a240-4c74-8e67-0f2bcec80b8d"><img title="Page-2-Image-6" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="Page-2-Image-6" src="https://soft29.ru/blog/mediaresource/96ae03db-be68-4d2d-8d53-c861c3ebb999" width="434" height="94"></a></font></p>
<p><font size="4" face="Times New Roman">Current theme is designed in a way that all changes should be performed in scss-files based on which css-files are generated. The ant script above finds these scss files and generates css. So, for example, if you want to perform some change in <em>mueller-theme\docroot\_diffs\css\header.scss</em>, reproduce the 1-10 steps and the updated war will have these new changes.</font></p>
<p><font size="4" face="Times New Roman">In this post we’ve got through the topic of Liferay Theme building using its SDK and embedded Ant scripts. I hope it is helpful.</font></p>https://soft29.ru/blog/entry/odoo-10-setup-and-debugOdoo 10 setup and debug in EclipseStanislav2017-01-24T09:32:32+00:002017-08-14T08:40:20+00:00<p align="justify"><font size="4" face="Times New Roman">Hi everyone, I’m writing this post to describe an installation process of Odoo and its debug in Eclipse IDE on Windows. There are articles exist on the internet dedicated to this theme, but none of them covers this task in relation to Odoo version 10. Odoo 10 differs from the other versions, plus it ships with another set of setup files, so if you’re interested in detailed description of Odoo setup in Eclipse – this post may be helpful.</font></p><p align="justify"><font size="4" face="Times New Roman">Hi everyone, I’m writing this post to describe an installation process of Odoo and its debug in Eclipse IDE on Windows. There are articles exist on the internet dedicated to this theme, but none of them covers this task in relation to Odoo version 10. Odoo 10 differs from the other versions, plus it ships with another set of setup files, so if you’re interested in detailed description of Odoo setup in Eclipse – this post may be helpful.</font></p> <p align="justify"><font face="Times New Roman"><font size="4">We need the source code of Odoo to make the debugging possible, so let’s start with Odoo 10 source code downloading: you can do this either by going to </font><a title="odoo" href="https://github.com/odoo/odoo" rel="nofollow" target="_blank"><font size="4">GitHub</font></a><font size="4"> or just by downloading it from Odoo’s official </font><a title="odoo" href="https://www.odoo.com/page/download" rel="nofollow" target="_blank"><font size="4">site</font></a><font size="4">. In first case you’ll have to wait much longer since there’s the entire commit history is downloaded as well. Let’s say that Odoo source is downloaded to <em>C:/Users/Lenovo/git/Odoo</em>.</font></font></p> <p align="justify"><font size="4" face="Times New Roman">Odoo 10 is based on Python 2.7 and if you don’t have it installed locally – please do it using the official <a title="python" href="https://www.python.org/downloads" rel="nofollow" target="_blank">site</a> of Python. Odoo requires a few Python libs (such as Babel, ebaysdk, lxml, python-ldap etc.) that are not shipped within a standard Python bundle, these libs should be installed manually to your Python. Luckily Pip package manager is already persist in downloaded Python distributive and you can easily use it to install required packages. Within Odoo distribution directory you can find <em>requirements.txt</em> file that keeps all package names + version to be installed. In this way go to <em>%Python_dir%/Scripts</em> folder where <em>pip.exe</em> is located and run the following command (assuming <em>requirements.txt</em> is placed in current dir):</font></p> <div align="justify"><pre style="color: #000">pip install -r requirements.txt
</pre></div>
<p align="justify"><font size="4" face="Times New Roman">After that almost all packages required to Odoo will be installed. Since we’re on Windows we need to install <em>win32service</em> as well. This module is not stored in Pip repository, it should be downloaded from the <a title="odoo" href="https://sourceforge.net/projects/pywin32/files/pywin32/" rel="nofollow" target="_blank">sourceforge</a> directly taking in mind the 2.7 Python version. I’d recommend to download and run exe-file exactly from this source, since we’ve tried to take <em>win32service</em> from the other repositories, none of them worked.</font></p>
<p align="justify"><font size="4" face="Times New Roman">Now it’s time to start Eclipse. Install (if not yet) <strong>PyDev</strong> plugin: go to Window -> Preferences -> Install/Update -> Available Software Sites. Fill in new site name, e.g. “PyDev” and its URL <a href="http://pydev.org/updates">http://pydev.org/updates</a>. </font></p>
<p align="justify"><font size="4" face="Times New Roman">Next step is to set up Python Interpreter that will be used by PyDev. Interpreter itself is a local dir where the Python is installed. Go to Window –> Preferences –> PyDev –> Interpreters –> Python Interpreter –> New, then specify a path to your Python installation and click Save. As an example, assume that installation dir is <em>C:/Python27</em> then PyDev will search for its subdirs to find all modules location and add them to System PYTHONPATH, the final look of the interpreter view:</font></p>
<p align="justify"><a href="https://soft29.ru/blog/mediaresource/4b9414aa-0848-4f4f-b3c1-8acd3261afba"><img title="1" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="1" src="https://soft29.ru/blog/mediaresource/47eb8c4f-042b-4d79-ab11-f546e7c44e04" width="519" height="402"></a></p>
<p align="justify"><font size="4" face="Times New Roman">When the Python interpreter is installed, we can move further. Next step is to create new PyDev project in Eclipse. It’s pretty straightforward, pick <em>Python</em> project type, specify project name and select the Python interpreter we’ve just created. If you wish to attach Odoo source code to current project (we will need it since we’ll have custom code that uses Odoo modules and project’s main module <em>starter.py</em> refers to <em>odoo</em> module as well – see below), then open project Properties window, navigate to PyDev –PYTHONPATH, go to External Libraries path, add Odoo source folder (source folder is the one we’ve created while cloning the Odoo Git repo):</font></p>
<p align="justify"><a href="https://soft29.ru/blog/mediaresource/85a0ecf6-d604-4246-8110-d842c6ade3db"><img title="1" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="1" src="https://soft29.ru/blog/mediaresource/81e67b02-198e-464a-ac3f-13cb0665e5e4" width="512" height="365"></a></p>
<p align="justify"><font size="4" face="Times New Roman">Good, we’re almost done. Let’s configure Debug configuration. Create a new Python Run, select the project we’ve created, define main module. Main module is the python file that actually starts Odoo. This file called <em>starter.py</em> and has the content:</font></p>
<div align="justify"><pre style="color: #000"><span style="color: #00f">import</span> odoo
<span style="color: #00f">if</span> <span style="font-weight: 700; color: #33f">__name__</span> <span style="color: #00f">==</span> <span style="color: #093">"__main__"</span>:
odoo.cli.main()
</pre></div>
<p align="justify"><font size="4" face="Times New Roman">In previous versions of Odoo (earlier than 10) this main module was called <em>openerp-server.py</em>, in Odoo 10 it is missing, so there’s need to create a new one, e.g. <em>starter.py</em>. Main tab of Debug configuration looks so:</font></p>
<p align="justify"><a href="https://soft29.ru/blog/mediaresource/31e29f8e-4d17-4cca-986b-5397cd5ac4f5"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://soft29.ru/blog/mediaresource/5156bfca-a9d2-4ecf-8345-e63b4bac7125" width="515" height="485"></a></p>
<p align="justify"><font size="4" face="Times New Roman">The final thing we should define on Debug configuration form is to add Odoo configuration file that keeps information about database URL, user name, DB password + source code locations. This file <em>odoo.conf</em> can be found under Odoo distribution dir, the minimal and sufficient version of it may look as following: </font></p>
<div align="justify"><pre style="color: #000">[options]
db_host <span style="color: #00f">=</span> <span style="color: #9700cc">False</span>
db_port <span style="color: #00f">=</span> <span style="color: #9700cc">False</span>
db_user <span style="color: #00f">=</span> odoo
db_password <span style="color: #00f">=</span> <span style="color: #9700cc">False</span>
addons_path <span style="color: #00f">=</span> C:\Users\Lenovo\git\odoo\addons,C:\workspace_o
</pre></div>
<p align="justify"><font size="4" face="Times New Roman">Now we need to specify for our Debug configuration a path to current Odoo configuration file. Go to <em>Arguments</em> tab –> Program arguments –> Variables –> Edit Variables –> New. Here create a new variable with any name, the Value of the variable should be a path to the config file: <strong>--config=%PATH%/odoo.conf</strong>.</font></p>
<p align="justify"><font size="4" face="Times New Roman">Finally you can run you Debug configuration, set up some breakpoint and see how the Odoo (as well as your custom module) execution stops for debug purposes, the debug trace and program variables can be watched.</font></p>
<p align="justify"><font size="4" face="Times New Roman"></font></p>https://soft29.ru/blog/entry/liferay-php-portlets-a-fewLiferay PHP Portlets: a few recommendations prior developmentStanislav2016-12-14T09:55:59+00:002017-03-19T09:59:26+00:00<p><font size="4" face="Times New Roman">It’s well known, that Liferay includes special types of portlets called <a href="https://web.liferay.com/community/wiki/-/wiki/Main/PHP+Portlets" rel="nofollow" target="_blank">PHP Portlets</a> which allow to use a standard PHP code in order to provide desired functionality. But there are some issues may occur during PHP-portlet development that are not so evident to fix and find a solution on the internet. Such common issues are covered in this post, solutions to them are also provided. </font></p><p><font size="4" face="Times New Roman">It’s well known, that Liferay includes special types of portlets called <a href="https://web.liferay.com/community/wiki/-/wiki/Main/PHP+Portlets" rel="nofollow" target="_blank">PHP Portlets</a> which allow to use a standard PHP code in order to provide desired functionality. But there are some issues may occur during PHP-portlet development that are not so evident to fix and find a solution on the internet. Such common issues are covered in this post, solutions to them are also provided. </font></p> <p><font size="4" face="Times New Roman">Liferay is shipped with a <a title="liferay php" href="http://quercus.caucho.com/" rel="nofollow" target="_blank">Quercus</a> library, which is Caucho Technology. This lib is a Java implementation of PHP5, that is able to treat PHP code and PHP modules, such as PDO, MySQL, and JSON, and convert it to Java. In this way Liferay can handle PHP code having Quercus lib entirely written in Java and PHP being compiled into Java by this lib. During the work with Quercus within Liferay the following problems can take place:</font></p> <p><font size="4" face="Times New Roman">1. The PHP-portlet deployable entity is just a single (in the simplest case) php file that is zipped into an archive. And any PHP archive should have a file named index.php in it, it’s a Liferay entry point. Otherwise the portlet silent fails and you’ll see the message “</font><font size="4" face="Times New Roman">phpapp.zip is temporarily unavailable”, assuming that <em>phpapp.zip</em> is the deployed archive:</font></p> <p><font size="4" face="Times New Roman"><a href="https://soft29.ru/blog/mediaresource/6b32ee59-3bed-4945-902c-2f4136f96e0c"><img title="liferay php" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="liferay php" src="https://soft29.ru/blog/mediaresource/1c5b0381-feb2-474e-b0d5-58b62d45456a" width="240" height="40"></a> </font></p> <p><font size="4" face="Times New Roman">2. During the deployment of PHP-portlet, Liferay searches for all URLs within provided php-files and enhances them according to Liferay specification. For example, Liferay will try to find and replace ahrefs, links, form action urls on your php pages. It will convert HTML <em>form</em> tag into Liferay POST/GET url, taking into account <em>method </em>and <em>action </em>attributes provided, e.g. <form action="index.php" method="post"> will look quite different in the end. Also Liferay retrieves url parameters and wraps them according to its notation. A developer should be aware of these points for smoother implementation process. </font></p> <p><font size="4" face="Times New Roman">3. If you define an empty <em>action</em> url within <em>form</em> tag: <em><form action="" method="post"></em>, then you’ll see an exception in your logs: </font></p><pre style="color: #000">java.lang.StringIndexOutOfBoundsException<span style="color: #00f">: </span>String index out of <span style="font-weight: 700; color: #33f">range</span><span style="color: #00f">: </span><span style="color: #06f">0</span>
at java.lang.String.charAt(String.java<span style="color: #00f">:</span><span style="color: #06f">658</span>)
at com.liferay.util.bridges.common.ScriptPostProcess.doProcessPage(ScriptPostProcess.java<span style="color: #00f">:</span><span style="color: #06f">161</span>)
at com.liferay.util.bridges.common.ScriptPostProcess.processPage(ScriptPostProcess.java<span style="color: #00f">:</span><span style="color: #06f">68</span>)
at com.liferay.util.bridges.common.ScriptPostProcess.postProcessPage(ScriptPostProcess.java<span style="color: #00f">:</span><span style="color: #06f">58</span>)
at com.liferay.util.bridges.php.PHPPortlet.rewriteURLs(PHPPortlet.java<span style="color: #00f">:</span><span style="color: #06f">250</span>)
</pre>
<p><font size="4" face="Times New Roman">This’s happening because of URL substitution by Liferay as mentioned in 3. </font></p>
<p><font size="4" face="Times New Roman">4. To pass a parameter within a POST/GET method on a php-form addressed to Liferay, you’ll need to add a portlet name prefix to each parameter. For instance, let’s assume that portlet name is <em>phpappzip_WAR_phpappzip</em> and parameter name to pass is <em>search</em>, then the result html will look so:</font></p><pre style="color: #000"><span style="color: #03c"><<span style="font-weight: 700">form</span> <span style="color: #36c; font-style: italic">action</span>=<span style="color: #093">"index.php"</span> <span style="color: #36c; font-style: italic">method</span>=<span style="color: #093">"post"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">input</span> <span style="color: #36c; font-style: italic">type</span>=<span style="color: #093">"text"</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"_phpappzip_WAR_phpappzip_search"</span> <span style="color: #36c; font-style: italic">value</span>=<span style="color: #093">" . $search . "</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">form</span>></span>
</pre>
<p><font size="4" face="Times New Roman">And then, to retrieve the parameter in PHP, need to invoke:</font></p><pre style="color: #000">$search <span style="color: #00f">=</span> isset($_POST[<span style="color: #093">'search'</span>]) <span style="color: #00f">?</span> $_POST[<span style="color: #093">'search'</span>] <span style="color: #00f">:</span> <span style="color: #093">''</span>;
</pre>
<p><font size="4" face="Times New Roman">5. One more important thing should be noted: SQL queries on PHP-pages. If you have a sql-query on Liferay side and it’s addressed to an external database, e.g. via PHP PDO, then current query may fail if the request is too time consuming (because of its complexity). The following stacktrace will appear: </font></p><pre style="color: #000"><span style="color: #06f">13</span><span style="color: #00f">:</span><span style="color: #06f">33</span><span style="color: #00f">:</span><span style="color: #06f">16</span>,<span style="color: #06f">693</span> ERROR [RuntimePageImpl<span style="color: #00f">-</span><span style="color: #06f">2</span>][PHPPortlet<span style="color: #00f">:</span><span style="color: #06f">241</span>] <span style="font-weight: 700; color: #33f">Error</span> processing PHP
com.caucho.quercus.QuercusModuleException<span style="color: #00f">:</span> java.io.IOException<span style="color: #00f">:</span> <span style="font-weight: 700; color: #33f">Stream</span> closed
at com.caucho.quercus.env.StringValue.appendReadAll(StringValue.java<span style="color: #00f">:</span><span style="color: #06f">1746</span>)
at com.caucho.quercus.env.Post.fillPost(Post.java<span style="color: #00f">:</span><span style="color: #06f">158</span>)
at com.caucho.quercus.env.Post.fillPost(Post.java<span style="color: #00f">:</span><span style="color: #06f">78</span>)
at com.caucho.quercus.env.Env.fillPost(Env.java<span style="color: #00f">:</span><span style="color: #06f">559</span>)
at com.caucho.quercus.env.Env.<span style="font-weight: 700; color: #33f">start</span>(Env.java<span style="color: #00f">:</span><span style="color: #06f">936</span>)
at com.caucho.quercus.servlet.QuercusServletImpl.service(QuercusServletImpl.java<span style="color: #00f">:</span><span style="color: #06f">165</span>)
at com.caucho.quercus.servlet.QuercusServlet.service(QuercusServlet.java<span style="color: #00f">:</span><span style="color: #06f">592</span>)
at com.liferay.util.bridges.php.PHPPortlet.processPHP(PHPPortlet.java<span style="color: #00f">:</span><span style="color: #06f">224</span>)
Caused by<span style="color: #00f">:</span> java.io.IOException<span style="color: #00f">:</span> <span style="font-weight: 700; color: #33f">Stream</span> closed
at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java<span style="color: #00f">:</span><span style="color: #06f">312</span>)
at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java<span style="color: #00f">:</span><span style="color: #06f">200</span>)
at com.caucho.quercus.env.StringValue.appendReadAll(StringValue.java<span style="color: #00f">:</span><span style="color: #06f">1733</span>)
... <span style="color: #06f">49</span> more
</pre>
<p><font size="4" face="Times New Roman">The simplest way to overcome this, is just to limit the query returning result. In case of MySQL, just append <em>LIMIT 10</em> to your query. Usually requests from developer PCs can take more time, comparing to the ones deployed to a server (because of network bandwidth as well), so basically it’s worth to limit queries on dev-machines only. </font></p>
<p><font size="4" face="Times New Roman">6. When you deploy a PHP-portlet, it goes by default to <em>Undefined</em> category. To fix it, just add <em>liferay-display.xml </em>to deployable archive.</font></p>
<p><font size="4" face="Times New Roman">This article covers a few important issues that can be faced at a time of Liferay PHP portlet development. Solutions are provided as well. I hope it is useful. </font></p>https://soft29.ru/blog/entry/liferay-play-video-in-popupLiferay: Play Video in Popup WindowStanislav2016-10-15T10:21:41+00:002017-03-05T14:20:01+00:00<p><font size="4" face="Times New Roman">During Liferay customizations one may need to embed playable videos to portal: to a body of a page or to a popup. There are different posts exist on the internet about how to create a popup in Liferay, but none of them covers this common task – a video within a window. And in this article we’ll see 2 short ways how we can accomplish that. </font></p><p><font size="4" face="Times New Roman">During Liferay customizations one may need to embed playable videos to portal: to a body of a page or to a popup. There are different posts exist on the internet about how to create a popup in Liferay, but none of them covers this common task – a video within a window. And in this article we’ll see 2 short ways how we can accomplish that. </font></p> <p><font size="4" face="Times New Roman">The first approach implies the application of AlloyUI JS library which is widely used within Liferay 6.2. Actually the entire user interface of Liferay 6.2 is built using this framework, so having this involved we’ll get our custom components to look accordingly to Liferay UI. AlloyUI already has 2 modules that are very helpful to us: <em>aui-video</em> – to create a video player pane, <em>aui-modal</em> – to build a popup window. Both of these modules use predefined div ID to render the final component: </font></p> <ul> <li><font size="4" face="Times New Roman">the <em>aui-video</em> module – to embed video player into this div (<em>A</em> – is the reference to AlloyUI lib that will be injected a bit later): </font><pre style="background: #f6f8ff; color: #000020"><span style="font-weight: bold; color: #200080">new</span> A<span style="color: #308080">.</span>Video<span style="color: #308080">(</span>
<span style="color: #406080">{</span>
boundingBox<span style="color: #406080">:</span> <span style="color: #800000">"</span><span style="color: #1060b6">#videoDiv_someId</span><span style="color: #800000">"</span><span style="color: #308080">,</span>
fixedAttributes<span style="color: #406080">:</span> <span style="color: #406080">{</span>
allowfullscreen<span style="color: #406080">:</span> <span style="color: #800000">'</span><span style="color: #1060b6">true</span><span style="color: #800000">'</span>
<span style="color: #406080">}</span><span style="color: #308080">,</span>
url<span style="color: #406080">:</span> videoUrl
<span style="color: #406080">}</span>
<span style="color: #308080">)</span><span style="color: #308080">.</span>render<span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
</pre></li></ul>
<ul>
<li><font size="4" face="Times New Roman">the <em>aui-modal</em> module – to include current div into the popup it creates (<em>A</em> – is the reference to AlloyUI lib that will be injected a bit later): </font><pre style="background: #f6f8ff; color: #000020"><span style="font-weight: bold; color: #200080">new</span> A<span style="color: #308080">.</span>Modal<span style="color: #308080">(</span> <span style="color: #406080">{</span>
resizable<span style="color: #406080">:</span> <span style="color: #0f4d75">false</span><span style="color: #308080">,</span>
centered<span style="color: #406080">:</span> <span style="color: #0f4d75">true</span><span style="color: #308080">,</span>
render<span style="color: #406080">:</span> <span style="color: #800000">"</span><span style="color: #1060b6">#videoDiv_someId</span><span style="color: #800000">"</span>
<span style="color: #406080">}</span>
<span style="color: #308080">)</span><span style="color: #308080">.</span>render<span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
</pre></li></ul>
<p><font size="4" face="Times New Roman">At this point the video player popup is created, one thing is missing – the <em>videoDiv_someId</em> div wrapper. We can create it by means of JavaScript, not using direct HTML writing. And the final function which has only one string parameter – the url of embedded video, will look as following: </font></p><pre style="background: #f6f8ff; color: #000020"><span style="font-weight: bold; color: #200080">function</span> showVideo<span style="color: #308080">(</span>videoUrl<span style="color: #308080">)</span> <span style="color: #406080">{</span>
<span style="font-weight: bold; color: #200080">var</span> div <span style="color: #308080">=</span> document<span style="color: #308080">.</span>getElementById<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">videoDiv_someId</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">if</span> <span style="color: #308080">(</span><span style="color: #308080">!</span>div<span style="color: #308080">)</span> <span style="color: #406080">{</span>
<span style="font-weight: bold; color: #200080">var</span> div <span style="color: #308080">=</span> document<span style="color: #308080">.</span>createElement<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">div</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
div<span style="color: #308080">.</span>setAttribute<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">id</span><span style="color: #800000">"</span><span style="color: #308080">,</span> <span style="color: #800000">"</span><span style="color: #1060b6">videoDiv_someId</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
document<span style="color: #308080">.</span>body<span style="color: #308080">.</span>appendChild<span style="color: #308080">(</span>div<span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span>
YUI<span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #308080">.</span>use<span style="color: #308080">(</span>
<span style="color: #800000">'</span><span style="color: #1060b6">aui-video</span><span style="color: #800000">'</span><span style="color: #308080">,</span> <span style="color: #800000">'</span><span style="color: #1060b6">aui-modal</span><span style="color: #800000">'</span><span style="color: #308080">,</span>
<span style="font-weight: bold; color: #200080">function</span><span style="color: #308080">(</span>A<span style="color: #308080">)</span> <span style="color: #406080">{</span>
<span style="font-weight: bold; color: #200080">new</span> A<span style="color: #308080">.</span>Video<span style="color: #308080">(</span><span style="color: #406080">{</span>
boundingBox<span style="color: #406080">:</span> <span style="color: #800000">"</span><span style="color: #1060b6">#videoDiv_someId</span><span style="color: #800000">"</span><span style="color: #308080">,</span>
fixedAttributes<span style="color: #406080">:</span> <span style="color: #406080">{</span>
allowfullscreen<span style="color: #406080">:</span> <span style="color: #800000">'</span><span style="color: #1060b6">true</span><span style="color: #800000">'</span>
<span style="color: #406080">}</span><span style="color: #308080">,</span>
url<span style="color: #406080">:</span> videoUrl
<span style="color: #406080">}</span>
<span style="color: #308080">)</span><span style="color: #308080">.</span>render<span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">new</span> A<span style="color: #308080">.</span>Modal<span style="color: #308080">(</span><span style="color: #406080">{</span>
bodyContent<span style="color: #406080">:</span> <span style="color: #800000">'</span><span style="color: #1060b6">Modal body</span><span style="color: #800000">'</span><span style="color: #308080">,</span>
resizable<span style="color: #406080">:</span> <span style="color: #0f4d75">false</span><span style="color: #308080">,</span>
centered<span style="color: #406080">:</span> <span style="color: #0f4d75">true</span><span style="color: #308080">,</span>
render<span style="color: #406080">:</span> <span style="color: #800000">"</span><span style="color: #1060b6">#videoDiv_someId</span><span style="color: #800000">"</span>
<span style="color: #406080">}</span>
<span style="color: #308080">)</span><span style="color: #308080">.</span>render<span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span>
<span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span>
</pre>
<p><font size="4" face="Times New Roman">Also please note that the references to AlloyUI already included in Liferay so we don’t need to inject this lib directly anywhere.</font></p>
<p><font size="4" face="Times New Roman">The second option how we can create the popup video in Liferay is to use a new <a title="liferay" href="http://www.w3schools.com/html/html5_video.asp" rel="nofollow" target="_blank">HTML5 video tag</a> to render video and JQuery to manage the popup. We don’t involve AlloyUI here, so the look of the components will differ a bit from the Liferay’s. To create a modal window let’s invoke <a title="liferay" href="https://jqueryui.com/dialog" rel="nofollow" target="_blank">dialog</a> function of JQuery UI library: </font></p>
<p><a href="https://soft29.ru/blog/mediaresource/55a127d0-2c7c-401f-8ba6-9d0005da6f68"><img title="win liferay" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="win liferay" src="https://soft29.ru/blog/mediaresource/230352da-56c6-4378-a4ed-449ed41c197c" width="240" height="134"></a></p>
<p><font size="4" face="Times New Roman">It doesn’t look very cute by default so we need to customize its theme/CSS a little.</font></p>
<p><font size="4" face="Times New Roman">The video tag can be created using Javascript as well – in this way there is no need to write any HTML code, all code is one function encapsulated: </font></p><pre style="background: #f6f8ff; color: #000020"><span style="font-weight: bold; color: #200080">function</span> showVideo<span style="color: #308080">(</span>videoUrl<span style="color: #308080">)</span> <span style="color: #406080">{</span>
<span style="font-weight: bold; color: #200080">var</span> div <span style="color: #308080">=</span> document<span style="color: #308080">.</span>createElement<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">div</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
div<span style="color: #308080">.</span>setAttribute<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">id</span><span style="color: #800000">"</span><span style="color: #308080">,</span> <span style="color: #800000">"</span><span style="color: #1060b6">videoDiv_someId</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">var</span> video <span style="color: #308080">=</span> document<span style="color: #308080">.</span>createElement<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">video</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
video<span style="color: #308080">.</span>setAttribute<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">src</span><span style="color: #800000">"</span><span style="color: #308080">,</span> videoUrl<span style="color: #308080">)</span><span style="color: #406080">;</span>
video<span style="color: #308080">.</span>setAttribute<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">type</span><span style="color: #800000">"</span><span style="color: #308080">,</span> <span style="color: #800000">"</span><span style="color: #1060b6">video/mp4</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
video<span style="color: #308080">.</span>setAttribute<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">controls</span><span style="color: #800000">"</span><span style="color: #308080">,</span> <span style="color: #800000">"</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
video<span style="color: #308080">.</span>setAttribute<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">width</span><span style="color: #800000">"</span><span style="color: #308080">,</span> <span style="color: #800000">"</span><span style="color: #1060b6">680</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
video<span style="color: #308080">.</span>setAttribute<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">height</span><span style="color: #800000">"</span><span style="color: #308080">,</span> <span style="color: #800000">"</span><span style="color: #1060b6">440</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
div<span style="color: #308080">.</span>appendChild<span style="color: #308080">(</span>video<span style="color: #308080">)</span><span style="color: #406080">;</span>
document<span style="color: #308080">.</span>getElementsByTagName<span style="color: #308080">(</span><span style="color: #800000">'</span><span style="color: #1060b6">body</span><span style="color: #800000">'</span><span style="color: #308080">)</span><span style="color: #308080">[</span><span style="color: #008c00">0</span><span style="color: #308080">]</span><span style="color: #308080">.</span>appendChild<span style="color: #308080">(</span>div<span style="color: #308080">)</span><span style="color: #406080">;</span>
$<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">#videoDiv_someId</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #308080">.</span>dialog<span style="color: #308080">(</span><span style="color: #406080">{</span>
modal <span style="color: #406080">:</span> <span style="color: #0f4d75">true</span><span style="color: #308080">,</span>
width <span style="color: #406080">:</span> <span style="color: #008c00">720</span><span style="color: #308080">,</span>
hight <span style="color: #406080">:</span> <span style="color: #008c00">480</span><span style="color: #308080">,</span>
resizable <span style="color: #406080">:</span> <span style="color: #0f4d75">false</span><span style="color: #308080">,</span>
close <span style="color: #406080">:</span> <span style="font-weight: bold; color: #200080">function</span><span style="color: #308080">(</span><span style="color: #308080">)</span> <span style="color: #406080">{</span>
document<span style="color: #308080">.</span>getElementById<span style="color: #308080">(</span><span style="color: #800000">"</span><span style="color: #1060b6">videoDiv_someId</span><span style="color: #800000">"</span><span style="color: #308080">)</span><span style="color: #308080">.</span>remove<span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span>
<span style="color: #406080">}</span>
<span style="color: #308080">)</span>
</pre>
<p><font size="4" face="Times New Roman">Also please note that JQuery library is already included within Liferay, but we shouldn’t forget to link <em>JQuery UI</em>.</font></p>
<p><font size="4" face="Times New Roman"><em>Video</em> HTML5 tag allows to render streams from different sources, but for Youtube videos please use iframe tag as show at <a title="liferay" href="http://www.w3schools.com/html/html_youtube.asp" rel="nofollow" target="_blank">w3 schools</a>. </font></p>
<p><font size="4" face="Times New Roman">Ok we’re about to finish this article. We saw in it 2 options how to create a popup window in Liferay and play a video right on it. I hope you liked it.</font></p>https://soft29.ru/blog/entry/alfresco-extend-share-javascript-componentAlfresco: extend Share javascript componentStanislav2016-09-27T10:05:22+00:002017-03-05T14:26:44+00:00<p><font size="4" face="Times New Roman">There is very common task that many developers face during Alfresco Share customization – how to extend client side javascript of a Share component. We all know that Alfresco combines for a component both server side javascript as well as a client side JS. For the first case Alfresco provides Module <a title="alfresco" href="http://docs.alfresco.com/4.2/concepts/dev-extensions-share.html" rel="nofollow" target="_blank">extension</a> approach to alter server side JS and for the second case – the answer is not so clear. Sure we can modify core JS files directly on a web server, but it’s a bad practice since they can be later overwritten during Alfresco update, so it’s better to use extensions keeping changes in separate files. In this article we’ll take as an example the core Discussions Dashlet and then modify its client side javascript code.</font></p><p><font size="4" face="Times New Roman">There is very common task that many developers face during Alfresco Share customization – how to extend client side javascript of a Share component. We all know that Alfresco combines for a component both server side javascript as well as a client side JS. For the first case Alfresco provides Module <a title="alfresco" href="http://docs.alfresco.com/4.2/concepts/dev-extensions-share.html" rel="nofollow" target="_blank">extension</a> approach to alter server side JS and for the second case – the answer is not so clear. Sure we can modify core JS files directly on a web server, but it’s a bad practice since they can be later overwritten during Alfresco update, so it’s better to use extensions keeping changes in separate files. In this article we’ll take as an example the core Discussions Dashlet and then modify its client side javascript code.</font></p> <p><font size="4" face="Times New Roman">So let’s start with a looking for Discussions Dashlet component, its id and location. For this we can use SurfBug: just add Discussions Dashlet to User Dashboard and then <a title="alfresco" href="http://docs.alfresco.com/4.1/concepts/Surf_v4_surfbug.html" rel="nofollow" target="_blank">enable</a> SurfBug. In the result we get the path of the component files <em>org.alfresco.components.dashlets </em>and its id <em>forum-summary</em>. Please navigate to share\WEB-INF\classes\alfresco\site-webscripts\ having just found path appended and you’ll see there are <em>forum-summary.get.* </em>files, here <em>forum-summary.get.js – </em>is a server side JS that get interpreted by Alfresco Surf engine. Let’s take a look into it: it instantiates 3 widgets, one of them is the Discussions Dashlet: </font></p><pre style="background: #f6f8ff; color: #000020"><span style="font-weight: bold; color: #200080">var</span> forumSummary <span style="color: #308080">=</span> <span style="color: #406080">{</span>
id<span style="color: #406080">:</span> <span style="color: #800000">"</span><span style="color: #1060b6">ForumSummary</span><span style="color: #800000">"</span><span style="color: #308080">,</span>
name<span style="color: #406080">:</span> <span style="color: #800000">"</span><span style="color: #1060b6">Alfresco.dashlet.ForumSummary</span><span style="color: #800000">"</span><span style="color: #308080">,</span>
options <span style="color: #406080">:</span> <span style="color: #406080">{</span>
siteId <span style="color: #406080">:</span> <span style="color: #308080">(</span>page<span style="color: #308080">.</span>url<span style="color: #308080">.</span>templateArgs<span style="color: #308080">.</span>site <span style="color: #308080">!=</span> <span style="color: #0f4d75">null</span><span style="color: #308080">)</span> <span style="color: #406080">?</span> page<span style="color: #308080">.</span>url<span style="color: #308080">.</span>templateArgs<span style="color: #308080">.</span>site <span style="color: #406080">:</span> <span style="color: #800000">"</span><span style="color: #800000">"</span><span style="color: #308080">,</span>
searchRootNode <span style="color: #406080">:</span> <span style="color: #308080">(</span>config<span style="color: #308080">.</span>scoped<span style="color: #308080">[</span><span style="color: #800000">'</span><span style="color: #1060b6">RepositoryLibrary</span><span style="color: #800000">'</span><span style="color: #308080">]</span><span style="color: #308080">[</span><span style="color: #800000">'</span><span style="color: #1060b6">root-node</span><span style="color: #800000">'</span><span style="color: #308080">]</span><span style="color: #308080">)</span><span style="color: #308080">.</span>value<span style="color: #308080">,</span>
filters <span style="color: #406080">:</span> model<span style="color: #308080">.</span>filters<span style="color: #308080">,</span>
regionId <span style="color: #406080">:</span> args<span style="color: #308080">[</span><span style="color: #800000">'</span><span style="color: #1060b6">region-id</span><span style="color: #800000">'</span><span style="color: #308080">]</span>
<span style="color: #406080">}</span>
<span style="color: #406080">}</span><span style="color: #406080">;</span>
</pre>
<p><font size="4" face="Times New Roman">The <strong>name</strong> attribute in this snippet refers by ID to client side Javascript classes (i.e. constructor functions). In this way the widget is created taking JS as a source of UI. Now let’s find this javascript class by "Alfresco.dashlet.ForumSummary" ID or simply looking to <em>forum-summary.get.html.ftl. </em>It’s a <em>webapps\share\components\dashlets\forum-summary.js</em>:</font></p><pre style="background: #f6f8ff; color: #000020"><span style="color: #308080">(</span><span style="font-weight: bold; color: #200080">function</span><span style="color: #308080">(</span><span style="color: #308080">)</span> <span style="color: #406080">{</span>
<span style="color: #595979">/* a part of code skipped*/</span>
Alfresco<span style="color: #308080">.</span>dashlet<span style="color: #308080">.</span>ForumSummary <span style="color: #308080">=</span> <span style="font-weight: bold; color: #200080">function</span> ForumSummary_constructor<span style="color: #308080">(</span>htmlId<span style="color: #308080">)</span> <span style="color: #406080">{</span>
Alfresco<span style="color: #308080">.</span>dashlet<span style="color: #308080">.</span>ForumSummary<span style="color: #308080">.</span>superclass<span style="color: #308080">.</span><span style="color: #007d45">constructor</span><span style="color: #308080">.</span>call<span style="color: #308080">(</span><span style="font-weight: bold; color: #200080">this</span><span style="color: #308080">,</span> <span style="color: #800000">"</span><span style="color: #1060b6">Alfresco.dashlet.ForumSummary</span><span style="color: #800000">"</span><span style="color: #308080">,</span> htmlId<span style="color: #308080">,</span> <span style="color: #308080">[</span><span style="color: #800000">"</span><span style="color: #1060b6">container</span><span style="color: #800000">"</span><span style="color: #308080">,</span> <span style="color: #800000">"</span><span style="color: #1060b6">datasource</span><span style="color: #800000">"</span><span style="color: #308080">,</span> <span style="color: #800000">"</span><span style="color: #1060b6">datatable</span><span style="color: #800000">"</span><span style="color: #308080">]</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #595979">// Services</span>
<span style="font-weight: bold; color: #200080">this</span><span style="color: #308080">.</span>services<span style="color: #308080">.</span>preferences <span style="color: #308080">=</span> <span style="font-weight: bold; color: #200080">new</span> Alfresco<span style="color: #308080">.</span>service<span style="color: #308080">.</span>Preferences<span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">return</span> <span style="font-weight: bold; color: #200080">this</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span><span style="color: #406080">;</span>
YAHOO<span style="color: #308080">.</span>extend<span style="color: #308080">(</span>Alfresco<span style="color: #308080">.</span>dashlet<span style="color: #308080">.</span>ForumSummary<span style="color: #308080">,</span> Alfresco<span style="color: #308080">.</span>component<span style="color: #308080">.</span>Base<span style="color: #308080">,</span> <span style="color: #406080">{</span>
<span style="color: #595979">/* a part of code skipped*/</span>
<span style="color: #406080">}</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span><span style="color: #308080">)</span><span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
</pre>
<p><font size="4" face="Times New Roman">Having the JS that generates the UI of the Discussions Dashlet, let’s slightly modify its layout. In order not to change this out-of-the-box Alfresco file we’ll extend the class within it. To make this happen – create a new file <em>custom-discussions-dashlet.js</em> in <em>META_INF/components</em> directory located in any jar-file of the Share web application. Then add custom class, having <em>buildDescription()</em> function (the one that performs required UI rendering in core <em>forum-summary.js</em>) overridden: </font></p><pre style="background: #f6f8ff; color: #000020"><span style="color: #308080">(</span><span style="font-weight: bold; color: #200080">function</span><span style="color: #308080">(</span><span style="color: #308080">)</span> <span style="color: #406080">{</span>
Alfresco<span style="color: #308080">.</span>dashlet<span style="color: #308080">.</span>CustomForumSummary <span style="color: #308080">=</span> <span style="font-weight: bold; color: #200080">function</span> CustomForumSummary_constructor<span style="color: #308080">(</span>htmlId<span style="color: #308080">)</span><span style="color: #406080">{</span>
Alfresco<span style="color: #308080">.</span>dashlet<span style="color: #308080">.</span>CustomForumSummary<span style="color: #308080">.</span>superclass<span style="color: #308080">.</span><span style="color: #007d45">constructor</span><span style="color: #308080">.</span>call<span style="color: #308080">(</span><span style="font-weight: bold; color: #200080">this</span><span style="color: #308080">,</span> htmlId<span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">return</span> <span style="font-weight: bold; color: #200080">this</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span><span style="color: #406080">;</span>
<span style="color: #595979">/**</span>
<span style="color: #595979"> * Extended from ForumSummary</span>
<span style="color: #595979"> */</span>
YAHOO<span style="color: #308080">.</span>extend<span style="color: #308080">(</span>Alfresco<span style="color: #308080">.</span>dashlet<span style="color: #308080">.</span>CustomForumSummary<span style="color: #308080">,</span> Alfresco<span style="color: #308080">.</span>dashlet<span style="color: #308080">.</span>ForumSummary<span style="color: #308080">,</span> <span style="color: #406080">{</span>
buildDescription<span style="color: #406080">:</span> <span style="font-weight: bold; color: #200080">function</span> ForumSummary_buildDescription<span style="color: #308080">(</span>elCell<span style="color: #308080">,</span> oRecord<span style="color: #308080">,</span> oColumn<span style="color: #308080">,</span> oData<span style="color: #308080">)</span> <span style="color: #406080">{</span>
elCell<span style="color: #308080">.</span>innerHTML <span style="color: #308080">=</span> <span style="color: #800000">"</span><span style="color: #1060b6"><div>Custom discussion</div></span><span style="color: #800000">"</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span>
<span style="color: #406080">}</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span><span style="color: #308080">)</span><span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
</pre>
<p><font size="4" face="Times New Roman">On this step the new class is created, now we need to add reference to it in the dashlet. In <em>forum-summary.get.html.ftl </em>there are links to Javascripts in the header. Using Share module <a title="alfresco" href="http://docs.alfresco.com/4.2/concepts/dev-extensions-share.html" rel="nofollow" target="_blank">extensions</a> we can add our link to just created JS class. The ftl extension will look líke <em>forum-summary.get.html.ftl</em>: </font></p><pre style="background: #f6f8ff; color: #000020"><span style="color: #308080"><</span><span style="color: #308080">#</span><span style="color: #308080">-</span><span style="color: #308080">-</span> Add a Javascript declaration <span style="color: #308080">-</span><span style="color: #308080">-</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">@</span>markup id<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">forum-custom-js</span><span style="color: #800000">"</span> target<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">js</span><span style="color: #800000">"</span> action<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">after</span><span style="color: #800000">"</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">@</span>script src<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">${url.context}/res/components/custom-discussions-dashlet.js</span><span style="color: #800000">"</span> group<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">dashlets</span><span style="color: #800000">"</span> <span style="color: #308080">/</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">/</span><span style="color: #308080">@</span><span style="color: #308080">></span>
</pre>
<p><font size="4" face="Times New Roman">Please note that we do an <strong>after</strong> extension, not <strong>replace</strong>. We can’t remove a reference to the parent of our custom class. Finally, there’s left one thing – we need to change the dashlet’s code, so custom class could be initiated. For this, we do a replacement of the Discussions widget in widget array with a custom one. Extended <em>Forum-summary.js</em> looks as following:</font></p><pre style="background: #f6f8ff; color: #000020"><span style="color: #595979">// replace ForumSummary with CustomForumSummary</span>
<span style="font-weight: bold; color: #200080">for</span> <span style="color: #308080">(</span>i <span style="color: #308080">=</span> <span style="color: #008c00">0</span><span style="color: #406080">;</span> i <span style="color: #308080"><</span> model<span style="color: #308080">.</span>widgets<span style="color: #308080">.</span><span style="font-weight: bold; color: #200080">length</span><span style="color: #406080">;</span> i<span style="color: #308080">++</span><span style="color: #308080">)</span> <span style="color: #406080">{</span>
<span style="font-weight: bold; color: #200080">if</span> <span style="color: #308080">(</span>model<span style="color: #308080">.</span>widgets<span style="color: #308080">[</span>i<span style="color: #308080">]</span><span style="color: #308080">.</span>id <span style="color: #308080">==</span> <span style="color: #800000">'</span><span style="color: #1060b6">ForumSummary</span><span style="color: #800000">'</span><span style="color: #308080">)</span> <span style="color: #406080">{</span>
model<span style="color: #308080">.</span>widgets<span style="color: #308080">.</span>splice<span style="color: #308080">(</span>i<span style="color: #308080">,</span> <span style="color: #008c00">1</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">var</span> customForumSummary <span style="color: #308080">=</span> <span style="color: #406080">{</span>
id <span style="color: #406080">:</span> <span style="color: #800000">"</span><span style="color: #1060b6">customForumSummary</span><span style="color: #800000">"</span><span style="color: #308080">,</span>
name <span style="color: #406080">:</span> <span style="color: #800000">"</span><span style="color: #1060b6">Alfresco.dashlet.CustomForumSummary</span><span style="color: #800000">"</span><span style="color: #308080">,</span>
options <span style="color: #406080">:</span> <span style="color: #406080">{</span>
siteId <span style="color: #406080">:</span> <span style="color: #308080">(</span>page<span style="color: #308080">.</span>url<span style="color: #308080">.</span>templateArgs<span style="color: #308080">.</span>site <span style="color: #308080">!=</span> <span style="color: #0f4d75">null</span><span style="color: #308080">)</span> <span style="color: #406080">?</span> page<span style="color: #308080">.</span>url<span style="color: #308080">.</span>templateArgs<span style="color: #308080">.</span>site
<span style="color: #406080">:</span> <span style="color: #800000">"</span><span style="color: #800000">"</span><span style="color: #308080">,</span>
searchRootNode <span style="color: #406080">:</span> <span style="color: #308080">(</span>config<span style="color: #308080">.</span>scoped<span style="color: #308080">[</span><span style="color: #800000">'</span><span style="color: #1060b6">RepositoryLibrary</span><span style="color: #800000">'</span><span style="color: #308080">]</span><span style="color: #308080">[</span><span style="color: #800000">'</span><span style="color: #1060b6">root-node</span><span style="color: #800000">'</span><span style="color: #308080">]</span><span style="color: #308080">)</span><span style="color: #308080">.</span>value<span style="color: #308080">,</span>
filters <span style="color: #406080">:</span> model<span style="color: #308080">.</span>filters<span style="color: #308080">,</span>
regionId <span style="color: #406080">:</span> args<span style="color: #308080">[</span><span style="color: #800000">'</span><span style="color: #1060b6">region-id</span><span style="color: #800000">'</span><span style="color: #308080">]</span>
<span style="color: #406080">}</span>
<span style="color: #406080">}</span><span style="color: #406080">;</span>
model<span style="color: #308080">.</span>widgets<span style="color: #308080">.</span>push<span style="color: #308080">(</span>customForumSummary<span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span>
<span style="color: #406080">}</span>
</pre>
<p><font size="4" face="Times New Roman">In this snippet we can see how our custom widget is created using <strong>name </strong>attribute as reference to the class and then how it replaces the core widget. </font></p>
<p><font size="4" face="Times New Roman">That’s it, I hope my post was helpful. Thank you for interest!</font></p>https://soft29.ru/blog/entry/liferay-7-embed-language-portletLiferay 7 embed Language Portlet in HeaderStanislav2016-07-20T09:06:15+00:002017-03-05T14:27:03+00:00<p><font size="4" face="Times New Roman">Hey there, the new Liferay 7 is out now. With this version we become a brand new concept (which implements the <a title="liferay" href="https://en.wikipedia.org/wiki/OSGi" rel="nofollow" target="_blank">OSGi</a> specification) of Liferay Plugins development and integration within a portal. Having OSGi implemented, Liferay moves from a hack-like style of Plugin management, where each Plugin is a separate web application stored as a war-file on an app server, to a much cleaner concept. There are many global changes in Liferay 7 and in this post we’ll see how to insert Language Portlet to custom Theme header in accordance with this new architecture.</font></p><p><font size="4" face="Times New Roman">Hey there, the new Liferay 7 is out now. With this version we become a brand new concept (which implements the <a title="liferay" href="https://en.wikipedia.org/wiki/OSGi" rel="nofollow" target="_blank">OSGi</a> specification) of Liferay Plugins development and integration within a portal. Having OSGi implemented, Liferay moves from a hack-like style of Plugin management, where each Plugin is a separate web application stored as a war-file on an app server, to a much cleaner concept. There are many global changes in Liferay 7 and in this post we’ll see how to insert Language Portlet to custom Theme header in accordance with new architecture.</font></p> <p><font size="4" face="Times New Roman">One of the most important changes in Liferay 7 is that the Velocity template engine is deprecated; since now there is left the Freemarker template <a title="liferay" href="http://freemarker.org/" rel="nofollow" target="_blank">engine</a> only. Having this in mind let’s find a code of a theme where Language Portlet should be placed to. Normally theme’s header persists in <em>/templates/portal_normal.ftl</em> freemarker template file, so we need to use a Language Portlet tag from <em>liferay-ui.tld</em> JSP tag library in it. This is how <em>portal_normal.ftl</em> of the <em>Classic </em>theme should look like:</font></p><pre style="background: #f6f8ff; color: #000020"><span style="color: #308080"><</span>header class<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">container-fluid-1280</span><span style="color: #800000">"</span> id<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">banner</span><span style="color: #800000">"</span> role<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">banner</span><span style="color: #800000">"</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #003060">div</span> class<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">row</span><span style="color: #800000">"</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #003060">div</span> class<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">navbar-header</span><span style="color: #800000">"</span> id<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">heading</span><span style="color: #800000">"</span><span style="color: #308080">></span>
<span style="color: #308080"><</span>a class<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">${logo_css_class}</span><span style="color: #800000">"</span> href<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">${site_default_url}</span><span style="color: #800000">"</span> title<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6"><@liferay.language_format arguments=</span><span style="color: #800000">"</span>$<span style="color: #406080">{</span>site_name<span style="color: #406080">}</span><span style="color: #800000">"</span><span style="color: #1060b6"> key=</span><span style="color: #800000">"</span>go<span style="color: #308080">-</span>to<span style="color: #308080">-</span>x<span style="color: #800000">"</span><span style="color: #1060b6"> /></span><span style="color: #800000">"</span><span style="color: #308080">></span>
<span style="color: #308080"><</span>img alt<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">${logo_description}</span><span style="color: #800000">"</span> height<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">64</span><span style="color: #800000">"</span> src<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">${site_logo}</span><span style="color: #800000">"</span> <span style="color: #308080">/</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">/</span>a<span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="background: #dd9999; font-weight: bold; color: #ffffff; font-style: italic">#if</span> show_site_name<span style="color: #308080">></span>
<span style="color: #308080"><</span>span class<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">site-name</span><span style="color: #800000">"</span> title<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6"><@liferay.language_format arguments=</span><span style="color: #800000">"</span>$<span style="color: #406080">{</span>site_name<span style="color: #406080">}</span><span style="color: #800000">"</span><span style="color: #1060b6"> key=</span><span style="color: #800000">"</span>go<span style="color: #308080">-</span>to<span style="color: #308080">-</span>x<span style="color: #800000">"</span><span style="color: #1060b6"> /></span><span style="color: #800000">"</span><span style="color: #308080">></span>
$<span style="color: #406080">{</span>site_name<span style="color: #406080">}</span>
<span style="color: #308080"><</span><span style="color: #308080">/</span>span<span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">/</span><span style="background: #dd9999; font-weight: bold; color: #ffffff; font-style: italic">#if</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="background: #dd9999; font-weight: bold; color: #ffffff; font-style: italic">#if</span> is_setup_complete<span style="color: #308080">></span>
<span style="color: #308080"><</span>button aria<span style="color: #308080">-</span>controls<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">navigation</span><span style="color: #800000">"</span> aria<span style="color: #308080">-</span>expanded<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">false</span><span style="color: #800000">"</span> class<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">collapsed navbar-toggle</span><span style="color: #800000">"</span> data<span style="color: #308080">-</span>target<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">#navigationCollapse</span><span style="color: #800000">"</span> data<span style="color: #308080">-</span>toggle<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">collapse</span><span style="color: #800000">"</span> type<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">button</span><span style="color: #800000">"</span><span style="color: #308080">></span>
<span style="color: #308080"><</span>span class<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">icon-bar</span><span style="color: #800000">"</span><span style="color: #308080">></span><span style="color: #308080"><</span><span style="color: #308080">/</span>span<span style="color: #308080">></span>
<span style="color: #308080"><</span>span class<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">icon-bar</span><span style="color: #800000">"</span><span style="color: #308080">></span><span style="color: #308080"><</span><span style="color: #308080">/</span>span<span style="color: #308080">></span>
<span style="color: #308080"><</span>span class<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">icon-bar</span><span style="color: #800000">"</span><span style="color: #308080">></span><span style="color: #308080"><</span><span style="color: #308080">/</span>span<span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">/</span>button<span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #003060">div</span> class<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">pull-right user-personal-bar</span><span style="color: #800000">"</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">@</span>liferay<span style="color: #308080">.</span>user_personal_bar <span style="color: #308080">/</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">/</span><span style="color: #003060">div</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #003060">div</span> class<span style="color: #308080">=</span><span style="color: #800000">"</span><span style="color: #1060b6">pull-right language-bar</span><span style="color: #800000">"</span><span style="color: #308080">></span>
<span style="color: #308080"><strong><</span><span style="color: #308080">@</span>liferay_ui<span style="color: #308080">[</span><span style="color: #800000">"</span><span style="color: #1060b6">language</span><span style="color: #800000">"</span><span style="color: #308080">]</span><span style="color: #308080">/</span><span style="color: #308080">></span> <span style="color: #308080"><</strong></span>
<span style="color: #308080"><</span><span style="color: #308080">/</span><span style="color: #003060">div</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">/</span><span style="background: #dd9999; font-weight: bold; color: #ffffff; font-style: italic">#if</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">/</span><span style="color: #003060">div</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="background: #dd9999; font-weight: bold; color: #ffffff; font-style: italic">#include</span> <span style="color: #800000">"</span><span style="color: #1060b6">${full_templates_path}/navigation.ftl</span><span style="color: #800000">"</span> <span style="color: #308080">/</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">/</span><span style="color: #003060">div</span><span style="color: #308080">></span>
<span style="color: #308080"><</span><span style="color: #308080">/</span>header<span style="color: #308080">></span>
</pre>
<p><font size="4" face="Times New Roman">In current template there is actually 1 line is added that injects Language Portlet to the Classic’s theme header: </font><@liferay_ui["language"]/>.</p>
<p><font size="4" face="Times New Roman">Since Liferay 7 implements the OSGi technology all tld tag libraries are stored in separate OSGi bundles (not shipped within each portlet). It means that we don’t need to embed any tag library in our theme bundle. We don’t even need to refer the demanded taglib in ftl template in order to inject some tag, all mapping are done automatically by the OSGi.</font></p>
<p><font size="4" face="Times New Roman">Also CSS stylings can be added to Language portlet so it is located right next to SignIn portlet, just append this class to <em>src/css/_custom.scss</em>:</font></p><pre style="background: #f6f8ff; color: #000020">@media only screen and (min-width: <span style="color: #008c00">768</span>px) {
<span style="color: #595979"> #banner .language-bar {</span>
position: absolute<span style="color: #406080">;</span>
right: <span style="color: #008c00">160</span>px<span style="color: #406080">;</span>
top: <span style="color: #008c00">0</span>px<span style="color: #406080">;</span>
}
}
</pre>
<p><font size="4" face="Times New Roman">After the theme is built and deployed the header will look as following: </font></p>
<blockquote>
<p><a href="https://soft29.ru/blog/mediaresource/e84a2fe7-9e28-4aa6-b859-31be437336b9"><img title="liferay 7 language" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="liferay 7 language" src="https://soft29.ru/blog/mediaresource/94706495-6d76-43bb-bdf3-34601eb98c46" width="353" height="97"></a></p></blockquote>
<p><font size="4" face="Times New Roman">And finally, to refine languages list go to<em> Site Configuration</em> –> <em>Site Settings</em> –> <em>Languages</em> tab and leave only current languages:</font></p>
<p><font size="4" face="Times New Roman"><a href="https://soft29.ru/blog/mediaresource/8e307fba-0020-4e47-88a9-3500b30561f4"><img title="liferay 7 language" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="liferay 7 language" src="https://soft29.ru/blog/mediaresource/ad6e9dd7-a2ae-4942-8834-d10ba5a9716b" width="775" height="417"></a> </font></p>
<p><font size="4" face="Times New Roman">Ok, that’s it, in the end we got the Language selector embedded using freemarker template engine in Theme’s header that corresponds to Liferay 7 OSGi specification.</font></p>https://soft29.ru/blog/entry/linux-postgresql-automated-backups-alfrescoLinux PostgreSQL automated backups (Alfresco ECM as an example)Stanislav2016-07-05T16:01:22+00:002017-03-05T14:27:15+00:00<p><font size="4" face="Times New Roman">One of the common tasks for any online business is to keep its data consistent. To achieve this task a database backing up technique can be used. PostgreSQL is a wide spread DBMS that is very popular within web application world and in this guide we’ll see how to automate backups creation process for PostgreSQL on Linux taking as an example Alfresco ECM web application.</font></p> <p><font size="4" face="Times New Roman">One of the common tasks for any online business is to keep its data consistent. To achieve this task a database backing up technique can be used. PostgreSQL is a wide spread DBMS that is very popular within web application world and in this guide we’ll see how to automate backups creation process for PostgreSQL on Linux taking as an example Alfresco ECM web application.</font></p> <p><font size="4" face="Times New Roman">Let’s assume that PostgreSQL is already installed on your target Linux system. At the time of the installation the <em>postgres</em> user will be automatically created – it’s a default PostgreSQL user which will be used to manage backing up process, this user already exists within DBMS and will be granted to perform dumping of the database. Let’s switch to home of <em>postgres</em> user: </font></p><pre style="background: #f6f8ff; color: #000020">su <span style="color: #308080">-</span> postgres
</pre>
<p><font size="4" face="Times New Roman">Now we should create (if not exists) a .<em>pgpass</em> file in the root dir of the <em>postgres</em> user and grant this user a permission to connect to a database. To allow this the entity of the <strong><em>hostname:port:database:username:password</em></strong> format should be appended to current file:</font></p><pre style="background: #f6f8ff; color: #000020"><span style="color: #003060">printf</span> <span style="color: #800000">"</span><span style="color: #1060b6">localhost:5432:alfresco:postgres:some_password</span><span style="color: #800000">"</span> <span style="color: #308080">></span> <span style="color: #308080">.</span>pgpass
</pre>
<blockquote>
<p><font size="4" face="Times New Roman">On Unix systems, the permissions on .pgpass must disallow any access to world or group; achieve this by the command chmod 0600 ~/.pgpass. If the permissions are less strict than this, the file will be ignored.</font></p></blockquote>
<p><font size="4" face="Times New Roman">So let’s run a command assuming we are in the root dir:</font></p><pre style="background: #f6f8ff; color: #000020">chmod <span style="color: #008c00">0600</span> <span style="color: #308080">.</span>pgpass
</pre>
<p><font size="4" face="Times New Roman">The second step after the permissions to <em>postgres</em> user are granted is to write a system shell script that will actually do a backing up of the database and printing some log (containing info about scheduler timings):</font></p><pre style="background: #f6f8ff; color: #000020"><span style="color: #595979">#!</span><span style="color: #007997">/bin/sh</span>
<span style="color: #007d45">DBBACK</span><span style="color: #308080">=</span><span style="background: #cceeee; color: #000000">`date +%Y-%m-%d-dbback</span><span style="background: #cceeee; font-weight: bold; color: #200080">.</span><span style="background: #cceeee; color: #000000">sql`</span>
<span style="color: #007d45">START_TIME</span><span style="color: #308080">=</span><span style="background: #cceeee; color: #000000">`date +%Y-%m-%d</span><span style="background: #cceeee; color: #308080">:</span><span style="background: #cceeee; color: #000000">%H</span><span style="background: #cceeee; color: #308080">:</span><span style="background: #cceeee; color: #000000">%M</span><span style="background: #cceeee; color: #308080">:</span><span style="background: #cceeee; color: #000000">%S`</span>
<span style="font-weight: bold; color: #7779bb">echo</span> <span style="color: #1060b6">"start </span><span style="color: #007d45">$START_TIME</span><span style="color: #1060b6">"</span> <span style="color: #e34adc">></span><span style="color: #e34adc">></span> <span style="color: #40015a">/opt/alfresco1/postgresql/backups/backup.log</span>
pg_dump -h localhost alfresco <span style="color: #44aadd">-f</span> <span style="color: #40015a">/opt/alfresco1/postgresql/backups</span><span style="color: #40015a">/</span><span style="color: #007d45">$DBBACK</span>
<span style="color: #007d45">END_TIME</span><span style="color: #308080">=</span><span style="background: #cceeee; color: #000000">`date +%Y-%m-%d</span><span style="background: #cceeee; color: #308080">:</span><span style="background: #cceeee; color: #000000">%H</span><span style="background: #cceeee; color: #308080">:</span><span style="background: #cceeee; color: #000000">%M</span><span style="background: #cceeee; color: #308080">:</span><span style="background: #cceeee; color: #000000">%S`</span>
<span style="font-weight: bold; color: #7779bb">echo</span> <span style="color: #1060b6">"finish </span><span style="color: #007d45">$END_TIME</span><span style="color: #1060b6">"</span> <span style="color: #e34adc">></span><span style="color: #e34adc">></span> <span style="color: #40015a">/opt/alfresco1/postgresql/backups/backup.log</span>
<span style="font-weight: bold; color: #7779bb">exit</span> <span style="color: #008c00">0</span>
</pre>
<p><font size="4" face="Times New Roman">This script uses PostgreSQL <em>pg_dump</em> command and store in <em>backup.sh </em>file.</font></p>
<p><font size="4" face="Times New Roman">The third step will be to setup some scheduler that will periodically run the upper script using the permissions granted in the first step. <strong>Crontab</strong> is a very handy Linux tool that can manage a periodic script execution, it uses well known crontab <a href="http://www.nncron.ru/help/EN/working/cron-format.htm" rel="nofollow" target="_blank">notation</a>. Using this notation we can define backup process starting from Monday to Friday at 3am (night time is a right time to perform such activities): <strong>0 3 * * 1,2,3,4,5</strong>. Having cron-coded script execution string we should save it to <em>crontab</em> tool. Assuming we are under <em>postgres </em>user, run (<em>-e</em> edit mode, <em>–l</em> list mode):</font></p><pre style="background: #f6f8ff; color: #000020">crontab <span style="color: #308080">-</span>e
</pre>
<p><font size="4" face="Times New Roman">and add the following string, where latter part is the path to the script to be executed:</font></p><pre style="background: #f6f8ff; color: #000020"><span style="color: #008c00">0</span> <span style="color: #008c00">3</span> <span style="color: #308080">*</span> <span style="color: #308080">*</span> <span style="color: #008c00">1</span><span style="color: #308080">,</span><span style="color: #008c00">2</span><span style="color: #308080">,</span><span style="color: #008c00">3</span><span style="color: #308080">,</span><span style="color: #008c00">4</span><span style="color: #308080">,</span><span style="color: #008c00">5</span> <span style="color: #308080">/</span>opt<span style="color: #308080">/</span>alfresco1<span style="color: #308080">/</span>postgresql<span style="color: #308080">/</span>scripts<span style="color: #308080">/</span>backup<span style="color: #308080">.</span>sh
</pre>
<p><font size="4" face="Times New Roman">To sum up: we get the scheduled (automated) backing up process that works on PostgreSQL DBMS and Linux. This approach can be used to dump db data of any web application, e.g. Alfresco. Backups will be stored daily (from Mo to Fri) as shown in the image:</font></p>
<p><a href="https://soft29.ru/blog/mediaresource/d544d99e-30eb-43f6-9683-b32ea95d08a6"><img title="alfresco" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; border-left: 0px; display: block; padding-right: 0px; margin-right: auto" border="0" alt="alfresco" src="https://soft29.ru/blog/mediaresource/5372e6dd-6062-42b5-a709-67c58e2736c6" width="788" height="137"></a></p>https://soft29.ru/blog/entry/enable-ssl-https-for-alfrescoEnable SSL https for Alfresco or any other web applicationStanislav2016-06-08T12:27:00+00:002017-03-05T14:27:26+00:00<p><font size="4" face="Times New Roman">Enabling of SSL-secured connection for some web application (Alfresco ECM, for instance) is a very common task within web community. That’s right, most of the companies running their own business over the internet require a protection of exchanged data. In current post I’ll show how to switch on such connection by means of Apache Web Server.</font></p><p><font size="4" face="Times New Roman">Enabling of SSL-secured connection for some web application (Alfresco ECM, for instance) is a very common task within web community. That’s right, most of the companies running their own business over the internet require a protection of exchanged data. In current post I’ll show how to switch on such connection by means of Apache Web Server.</font></p> <p><font size="4" face="Times New Roman">Ok, let’s start. Assume that we have some web application (Alfresco ECM is our case), this application is managed by Tomcat servlet container. Sure we can configure Tomcat in a way it to listen for requests on port 8443 (Tomcat default SSL port), but in case of Alfresco, when we have two webapps (/alfresco and /share), it’s simpler to use Apache server in conjunction with Tomcat: no need to change anything in Alfresco configuration (e.g. in share-custom-config.xml), no need to tweak Tomcat. If you have a web application other than Alfresco, then Tomcat + Apache Server might be a good option as well: </font></p> <ol> <ol> <li><font size="4" face="Times New Roman">Tomcat is not as fast as Apache when it comes to static pages.</font> <li><font size="4" face="Times New Roman">Tomcat is not as configurable as Apache.</font> <li><font size="4" face="Times New Roman">Tomcat is not as robust as Apache.</font> <li><font size="4" face="Times New Roman">Tomcat may not address many sites' need for functionality found only in Apache modules (e.g. Perl, PHP, etc.).</font></li></ol></ol> <p><font size="4" face="Times New Roman">Please install Apache2 if not yet installed and 2 Apache modules: <em>mod_jk</em> – the module that manages Apache Server to Tomcat communication,<em> mod_ssl</em> – the module that provides SSL v3 and TLS v1.x support. Commands to install <em>mod_jk</em> (<em>mod_ssl </em>is normally installed by default within Apache Server) and to enable both modules:</font></p><pre style="color: #000">sudo apt-get install libapache2-mod-jk
sudo a2enmod jk
sudo a2enmod ssl
</pre>
<p><font size="4" face="Times New Roman"></font> </p>
<p><font size="4" face="Times New Roman">In order to use SSL-secured connection we need the SSL-certificate. We can request such certificate at some official authority, e.g. at <a title="alfresco" href="https://www.digicert.com/csr-creation.htm" rel="nofollow" target="_blank">Digicert</a>, for this we should generate a <em>certificate request</em> file (*.crt). While generating the <em>crt</em>-file together with it an <em>jks</em>-file (java key store) can come from which a private key can be retrieved (<a title="alfresco" href="http://security.stackexchange.com/questions/3779/how-can-i-export-my-private-key-from-a-java-keytool-keystore" rel="nofollow" target="_blank">link</a>). After sending <em>crt </em>to the issuing authority, you’ll get back 2 certificate files: a digital certificate itself and a certificate chain file. Both of them and the private key should be referenced in Apache2 <em>VirtualHost</em> configuration:</font></p><pre style="background: #f9f9f9; color: #080808"><<span style="color: #bf4f24">VirtualHost</span> *:443>
ServerName somesite.com
SSLEngine On
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"
SSLHonorCipherOrder on
SSLProtocol All -SSLv2 -SSLv3
SSLCertificateFile /opt/alfresco/cert/somesite_com.crt
SSLCertificateKeyFile /opt/alfresco/cert/pr_key/somesite_com_pr.key
SSLCertificateChainFile /opt/alfresco/cert/DigiCertCA.crt
<Location />
SSLRequireSSL On
SSLVerifyClient optional
SSLRenegBufferSize 104860000
SSLVerifyDepth 1
SSLOptions +StdEnvVars +StrictRequire
</Location>
# Send everything for the context / to worker named worker1 via ajp13
JkMount /* ajp13_worker
</<span style="color: #bf4f24">VirtualHost</span>>
</pre>
<p><font size="4" face="Times New Roman">A couple important things should be noticed in this config:</font></p>
<ul>
<li><font size="4" face="Times New Roman"><strong>JkMount /* ajp13_worker</strong> – the id of the <em>mod_jk</em> AJP worker that will communicate to Tomcat AJP port. Tomcat is usually defined on localhost with port 8009 (see Tomcat’s <em>config/server.xml</em>). Apache mod_jk <em>worker.properties</em> excerpt: </font></li></ul>
<p><font size="4" face="Times New Roman"></font><pre style="background: #fff; color: #3b3b3b">#
# Defining a worker named ajp13_worker and of type ajp13
# Note that the name and the type do not have to match.
#
worker.ajp13_worker.port=8009
worker.ajp13_worker.host=localhost
worker.ajp13_worker.type=ajp13
</pre>
<ul>
<li><font size="4" face="Times New Roman"><strong>SSLCipherSuite</strong> – is one of the best Cipher Suite together with SSLProtocol configuration that can be used nowadays in order to get “A” rank at SSLLabs <a title="alfresco" href="https://www.ssllabs.com/" rel="license" target="_blank">test</a>. Modern browsers and OSs are very sensitive to outdated SSL protocols and ciphers (and this approach is right), so we need to enable only approved ones. <em>SSLProtocol All -SSLv2 –SSLv3 </em>– enable all SSL protocols except deprecated SSL2 (that causes DROWN vulnerability) and except deprecated SSL3 (that causes POODLE vulnerability). Modern and correct SSLCipherSuite set is also required especially for MacOS which is oriented to high level of security, otherwise https connection to your site may be rejected. </font></li></ul>
<p><font size="4" face="Times New Roman"></font> </p>
<p><font size="4" face="Times New Roman">That’s it, after apache2 is restarted, SSL connection will be established and you can navigate to your Alfresco/Share web application (or your custom web application) by means of https. You connection will correspond to modern security protocols and will have “A” rank at SSL checking services. </font></p>https://soft29.ru/blog/entry/alfresco-cad-dxf-dwg-previewAlfresco CAD (dxf, dwg) preview supportStanislav2016-04-05T16:09:44+00:002017-03-05T14:27:43+00:00<p><font size="4" face="Times New Roman">Hey there! Recently I got a chance to implement CAD format (DXF, DWG) support within Alfresco Share 5.0 so users could preview engineering documents. What’s an Alfresco previewer? The main concept lying behind it is to convert all variety of complex formats (doc, excel, txt, etc.) to a single one – PDF which is then can be rendered by Alfresco previewer. To handle word/excel/other conversion OpenOffice is used, to support DXF and DWG we need to find an appropriate CAD to PDF converter. To do this I will take a look at existing converters, evaluate them and show how to inject one of them to Alfresco to provide the preview of engineering drawings. This solution will work on Alfresco 5 (4th should also be fine) on Windows and Linux.</font></p><p><font size="4" face="Times New Roman">Hey there! Recently I got a chance to implement CAD format (DXF, DWG) support within Alfresco Share 5.0 so users could preview engineering documents. What’s an Alfresco previewer? The main concept lying behind it is to convert all variety of complex formats (doc, excel, txt, etc.) to a single one – PDF which is then can be rendered by Alfresco previewer. To handle word/excel/other conversion OpenOffice is used, to support DXF and DWG we need to find an appropriate CAD to PDF converter. To do this I will take a look at existing converters, evaluate them and show how to inject one of them to Alfresco to provide the preview of engineering drawings. This solution will work on Alfresco 5 (4th should also be fine) on Windows and Linux.</font></p> <p><font size="4" face="Times New Roman">First of all I’d like to show Alfresco content preview lifecycle:</font></p> <ol> <li><font size="4" face="Times New Roman">Some content is uploaded, let’s say *.doc </font> <li><font size="4" face="Times New Roman">A user opens the file for the first time </font> <li><font size="4" face="Times New Roman">Alfresco reads its mime-type </font> <li><font size="4" face="Times New Roman">Using content mime-type Alfresco finds the appropriate converter config </font> <li><font size="4" face="Times New Roman">In this config there is a path to external software that performs doc to pdf conversion (LibreOffice path) </font> <li><font size="4" face="Times New Roman">Doc to pdf c<font size="4" face="Times New Roman">onversion takes place </font>using LibreOffice </font> <li><font size="4" face="Times New Roman">Store generated pdf to content store and link it to document entity. After the generated pdf is stored, the conversion will not occur for the second time (when the user opens the file one more time). </font> <li><font size="4" face="Times New Roman">Alfresco previewer (that is a javascript component) is started that simply loads pdf rendition</font></li></ol> <p><font size="4" face="Times New Roman">A key moment we need to solve is to provide a CAD to PDF converter (step 6). Such converter is not shipped within Alfresco, only 3d party software exists which is proprietary in turn. There are 3 CAD to PDF market leaders were considered: </font></p> <ul> <li><font size="4" face="Times New Roman">Formtek software. If you type “DWG” under Alfresco add-ons </font><a title="alfresco" href="https://addons.alfresco.com/search" rel="nofollow" target="_blank"><font size="4" face="Times New Roman">page</font></a><font size="4" face="Times New Roman">, you’ll find references to Formtek add-ons mostly. All found modules presented as free of charge, but eventually they all require Formtek EDM </font><a title="alfresco" href="http://formtek.com/call2action/alfresco_edm/" rel="nofollow" target="_blank"><font size="4" face="Times New Roman">Module</font></a><font size="4"><font face="Times New Roman"> which in contrast is obligatory to pay. The price of this module is unknown, I’ve tried to communicate to the team of this project, but still waiting for reply.</font><font face="Times New Roman"> </font></font> <li><font size="4" face="Times New Roman">AcmeCADConverter </font><a title="alfresco" href="http://www.dwgtool.com/cadconvert.htm" rel="nofollow" target="_blank"><font size="4" face="Times New Roman">tool</font></a><font size="4" face="Times New Roman"> (price 99 euro), Windows version only (its team suggests to use wine on Linux to run it). Current converter showed good results, but unfortunately not all dwg files could be converted: some huge files (more than 5Mb) gave blank pdf as a transformation result. </font> <li><font size="4" face="Times New Roman">It turned out that the best solution to solve CAD to PDF conversion is a QCAD </font><a title="alfresco" href="http://www.qcad.org/" rel="nofollow" target="_blank"><font size="4" face="Times New Roman">library</font></a><font size="4" face="Times New Roman">, during tests it could convert dwg files of any complexity (> 10Mb), it can be run on Windows and Linux and its price is just 39 euro.</font></li></ul> <p><font size="4"><font face="Times New Roman">To enable dwg to pdf conversion within Alfresco we need to add a Transformer to newly created bean context located on Alfresco application class path <em>classpath:alfresco/extension/some-context.xml</em>:</font> </font></p><pre style="color: #000"><span style="color: #03c"><<span style="font-weight: 700">bean</span> <span style="color: #36c; font-style: italic">id</span>=<span style="color: #093">"transformer.dwg2pdf"</span>
class=<span style="color: #093">"org.alfresco.repo.content.transform.ProxyContentTransformer"</span>
parent=<span style="color: #093">"baseContentTransformer"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">property</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"worker"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">ref</span> <span style="color: #36c; font-style: italic">bean</span>=<span style="color: #093">"transformer.worker.dwg2pdf"</span> /></span>
<span style="color: #03c"></<span style="font-weight: 700">property</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">bean</span>></span>
</pre>
<p><font size="4" face="Times New Roman">And a Worker to the same context file:</font></p><pre style="color: #000"><span style="color: #03c"><<span style="font-weight: 700">bean</span> <span style="color: #36c; font-style: italic">id</span>=<span style="color: #093">"transformer.worker.dwg2pdf"</span>
class=<span style="color: #093">"org.alfresco.repo.content.transform.RuntimeExecutableContentTransformerWorker"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">property</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"mimetypeService"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">ref</span> <span style="color: #36c; font-style: italic">bean</span>=<span style="color: #093">"mimetypeService"</span> /></span>
<span style="color: #03c"></<span style="font-weight: 700">property</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">property</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"checkCommand"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">bean</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"transformer.dwg2pdf.checkCommand"</span> <span style="color: #36c; font-style: italic">class</span>=<span style="color: #093">"org.alfresco.util.exec.RuntimeExec"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">property</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"commandsAndArguments"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">map</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">entry</span> <span style="color: #36c; font-style: italic">key</span>=<span style="color: #093">"Linux.*"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">list</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>sh<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>-c<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>${dwg2pdf.root}/dwg2pdf -h<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">list</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">entry</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">entry</span> <span style="color: #36c; font-style: italic">key</span>=<span style="color: #093">"Windows.*"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">list</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>cmd<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>/C<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>cd ${dwg2pdf.root} <span style="color: #6782d3">&amp;</span><span style="color: #6782d3">&amp;</span> dwg2pdf -h<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">list</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">entry</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">map</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">property</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">bean</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">property</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">property</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"transformCommand"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">bean</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"transformer.dwg2pdf.Command"</span> <span style="color: #36c; font-style: italic">class</span>=<span style="color: #093">"org.alfresco.util.exec.RuntimeExec"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">property</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"commandsAndArguments"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">map</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">entry</span> <span style="color: #36c; font-style: italic">key</span>=<span style="color: #093">"Linux.*"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">list</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>sh<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>-c<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>${dwg2pdf.root}/dwg2pdf -f -a -o ${target} ${source}<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">list</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">entry</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">entry</span> <span style="color: #36c; font-style: italic">key</span>=<span style="color: #093">".*"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">list</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>cmd<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>/C<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>cd ${dwg2pdf.root} <span style="color: #6782d3">&amp;</span><span style="color: #6782d3">&amp;</span> dwg2pdf -f -a -o ${target} ${source}<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">list</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">entry</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">map</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">property</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">property</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"waitForCompletion"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">value</span>></span>true<span style="color: #03c"></<span style="font-weight: 700">value</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">property</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">bean</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">property</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">bean</span>></span>
</pre>
<p><font size="4" face="Times New Roman">A couple notes regarding Worker config:</font></p>
<ul>
<li><font size="4" face="Times New Roman">${dwg2pdf.root} is taken from alfresco-global.properties, a snippet of this file is shown below</font>
<li><font size="4" face="Times New Roman">${target} ${source} values are injected automatically by Alfresco, when a file is opened and a conversion is triggered</font>
<li><font size="4" face="Times New Roman"><em>checkCommand</em> is a command run on system start. If succeed then current Worker is enabled at a time of Alfresco usage</font>
<li><font size="4" face="Times New Roman"><em>transformerCommand</em> is actually a command that triggered when a content conversion is requested. You can type here any parameters suitable for QCAD (or any other converter you use). For QCAD case: –f is force file overwrite, –a is to adjust page layout, –o is the output filename</font></li></ul>
<p><font size="4" face="Times New Roman">DXF Worker and Transformer configuration looks similar to DWG config, the only should be changed is the bean ids <em>transformer.dwg2pdf</em> to <em>transformer.dxf2pdf</em> and <em>transformer.worker.dwg2pdf</em> to <em>transformer.worker.dxf2pdf</em>.</font></p>
<p><font size="4" face="Times New Roman">The final thing we should do is to add developed transformers to alfresco-global.properties: </font></p><pre style="color: #000">dwg2pdf.root=<span style="color: #00f">/</span>opt<span style="color: #00f">/</span>qcad
content.transformer.dwg2pdf.priority=<span style="color: #06f">50</span>
content.transformer.dwg2pdf.extensions.dwg.pdf.supported=<span style="color: #9700cc">true</span>
content.transformer.dwg2pdf.extensions.dwg.pdf.priority=<span style="color: #06f">50</span>
content.transformer.dxf2pdf.priority=<span style="color: #06f">50</span>
content.transformer.dxf2pdf.extensions.dxf.pdf.supported=<span style="color: #9700cc">true</span>
content.transformer.dxf2pdf.extensions.dxf.pdf.priority=<span style="color: #06f">50</span>
</pre>
<p><font size="4" face="Times New Roman">A few notes regarding this:</font></p>
<ul>
<li><font size="4" face="Times New Roman"><em>dwg2pdf.root</em> is the path to QCAD (Linux version) that injected to developed spring context</font>
<li><font size="4" face="Times New Roman"><em>transformer.dwg2pdf</em> is our transformer bean ids prefixed with a <em>content</em> keyword to refer to converters. <em>Extensions</em> keyword means that when a content with dwg or dxf mime-type is found, Alfresco will try to convert it to pdf.</font></li></ul>
<p><font size="4" face="Times New Roman">That’s it, after Alfresco restart CAD preview should work. And here is a complete source <a title="alfresco" href="https://github.com/verve111/alfresco_cad_support/" target="_blank">code</a> that incudes Ant script to build a jar which can be deployed on top of your Alfresco. </font></p>https://soft29.ru/blog/entry/soft29-directionsСофт 29: Основные направления деятельностиStanislav2016-03-01T07:47:35+00:002017-07-25T06:51:37+00:00<p align="justify"><font size="4" face="Times New Roman">Компания Софт29 является одним из ведущих экспертов по внедрению систем электронного документооборота на базе Alfresco и корпоративных порталов на Liferay по Северо-Западному округу и другим регионам РФ, в том числе г. Москва, Санкт-Петербург</font></p><p align="justify"><font size="4" face="Times New Roman">Компания Софт29 является одним из ведущих экспертов по внедрению систем электронного документооборота на базе Alfresco и корпоративных порталов на Liferay по Северо-Западному округу и другим регионам РФ, в том числе г. Москва, Санкт-Петербург, Новосибирск, Архангельск. Компанией успешно реализован ряд проектов для таких госкомпаний как: ГУП ТЭК Санкт-Петербург, ФГБУ Обь-Иртышское управление по гидрометеорологии и мониторингу окружащей среды, Комитет по информатизации и связи при Правительстве Санкт-Петербурга и пр.</font></p> <p align="justify"><font size="4" face="Times New Roman"><a href="https://soft29.ru/blog/mediaresource/ea6612eb-8cf1-4eb2-bdfc-d790b9b575ba"><img title="alfresco_logo_thumb4" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; margin: 0px; display: inline; padding-right: 0px" border="0" alt="alfresco_logo_thumb4" src="https://soft29.ru/blog/mediaresource/2e533031-0552-481a-af69-bb69cb00543d" width="176" height="55"></a>Внедрение системы электронного документооборота (СЭД) позволит автоматизировать работу с документами в организации и тем самым снизить затраты на ведение документооборота. Одним из главных достоинств СЭД на базе Alfresco является то, что лицензия не требует отчислений (оплата происходит только за установку/доработку под нужды заказчика). Также подтверждены надежность системы и соответствие стандартам РФ. <br>Прочие преимущества внедрения СЭД: </font></p> <ul> <li> <div align="justify"><font size="4" face="Times New Roman">Управление документами, договорами, задачами;</font> </div> <li> <div align="justify"><font size="4" face="Times New Roman">Атрибутивный и полнотекстовый поиск;</font> </div> <li> <div align="justify"><font size="4" face="Times New Roman">Возможность работы в системе сотрудников с любых устройств через браузер без установки дополнительного ПО;</font> </div> <li> <div align="justify"><font size="4" face="Times New Roman">Уменьшение количества безвозвратно утерянных документов, отход от дублирования;</font> </div> <li> <div align="justify"><font size="4" face="Times New Roman">Отчеты (контроль исполнительской дисциплины и пр.); поддержка версионности документов; ЭЦП; интеграция с MSOffice и пр.</font></div></li></ul> <p align="justify"><font size="4" face="Times New Roman"></font></p> <p align="justify"><font size="4" face="Times New Roman"><a href="https://soft29.ru/blog/mediaresource/7ade65b6-06cf-4359-98bc-10b5a4e2e583"><img title="liferay_thumb2" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; margin: 0px; display: inline; padding-right: 0px" border="0" alt="liferay_thumb2" src="https://soft29.ru/blog/mediaresource/9256473f-5fdd-4abc-a606-b59878046604" width="161" height="44"></a>Внедрение корпоративного портала обеспечит быстрый доступ сотрудников через личный кабинет к информационным ресурсам организации: почтовый ящик, календарь, поручения, отчеты, документы. Для внешних пользователей портал выглядит как стандартный веб-сайт. Портал является открытым ПО, то есть лицензия не требует отчислений (оплата происходит только за установку/доработку под нужды заказчика).</font></p> <p align="justify"><font size="4" face="Times New Roman"><a href="https://soft29.ru/blog/mediaresource/87377648-1f1b-4ef8-be33-d75338a35318"><img title="odoo_logo_thumb2" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; margin: 0px; display: inline; padding-right: 0px" border="0" alt="odoo_logo_thumb2" src="https://soft29.ru/blog/mediaresource/1968c9b7-86d5-4333-8050-b96d08f829ff" width="153" height="74"></a>Также компания предлагает услуги по внедрению ERP-систем Odoo (модули: CRM, склад, управление персоналом и др.). Данная система построена на открытых исходных кодах (open source), что в свою очередь подразумевает отсутствие лицензионных выплат за данное решение. </font></p> <p align="justify"><font size="4" face="Times New Roman">Помимо представленных систем компания “Софт29” занимается разработкой Веб-приложений корпоративного назначения. </font></p> <p align="justify"><font size="4" face="Times New Roman">Мы готовы ответить на все ваши вопросы в удобной для вас форме:</font></p> <ul> <li> <div align="justify"><font size="4" face="Times New Roman">по телефону +7(8182)-472050</font></div> <li> <div align="justify"><font size="4" face="Times New Roman">по e-mail <a href="mailto:info@soft29.ru">info@soft29.ru</a></font></div> <li> <div align="justify"><font size="4" face="Times New Roman">личная встреча или презентация.</font></div></li></ul> <p align="justify"><font size="4" face="Times New Roman"></font></p>Софт 29: Основные направления деятельности https://soft29.ru/blog/entry/liferay-7-alpha-beta-servicebuilderLiferay 7 alpha/beta ServiceBuilderStanislav2016-01-31T12:20:25+00:002017-03-05T14:28:01+00:00<p><font size="4" face="Times New Roman">Hey, we are all watching with great interest to the first coming stable Liferay 7 release (<a title="liferay7" href="https://dev.liferay.com/web/liferay-7-community-expedition" rel="nofollow" target="_blank">link</a>). By now alpha and even beta releases are available for testing, they include new cool features and improvements. But still some old functionality is not migrated to the new releases. In this post I’ll talk about ServiceBuilder which is still not officially published and is missing in current alphas and betas. Anyway it is possible to find and use Liferay 7 ServiceBuilder (in liferay-ce repository). In this post I’ll show how to do it.</font></p> <p><font size="4" face="Times New Roman">Hey, we are all watching with great interest to the first coming stable Liferay 7 release (<a title="liferay7" href="https://dev.liferay.com/web/liferay-7-community-expedition" rel="nofollow" target="_blank">link</a>). By now alpha and even beta releases are available for testing, they include new cool features and improvements. But still some old functionality is not migrated to the new releases. In this post I’ll talk about ServiceBuilder which is still not officially published and is missing in current alphas and betas. Anyway it is possible to find and use Liferay 7 ServiceBuilder (in liferay-ce repository). In this post I’ll show how to do it.</font></p> <p><font size="4" face="Times New Roman">As said above, ServiceBuilder is missing in current Liferay 7 alpha and beta releases, you won’t find it in corresponding SDKs / Tomcat / Artifact bundles on official downloads page (<a title="liferay7" href="https://dev.liferay.com/web/liferay-7-community-expedition" rel="nofollow" target="_blank">link</a>). But anyway this feature of Liferay is important and can greatly ease our lives when we need to generate a bunch of Java (and other) files from a single service.xml. </font></p> <p><font size="4" face="Times New Roman">Assume that we have a default <em>pom.xml</em> configuration. Up-to-date Liferay 7 ServiceBuilder can be found in this Maven repository:</font></p><pre style="background: #fff; color: #000"><span style="color: #1c02ff"><<span style="font-weight: 700">repositories</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">repository</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">id</span>></span>liferay-ce<span style="color: #1c02ff"></<span style="font-weight: 700">id</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">url</span>></span>https://repository.liferay.com/nexus/content/groups/liferay-ce/<span style="color: #1c02ff"></<span style="font-weight: 700">url</span>></span>
<span style="color: #1c02ff"></<span style="font-weight: 700">repository</span>></span>
<span style="color: #1c02ff"></<span style="font-weight: 700">repositories</span>></span>
</pre>
<p><font size="4" face="Times New Roman">And the Maven plugin that includes ServiceBuilder has the following id and configuration, that should be added to the <em>build->plugins</em> section of your pom.xml: </font></p><pre style="background: #fff; color: #000"><span style="color: #1c02ff"><<span style="font-weight: 700">plugin</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">groupId</span>></span>com.liferay<span style="color: #1c02ff"></<span style="font-weight: 700">groupId</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">artifactId</span>></span>com.liferay.portal.tools.service.builder<span style="color: #1c02ff"></<span style="font-weight: 700">artifactId</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">version</span>></span>1.0.67<span style="color: #1c02ff"></<span style="font-weight: 700">version</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">configuration</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">apiDirName</span>></span>../blade.servicebuilder.api/src/main/java<span style="color: #1c02ff"></<span style="font-weight: 700">apiDirName</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">beanLocatorUtil</span>></span>com.liferay.util.bean.PortletBeanLocatorUtil<span style="color: #1c02ff"></<span style="font-weight: 700">beanLocatorUtil</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">hbmFileName</span>></span>src/main/resources/META-INF/module-hbm.xml<span style="color: #1c02ff"></<span style="font-weight: 700">hbmFileName</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">implDirName</span>></span>src/main/java<span style="color: #1c02ff"></<span style="font-weight: 700">implDirName</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">inputFileName</span>></span>service.xml<span style="color: #1c02ff"></<span style="font-weight: 700">inputFileName</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">modelHintsFileName</span>></span>src/main/resources/META-INF/portlet-model-hints.xml<span style="color: #1c02ff"></<span style="font-weight: 700">modelHintsFileName</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">osgiModule</span>></span>true<span style="color: #1c02ff"></<span style="font-weight: 700">osgiModule</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">propsUtil</span>></span>blade.servicebuilder.service.util.PropsUtil<span style="color: #1c02ff"></<span style="font-weight: 700">propsUtil</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">resourcesDirName</span>></span>src/main/resources<span style="color: #1c02ff"></<span style="font-weight: 700">resourcesDirName</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">springFileName</span>></span>src/main/resources/META-INF/spring/module-spring.xml<span style="color: #1c02ff"></<span style="font-weight: 700">springFileName</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">springNamespaces</span>></span>beans,osgi<span style="color: #1c02ff"></<span style="font-weight: 700">springNamespaces</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">sqlDirName</span>></span>src/main/resources/META-INF/sql<span style="color: #1c02ff"></<span style="font-weight: 700">sqlDirName</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">sqlFileName</span>></span>tables.sql<span style="color: #1c02ff"></<span style="font-weight: 700">sqlFileName</span>></span>
<span style="color: #1c02ff"><<span style="font-weight: 700">testDirName</span>></span>src/test<span style="color: #1c02ff"></<span style="font-weight: 700">testDirName</span>></span>
<span style="color: #1c02ff"></<span style="font-weight: 700">configuration</span>></span>
<span style="color: #1c02ff"></<span style="font-weight: 700">plugin</span>></span>
</pre>
<p><font size="4" face="Times New Roman">Configuration section parameters are pretty straightforward if you had an experience in Liferay 6 or earlier. Now our <em>pom</em> is ready to build/generate service files, so just run in terminal:</font></p>
<p> </p>
<p><font size="4" face="Times New Roman"></font></p><pre style="background: #fff; color: #000">mvn liferay:build-service
</pre>https://soft29.ru/blog/entry/alfresco-resolve-node-path-programmaticallyAlfresco: resolve node path programmatically in JavaStanislav2015-11-19T11:23:39+00:002017-03-05T14:28:14+00:00<p><font size="4" face="Times New Roman">If you have a requirement to retrieve a particular Node path within an Alfresco repository by means of Java then this article for you. Also I’ll show how to get a subpath of this path and use it in Lucene search queries.</font></p><p><font size="4" face="Times New Roman">If you have a requirement to retrieve a particular Node path within an Alfresco repository by means of Java then this article for you. Also I’ll show how to get a subpath of this path and use it in Lucene search queries.</font></p> <p><font size="4" face="Times New Roman">To get a node path by its <a title="NodeRef" href="http://dev.alfresco.com/resource/docs/java/org/alfresco/service/cmr/repository/NodeRef.html" rel="nofollow" target="_blank">NodeRef</a> is pretty simple, just use <em>org.alfresco.service.cmr.repository.NodeService.getPath(NodeRef nodeRef)</em>. This method returns a result of type <a title="Path" href="http://dev.alfresco.com/resource/AlfrescoOne/5.0/PublicAPI/org/alfresco/service/cmr/repository/Path.html" rel="nofollow" target="_blank">Path</a>. Though this type is really useful, in most cases we need node path presentation as standard Java String. To do this we can use <em>org.alfresco.service.cmr.repository.Path.toPrefixString(NamespacePrefixResolver resolver)</em> method which gives back a string path consisting of node names each prefixed with its namespace (e.g. “app:company_home/st:sites/cm:test/cm:documentLibrary”). This string representation is very handy when we need to execute search queries within Alfresco repository. So <em>NamespacePrefixResolver</em> variable as a parameter should be passed, here <em>DynamicNamespacePrefixResolver</em> implementation can be used, having all namespaces registered (those that persist in current path). As an example let’s consider a Folder node in some site Document library, NamespacePrefixResolver method for it will look: </font></p><pre style="background: #fff; color: #000"><span style="font-weight: 700; color: #0100b6">import</span> org.alfresco.service.namespace.DynamicNamespacePrefixResolver;
<span style="font-weight: 700; color: #0100b6">import</span> org.alfresco.service.namespace.NamespaceService;
<span style="font-weight: 700; color: #0100b6">import</span> org.alfresco.repo.site.SiteModel;
private static DynamicNamespacePrefixResolver getNamespaceResolver() {
DynamicNamespacePrefixResolver resolver <span style="font-weight: 700; color: #0100b6">=</span> <span style="font-weight: 700; color: #0100b6">new</span> DynamicNamespacePrefixResolver(<span style="color: #585cf6; font-style: italic">null</span>);
resolver<span style="font-weight: 700; color: #0100b6">.</span>registerNamespace(NamespaceService<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>CONTENT_MODEL_PREFIX</span>, NamespaceService<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>CONTENT_MODEL_1_0_URI</span>);
resolver<span style="font-weight: 700; color: #0100b6">.</span>registerNamespace(NamespaceService<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>APP_MODEL_PREFIX</span>, NamespaceService<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>APP_MODEL_1_0_URI</span>);
resolver<span style="font-weight: 700; color: #0100b6">.</span>registerNamespace(SiteModel<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>SITE_MODEL_PREFIX</span>, SiteModel<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>SITE_MODEL_URL</span>);
<span style="font-weight: 700; color: #0100b6">return</span> resolver;
}
</pre>
<p><font size="4" face="Times New Roman">The final method to get node path (including subpath retrieval example) will look as following: </font></p><pre style="background: #fff; color: #000">public static String getPathWithoutLastNode(NodeRef nr) {
Path fullPath <span style="font-weight: 700; color: #0100b6">=</span> nodeService<span style="font-weight: 700; color: #0100b6">.</span>getPath(nr);
Path path <span style="font-weight: 700; color: #0100b6">=</span> fullPath<span style="font-weight: 700; color: #0100b6">.</span>subPath(<span style="color: #cd0000; font-style: italic">4</span>, fullPath<span style="font-weight: 700; color: #0100b6">.</span>size());
String pathStr <span style="font-weight: 700; color: #0100b6">=</span> path<span style="font-weight: 700; color: #0100b6">.</span>toPrefixString(getNamespaceResolver());
<span style="font-weight: 700; color: #0100b6">return</span> pathStr;
}
</pre>
<p><font size="4" face="Times New Roman">Here should be mentioned that <em>subPath()</em> method has start node number and end node number as a first and second parameter respectively. So for some folder that persist in root level of the <em>cm:test</em> site document library (<em>test </em>is the name of site), fullPath will “app:company_home/st:sites/cm:test/cm:documentLibrary/cm:FAQ_x0020_CT_x0020_standart”, and subPath “cm:documentLibrary/cm:FAQ_x0020_CT_x0020_standart”. Also folder name is “FAQ CT Standart”, where spaces escaped with “_x0020_”, other special symbols are also escaped by <em>toPrefixString()</em> method. Using this string path we can built some Lucene search query, for example:</font></p><pre style="background: #fff; color: #000">public static List<NodeRef> getTargetFolderPaths(NodeRef nr) {
List<NodeRef> res <span style="font-weight: 700; color: #0100b6">=</span> <span style="font-weight: 700; color: #0100b6">new</span> ArrayList<NodeRef>();
<span style="font-weight: 700; color: #0100b6">if</span> (<span style="font-weight: 700; color: #0100b6">!</span>nodeService<span style="font-weight: 700; color: #0100b6">.</span>exists(nr)) {
System<span style="font-weight: 700; color: #0100b6">.</span>out<span style="font-weight: 700; color: #0100b6">.</span>println(<span style="color: #d80800">"WARN. node is not exists: "</span> <span style="font-weight: 700; color: #0100b6">+</span> nr);
<span style="font-weight: 700; color: #0100b6">return</span> res;
}
String pathStr <span style="font-weight: 700; color: #0100b6">=</span> getPathWithoutLastNode(nr);
ResultSet resultSet <span style="font-weight: 700; color: #0100b6">=</span> searchService<span style="font-weight: 700; color: #0100b6">.</span>query(<span style="font-weight: 700; color: #0100b6">new</span> StoreRef(StoreRef<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>PROTOCOL_WORKSPACE</span>,
<span style="color: #d80800">"SpacesStore"</span>), SearchService<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>LANGUAGE_LUCENE</span>,
MessageFormat<span style="font-weight: 700; color: #0100b6">.</span>format(<span style="color: #d80800">"PATH:<span style="color: #26b31a">\"</span>//{0}<span style="color: #26b31a">\"</span>"</span>, pathStr));
<span style="font-weight: 700; color: #0100b6">for</span> (ResultSetRow row <span style="font-weight: 700; color: #0100b6">:</span> resultSet) {
NodeRef remoteFolder <span style="font-weight: 700; color: #0100b6">=</span> row<span style="font-weight: 700; color: #0100b6">.</span>getNodeRef();
<span style="font-weight: 700; color: #0100b6">if</span> (<span style="font-weight: 700; color: #0100b6">!</span>siteService<span style="font-weight: 700; color: #0100b6">.</span>getSite(remoteFolder)<span style="font-weight: 700; color: #0100b6">.</span>getShortName()
.equals(Utils<span style="font-weight: 700; color: #0100b6">.</span>getSiteShortName(Constants<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>MANAGER_SITE</span>))) {
res<span style="font-weight: 700; color: #0100b6">.</span>add(remoteFolder);
}
}
resultSet<span style="font-weight: 700; color: #0100b6">.</span>close();
<span style="font-weight: 700; color: #0100b6">return</span> res;
}
</pre>
<p><font size="4" face="Times New Roman">This method searches for a root folder by its name within document libraries of each site. If it is found, then further action performed. </font></p>https://soft29.ru/blog/entry/alfresco-basic-content-operations-inAlfresco basic content operations in JavaStanislav2015-10-28T12:24:17+00:002017-03-05T14:28:28+00:00<p><font size="4" face="Times New Roman">The post is dedicated to the task of file (content) operations (create, search, copy, aspect management) in Alfresco Java. As an example is taken some production <a href="http://soft29.info/blog/entry/alfresco-custom-rules-and-actions" target="_blank">case</a> which demonstrates how to implement all these operations.</font></p> <p><font size="4" face="Times New Roman">The post is dedicated to the task of file (content) operations (create, search, copy, aspect management) in Alfresco Java. As an example is taken some production <a href="https://soft29.ru/blog/entry/alfresco-custom-rules-and-actions" target="_blank">case</a> which demonstrates how to implement all these operations.</font></p> <p><font size="4" face="Times New Roman">First of all the simplest way to search for a file or subfolder in some folder is to use <em>org.alfresco.service.cmr.model.FileFolderService.searchSimple(folderNodeRef, fileOrFolderName)</em>. </font></p> <p><font size="4" face="Times New Roman">File or folder can be renamed by this service as well <em>org.alfresco.service.cmr.model.FileFolderService.rename(FileOrFolderNodeRef, newName). </em></font></p> <p><font size="4" face="Times New Roman">File or folder can also be copied or moved to another folder by means of methods <em>org.alfresco.service.cmr.model.FileFolderService.copy(FileOrFolderNodeRef, targetParentRef, newName) </em>and<em> org.alfresco.service.cmr.model.FileFolderService.move(FileOrFolderNodeRef, targetParentRef, newName) </em>accordingly.</font></p> <p><font size="4" face="Times New Roman">In my previous <a href="https://soft29.ru/blog/entry/alfresco-custom-rules-and-actions" target="_blank">article</a> there was a production task to create an Alfresco <a href="https://wiki.alfresco.com/wiki/Custom_Actions" rel="nofollow" target="_blank">action</a> that copies an uploaded content to the target document library locations. </font><font size="4" face="Times New Roman">As a summary code that combines all described content operations an Alfresco action is presented. The name of this action is <em>copycontent</em>, it’s important to specify this id within spring context for current bean in order Alfresco <a href="http://docs.alfresco.com/4.1/tasks/library-folder-rules-define.html" rel="nofollow" target="_blank">folder rule</a> could trigger this action upon upload events. Here is Spring excerpt: </font></p><pre style="background: #ffffff; color: #000000"><span style="color: #7f0055"><</span><span style="color: #7f0055">bean</span> id=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">copycontent</span><span style="color: #2a00ff">"</span> class=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">alfresco.extension.de.CopyContentAction</span><span style="color: #2a00ff">"</span>
parent=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">action-executer</span><span style="color: #2a00ff">"</span><span style="color: #7f0055">></span>
<span style="color: #7f0055"><</span><span style="color: #7f0055">property</span> name=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">nodeService</span><span style="color: #2a00ff">"</span> ref=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">nodeService</span><span style="color: #2a00ff">"</span> <span style="color: #7f0055">/></span>
<span style="color: #7f0055"><</span><span style="color: #7f0055">property</span> name=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">searchService</span><span style="color: #2a00ff">"</span> ref=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">SearchService</span><span style="color: #2a00ff">"</span> <span style="color: #7f0055">/></span>
<span style="color: #7f0055"><</span><span style="color: #7f0055">property</span> name=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">fileFolderService</span><span style="color: #2a00ff">"</span> ref=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">FileFolderService</span><span style="color: #2a00ff">"</span> <span style="color: #7f0055">/></span>
<span style="color: #7f0055"><</span><span style="color: #7f0055">property</span> name=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">siteService</span><span style="color: #2a00ff">"</span> ref=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">siteService</span><span style="color: #2a00ff">"</span> <span style="color: #7f0055">/></span>
<span style="color: #7f0055"></</span><span style="color: #7f0055">bean</span><span style="color: #7f0055">></span>
</pre>
<p><font size="4" face="Times New Roman">And copy content action should extend <em>org.alfresco.repo.action.executerActionExecuterAbstractBase</em>: </font></p><pre style="background: #fff; color: #000"><span style="font-weight: 700; color: #0100b6">package</span> alfresco.extension.de;
public class <span style="font-style: italic">CopyContentAction</span> extends <span style="font-style: italic">ActionExecuterAbstractBase</span> {
@Override
protected void <span style="font-weight: 700; color: #0000a2">executeImpl</span>(Action <span style="font-style: italic">actionArg</span>, NodeRef <span style="font-style: italic">nr</span>) {
<span style="font-weight: 700; color: #0100b6">if</span> ((nodeService<span style="font-weight: 700; color: #0100b6">.</span>getType(nr)<span style="font-weight: 700; color: #0100b6">.</span>equals(ContentModel<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>TYPE_CONTENT</span>)
<span style="font-weight: 700; color: #0100b6">&&</span> isParentCentralizedFolder(nr)) {
List<NodeRef> remoteFolders <span style="font-weight: 700; color: #0100b6">=</span> getTargetFolderPaths(nr);
<span style="font-weight: 700; color: #0100b6">for</span> (NodeRef remoteFolder <span style="font-weight: 700; color: #0100b6">:</span> remoteFolders) {
String fileOrFolderName <span style="font-weight: 700; color: #0100b6">=</span> nodeService<span style="font-weight: 700; color: #0100b6">.</span>getProperty(nr, ContentModel<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>PROP_NAME</span>)<span style="font-weight: 700; color: #0100b6">.</span>toString();
<span style="font-weight: 700; color: #0100b6">if</span> (<span style="font-weight: 700; color: #0100b6">!</span>nodeService<span style="font-weight: 700; color: #0100b6">.</span>hasAspect(remoteFolder, Constants<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>ASPECT_CENTRALIZED</span>)) {
<span style="color: #00b418">// if target remote folder is not centralized - skip. </span>
<span style="font-weight: 700; color: #0100b6">continue</span>;
}
<span style="font-weight: 700; color: #0100b6">if</span> (fileFolderService<span style="font-weight: 700; color: #0100b6">.</span>searchSimple(remoteFolder, fileOrFolderName) <span style="font-weight: 700; color: #0100b6">!=</span> <span style="color: #585cf6; font-style: italic">null</span>) {
<span style="color: #00b418">// if file or folder with current name exists - skip. </span>
<span style="font-weight: 700; color: #0100b6">continue</span>;
}
<span style="font-weight: 700; color: #0100b6">try</span> {
fileFolderService<span style="font-weight: 700; color: #0100b6">.</span>copy(nr, remoteFolder, <span style="color: #585cf6; font-style: italic">null</span>);
} <span style="font-weight: 700; color: #0100b6">catch</span> (FileExistsException e) {
log<span style="font-weight: 700; color: #0100b6">.</span>e();
} <span style="font-weight: 700; color: #0100b6">catch</span> (FileNotFoundException e) {
log<span style="font-weight: 700; color: #0100b6">.</span>e();
}
}
}
}
private boolean isParentCentralizedFolder(NodeRef nr) {
NodeRef parent <span style="font-weight: 700; color: #0100b6">=</span> nodeService<span style="font-weight: 700; color: #0100b6">.</span>getPrimaryParent(nr)<span style="font-weight: 700; color: #0100b6">.</span>getParentRef();
<span style="font-weight: 700; color: #0100b6">if</span> (parent <span style="font-weight: 700; color: #0100b6">==</span> <span style="color: #585cf6; font-style: italic">null</span>) {
<span style="font-weight: 700; color: #0100b6">return</span> <span style="color: #585cf6; font-style: italic">false</span>;
}
<span style="font-weight: 700; color: #0100b6">return</span> nodeService<span style="font-weight: 700; color: #0100b6">.</span>getType(parent)<span style="font-weight: 700; color: #0100b6">.</span>equals(ContentModel<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>TYPE_FOLDER</span>) <span style="font-weight: 700; color: #0100b6">&&</span> nodeService<span style="font-weight: 700; color: #0100b6">.</span>hasAspect(parent, Constants<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>ASPECT_CENTRALIZED</span>);
}
}
</pre>
<p><font size="4" face="Times New Roman">So this how Alfresco folder action implemented including basic content operations.</font></p>https://soft29.ru/blog/entry/invoke-an-action-of-coreInvoke an action of core Liferay portletStanislav2015-09-11T12:46:43+00:002017-03-05T14:28:37+00:00<p><font size="4" face="Times New Roman">Liferay is shipped with a bunch of certain preinstalled portlets, such as <em>Documents and Media</em>, <em>Message Boards</em>, <em>Asset Publisher</em>, etc. Each of these portlets has a set of predefined struts actions (definitions can be found under <em>ROOT/WEB-INF/struts-config.xml</em>) which are invoked upon some user action in current portlet. During a custom portlet development there is a need may occur to call any of these actions, for example folder create/update/delete of <em>Documents and Media (Document Library)</em>, without copying a core functionality to custom portlet. This article will demonstrate how to make it work, taking as an example <em>Move Folder</em> action of <em>Documents and Media</em> portlet.</font></p><p><font size="4" face="Times New Roman">Liferay is shipped with a bunch of certain preinstalled portlets, such as <em>Documents and Media</em>, <em>Message Boards</em>, <em>Asset Publisher</em>, etc. Each of these portlets has a set of predefined struts actions (definitions can be found under <em>ROOT/WEB-INF/struts-config.xml</em>) which are invoked upon some user action in current portlet. During a custom portlet development there is a need may occur to call any of these actions, for example folder create/update/delete of <em>Documents and Media (Document Library)</em>, without copying a core functionality to custom portlet. This article will demonstrate how to make it work, taking as an example <em>Move Folder</em> action of <em>Documents and Media</em> portlet.</font></p> <p><font size="4" face="Times New Roman">Each Struts action has two phases <em>render</em> and actually <em>action</em>. Unfortunately, it is not possible to invoke <em>render</em> phase of core portlet, because at the time of the call there is redirect to this portlet jsp should occur and if the portlet is not placed within current page then an error displayed. An <em>action</em> phase in contrast is always welcome to invoke. </font></p> <p><font size="4" face="Times New Roman">So let’s imagine a situation when we need to manipulate folders within Document Library from inside of our custom portlet, more specifically on custom portlet button click – do move folder action. First step is to get this action’s struts id from <em>ROOT/WEB-INF/struts-config.xml</em>: prefix “document_library”, postfix “folder” and “move”:</font></p><pre style="background: #ffffff; color: #000000"><span style="color: #7f0055"><</span><span style="color: #7f0055">action</span> path=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">/document_library/move_folder</span><span style="color: #2a00ff">"</span> type=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">com.liferay.portlet.documentlibrary.action.EditFolderAction</span><span style="color: #2a00ff">"</span><span style="color: #7f0055">></span>
<span style="color: #7f0055"><</span><span style="color: #7f0055">forward</span> name=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">portlet.document_library.edit_folder</span><span style="color: #2a00ff">"</span> path=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">portlet.document_library.move_folder</span><span style="color: #2a00ff">"</span> <span style="color: #7f0055">/></span>
<span style="color: #7f0055"><</span><span style="color: #7f0055">forward</span> name=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">portlet.document_library.error</span><span style="color: #2a00ff">"</span> path=<span style="color: #2a00ff">"</span><span style="color: #2a00ff">portlet.document_library.error</span><span style="color: #2a00ff">"</span> <span style="color: #7f0055">/></span>
<span style="color: #7f0055"></</span><span style="color: #7f0055">action</span><span style="color: #7f0055">></span>
</pre>
<p> </p>
<p><font size="4" face="Times New Roman">The id of the action <em>/document_library/move_folder</em>, one more thing we need to check is the parameters that may be required for the action class. To do this please have a look at action class source code, luckily Liferay is an opensource product, so it is pretty easy (EditFolderAction.java is our case): </font></p><pre style="background: #ffffff; color: #000000"><span style="font-weight: bold; color: #7f0055">public</span> void processAction(ActionMapping actionMapping, ActionForm actionForm, PortletConfig portletConfig, ActionRequest actionRequest, ActionResponse actionResponse)
<span style="font-weight: bold; color: #7f0055">throws</span> Exception {
<span style="font-weight: bold; color: #7f0055">String</span> cmd = ParamUtil.getString(actionRequest, <span style="color: #2a00ff">"cmd"</span>);
<span style="font-weight: bold; color: #7f0055">try</span> {
<span style="font-weight: bold; color: #7f0055">if</span> ((cmd.equals(<span style="color: #2a00ff">"add"</span>)) || (cmd.equals(<span style="color: #2a00ff">"update"</span>))) {
updateFolder(actionRequest);
} <span style="font-weight: bold; color: #7f0055">else</span> <span style="font-weight: bold; color: #7f0055">if</span> (cmd.equals(<span style="color: #2a00ff">"delete"</span>)) {
deleteFolders(actionRequest, <span style="font-weight: bold; color: #7f0055">false</span>);
} <span style="font-weight: bold; color: #7f0055">else</span> <span style="font-weight: bold; color: #7f0055">if</span> (cmd.equals(<span style="color: #2a00ff">"move"</span>)) {
moveFolders(actionRequest, <span style="font-weight: bold; color: #7f0055">false</span>);
} ...
}
}
<span style="font-weight: bold; color: #7f0055">protected</span> void moveFolders(ActionRequest actionRequest, boolean moveFromTrash)
<span style="font-weight: bold; color: #7f0055">throws</span> Exception {
<span style="font-weight: bold; color: #7f0055">long</span>[] folderIds = <span style="font-weight: bold; color: #7f0055">null</span>;
<span style="font-weight: bold; color: #7f0055">long</span> folderId = ParamUtil.getLong(actionRequest, <span style="color: #2a00ff">"folderId"</span>);
<span style="font-weight: bold; color: #7f0055">if</span> (folderId > 0L) {
folderIds = <span style="font-weight: bold; color: #7f0055">new</span> <span style="font-weight: bold; color: #7f0055">long</span>[] { folderId };
} <span style="font-weight: bold; color: #7f0055">else</span> {
folderIds = StringUtil.split(
ParamUtil.getString(actionRequest, <span style="color: #2a00ff">"folderIds"</span>), 0L);
}
<span style="font-weight: bold; color: #7f0055">long</span> parentFolderId = ParamUtil.getLong(
actionRequest, <span style="color: #2a00ff">"parentFolderId"</span>);
ServiceContext serviceContext = ServiceContextFactory.getInstance(
DLFileEntry.class.getName(), actionRequest);
<span style="font-weight: bold; color: #7f0055">for</span> (<span style="font-weight: bold; color: #7f0055">long</span> moveFolderId : folderIds) {
DLAppServiceUtil.moveFolder(
moveFolderId, parentFolderId, serviceContext);
}
}
</pre>
<p><font size="4" face="Times New Roman"></font> </p>
<p><font size="4" face="Times New Roman">From the code above the following required parameters retrieved</font>:</p>
<ul>
<li><font size="4" face="Times New Roman">cmd – command to perform (may be add/delete/move …)</font>
<li><font size="4" face="Times New Roman">folderId or <font size="4" face="Times New Roman">folderIds – ids of the folders to move </font></font>
<li><font size="4" face="Times New Roman">parentFolderId – id of the folder to move to.</font></li></ul>
<p><font size="4" face="Times New Roman">To invoke the action phase of current action we need to generate a URL of this action. Because desired action is outside of custom portlet we should use <em>liferay-portlet:actionURL</em> jsp tag, which wraps all required ids and parameters with outer portlet id prefixes. In our case final URL tag will look so: </font></p>
<p><font size="4" face="Times New Roman"></font></p><pre style="background: #ffffff; color: #000000"><span style="color: #7f0055"><</span><span style="color: #7f0055">liferay-portlet:actionURL</span> portletName=<span style="color: #2a00ff">"</span><span style="background: #ffffe8; color: #7f0055"><%</span><span style="background: #ffffe8; color: #000000">=</span><span style="background: #ffffe8; color: #000000">PortletKeys</span><span style="background: #ffffe8; color: #000000">.</span><span style="background: #ffffe8; color: #000000">DOCUMENT_LIBRARY</span><span style="background: #ffffe8; color: #7f0055">%></span><span style="color: #2a00ff">"</span> var=<span style="color: #2a00ff">"moveFolderURL"</span> plid=<span style="color: #2a00ff">"</span><span style="background: #ffffe8; color: #7f0055"><%</span><span style="background: #ffffe8; color: #000000">=</span><span style="background: #ffffe8; color: #000000">themeDisplay</span><span style="background: #ffffe8; color: #000000">.</span><span style="background: #ffffe8; color: #000000">getPlid</span><span style="background: #ffffe8; color: #000000">(</span><span style="background: #ffffe8; color: #000000">)</span><span style="background: #ffffe8; color: #7f0055">%></span><span style="color: #2a00ff">"</span> varImpl=<span style="color: #2a00ff">"moveFolderURL"</span>
<portlet:param name=<span style="color: #2a00ff">"struts_action"</span> value=<span style="color: #2a00ff">"/document_library/move_folder"</span> <span style="color: #7f0055">/></span>
<span style="color: #7f0055"><</span><span style="color: #7f0055">portlet:</span><span style="font-weight: bold; color: #7f0055">param</span> name=<span style="color: #2a00ff">"cmd"</span> value=<span style="color: #2a00ff">"</span><span style="background: #ffffe8; color: #7f0055"><%</span><span style="background: #ffffe8; color: #000000">=</span><span style="background: #ffffe8; color: #000000"> Constants</span><span style="background: #ffffe8; color: #000000">.</span><span style="background: #ffffe8; color: #000000">MOVE </span><span style="background: #ffffe8; color: #7f0055">%></span><span style="color: #2a00ff">"</span> <span style="color: #7f0055">/></span>
<span style="color: #7f0055"><</span><span style="color: #7f0055">portlet:</span><span style="font-weight: bold; color: #7f0055">param</span> name=<span style="color: #2a00ff">"folderId"</span> value=<span style="color: #2a00ff">"</span><span style="background: #ffffe8; color: #7f0055"><%</span><span style="background: #ffffe8; color: #000000">=</span><span style="background: #ffffe8; color: #000000"> </span><span style="background: #ffffe8; font-weight: bold; color: #7f0055">String</span><span style="background: #ffffe8; color: #000000">.</span><span style="background: #ffffe8; color: #000000">valueOf</span><span style="background: #ffffe8; color: #000000">(</span><span style="background: #ffffe8; color: #000000">folderId</span><span style="background: #ffffe8; color: #000000">)</span><span style="background: #ffffe8; color: #000000"> </span><span style="background: #ffffe8; color: #7f0055">%></span><span style="color: #2a00ff">"</span> <span style="color: #7f0055">/></span>
<span style="color: #7f0055"><</span><span style="color: #7f0055">portlet:</span><span style="font-weight: bold; color: #7f0055">param</span> name=<span style="color: #2a00ff">"parentFolderId"</span> value=<span style="color: #2a00ff">"</span><span style="background: #ffffe8; color: #7f0055"><%</span><span style="background: #ffffe8; color: #000000">=</span><span style="background: #ffffe8; color: #000000"> </span><span style="background: #ffffe8; font-weight: bold; color: #7f0055">String</span><span style="background: #ffffe8; color: #000000">.</span><span style="background: #ffffe8; color: #000000">valueOf</span><span style="background: #ffffe8; color: #000000">(</span><span style="background: #ffffe8; color: #000000">parentFolderId</span><span style="background: #ffffe8; color: #000000">)</span><span style="background: #ffffe8; color: #000000"> </span><span style="background: #ffffe8; color: #7f0055">%></span><span style="color: #2a00ff">"</span> <span style="color: #7f0055">/></span>
<span style="color: #7f0055"></</span><span style="color: #7f0055">liferay-portlet:actionURL</span><span style="color: #7f0055">></span>
</pre>
<p> </p>
<p><font size="4" face="Times New Roman">In the jsp code below we can find the id of struts action plus parameters found in action class. One more important thing here is a portletName attribute, which is the outer portlet id (document library portlet in our case). The final step is to link implemented action with button control:</font></p><pre style="background: #ffffff; color: #000000"><span style="color: #7f0055"><</span><span style="color: #7f0055">aui:</span><span style="font-weight: bold; color: #7f0055">button</span> value=<span style="color: #2a00ff">"move folder"</span> onClick=<span style="color: #2a00ff">"</span><span style="background: #ffffe8; color: #7f0055"><%</span><span style="background: #ffffe8; color: #000000">=</span><span style="background: #ffffe8; color: #000000"> moveFolderURL</span><span style="background: #ffffe8; color: #000000">.</span><span style="background: #ffffe8; color: #000000">toString</span><span style="background: #ffffe8; color: #000000">(</span><span style="background: #ffffe8; color: #000000">)</span><span style="background: #ffffe8; color: #000000"> </span><span style="background: #ffffe8; color: #7f0055">%></span><span style="color: #2a00ff">"</span> <span style="color: #7f0055">/></span>
</pre>
<p> </p>
<p><font size="4" face="Times New Roman">As a result we get a button that invokes move_folder struts action of <em>Documents and Media </em>portlet. In this way all predefined struts actions can be called with little exceptions, when parameters can not be wrapped with target portlet id.</font></p>https://soft29.ru/blog/entry/alfresco-custom-rules-and-actionsAlfresco custom rules and actions in javaStanislav2015-08-17T09:10:31+00:002017-03-05T14:28:58+00:00<p><font size="4" face="Times New Roman">Post is dedicated to the task of rules and actions development in Alfresco Java. As an example is taken some production case which demonstrates how to implement this task. </font></p><p><font size="4" face="Times New Roman">Post is dedicated to the task of rules and actions development in Alfresco Java. As an example is taken some production case which demonstrates how to implement this task. </font></p> <p><font size="4" face="Times New Roman">Production case: given some amount of sites in Alfresco, one of them concerned to be centralized. “Centralized” means that any folder or content (file) that is created within document library of Centralized site should be copied to all other sites preserving the same folder and content hierarchy. To resolve this issue first of all an Alfresco <a href="http://docs.alfresco.com/4.1/tasks/library-folder-rules-define.html" rel="nofollow" target="_blank">rule</a> should be created. This rule will be bounded to the document library (of Centralized site) and will trigger the copy Alfresco <a href="https://wiki.alfresco.com/wiki/Custom_Actions" rel="nofollow" target="_blank">action</a> when new content created within this library. </font></p> <p><font size="4" face="Times New Roman">So what is the Alfresco rule? Alfresco rule is a some definition that invokes an action upon determined node event (create, update, etc) when all conditions are met. For example, let’s create a Rule for document library of a site. In the beginning we need to get a node to bind the rule to. This node will be a document library which is a core node for content storage of every alfresco site:</font></p><pre style="background: #f6f8ff; color: #000020"><span style="font-weight: bold; color: #200080">import</span><span style="color: #004a43"> org</span><span style="color: #308080">.</span><span style="color: #004a43">alfresco</span><span style="color: #308080">.</span><span style="color: #004a43">service</span><span style="color: #308080">.</span><span style="color: #004a43">cmr</span><span style="color: #308080">.</span><span style="color: #004a43">site</span><span style="color: #308080">.</span><span style="color: #004a43">SiteService</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">import</span><span style="color: #004a43"> org</span><span style="color: #308080">.</span><span style="color: #004a43">alfresco</span><span style="color: #308080">.</span><span style="color: #004a43">service</span><span style="color: #308080">.</span><span style="color: #004a43">cmr</span><span style="color: #308080">.</span><span style="color: #004a43">repository</span><span style="color: #308080">.</span><span style="color: #004a43">NodeService</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">private</span> <span style="font-weight: bold; color: #200080">static</span> <span style="font-weight: bold; color: #200080">final</span> String _DOC_LIB = "documentLibrary"<span style="color: #308080">;</span>
<span style="font-weight: bold; color: #200080">private</span> <span style="font-weight: bold; color: #200080">static</span> NodeService nodeService<span style="color: #308080">;</span>
<span style="font-weight: bold; color: #200080">private</span> <span style="font-weight: bold; color: #200080">static</span> SiteService siteService<span style="color: #308080">;</span>
<span style="font-weight: bold; color: #200080">public</span> void go(NodeRef siteRef) <span style="color: #406080">{</span>
NodeRef docLibRef <span style="color: #308080">=</span> createOrGetDocLib<span style="color: #308080">(</span>siteRef<span style="color: #308080">)</span><span style="color: #406080">;</span>
createRule<span style="color: #308080">(</span>docLibRef<span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span>
<span style="font-weight: bold; color: #200080">private</span> <span style="font-weight: bold; color: #200080">static</span> NodeRef createOrGetDocLib(NodeRef siteRef) <span style="color: #406080">{</span>
<span style="font-weight: bold; color: #6679aa">String</span> shortName <span style="color: #308080">=</span> <span style="color: #308080">(</span><span style="font-weight: bold; color: #6679aa">String</span><span style="color: #308080">)</span> nodeService<span style="color: #308080">.</span>getProperty<span style="color: #308080">(</span>siteRef<span style="color: #308080">,</span> ContentModel<span style="color: #308080">.</span>PROP_NAME<span style="color: #308080">)</span><span style="color: #406080">;</span>
NodeRef docLibRef <span style="color: #308080">=</span> <span style="font-weight: bold; color: #200080">null</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">if</span> <span style="color: #308080">(</span><span style="color: #308080">!</span>siteService<span style="color: #308080">.</span>hasContainer<span style="color: #308080">(</span>shortName<span style="color: #308080">,</span> _DOC_LIB<span style="color: #308080">)</span><span style="color: #308080">)</span> <span style="color: #406080">{</span>
docLibRef <span style="color: #308080">=</span> siteService<span style="color: #308080">.</span>createContainer<span style="color: #308080">(</span>shortName<span style="color: #308080">,</span> _DOC_LIB<span style="color: #308080">,</span> <span style="font-weight: bold; color: #200080">null</span><span style="color: #308080">,</span> <span style="font-weight: bold; color: #200080">null</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span> <span style="font-weight: bold; color: #200080">else</span> <span style="color: #406080">{</span>
docLibRef <span style="color: #308080">=</span> siteService<span style="color: #308080">.</span>getContainer<span style="color: #308080">(</span>shortName<span style="color: #308080">,</span> _DOC_LIB<span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #406080">}</span>
<span style="font-weight: bold; color: #200080">return</span> docLibRef<span style="color: #406080">;</span>
<span style="color: #406080">}</span>
</pre>
<p><font size="4" face="Times New Roman"></font> </p>
<p><font size="4" face="Times New Roman">In the code above should be mentioned that <em>nodeService</em> and <em>siteService</em> are Alfresco spring beans that should be defined as properties in the spring context file of our application (setters in this code are omitted). </font></p>
<p><font size="4" face="Times New Roman">Rule creation looks as following, method names speak for themselves: </font></p><pre style="background: #f6f8ff; color: #000020"><span style="font-weight: bold; color: #200080">import</span><span style="color: #004a43"> org</span><span style="color: #308080">.</span><span style="color: #004a43">alfresco</span><span style="color: #308080">.</span><span style="color: #004a43">service</span><span style="color: #308080">.</span><span style="color: #004a43">cmr</span><span style="color: #308080">.</span><span style="color: #004a43">rule</span><span style="color: #308080">.</span><span style="color: #004a43">Rule</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">import</span><span style="color: #004a43"> org</span><span style="color: #308080">.</span><span style="color: #004a43">alfresco</span><span style="color: #308080">.</span><span style="color: #004a43">service</span><span style="color: #308080">.</span><span style="color: #004a43">cmr</span><span style="color: #308080">.</span><span style="color: #004a43">rule</span><span style="color: #308080">.</span><span style="color: #004a43">RuleService</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">import</span><span style="color: #004a43"> org</span><span style="color: #308080">.</span><span style="color: #004a43">alfresco</span><span style="color: #308080">.</span><span style="color: #004a43">service</span><span style="color: #308080">.</span><span style="color: #004a43">cmr</span><span style="color: #308080">.</span><span style="color: #004a43">rule</span><span style="color: #308080">.</span><span style="color: #004a43">RuleType</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">private</span> void createRule(NodeRef parent) <span style="color: #406080">{</span>
Rule rule <span style="color: #308080">=</span> <span style="font-weight: bold; color: #200080">new</span> Rule<span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="color: #595979">// rule type RuleType.INBOUND to invoke rule on node create events</span>
rule<span style="color: #308080">.</span>setRuleType<span style="color: #308080">(</span>RuleType<span style="color: #308080">.</span>INBOUND<span style="color: #308080">)</span><span style="color: #406080">;</span>
rule<span style="color: #308080">.</span>applyToChildren<span style="color: #308080">(</span><span style="font-weight: bold; color: #200080">true</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #6679aa">String</span> title <span style="color: #308080">=</span> <span style="color: #1060b6">"Copy content on creation"</span><span style="color: #406080">;</span>
rule<span style="color: #308080">.</span>setTitle<span style="color: #308080">(</span>title<span style="color: #308080">)</span><span style="color: #406080">;</span>
rule<span style="color: #308080">.</span>setAction<span style="color: #308080">(</span>getCompositeAction<span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
ruleService<span style="color: #308080">.</span>saveRule<span style="color: #308080">(</span>parent<span style="color: #308080">,</span> rule<span style="color: #308080">)</span><span style="color: #406080">;</span> <span style="color: #595979">// Bind the rule to the docLib </span>
<span style="color: #406080">}</span>
</pre>
<p> </p>
<p><font size="4" face="Times New Roman">There is left to create the action. The Alfresco documentation says:</font></p>
<blockquote>
<p><font size="4" face="Times New Roman">An action is a unit of work that can be performed against a </font><a href="https://wiki.alfresco.com/wiki/Terminology_Guide#Fundamental"><font size="4" face="Times New Roman">node</font></a><font size="4" face="Times New Roman">. It is configured through a content rule. For example, moving a node, copying a node, checking a node in, transfoming the contents of a node, etc.</font></p></blockquote>
<p><font size="4" face="Times New Roman">Action’s code:</font></p><pre style="background: #f6f8ff; color: #000020"><span style="font-weight: bold; color: #200080">import</span><span style="color: #004a43"> org</span><span style="color: #308080">.</span><span style="color: #004a43">alfresco</span><span style="color: #308080">.</span><span style="color: #004a43">service</span><span style="color: #308080">.</span><span style="color: #004a43">cmr</span><span style="color: #308080">.</span><span style="color: #004a43">action</span><span style="color: #308080">.</span><span style="color: #004a43">CompositeAction</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">import</span><span style="color: #004a43"> org</span><span style="color: #308080">.</span><span style="color: #004a43">alfresco</span><span style="color: #308080">.</span><span style="color: #004a43">repo</span><span style="color: #308080">.</span><span style="color: #004a43">action</span><span style="color: #308080">.</span><span style="color: #004a43">evaluator</span><span style="color: #308080">.</span><span style="color: #004a43">IsSubTypeEvaluator</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">import</span><span style="color: #004a43"> org</span><span style="color: #308080">.</span><span style="color: #004a43">alfresco</span><span style="color: #308080">.</span><span style="color: #004a43">model</span><span style="color: #308080">.</span><span style="color: #004a43">ContentModel</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">private</span> <span style="font-weight: bold; color: #200080">static</span> ActionService actionService<span style="color: #308080">;</span>
<span style="font-weight: bold; color: #200080">private</span> CompositeAction getCompositeAction() <span style="color: #406080">{</span>
CompositeAction compositeAction <span style="color: #308080">=</span> actionService<span style="color: #308080">.</span>createCompositeAction<span style="color: #308080">(</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
ActionCondition actionCondition <span style="color: #308080">=</span> actionService<span style="color: #308080">.</span>createActionCondition<span style="color: #308080">(</span>IsSubTypeEvaluator<span style="color: #308080">.</span>NAME<span style="color: #308080">)</span><span style="color: #406080">;</span>
Map<span style="color: #308080"><</span><span style="font-weight: bold; color: #6679aa">String</span><span style="color: #308080">,</span> <span style="font-weight: bold; color: #6679aa">Serializable</span><span style="color: #308080">></span> conditionParameters <span style="color: #308080">=</span> <span style="font-weight: bold; color: #200080">new</span> HashMap<span style="color: #308080"><</span><span style="font-weight: bold; color: #6679aa">String</span><span style="color: #308080">,</span> <span style="font-weight: bold; color: #6679aa">Serializable</span><span style="color: #308080">></span><span style="color: #308080">(</span><span style="color: #008c00">1</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
conditionParameters<span style="color: #308080">.</span>put<span style="color: #308080">(</span>IsSubTypeEvaluator<span style="color: #308080">.</span>PARAM_TYPE<span style="color: #308080">,</span> ContentModel<span style="color: #308080">.</span>TYPE_CMOBJECT<span style="color: #308080">)</span><span style="color: #406080">;</span>
actionCondition<span style="color: #308080">.</span>setParameterValues<span style="color: #308080">(</span>conditionParameters<span style="color: #308080">)</span><span style="color: #406080">;</span>
compositeAction<span style="color: #308080">.</span>addActionCondition<span style="color: #308080">(</span>actionCondition<span style="color: #308080">)</span><span style="color: #406080">;</span>
Action action <span style="color: #308080">=</span> actionService<span style="color: #308080">.</span>createAction<span style="color: #308080">(</span><span style="color: #1060b6">"copycontent"</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
action<span style="color: #308080">.</span>setExecuteAsynchronously<span style="color: #308080">(</span><span style="font-weight: bold; color: #200080">false</span><span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #6679aa">String</span> title <span style="color: #308080">=</span> <span style="color: #1060b6">"Copy content on creation"</span><span style="color: #406080">;</span>
action<span style="color: #308080">.</span>setTitle<span style="color: #308080">(</span>title<span style="color: #308080">)</span><span style="color: #406080">;</span>
<span style="font-weight: bold; color: #200080">return</span> action<span style="color: #406080">;</span>
<span style="color: #406080">}</span>
</pre>
<p> </p>
<p><font size="4" face="Times New Roman">In the code above should be noticed:</font></p>
<ul>
<li><font size="4" face="Times New Roman">The condition for the action is defined. Here involved Alfresco evaluator to check whether the type of the node is a subtype of <em>cm_object. ContentModel.TYPE_FOLDER </em>and<em> ContentModel.TYPE_CONTENT </em>are subtypes of this type, in this way the action is triggered for the nodes of these types only.</font>
<li><font size="4" face="Times New Roman"><em>createAction(String name)</em> method used to extract action spring bean by its id (“<em>copycontent</em>”). An example of action bean will be given in the following <a href="https://soft29.ru/blog/entry/alfresco-basic-content-operations-in" target="_blank">post</a> of our blog.</font></li></ul>
<p><font size="4" face="Times New Roman"></font> </p>
<p><font size="4" face="Times New Roman">That’s it. As a result we’ve created an Alfresco content rule that executed upon node creation (condition == subtype <em>cm_object</em>) within document library of a site. </font></p>https://soft29.ru/blog/entry/alfresco-extension-module-evaluatorsAlfresco Extension Module evaluatorsStanislav2015-07-30T14:05:02+00:002017-03-05T14:29:32+00:00<p><font size="4" face="Times New Roman"></font></p> <p><font size="4" face="Times New Roman">Since Alfresco 4 the extension modules were presented which allow to change/extend default Share components. This is very useful when we need to perform some minor changes to the graphical user interface within Alfresco. </font><font size="4" face="Times New Roman">In this tutorial I will present how to implement such kind of module having as an example Share’s Site Members Dashlet that we’ll hide or show depending on user role. Evaluator logic will be implemented in Java. </font></p><p><font size="4" face="Times New Roman"></font></p> <p><font size="4" face="Times New Roman">Since Alfresco 4 the extension modules were presented which allow to change/extend default Share components. This is very useful when we need to perform some minor changes to the graphical user interface within Alfresco. </font><font size="4" face="Times New Roman">In this tutorial I will present how to implement such kind of module having as an example Share’s Site Members Dashlet that we’ll hide or show depending on user role. Evaluator logic will be implemented in Java. </font></p> <p><font size="4" face="Times New Roman">First of all we need to define an extension module config. A quick guide how to do this can be found under Alfresco official <a title="alfresco" href="http://docs.alfresco.com/4.0/tasks/Create-a-Model.html" rel="nofollow" target="_blank">page</a>. This config can be placed to <em><TOMCAT>/shared/classes/alfresco/web-extension/site-data/extensions/some-extension.xml </em>or <em><TOMCAT>/share/WEB-INF/lib/custom.jar/alfresco/site-data/extensions/some-extension.xml. </em>I personally prefer the latter option because the extension modules are always related to Share application and it’s convenient to keep all customization files packed in a single jar. <em>Some-extension.xml</em> will look as following:</font></p> <p><font size="4" face="Times New Roman"></font></p><pre style="color: #000"><span style="color: #03c"><<span style="font-weight: 700">extension</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">modules</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">module</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">id</span>></span>Conditionally Hide Site Members Dashlet<span style="color: #03c"></<span style="font-weight: 700">id</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">auto-deploy</span>></span>true<span style="color: #03c"></<span style="font-weight: 700">auto-deploy</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">components</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">component</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">region-id</span>></span>component-1-1<span style="color: #03c"></<span style="font-weight: 700">region-id</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">source-id</span>></span>site/{site}/dashboard<span style="color: #03c"></<span style="font-weight: 700">source-id</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">scope</span>></span>page<span style="color: #03c"></<span style="font-weight: 700">scope</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">sub-components</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">sub-component</span> <span style="color: #36c; font-style: italic">id</span>=<span style="color: #093">"default"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">evaluations</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">evaluation</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">evaluators</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">evaluator</span> <span style="color: #36c; font-style: italic">type</span>=<span style="color: #093">"evaluator.module.IsUserInRole"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">params</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">role</span>></span>CustomConsumer<span style="color: #03c"></<span style="font-weight: 700">role</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">params</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">evaluator</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">evaluators</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">render</span>></span>false<span style="color: #03c"></<span style="font-weight: 700">render</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">evaluation</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">evaluations</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">sub-component</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">sub-components</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">component</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">components</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">module</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">modules</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">extension</span>></span>
</pre>
<p> </p>
<p><font size="4" face="Times New Roman">In this configuration should be clarified: </font></p>
<ul>
<li><font size="4" face="Times New Roman"><strong>source-id</strong> is the page id which current module belongs to. This id can be found by means of Alfresco <a href="http://docs.alfresco.com/4.0/tasks/Create-a-Model-example.html" rel="nofollow" target="_blank">Surfbug</a>. In our case it is the Share Dashboard that contains all user dashlets.</font>
<li><font size="4" face="Times New Roman"><strong>region-id</strong> is the id of the component within parent page. This id can also be found with the Surfbug.</font>
<li><font size="4" face="Times New Roman"><strong>evaluator.type</strong> is the id of a bean that extends Alfresco Evaluator class. A snippet of Spring context is shown below.</font>
<li><font size="4" face="Times New Roman"><strong>params</strong> tag is a parameter wrapper that is passed to our evaluator. <strong>Role</strong> is a custom tag, text element of which will be taken by Evaluator to get value.</font>
<li><font size="4" face="Times New Roman"><strong>render</strong> false: hide current dashlet for every user in role <em>CustomConsumer </em>depending on evaluation result.</font></li></ul>
<p><font size="4" face="Times New Roman">The snippet of spring context that defines our evaluator may be put to <em><TOMCAT>/share/WEB-INF/lib/custom.jar/alfresco/web-extension/share-custom-context.xml</em>. It looks so:</font></p><pre style="color: #000"> <span style="color: #03c"><<span style="font-weight: 700">bean</span> <span style="color: #36c; font-style: italic">id</span>=<span style="color: #093">"evaluator.module.IsUserInRole"</span> <span style="color: #36c; font-style: italic">class</span>=<span style="color: #093">"alfresco.extension.de.evaluators.IsUserInRole"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">property</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"slingshotEvaluatorUtil"</span> <span style="color: #36c; font-style: italic">ref</span>=<span style="color: #093">"slingshot.evaluator.utility"</span> /></span>
<span style="color: #03c"></<span style="font-weight: 700">bean</span>></span>
</pre>
<p> </p>
<p><font size="4" face="Times New Roman">For this bean we also inject EvalutorUtility bean which in turn defined in Alfresco default context. The java class of our evaluator:</font></p><pre style="background: #fff; color: #000"><span style="font-weight: 700; color: #0100b6">import</span> java.util.ArrayList;
<span style="font-weight: 700; color: #0100b6">import</span> java.util.List;
<span style="font-weight: 700; color: #0100b6">import</span> java.util.Map;
<span style="font-weight: 700; color: #0100b6">import</span> org.alfresco.web.extensibility.SlingshotEvaluatorUtil;
<span style="font-weight: 700; color: #0100b6">import</span> org.springframework.extensions.surf.RequestContext;
<span style="font-weight: 700; color: #0100b6">import</span> org.springframework.extensions.surf.extensibility.impl.DefaultSubComponentEvaluator;
<span style="font-weight: 700; color: #0100b6">import</span> org.springframework.extensions.surf.support.ThreadLocalRequestContext;
public class <span style="font-style: italic">IsUserInRole</span> extends <span style="font-style: italic">DefaultSubComponentEvaluator</span> {
protected SlingshotEvaluatorUtil util <span style="font-weight: 700; color: #0100b6">=</span> <span style="color: #585cf6; font-style: italic">null</span>;
public void <span style="font-weight: 700; color: #0000a2">setSlingshotEvaluatorUtil</span>(SlingshotEvaluatorUtil <span style="font-style: italic">slingshotExtensibilityUtil</span>) {
<span style="color: #0206ff; font-style: italic">this</span><span style="font-weight: 700; color: #0100b6">.</span>util <span style="font-weight: 700; color: #0100b6">=</span> slingshotExtensibilityUtil;
}
public boolean <span style="font-weight: 700; color: #0000a2">evaluate</span>(RequestContext <span style="font-style: italic">context</span>, Map<String, String> <span style="font-style: italic">params</span>) {
final RequestContext rc <span style="font-weight: 700; color: #0100b6">=</span> ThreadLocalRequestContext<span style="font-weight: 700; color: #0100b6">.</span>getRequestContext();
List<String> groups <span style="font-weight: 700; color: #0100b6">=</span> <span style="font-weight: 700; color: #0100b6">new</span> ArrayList<String>();
String roles <span style="font-weight: 700; color: #0100b6">=</span> params<span style="font-weight: 700; color: #0100b6">.</span>get(<span style="color: #d80800">"role"</span>);
<span style="font-weight: 700; color: #0100b6">for</span> (String role <span style="font-weight: 700; color: #0100b6">:</span> roles<span style="font-weight: 700; color: #0100b6">.</span>split(<span style="color: #d80800">","</span>)) {
groups<span style="font-weight: 700; color: #0100b6">.</span>add(role);
}
boolean hasMembership <span style="font-weight: 700; color: #0100b6">=</span> <span style="color: #0206ff; font-style: italic">this</span><span style="font-weight: 700; color: #0100b6">.</span>util<span style="font-weight: 700; color: #0100b6">.</span>isMemberOfGroups(rc, groups, <span style="color: #585cf6; font-style: italic">false</span>);
<span style="font-weight: 700; color: #0100b6">return</span> hasMembership;
}
}
</pre>
<p><font size="4" face="Times New Roman">As a result we get an evaluator which checks whether logged in user is in <em>CustomConsumer </em>role and if so when hide Site Members Dashlet for this user or show otherwise. </font></p>https://soft29.ru/blog/entry/liferay-upload-files-programmatically-inLiferay upload files programmatically in javaStanislav2015-07-17T12:50:24+00:002017-03-05T14:29:45+00:00<p><font size="4" face="Times New Roman">If you have a requirement to implement a file upload form in your Liferay portlet then this article for you. </font><font size="4" face="Times New Roman">In this post I will provide a simple upload jsp form and java code snippet that handles file upload action and stores uploaded file to Liferay document library. </font></p><p><font size="4" face="Times New Roman">If you have a requirement to implement a file upload form in your Liferay portlet then this article for you. </font><font size="4" face="Times New Roman">In this post I will provide a simple upload jsp form and java code snippet that handles file upload action and stores uploaded file to Liferay document library. </font></p> <p><font size="4" face="Times New Roman">Upload form written in JSP looks like:</font></p><pre style="color: #000"><span style="color: #03c"><<span style="font-weight: 700">portlet:actionURL</span> <span style="color: #36c; font-style: italic">var</span>=<span style="color: #093">"portletActionURL"</span> /></span>
<span style="color: #03c"><<span style="font-weight: 700">aui:form</span> <span style="color: #36c; font-style: italic">action</span>=<span style="color: #093">"<%= portletActionURL %>"</span> <span style="color: #36c; font-style: italic">method</span>=<span style="color: #093">"POST"</span> <span style="color: #36c; font-style: italic">enctype</span>=<span style="color: #093">"multipart/form-data"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">aui:row</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">aui:column</span> <span style="color: #36c; font-style: italic">columnWidth</span>=<span style="color: #093">"50"</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">input</span> <span style="color: #36c; font-style: italic">type</span>=<span style="color: #093">"file"</span> <span style="color: #36c; font-style: italic">class</span>=<span style="color: #093">"multi"</span> <span style="color: #36c; font-style: italic">maxlength</span>=<span style="color: #093">"10"</span> <span style="color: #36c; font-style: italic">name</span>=<span style="color: #093">"attachedFile"</span>/></span>
<span style="color: #03c"></<span style="font-weight: 700">aui:column</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">aui:row</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">aui:button</span><span style="color: #36c; font-style: italic">-row</span>></span>
<span style="color: #03c"><<span style="font-weight: 700">aui:button</span> <span style="color: #36c; font-style: italic">type</span>=<span style="color: #093">"submit"</span> /></span>
<span style="color: #03c"></<span style="font-weight: 700">aui:button</span><span style="color: #36c; font-style: italic">-row</span>></span>
<span style="color: #03c"></<span style="font-weight: 700">aui:form</span>></span>
</pre>
<p><font size="4" face="Times New Roman"></font> </p>
<p><font size="4" face="Times New Roman">What should be mentioned here:</font></p>
<ul>
<li><font size="4" face="Times New Roman">portletActionURL – is a url generated by portlet:actionURL tag. This url points to the portlet action handler that controls all action requests. In our case <em>javax.portlet.ActionRequest</em> will be analysed whether there is file attached or not. And if file is found when it will be stored to Liferay document library.</font>
<li><font size="4" face="Times New Roman">method=”POST” – we always should specify POST method (not GET or other method) when we deal with file upload.</font>
<li><font size="4" face="Times New Roman">enctype="multipart/form-data" and <input type="file"> - if we want to develop a client side file upload and <input type=”file”> tag given, when we should always encode the content of the request with multipart/form-data.</font>
<li><font size="4" face="Times New Roman">for every upload form, a submit button should exist.</font>
<li><font size="4" face="Times New Roman">input file field has a <em>class “multi”</em>. It took me some time to find a good javascript plugin that provides multiple file upload on submit action (with the ability to remove added files). Finally I’ve found <a href="http://www.fyneworks.com/jquery/multifile/" rel="nofollow" target="_blank">jquery-multifile</a> tiny library that has simple but sufficient graphical user interface. To plug this library to input file field the <em>class=“multi”</em> should be added.</font></li></ul>
<p><font size="4" face="Times New Roman"></font> </p>
<p><font size="4" face="Times New Roman">Java portlet action handler that receives client requests on submit will have the following methods - first we need to check if the file exists in incoming request:</font></p><pre style="color: #000"> <span style="font-weight: 700">public</span> <span style="font-weight: 700">static</span> <span style="font-weight: 700">boolean</span> isAttachExistsInRequest(<span style="font-weight: 700">final</span> <span style="font-weight: 700">UploadPortletRequest</span> req) {
<span style="font-weight: 700">final</span> <span style="font-weight: 700">FileItem</span>[] arr <span style="color: #00f">=</span> req<span style="color: #00f">.</span>getMultipartParameterMap()<span style="color: #00f">.</span>get(
<span style="color: #093">"attachedFile"</span>);
<span style="color: #00f">if</span> (arr <span style="color: #00f">==</span> <span style="color: #9700cc">null</span> <span style="color: #00f">||</span> arr<span style="color: #00f">.</span>length <span style="color: #00f">==</span> <span style="color: #06f">0</span>) {
<span style="color: #00f">return</span> <span style="color: #9700cc">false</span>;
}
<span style="font-weight: 700">boolean</span> isAttachExists <span style="color: #00f">=</span> <span style="color: #9700cc">false</span>;
<span style="color: #00f">for</span> (<span style="font-weight: 700">final</span> <span style="font-weight: 700">FileItem</span> fi <span style="color: #00f">:</span> arr) {
<span style="color: #06f; font-style: italic">// fileName == "" when no file input (type=file) has no files</span>
<span style="color: #06f; font-style: italic">// uploaded</span>
<span style="color: #00f">if</span> (<span style="color: #00f">!</span>fi<span style="color: #00f">.</span>getFileName()<span style="color: #00f">.</span>isEmpty()) {
isAttachExists <span style="color: #00f">=</span> <span style="color: #9700cc">true</span>;
}
}
<span style="color: #00f">return</span> isAttachExists;
}
</pre>
<p> </p>
<p><font size="4" face="Times New Roman">Second, if we’ve found that the files exist in request when we need to store them:</font></p><pre style="color: #000"><span style="font-weight: 700">public</span> <span style="font-weight: 700">static</span> <span style="font-weight: 700">void</span> storeAttachments(<span style="font-weight: 700">final</span> <span style="font-weight: 700">UploadPortletRequest</span> req)
throws <span style="font-weight: 700">PrincipalException</span>, <span style="font-weight: 700">Exception</span> {
<span style="color: #00f">if</span> (isAttachExistsInRequest(req)) {
<span style="font-weight: 700">final</span> <span style="font-weight: 700">FileItem</span>[] arr <span style="color: #00f">=</span> req<span style="color: #00f">.</span>getMultipartParameterMap()<span style="color: #00f">.</span>get(
<span style="color: #093">"attachedFile"</span>);
<span style="font-weight: 700">final</span> <span style="font-weight: 700">long</span> repoId <span style="color: #00f">=</span> getRepositoryId(req);
<span style="font-weight: 700">final</span> <span style="font-weight: 700">long</span> folderId <span style="color: #00f">=</span> getSomeFolder();
<span style="font-weight: 700">final</span> <span style="font-weight: 700">ServiceContext</span> sc <span style="color: #00f">=</span> <span style="color: #00f">new</span> <span style="font-weight: 700">ServiceContext</span>();
<span style="color: #00f">for</span> (<span style="font-weight: 700">final</span> <span style="font-weight: 700">FileItem</span> fi <span style="color: #00f">:</span> arr) {
createFileEntry(repoId, folderId, fi<span style="color: #00f">.</span>getFileName(),
fi<span style="color: #00f">.</span>getContentType(), fi<span style="color: #00f">.</span>getSize(), fi<span style="color: #00f">.</span>getInputStream(),
sc);
}
}
}
<span style="font-weight: 700">private</span> <span style="font-weight: 700">static</span> <span style="font-weight: 700">long</span> getRepositoryId(<span style="font-weight: 700">final</span> <span style="font-weight: 700">UploadPortletRequest</span> req) {
<span style="font-weight: 700">final</span> <span style="font-weight: 700">ThemeDisplay</span> themeDisplay <span style="color: #00f">=</span> (<span style="font-weight: 700">ThemeDisplay</span>) req
.getAttribute(<span style="font-weight: 700">WebKeys</span><span style="color: #6782d3"><span style="color: #00f">.</span>THEME_DISPLAY</span>);
<span style="color: #00f">return</span> themeDisplay<span style="color: #00f">.</span>getScopeGroupId();
}
<span style="font-weight: 700">public</span> <span style="font-weight: 700">static</span> <span style="font-weight: 700">void</span> createFileEntry(<span style="font-weight: 700">final</span> <span style="font-weight: 700">long</span> repositoryId,
<span style="font-weight: 700">final</span> <span style="font-weight: 700">long</span> folderId, <span style="font-weight: 700">final</span> <span style="font-weight: 700">String</span> fileName, <span style="font-weight: 700">final</span> <span style="font-weight: 700">String</span> mimeType,
<span style="font-weight: 700">final</span> <span style="font-weight: 700">long</span> fileLength, <span style="font-weight: 700">final</span> <span style="font-weight: 700">InputStream</span> is,
<span style="font-weight: 700">final</span> <span style="font-weight: 700">ServiceContext</span> serviceContext
) {
<span style="color: #00f">try</span> {
<span style="font-weight: 700">final</span> <span style="font-weight: 700">FileEntry</span> entry <span style="color: #00f">=</span> <span style="font-weight: 700">DLAppServiceUtil</span><span style="color: #00f">.</span>addFileEntry(repositoryId,
folderId, fileName, mimeType, fileName, <span style="color: #9700cc">null</span>, <span style="color: #9700cc">null</span>, is,
fileLength, serviceContext);
} <span style="color: #00f">catch</span> (<span style="font-weight: 700">final</span> <span style="font-weight: 700">Exception</span> e) {
log<span style="color: #00f">.</span>error(<span style="color: #093">"Utils::createFileEntry Exception"</span>, e);
}
}
</pre>
<p> </p>
<p><font size="4" face="Times New Roman">And in the end here is the final look of the file uploader:</font></p>
<p><a href="https://soft29.ru/blog/mediaresource/91f4fa92-c50f-4a78-b8bf-6b31958e8fca"><img title="liferay development" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="liferay development" src="https://soft29.ru/blog/mediaresource/6a153039-a683-4880-9256-28e50629177c" width="239" height="168"></a></p>
<p><font size="4" face="Times New Roman"></font> </p>
<p><font size="4" face="Times New Roman"></font></p>https://soft29.ru/blog/entry/liferay-execute-java-code-asLiferay: execute java code as administrator userStanislav2015-07-01T13:20:51+00:002017-03-05T14:30:17+00:00<p><font size="4" face="Times New Roman">During Liferay java code development you may need to run your code as administrator user. This can happen when some user logged in with a restricted role, but it is required to execute an user action with higher privileges (admin or others). The examples of these user actions: grant permissions to some folder or file, search for content in spaces which the user does not belong to, perform system actions (create new role in portal etc.) and many others. In this blog post I’ll show how to make it possible. </font></p><p><font size="4" face="Times New Roman">During Liferay java code development you may need to run your code as administrator user. This can happen when some user logged in with a restricted role, but it is required to execute an user action with higher privileges (admin or others). The examples of these user actions: grant permissions to some folder or file, search for content in spaces which the user does not belong to, perform system actions (create new role in portal etc.) and many others. In this blog post I’ll show how to make it possible. </font></p> <p><font size="4" face="Times New Roman">The main approach will have the following steps: </font></p> <ol> <li><font size="4" face="Times New Roman">Save <em>com.liferay.portal.security.permission.PermissionChecker</em> for current user.</font> <li><font size="4" face="Times New Roman">Get admin user in portal.</font> <li><font size="4" face="Times New Roman">Create a new <em>PermissionChecker </em>for retrieved admin user.</font> <li><font size="4" face="Times New Roman">Substitute current <em>PermissionChecker </em>with the just created <em>PermissionChecker</em>.<em> </em></font> <li><font size="4" face="Times New Roman">Execute<em> </em>required user action.</font> <li><font size="4" face="Times New Roman">Revert current admin PermissionChecker to the one of step 1. </font></li></ol> <p><font size="4" face="Times New Roman">In previous <a title="liferay get administrator user programmatically" href="https://soft29.ru/blog/entry/liferay-get-administrator-user-programmatically">article</a> I presented how to get Liferay administrator user. Having this retrieved user we can perform step 2. </font></p> <p><font size="4" face="Times New Roman">As the result we’ll have this function:</font></p> <p><font size="4" face="Times New Roman"> </font></p><pre style="background: #fff; color: #000"><span style="font-weight: 700; color: #0100b6">import</span> com.liferay.portal.security.auth.PrincipalThreadLocal;
<span style="font-weight: 700; color: #0100b6">import</span> com.liferay.portal.security.permission.PermissionChecker;
<span style="font-weight: 700; color: #0100b6">import</span> com.liferay.portal.security.permission.PermissionCheckerFactoryUtil;
<span style="font-weight: 700; color: #0100b6">import</span> com.liferay.portal.security.permission.PermissionThreadLocal;
private void asAdmin() {
<span style="font-weight: 700; color: #0100b6">try</span> {
PermissionChecker currentPermisionChecker <span style="font-weight: 700; color: #0100b6">=</span> PermissionThreadLocal<span style="font-weight: 700; color: #0100b6">.</span>getPermissionChecker();
User admin <span style="font-weight: 700; color: #0100b6">=</span> Utils<span style="font-weight: 700; color: #0100b6">.</span>getAdmin();
PrincipalThreadLocal<span style="font-weight: 700; color: #0100b6">.</span>setName(admin<span style="font-weight: 700; color: #0100b6">.</span>getUserId());
<span style="color: #00b418">// Create and set permission checker</span>
PermissionChecker permissionChecker <span style="font-weight: 700; color: #0100b6">=</span> PermissionCheckerFactoryUtil<span style="font-weight: 700; color: #0100b6">.</span>create(admin);
PermissionThreadLocal<span style="font-weight: 700; color: #0100b6">.</span>setPermissionChecker(permissionChecker);
<span style="color: #00b418">/* ...
user action that requires admin privileges */</span>
<span style="color: #00b418">// Set original checker</span>
PermissionThreadLocal<span style="font-weight: 700; color: #0100b6">.</span>setPermissionChecker(currentPermisionChecker);
} <span style="font-weight: 700; color: #0100b6">catch</span> (Exception e) {
e<span style="font-weight: 700; color: #0100b6">.</span>printStackTrace();
}
}
</pre>
<p> </p>
<p><font size="4" face="Times New Roman">Surely, user action in this function can be passed as a parameter that will enable to reuse this method many times. Thank you for interest.</font></p>https://soft29.ru/blog/entry/liferay-get-administrator-user-programmaticallyLiferay get administrator user programmatically in JavaStanislav2015-06-22T15:38:15+00:002017-03-05T14:30:34+00:00<p><font size="4" face="Times New Roman">If you need to retrieve a Liferay User with Administrator role in your Java code, then this article for you. </font></p> <p><font size="4" face="Times New Roman">If you need to retrieve a Liferay User with Administrator role in your Java code, then this article for you. To make it work we'll use a few defined Liferay's LocalServiceUtil classes. At the time of this article writing I've used Liferay 6.x version, but LocalServiceUtils appeared since 5.0 version, and PortalUtil class exist since 6.0 version only (can be easily substituted). That means that proposed approach will work on Liferay 5.0 and higher. <br>At the beginning we need to get an Administrator role within some Liferay company:</font></p> <p><font size="4" face="Times New Roman"> <br></p></font><pre style="background: #fff; color: #000"> public static Role getRoleById(final long companyId, final String roleStrId) {
<span style="font-weight: 700; color: #0100b6">try</span> {
<span style="font-weight: 700; color: #0100b6">return</span> RoleLocalServiceUtil<span style="font-weight: 700; color: #0100b6">.</span>getRole(companyId, roleStrId);
} <span style="font-weight: 700; color: #0100b6">catch</span> (final Exception e) {
log<span style="font-weight: 700; color: #0100b6">.</span>error(<span style="color: #d80800">"Utils::getRoleById Exception"</span>, e);
}
<span style="font-weight: 700; color: #0100b6">return</span> <span style="color: #585cf6; font-style: italic">null</span>;
}
</pre>
<p><font size="4" face="Times New Roman"></font> </p>
<p><font size="4" face="Times New Roman">To get a default companyId for current portal we can use <em>com.liferay.portal.util.PortalUtil</em> (since Liferay 6.0) or specify this companyId directly. Also <em>RoleConstants.ADMINISTRATOR</em> used to refer to predefined Liferay roles:</font></p><pre style="background: #fff; color: #000"><span style="font-weight: 700; color: #0100b6">import</span> com.liferay.portal.service.UserLocalServiceUtil;
<span style="font-weight: 700; color: #0100b6">import</span> com.liferay.portal.util.PortalUtil;
<span style="font-weight: 700; color: #0100b6">import</span> com.liferay.portal.model.RoleConstants;
public static User getAdmin() {
final long companyId <span style="font-weight: 700; color: #0100b6">=</span> PortalUtil<span style="font-weight: 700; color: #0100b6">.</span>getDefaultCompanyId();
Role role <span style="font-weight: 700; color: #0100b6">=</span> <span style="color: #585cf6; font-style: italic">null</span>;
<span style="font-weight: 700; color: #0100b6">try</span> {
role <span style="font-weight: 700; color: #0100b6">=</span> getRoleById(companyId, RoleConstants<span style="color: #c5060b; font-style: italic"><span style="font-weight: 700; color: #0100b6">.</span>ADMINISTRATOR</span>);
<span style="font-weight: 700; color: #0100b6">for</span> (final User admin <span style="font-weight: 700; color: #0100b6">:</span> UserLocalServiceUtil<span style="font-weight: 700; color: #0100b6">.</span>getRoleUsers(role
.getRoleId())) {
<span style="font-weight: 700; color: #0100b6">return</span> admin;
}
} <span style="font-weight: 700; color: #0100b6">catch</span> (final Exception e) {
log<span style="font-weight: 700; color: #0100b6">.</span>error(<span style="color: #d80800">"Utils::getAdmin Exception"</span>, e);
}
<span style="font-weight: 700; color: #0100b6">return</span> <span style="color: #585cf6; font-style: italic">null</span>;
}
</pre>
<p><font size="4" face="Times New Roman">Within this function we retrieve a list of all users with Administrator role and then return the first admin as a result of this function. That’s it.</font></p>