Sidebar

Using iTop

Creating your iTop

iTop Customization

"How to" examples
DataModel

User Interface

Automation & Ticket management

Portal Customization

Create a new portal

Prerequisite: You must be familiar with the Syntax used in Tutorials and have already created an extension.

learning:
Create a new portal from scratch
level:
Advanced
domains:
XML
min version:
2.7.0

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&amp;exec_page=index.php&amp;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)
A scope cannot enlarge the rights provides by the user's profiles, it can only restrict them further.
But a scope can bypass the 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, this Organizations 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

If portal users need to add attachments or edit TagSet fields, portal users will have to be allowed for 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

A portal user must always be allowed on his own organization, otherwise he cannot access the Portal.

Tip 4

If the class Person has an AttributeImage without a default value, this crashes the User Portal

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)
You may define a Portal, in which a user could create or modify an object which he could not see anymore just after his creation/modification, because the object would not/no more be included in his scopes

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.

This means that you cannot:
  • 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:

  1. Create 2 dedicated PHP classes that will generate proper (view or edit) URLs for that portal
  2. Register this class in the portal
  3. 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-&gt;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-&gt;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-&gt;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>&lt;Extension Code&gt;-icon.jpg</b> or <b>&lt;Extension Code&gt;-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>&lt;Extension Code&gt;-icon.jpg</b> or <b>&lt;Extension Code&gt;-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>&lt;Extension Code&gt;-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>&lt;Extension Code&gt;-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>
latest/customization/new_portal.txt · Last modified: 2024/09/10 10:25 by 127.0.0.1
Back to top
Contact us