Create a new portal
Prerequisite: You must be familiar with the Syntax used in Tutorials and have already created an extension.
In this tutorial, we will see what is required to create a new portal from scratch.
You still need to check the Portal XML reference as it
provides all customization possibilities, while
this tutorial does not.
You may read the Portal
customization Overview if the below tutorial is too detailled
for you.
Portal declaration
First you will have to declare that portal in XML.
-
You will specify, which users can access it and which cannot. But rather than specifying the Users, you will specify the profiles.
-
Note that the console, is a portal with a specific id (id=
backoffice
), but it works the same in terms of allowing or denying profiles.
Default declaration
Here is how it is declared within iTop out of the box:
- itop_design
-
<portals> <portal id="itop-portal"> <!-- This is the User Portal --> <...> <allow/> <!-- No specified profiles, means all allowed --> <deny/> <!-- No restriction, which explain why normal console users have access --> </portal> <portal id="backoffice"> <!-- This is the Console --> <...> <allow/> <!-- allow or deny can be used, combination is supported but wierd --> <deny> <!-- Users having one of the below profiles, will be denied --> <profile id="Portal user"/> </deny> <!-- deny takes precedence on allow --> </portal> </portals>
What do we want?
In this example, we will imagine that we want to create a new
portal, but keep also the standard User Portal
(id=itop-portal
) and of course keep the Console
When you start to have more than 2 portals, it's a bit more complicated to define profiles. You have to imagine all the cases, but a combination of allowed and denied profiles, can quickly end up in deadlock
The new portal must be restricted to users having a newly
created profile Extension Publisher
. Those users must
not have access to the console and may or may not
have access to the standard itop-portal, depending on if they have
Portal user
profile or not.
Users of my company, do not have Portal user
profile otherwise they could not access the console, which they
need to. But they still can access the itop-portal. Let's suppose
that I want to allow them to access the new portal also. In that
case I will create a new profile “XX employee” where XX is my
company name.
Possible declaration
Here is one way to declare the 3 portals, to achive the above requirements:
- itop_design
-
<portals> <portal id="backoffice" _delta="must_exist"> <deny _delta="redefine"> <profile id="Portal user"/> <profile id="Extension Publisher"/> </deny> </portal> <portal id="itop-portal" _delta="must_exist"> <allow _delta="redefine"> <profile id="Portal user"/> <profile id="XX employee"/> </allow> </portal> <portal id="extension-publisher-portal" _delta="define"> <url>pages/exec.php?exec_module=itop-portal-base&exec_page=index.php&portal_id=extension-publisher-portal</url> <rank>1.0</rank> <handler/> <allow> <profile id="Extension Publisher"/> <profile id="XX employee"/> </allow> <deny/> </portal> </portals>
Portal design
You need to specify:
-
classes
: which classes and objects will be displayed in the portal -
forms
: for each class, fields visibility and layout in creation, vizualisation and edition -
bricks
: define the menus and bricks allowing to navigate
Classes / Scopes
Scopes are required to specify explicitly the classes which will
be used in the portal. Unless you have defined a scope for a given
class, that class will never be visible in the
portal, regardless of your profile. You could well be
Administrator
that it would not be enough.
What user can see
-
A scope will define within a class, the instances of objects that the users can see.
-
The below example specify a read scope, which is applicable to all users accessing the portal, not because the id is “all” but because there is no <allowed_profiles> tag specified for this scope, which means all profiles are allowed.
- itop_design / module_designs / module_design@itop-portal / classes / class@Ticket / scopes
-
<scope id="all"> <oql_view><![CDATA[SELECT Ticket WHERE (Condition1) ]]></oql_view> </scope>
-
a scope can be limited to users having a particular profile, with the <allowed_profiles> tag.
-
If multiple scopes applies to a user, then they are combined with a UNION.
Example users with profile Portal power user
can
see Tickets defined by the scope “power”
- itop_design / module_designs / module_design@itop-portal / classes / class@Ticket / scopes
-
<scope id="power"> <oql_view><![CDATA[SELECT Ticket WHERE (Condition2) ]]></oql_view> <allowed_profiles> <allowed_profile id="Portal power user"/> </allowed_profiles> <scope>
But they can see also tickets included in the scope “all”, as a result they will see any Ticket returned by this OQL
SELECT Ticket WHERE (Condition1) UNION SELECT Ticket WHERE (Condition2)
Allowed organizations
limitation put on the
users:-
A portal user, might be restricted to his own organization, by setting
Allowed organizations
on his iTop user. That's quite common, especially for Service providers. Nevertheless, thisOrganizations
restriction defined at the user level, can be overwritten by a portal scope.-
It's not automatic and the scope must explicitely mention that the allowed organizations of the users must be ignored.
-
It applies for this scope and this one only.
-
An example of this tag can be found on the standard “User portal”, to enable users to see the catalogue of Services, despite they do not belong to the user's organization
-
- itop_design / module_designs / module_design@itop-portal / classes / class@Service / scopes
-
<class id="Service"> <scopes> <scope id="all"> <oql_view> <![CDATA[ SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :current_contact->org_id AND s.status != 'obsolete' ]]> </oql_view> <ignore_silos>true</ignore_silos> </scope> </scopes> </class>
Tip 1 Because of the UserProfileBrick which must be activated on any portal, the below scope must also be declared on every portal
- itop_design / module_designs / module_design@my-portal
-
<class id="User"> <scopes> <scope id="all"> <oql_view> <![CDATA[SELECT User AS U JOIN Person AS P ON U.contactid=P.id WHERE P.id = :current_contact_id]]> </oql_view> </scope> </scopes> </class>
Tip 2
read
on the special group All
classes (*)- itop_design / user_rights / profiles / profile@my-new-profiles / groups
-
<group id="*"> <actions> <action id="action:read">allow</action> </actions> </group>
Sidenote for Combodo customers: Classes Attachments
and
TagSetFieldDataFor_class__field_code
cannot be put in groups in the ITSM Designer in January 2021.
Tip 3
Tip 4
What user can modify
-
The scope can also define the objects that the users can modify. A user is never alloweds to modify an object that he cannot see, so when specifying what he can modify, just specify additional restriction if needed. For example if the user should be allowed to modify any Ticket that he can see, then
- itop_design / module_designs / module_design@itop-portal / classes / class@Ticket / scopes
-
<scope id="all"> <oql_edit><![CDATA[SELECT Ticket]]></oql_edit>
will do the job, regardless of the <oql_view> queries applying to the user.
Other example with more restriction on the Tickets which can be modified versus those which can be viewed
- itop_design / module_designs / module_design@itop-portal / classes / class@Ticket / scopes
-
<scope id="all"> <oql_view><![CDATA[SELECT Ticket WHERE (Condition3) ]]></oql_view> <oql_edit><![CDATA[SELECT Ticket WHERE (Condition4) ]]></oql_edit>
Then users will be only allowed to modify Tickets returned by this OQL
SELECT Ticket WHERE (Condition1) AND (Condition2)
Classes / others
Deny transitions
Allow to prevent users with some profile, to perform transitions that they are allowed to do in general, but that should not do it in this Portal, but rather on the Console or on another portal.
FIXME Propose an example of denying transitions on Ticket for
Support Agent
Lists display
This part allow to specify which fields from a given class, should be displayed when those objects are inside the form of another class, as a LinkedSet or LinkedSetIndirect attributes. If not specified, then the list of fields displayed will be the same as in the console, which may be fine.
FIXME add example of contacts within Ticket with screenshot
Forms
A form specify how objects of a class are displayed in the portal, including fields presentation and their flags: read only, writable, mandatory.
For a given class, you can have only one single
form, defining how to visualize the objects.
-
show or hide some fields based on the user profiles
-
show or hide some fields based on object status
In fact you can have different forms per object, assuming they are used in different situations (modes). You can have at maximum:
-
one form for object creation,
-
one form for modification,
-
one form for visualization,
-
and a form for each transition.
Those forms can be specific to a case or reuse in multiple situations.
Bricks
UrlMaker
UrlMaker classes allows iTop to build URLs pointing to a specific GUI (eg. your new portal). If no UrlMaker is registered for the portal, URL will be malformed in several places (eg. in notifications).
What you need to do:
-
Create 2 dedicated PHP classes that will generate proper (view or edit) URLs for that portal
-
Register this class in the portal
-
Register those classes globally so you can generate URLs to that portal in notifications
Here is the corresponding PHP snippet:
<?php use Combodo\iTop\Portal\UrlMaker\AbstractPortalUrlMaker; require_once APPROOT.'/lib/autoload.php'; /** * Hyperlinks to the "edition" of the object (vs view) */ class MyPortalEditUrlMaker extends AbstractPortalUrlMaker { const PORTAL_ID = 'my-portal'; } /** * Hyperlinks to the "view" of the object (vs edition) */ class MyPortalViewUrlMaker extends MyPortalEditUrlMaker { /** * @inheritDoc */ public static function MakeObjectURL($sClass, $iId) { return static::PrepareObjectURL($sClass, $iId, 'view'); } } // Default portal hyperlink (for notifications) is the view hyperlink DBObject::RegisterURLMakerClass('my-portal', MyPortalViewUrlMaker::class);
And in the portal XML definition you'll get:
<module_design id="my-portal" _delta="define"> <properties> <urlmaker_class>MyPortalViewUrlMaker</urlmaker_class> <!-- ... --> </properties> <!-- ... --> </module_design>
Full XML
- itop_design / module_designs
-
<module_design id="my-portal" _delta="define"> <properties> <name>Developers portal</name> <urlmaker_class>MyPortalViewUrlMaker</urlmaker_class> <triggers_query> <![CDATA[SELECT TriggerOnPortalUpdate AS t WHERE t.target_class IN (:parent_classes)]]> </triggers_query> </properties> <classes> <class id="User"> <scopes> <scope id="all"> <oql_view> <![CDATA[SELECT User AS U JOIN Person AS P ON U.contactid=P.id WHERE P.id = :current_contact_id]]> </oql_view> </scope> </scopes> </class> <class id="Organization" _delta="define"> <scopes> <scope id="all"> <oql_view><![CDATA[SELECT Organization WHERE id = :current_contact->org_id]]></oql_view> </scope> </scopes> </class> <class id="Contact" _delta="define"> <scopes> <scope id="all"> <oql_view><![CDATA[SELECT Contact WHERE org_id = :current_contact->org_id]]></oql_view> </scope> </scopes> </class> <class id="Extension" _delta="define"> <scopes> <scope id="all"> <oql_view><![CDATA[SELECT Extension WHERE org_id = :current_contact->org_id]]></oql_view> <oql_edit><![CDATA[SELECT Extension]]></oql_edit> </scope> </scopes> </class> <class id="TargetExtension" _delta="define"> <scopes> <scope id="all"> <oql_view><![CDATA[SELECT TE FROM TargetExtension AS TE JOIN Extension AS E ON TE.extension_id = E.id WHERE E.org_id = :current_contact->org_id]]></oql_view> <oql_edit><![CDATA[SELECT TargetExtension]]></oql_edit> </scope> </scopes> <lists> <list id="list"> <items> <item id="target_date"> <rank>2</rank> </item> <item id="step"> <rank>3</rank> </item> </items> </list> <list id="default"> <items> <item id="extension_id"> <rank>2</rank> </item> <item id="step"> <rank>3</rank> </item> </items> </list> </lists> </class> <class id="LicenseType" _delta="define"> <scopes> <scope id="all"> <oql_view><![CDATA[SELECT LicenseType]]></oql_view> </scope> </scopes> </class> </classes> <action_rules> <action_rule id="contact-to-extension"> <source_oql><![CDATA[SELECT Contact AS C WHERE C.id = :current_contact_id]]></source_oql> <presets> <preset id="1">copy(org_id, org_id)</preset> <preset id="2">set(person_id, $current_contact_id$)</preset> <preset id="3">set(category, public)</preset> <preset id="4">set(status, beta)</preset> <preset id="5">set(acquisition_cost, 0)</preset> </presets> </action_rule> <action_rule id="extension-to-targetextension"> <source_class>Extension</source_class> <presets> <preset id="1">copy(id, extension_id)</preset> <preset id="2">set(freeze_date,$current_date$)</preset> </presets> </action_rule> </action_rules> <navigation_rules> <navigation_rule id="go-to-extension" xsi:type="go-to-object"> <!-- Mandatory, opens the first result from the OQL. Result may vary ¯\_(ツ)_/¯ but ":this" available! --> <oql>SELECT Extension WHERE id = :this->id</oql> <!-- Optional, mode of the object form, either view|edit --> <mode>edit</mode> <!-- Optional, how to open the object form, replace the current form (current), in a modal (modal) or in the page (page) --> <opening_target>current</opening_target> </navigation_rule> <navigation_rule id="go-to-extension-from-targetextension" xsi:type="go-to-object"> <!-- Mandatory, opens the first result from the OQL. Result may vary ¯\_(ツ)_/¯ but ":this" available! --> <oql>SELECT Extension WHERE id = :this->extension_id</oql> <!-- Optional, mode of the object form, either view|edit --> <mode>edit</mode> <!-- Optional, how to open the object form, replace the current form (current), in a modal (modal) or in the page (page) --> <opening_target>current</opening_target> </navigation_rule> <navigation_rule id="go-to-workshop" xsi:type="go-to-browse-brick"> <id>workshop</id> <browse_mode>mosaic</browse_mode> </navigation_rule> <navigation_rule id="go-to-version" xsi:type="go-to-object"> <!-- Mandatory, opens the first result from the OQL. Result may vary ¯\_(ツ)_/¯ but ":this" available! --> <oql>SELECT TargetExtension WHERE id = :this->id</oql> <!-- Optional, mode of the object form, either view|edit --> <mode>edit</mode> <!-- Optional, how to open the object form, replace the current form (current), in a modal (modal) or in the page (page) --> <opening_target>current</opening_target> </navigation_rule> <navigation_rule id="go-to-new-version" xsi:type="go-to-brick"> <route> <id>p_create_brick</id> <params> <param id="sBrickId">new-version</param> </params> </route> </navigation_rule> </navigation_rules> <forms> <form id="extension-create" _delta="define"> <class>Extension</class> <properties> <display_mode>compact</display_mode> <navigation_rules> <submit> <default>go-to-workshop</default> </submit> </navigation_rules> </properties> <fields/> <twig> <div class="row"> <div class="col-sm-4"> <fieldset> <legend>General information</legend> <div class="form_field" data-field-id="org_id_friendlyname" data-field-flags="read_only"/> <div class="form_field" data-field-id="name" data-field-flags="mandatory"/> <div class="form_field" data-field-id="build_identifier" data-field-flags="mandatory"/> <div class="form_field" data-field-id="licensetype_id" data-field-flags="mandatory"/> </fieldset> </div> <div class="col-sm-8"> <fieldset> <legend>Description</legend> <div class="form_field" data-field-id="short" data-field-flags="mandatory" data-field-display-mode="dense"/> <div class="form_field" data-field-id="description" data-field-flags="mandatory"/> <div class="form_field" data-field-id="wiki" data-field-flags="mandatory" data-field-display-mode="dense"/> <div class="form_field" data-field-id="repository_url" data-field-flags="" data-field-display-mode="dense"/> <div class="form_field" data-field-id="images" data-field-flags=""/> </fieldset> </div> </div> <br/> <div class="alert alert-info"> <div><b>Important:</b> Upload the cover image as an attachment. it will be used to represent the extension on iTop Hub.</div> <ul> <li>Accepted formats are JPG/PNG</li> <li>File must be named <b><Extension Code>-icon.jpg</b> or <b><Extension Code>-icon.png</b></li> <li>Size must be 256x170 pixels</li> </ul> </div> </twig> <modes> <mode id="create"/> </modes> </form> <form id="extension-edit" _delta="define"> <class>Extension</class> <properties> <display_mode>compact</display_mode> </properties> <fields/> <twig> <div class="row"> <div class="col-sm-6"> <fieldset> <legend>General information</legend> <div class="form_field" data-field-id="name" data-field-flags="mandatory"/> <div class="form_field" data-field-id="build_identifier" data-field-flags="read_only"/> <div class="form_field" data-field-id="licensetype_id" data-field-flags="mandatory"/> <div class="form_field" data-field-id="short" data-field-flags="mandatory" data-field-display-mode="dense"/> <div class="form_field" data-field-id="description" data-field-flags="mandatory"/> <div class="form_field" data-field-id="wiki" data-field-flags="mandatory" data-field-display-mode="dense"/> <div class="form_field" data-field-id="repository_url" data-field-flags="" data-field-display-mode="dense"/> <div class="form_field" data-field-id="images" data-field-flags=""/> <div class="form_field" data-field-id="tags" data-field-flags=""/> <div class="form_field" data-field-id="icon" data-field-flags="read_only"/> <div class="form_field" data-field-id="publication_date" data-field-flags="read_only" data-field-display-mode="dense"/> <div class="form_field" data-field-id="last_publication" data-field-flags="read_only" data-field-display-mode="dense"/> <div class="form_field" data-field-id="last_update" data-field-flags="read_only" data-field-display-mode="dense"/> <div class="form_field" data-field-id="org_id_friendlyname" data-field-flags="read_only"/> </fieldset> </div> <div class="col-sm-6"> <div class="form_field" data-field-id="targetextensions_list" data-field-flags="read_only" data-field-opened="true"/> <div class="form_field" data-field-id="log" data-field-flags=""/> </div> </div> <div class="alert alert-info"> <div><b>Important:</b> To change the cover image, upload it as an attachment. it will be used to represent the extension on iTop Hub.</div> <ul> <li>Accepted formats are JPG/PNG</li> <li>File must be named <b><Extension Code>-icon.jpg</b> or <b><Extension Code>-icon.png</b></li> <li>Size must be 256x170 pixels</li> </ul> </div> </twig> <modes> <mode id="edit"/> <mode id="view"/> </modes> </form> <form id="targetextension-create" _delta="define"> <class>TargetExtension</class> <properties> <display_mode>dense</display_mode> <always_show_submit>true</always_show_submit> <navigation_rules> <submit> <default>go-to-extension-from-targetextension</default> </submit> </navigation_rules> </properties> <fields/> <twig> <div class="row"> <div class="col-sm-4"> <fieldset> <legend>General information</legend> <div class="form_field" data-field-id="extension_id_friendlyname" data-field-flags=""/> <div class="form_field" data-field-id="extension_build_identifier" data-field-flags="read_only"/> <div class="form_field" data-field-id="version" data-field-flags=""/> <div class="form_field" data-field-id="itop_min_version" data-field-flags=""/> <div class="form_field" data-field-id="itop_max_version" data-field-flags=""/> <div class="form_field" data-field-id="step" data-field-flags="read_only"/> <div class="form_field" data-field-id="freeze_date" data-field-flags="read_only"/> </fieldset> </div> <div class="col-sm-8"> <div class="form_field" data-field-id="changelog" data-field-flags=""/> </div> </div> <br/> <div class="alert alert-info"> <div><b>Important:</b> Upload the zip package as an attachment.</div> <ul> <li>Accepted formats is ZIP only</li> <li>File must be named <b><Extension Code>-X.Y.Z-ABC.zip</b></li> <li>X, Y, Z must be version numbers (decimals) eg. 2.0.1</li> <li>ABC must be the build number (decimals) eg. 541 (internal build number) or 20201004120000 (date time)</li> </ul> </div> </twig> <modes> <mode id="create"/> </modes> </form> <form id="targetextension-edit" _delta="define"> <class>TargetExtension</class> <properties> <display_mode>dense</display_mode> <always_show_submit>true</always_show_submit> </properties> <fields/> <twig> <div class="row"> <div class="col-sm-4"> <fieldset> <legend>General information</legend> <div class="form_field" data-field-id="extension_id_friendlyname"/> <div class="form_field" data-field-id="extension_build_identifier" data-field-flags="read_only"/> <div class="form_field" data-field-id="version" data-field-flags=""/> <div class="form_field" data-field-id="itop_min_version" data-field-flags=""/> <div class="form_field" data-field-id="itop_max_version" data-field-flags=""/> <div class="form_field" data-field-id="step" data-field-flags="read_only"/> <div class="form_field" data-field-id="freeze_date" data-field-flags="read_only"/> <div class="form_field" data-field-id="target_date" data-field-flags="read_only"/> <div class="form_field" data-field-id="last_update" data-field-flags="read_only"/> </fieldset> </div> <div class="col-sm-8"> <div class="form_field" data-field-id="changelog" data-field-flags=""/> <div class="form_field" data-field-id="package" data-field-flags=""/> </div> </div> <br/> <div class="alert alert-info"> <div><b>Important:</b> To change the zip package, upload it as an attachment.</div> <ul> <li>Accepted formats is ZIP only</li> <li>File must be named <b><Extension Code>-X.Y.Z-ABC.zip</b></li> <li>X, Y, Z must be version numbers (decimals) eg. 2.0.1</li> <li>ABC must be the build number (decimals) eg. 541 (internal build number) or 20201004120000 (date time)</li> </ul> </div> </twig> <modes> <mode id="edit"/> <mode id="view"/> </modes> </form> <form id="targetextension-stimulus" _delta="define"> <class>TargetExtension</class> <properties> <navigation_rules> <submit> <default>go-to-version</default> </submit> <cancel> <default>go-to-version</default> </cancel> </navigation_rules> </properties> <fields/> <twig> <div> <div class="form_field" data-field-id="log" data-field-flags="must_prompt"/> </div> </twig> <modes> <mode id="apply_stimulus"> <stimuli> <stimulus id="ev_cancel"/> <stimulus id="ev_open"/> </stimuli> </mode> </modes> </form> <form id="targetextension-submit-stimulus" _delta="define"> <class>TargetExtension</class> <properties> <display_mode>dense</display_mode> <navigation_rules> <submit> <default>go-to-version</default> </submit> <cancel> <default>go-to-version</default> </cancel> </navigation_rules> </properties> <fields/> <twig> <div class="row"> <div class="col-sm-6"> <div class="form_field" data-field-id="itop_min_version" data-field-flags="must_prompt"/> </div> <div class="col-sm-6"> <div class="form_field" data-field-id="itop_max_version" data-field-flags="must_prompt"/> </div> </div> <div> <div class="form_field" data-field-id="changelog" data-field-flags="must_prompt"/> <div class="form_field" data-field-id="log" data-field-flags="must_prompt"/> </div> </twig> <modes> <mode id="apply_stimulus"> <stimuli> <stimulus id="ev_request_validation"/> <stimulus id="ev_republish"/> </stimuli> </mode> </modes> </form> <form id="contact" _delta="define"> <class>Person</class> <properties> <display_mode>compact</display_mode> </properties> <fields/> <twig> <div class="row"> <div class="form_field" data-field-id="civility" data-field-flags="read_only"/> <div class="form_field" data-field-id="first_name" data-field-flags="read_only"/> <div class="form_field" data-field-id="name" data-field-flags="read_only"/> <div class="form_field" data-field-id="email" data-field-flags="read_only"/> <div class="form_field" data-field-id="org_id_friendlyname" data-field-flags="read_only"/> </div> </twig> <modes> <mode id="view"/> </modes> </form> <form id="organization" _delta="define"> <class>Organization</class> <fields/> <twig> <div class="form_field" data-field-id="name" data-field-flags="read_only"/> </twig> </form> </forms> <bricks> <brick id="workshop" xsi:type="Combodo\iTop\Portal\Brick\BrowseBrick" _delta="define"> <active>true</active> <rank> <default>10</default> </rank> <width>6</width> <title> <default>Workshop</default> </title> <description>Modify your Extensions, add new versions, request publication,...</description> <decoration_class> <default>fas fa-drafting-compass fa-2x</default> </decoration_class> <levels> <level id="1"> <oql><![CDATA[SELECT Extension WHERE status !='obsolete']]></oql> <description_att>short</description_att> <image_att>icon</image_att> <fields> <field id="short"/> <field id="publication_date"/> <field id="last_publication"/> <field id="last_update"/> <field id="status"/> <field id="description"> <hidden>true</hidden> </field> </fields> <actions> <action id="edit" xsi:type="edit"> <rank>1</rank> </action> </actions> </level> </levels> <browse_modes> <availables> <mode id="mosaic"/> <mode id="list"/> </availables> <default>mosaic</default> </browse_modes> <data_loading>lazy</data_loading> </brick> <brick id="new-extension" xsi:type="Combodo\iTop\Portal\Brick\CreateBrick" _delta="define"> <active>true</active> <rank> <default>20</default> </rank> <width>6</width> <title> <default>New extension</default> </title> <description>Submit a brand new extension (new version of existing extensions can be added through the workshop)</description> <decoration_class> <default>fas fa-plus fa-2x</default> </decoration_class> <modal>true</modal> <class>Extension</class> <rules> <rule id="contact-to-extension"/> </rules> </brick> <!-- <brick id="new-version" xsi:type="Combodo\iTop\Portal\Brick\CreateBrick" _delta="define"> <active>false</active> <rank> <default>40</default> </rank> <width>6</width> <title> <default>New version</default> </title> <description>Submit a new version on the current extension</description> <decoration_class> <default>fas fa-plus fa-2x</default> </decoration_class> <modal>true</modal> <class>Extension</class> <rules> <rule id="extension-to-targetextension"/> </rules> </brick>--> <brick id="communication" xsi:type="Combodo\iTop\Portal\Brick\CommunicationBrick" _delta="define"> <rank>1</rank> <!-- float --> <oql><![CDATA[SELECT Communication WHERE portals MATCHES ('developer') AND status = 'ongoing' AND start_date <= :now]]></oql> <height>15</height> <!-- integer , size in em --> <width>12</width> <!-- integer , must be between 1 and 12 --> <title>Portal:Communications</title> </brick> <brick id="user-profile" xsi:type="Combodo\iTop\Portal\Brick\UserProfileBrick"> <rank> <default>1</default> </rank> <title> <default>Brick:Portal:UserProfile:Navigation:Dropdown:MyProfil</default> </title> <decoration_class> <default>fa fa-user fa-2x</default> </decoration_class> <!-- Show / hide some of the user profile forms by setting the tag value to true|false --> <!--<show_picture_form>true</show_picture_form>--> <!--<show_preferences_form>true</show_preferences_form>--> <!--<show_password_form>true</show_password_form>--> <form> <fields/> <twig> <div class="form_field" data-field-id="first_name" data-field-flags="read_only"/> <div class="form_field" data-field-id="name" data-field-flags="read_only"/> <div class="form_field" data-field-id="org_id" data-field-flags="read_only"/> <div class="form_field" data-field-id="email"/> <div class="form_field" data-field-id="phone"/> <div class="form_field" data-field-id="function"/> </twig> </form> </brick> </bricks> </module_design>