You are browsing the documentation for iTop 2.5 which is not the current version.

Consider browsing to iTop 3.2 documentation

Customer portal XML Reference

You must be familiar with the fundamentals of iTop XML design.

For an overview of the Customer Portal customization capabilities, please have a look at the page Customize your Customer Portal

The version 2.5 of iTop introduce a number of changes to the Portal Bricks (Dashlets). The complete list of modifications introduced by the new formats are described in chapter Changes history.

Structure

This structure is specific to a portal developped over the itop-portal-base library.

Search in the XML:
Tag Usage Description
<module_design id="PORTAL_UNIQUE_ID" xsi:type="portal"> mandatory Structural node defining configuration for a portal instance
<properties> mandatory Global properties of the instance
<name>portal:itop-portal</name> mandatory Portal name
<logo>../images/itop-logo.png</logo> optional Logo to use in the instance. If defined, will override the one from the Designer or env-xxx/branding/portal-logo.png
<themes> mandatory Declared CSS stylesheets
<theme id="custom">sample-portal-custom-css/custom.css</theme> zero or more List of CSS stylesheets. IDs bootstrap|portal|custom are loaded first, redefining them will override the default files. Then other IDs will be loaded. Path must be relative to /env-xxx
<templates> mandatory Declared TWIG templates
<template id="layout">sample-portal-alter-twig/layout.html.twig</template> zero or more List of TWIG template to override. Available values are "layout" for the whole page or "home" for the home page content. (Bricks content layout can be defined directly in each bricks)
<urlmaker_class>iTopPortalViewUrlMaker</urlmaker_class> optional Class used for generating objects view urls (eg. in notifications)
<triggers_query>SELECT TriggerOnPortalUpdate AS t WHERE t.target_class IN (:parent_classes)</triggers_query> mandatory OQL query to retrieve triggers when updating an object on the instance
<attachments> mandatory Structural node
<allow_delete>true</allow_delete> mandatory Can the attachments be deleted on the portal. Available values are true|false
<allowed_portals> mandatory Structural node
<opening_mode>tab</opening_mode> mandatory Defines how other portals -allowed for the current user- will be opened. Available values are "tab"|"self". "tab" opens the portal in a new tab, "self" opens it in the current tab. Default value is "tab".
<bricks> mandatory Declared bricks
<brick id="BRICK_UNIQUE_ID" xsi:type="ANY_FQCN_BRICK"> zero or more Declaration of a brick. ID must be unique through the collection. xsi:type must be the fully qualified class name (including the namespace). There can be several instances of a same xsi:type brick as long as their IDs are differents. See the various types of bricks in section Bricks (Note that only the common tags are displayed under this node)
<active>true</active> optional Is the brick active on the portal. Note that an active brick that is not visible on both the home page and the navigation menu can still be accessed from its URL. Available values are true|false, default is true
<width>6</width> optional Width of the tile on the home page. Value must be an integer between 1-12, 12 being the whole row width. Default value is 3.
<rank> optional Rank of the brick on the home page and the navigation menu. If not specified, will be placed first.
<default>1</default> optional Rank on both home page and navigation menu. Value must be an float.
<home>1</home> optional Rank on the home page only. Value must be an float.
<navigation_menu>1</navigation_menu> optional Rank on the navigation menu. Value must be an float.
<title> optional Title of the brick on the home page and the navigation menu.
<default>Brick:Portal:Foo:Bar</default> optional Title on both home page and navigation menu.
<home>Brick:Portal:Foo:Bar</home> optional Title on the home page only.
<navigation_menu>Brick:Portal:Foo:Bar</navigation_menu> optional Title on the navigation menu only.
<description>Brick:Portal:Foo:Bar+</description> optional Description of the brick that will be displayed on the home page.
<visible> optional Is the brick visible on the home page and the navigation menu. If not specified, default is true on both.
<home>true</home> optional Is the brick visible on the home page. Available values are true|false, default is true
<navigation_menu>true</navigation_menu> optional Is the brick visible on the navigation menu. Available values are true|false, default is true
<decoration_class> mandatory CSS classes for the brick's icon on the home page and the navigation menu. CSS classes from Glyphicon, Font Awesome or your own can be used.
<default>fa fa-user fa-2x</default> optional CSS classes applied on both home page and navigation menu.
<home>fa fa-user fa-2x</home> optional CSS classes applied on the home page.
<navigation_menu>fa fa-user fa-2x</navigation_menu> optional CSS classes applied on the navigation menu.
<templates> optional Templates of the brick that can be overrided
<template id="page"> zero or more Path to the TWIG template used to override the default one. Path must be relative to env-xxx. Can be used to cutomize the template of a brick's tile (home page) or page. ID attribute is mandatory and must be either "tile" or "page".
<security> optional Limitation on which profile can see this brick or not
<allowed_profiles>SELECT URP_Profiles WHERE name = 'Portal User'</allowed_profiles> optional OQL returning Profiles which members can see this brick. If omitted, all Profiles not explicitly denied can see it.
<denied_profiles>OQL</denied_profiles> optional Profiles which cannot see this brick.
<forms> mandatory Declared forms
<form id="name"> zero or more Declaration of a form. ID must be unique
<class>ServiceSubcategory</class> mandatory Object class of the form. Child classes will inherit this form if they have none defined
<properties> optional Structural node
<display_mode>cosy</display_mode> optional Display mode of the form fields. "cosy" for a regular labels over values layout; "compact" for a side-by-side layout with 25% space for label and 75% for input; "dense" for a side-by-side layout with input filling all available space. You can also use a custom css class that will be used on the form as "form_xxx", as well as on the fields as "form_field_xxx". Available values are cosy|compact|dense|CUSTOM_VALUE, default is cosy.
<always_show_submit>false</always_show_submit> optional When set to false, submit button is hidden when transitions are available on the object. Available values are true|false, default is false.
<fields> optional Declared fields. If empty, only fields from twig tag will be displayed. If omitted only fields from zlist details will be displayed, twig tag will be ignored
<field id="title"> zero or more Declaration of a field. ID must be a valid attribute code of the class. Will be placed one after the other if not placed in twig tag. Missing mandatory attributes will be automatically appended to the form
<slave>true</slave> optional Is the field slave. If present, flag will be merged we those from the datamodel/lifecycle. Available values are true|false, default is true
<read_only>true</read_only> optional Is the field read-only. If present, flag will be merged we those from the datamodel/lifecycle. Available values are true|false, default is true
<mandatory>true</mandatory> optional Is the field mandatory. If present, flag will be merged we those from the datamodel/lifecycle. Available values are true|false, default is true
<hidden>true</hidden> optional Is the field hidden. If present, flag will be merged we those from the datamodel/lifecycle. Available values are true|false, default is true
<must_prompt>true</must_prompt> optional Is the field must prompt. If present, flag will be merged we those from the datamodel/lifecycle. Available values are true|false, default is true
<must_change>true</must_change> optional Is the field must change. If present, flag will be merged we those from the datamodel/lifecycle. Available values are true|false, default is true
<twig> optional Form layout. You can use any HTML tags under this on, to make a perfect form template. If not defined, fields will be placed one after the other.
<div class="form_field" data-field-id="title" data-field-flags="mandatory" data-field-display-mode="cosy"> zero or more Place a div with an "data-field-id" attribute to get it in the form. Class tag is mandatory must contains "form_field" class. "data-field-id" attribute must be a valid attribute code for the class. Optionally, you can add a "data-field-display-mode" attribute to change the layout of this specific field (values can be cosy|compact|dense).
<modes> optional Declared modes. If not defined, form will be for all modes
<mode id="apply_stimulus"> zero or more Defines in which modes the form will be available for this class. Available values are view|edit|create|apply_stimulus
<stimuli> optional For apply_stimulus mode only, defined applicable stimuli
<stimulus id="name"> zero or more The current form will be used for transition based on this stimulus code
<classes> mandatory Declared classes
<class id="Contact"> zero or more Declaration of class. A class (or one of its ancestors) MUST be defined to be displayed in the instance. ID must be a valid Object class
<scopes> mandatory Declared scopes
<scope id="name"> zero or more Declaration of scope. A scope defines the set of objects a Profile can access, they are cumulative regarding the user profiles
<oql_view>SELECT Contact WHERE org_id = :current_contact->org_id</oql_view> mandatory OQL query that defines the set of objects allowed to view
<oql_edit>SELECT Contact WHERE id = :current_contact_id</oql_edit> optional OQL query that defines the set of objects allowed to edit. Note that this apply only on the sub set from oql_view.
<ignore_silos>false</ignore_silos> optional When set to "true", the OQLs of this scope will not be restricted to the current user's allowed organizations. Values can be true|false, default is "false"
<allowed_profiles> optional Declared profiles that will have access to that scope. If this tag is not present, all profiles will have access to the scope.
<allowed_profile id="Portal User"> zero or more Name of the Profile that will have access to that scope. ID must be unique and a valid Profile.
<lists> optional Presentation lists. As of today, used only in objects' LinkedSet in the portal.
<list id="default"> zero or more Declaration of list. ID must be unique. Supported values are 'list' (used for object's linkedset) and 'default' (used as fallback)
<items> mandatory Declared attributes to show in the list.
<item id="status"> zero or more Declaration of attribute. ID must be unique and must be a valid attribute code of the object class
<rank>10</rank> optional Rank of the attribute in the list. Lowest comes first.
<lifecycle> optional Lifecycle: stimuli denied per Profiles, if omitted user profiles definition is applied
<stimuli> mandatory List of events disabled on portal
<stimulus> at least one Declaration of a stimulus.
<denied_profiles> optional Declared profiles that will not have access to that stimulus. If this tag is not present, all profiles will be denied for the stimulus.
<denied_profile id="Portal User"> zero or more Name of the Profile that will not have access to that stimulus in the portal. ID must be a valid Profile.
<action_rules> optional Declared action rules. AR are actions that will be applied to an object / form in a specific brick, on a specific action. They can be combined.
<action_rule id="contact-to-userrequest"> zero or more Declaration of action rule. ID must be unique.
<source_oql><![CDATA[SELECT Contact AS C WHERE C.id = :current_contact_id]]></source_oql> optional When copying values from a specific object to another, use this tag to specify the OQL query. (eg. The current contact) Note: Use either source_oql or source_class but not both. Note 2: Abstract classes are NOT supported with the source_class tag, for now you must duplicate action rules for each leaf classes.
<source_class>Service</source_class> optional When copying values from the clicked object in a brick, use this tag to specify the object class. (eg. The Service or ServiceSubCategory in the Services catalog) Note : Use either source_oql or source_class but not both.
<presets> optional Declared presets
<preset id="1">set(caller_id, $current_contact_id$)</preset> zero or more Declaration of a preset. ID mus be unique. When combined with source_oql|source_class, will preset an attribute value of the object. Syntax is similar to ObjectCopier, supported verbs are set|copy.

Bricks

Here are the features built it the itop-portal-base library. You may implement your own brick in a separate module, provided that its name (i.e. xsi:type) does not interfere with the existing bricks.

Search in the XML:
Tag Usage Description
<brick id="UNIQUE_ID" xsi:type="Combodo\iTop\Portal\Brick\UserProfileBrick"> mandatory Connected user profile edition brick.
<show_picture_form>true</show_picture_form> optional Show / hide the user profile picture form. Available values are true|false, default is true.
<show_preferences_form>true</show_preferences_form> optional Show / hide the user preferences form. Available values are true|false, default is true.
<show_password_form>true</show_password_form> optional Show / hide the user password form. Available values are true|false, default is true.
<form> mandatory Contact informations edition form for the connected user. Behave exactly like global forms of the portal, please check the corresponding section.
<brick id="UNIQUE_ID" xsi:type="Combodo\iTop\Portal\Brick\BrowseBrick"> zero or more Navigate through an hierarchy of objects and specify actions for each levels (view or edit the object, create another object from this one). Navigation can be done from multiple views such as a regular list or a tree. An use case could be to browse the service catalog to find the one to create your request from.
<levels> mandatory Declared levels
<level id="UNIQUE_ID"> mandatory Declaration of level. ID must be unique, integer are safer, avoid '-'
<class>ServiceFamily</class> optional Object class to display on this level. Ignored if oql tag is present.
<oql><![CDATA[SELECT ServiceFamily]]></oql> optional OQL query to define a sub-set of an object class to display. Takes priority over class tag if defined.
<title> optional Title for that level. Mainly used in list mode as column header.
<name_att> optional Class attribute to be displayed as the object name. Default is "name" If actions are defined, the first one is available on that attribute
<tooltip_att> optional Class attribute to be displayed in a tooltip
<description_att> optional Class attribute to be displayed as a short text beside the object's name. (Note: This is not used in "list" mode)
<image_att> optional Class attribute to be displayed as an image beside the object's name. (Note: This is only used in "mosaic" mode for now)
<fields> optional List of extra fields to be displayed in list mode (used only for filtering in other modes)
<field id="UNIQUE_ID"> zero or more Class attribute to display as an extra information. Will be used when filtering. Must be a valid attribute code of the class
<hidden>false</hidden> optional Hide the field so it won't be displayed but still be used by the filter. Available values true|false, default is false.
<actions> optional Available actions for objects of that level.
<action id="UNIQUE_ID" xsi:type="drilldown"> zero or more ID must be unique. xsi:type defines the type of action. Available types are drilldown|view|create_from_this.
<title>Create a user request</title> optional String to display as the action's title
<icon_class>fc fc-new-request fc-1-6x fc-flip-horizontal</icon_class> optional CSS classes to use for the action decoration. Bootstrap glyphicons and FontAwesome classes are supported in addition to your own.
<opening_target>modal</opening_target> optional Defines how the action should be done. Values can be "modal" (Opens in a modal window), "new" (in a new window) or "self" (in the current window). Default value is "modal"
<rules> optional Action rules for the action
<rule id="UNIQUE_ID"> zero or more ID must be unique and correspond to a valid action rule of the portal definition.
<class>UserRequest</class> optional Available only for action[xsi:type="create_from_this"] when factory_method tag is not specified. Class name of the object to create.
<factory_method><![CDATA[\Ticket::CreateFromServiceSubcategory]]></factory_method> optional Available only for action[xsi:type="create_from_this"] when class tag is not specified. Fully qualified method name that will return the DBObject to use (eg. Returns an UserRequest or an Incident depending of the ServiceSubcategory in the standard portal).
<levels> optional Optional sub levels. Must be linked to the parent level through an attribute
<level id="UNIQUE_ID"> mandatory Declaration of sub level. Has the same tags as the parent level with the following as extras.
<parent_att>servicefamily_id</parent_att> mandatory Class attribute that linked the current class to the parent level's class
<browse_modes> mandatory List of browse modes
<availables> mandatory List of available browse modes
<mode id="UNIQUE_ID"> zero or more Available browse mode. ID must be unique and a valid browse mode. Standard modes are list|tree|mosaic.
<default>list</default> mandatory Default browse mode. Must be one of the available browse modes: list|tree|mosaic
<data_loading>auto</data_loading> mandatory Defines how the data will be loading. Available values are auto|full|lazy. auto means that data will be loaded in one shot if their count is less than the "lazy_loading_threshold" module_parameter of the instance. full means that all data will be loaded at once. lazy means that data will be loaded dynamically at each page / level. This parameter should be tuned to improve performances.
<brick id="UNIQUE_ID" xsi:type="Combodo\iTop\Portal\Brick\ManageBrick"> zero or more Allows to manage objects by grouping them on 2 axis and edit them. Typically to manage the ongoing requests or a group of CIs.
<class>Ticket</class> optional Class of object to manage in the brick. Required if no oql tag defined, ignored otherwise.
<oql><![CDATA[SELECT Ticket]]></oql> optional OQL query to define the subset of object to manage in the brick. Takes precedence over class tag.
<opening_target>modal</opening_target> optional Defines how the action shoul be done. Values can be "modal" (Opens in a modal window), "new" (a new window) or "self" (the current window). Default value is "modal"
<opening_mode>edit</opening_mode> optional Define how the objects should be opened. Values can be edit|view, default is "edit". Note that even if this is set to "edit", objects not allowed in edition for the user (scopes and security layers) will open in "view" mode.
<fields> optional List of attributes to display in the object list, if omitted zlist "list" will be used.
<field id="UNIQUE_ID"> zero or more Attribute to display in the list, will be used when filtering. ID must be a valid code attribute of the class.
<display_modes> optional List of display modes, if omitted will use 'list' as the only display type
<availables> optional List of available display modes
<mode id="UNIQUE_ID"> zero or more Available display modes. ID must be unique and a valid display mode. Standard modes are list|pie-chart|bar-chart.
<default> optional Default display mode. 'list' is used if tag is omitted. Must be one of the declared available display modes
<tile> optional Display mode of the brick's tile. Must be one of the following: text|badge|pie-chart|bar-chart|top-list. Default is "text"
<export> optional Allows the user to export data from the detailed list view. Provide either the ''fields'' to export or the tag ''export_default_fields'' set to true
<fields> optional List of attributes to export, if you don't want the default.
<field id="UNIQUE_ID"> zero or more Attribute to export. ID must be a valid code attribute of the class.
<export_default_fields>true</export_default_fields> optional If set to true, fields defined on the list DataModel will be used and ''Fields'' tag even if provided will be ignored, if set to false and no ''Fields'' defined, the export button will not be displayed.
<grouping> mandatory Declared grouping.
<tabs> mandatory Grouping on tabs is done over an OQL query whereas grouping on tables is done on the finalclass.
<show_tab_counts>false</show_tab_counts> optional Show object count for each tabs. Available values are true|false. Default is false.
<attribute>operational_status</attribute> optional Grouping on distinct values of an attribute. Optional if tag groups is present.
<limit>5</limit> optional Limit the groups number when grouping by attribute is done.
<show_others>true</show_others> optional When a limit is set, Aggregate the other results in one called 'Others'. Available values are true|false. Default is false.
<groups> optional Grouping by explicit OQL queries. Optional if tag attribute is present.
<group id="UNIQUE_ID"> zero or more Declaration of group
<rank>1</rank> optional Rank of the tab among the groups
<title>Brick:Portal:OngoingRequests:Tab:OnGoing</title> mandatory Title of the tab
<condition><![CDATA[SELECT Ticket AS T WHERE operational_status NOT IN ("closed", "resolved")]]></condition> mandatory OQL query that will be applied on the brick's object set to make that group
<data_loading>auto</data_loading> mandatory Defines how the data will be loading. Available values are auto|full|lazy. auto means that data will be loaded in one shot if their count is less than the "lazy_loading_threshold" module_parameter of the instance. full means that all data will be loaded at once. lazy means that data will be loaded dynamically at each page / level. This parameter shouldbe tuned to improve performances.
<brick id="UNIQUE_ID" xsi:type="Combodo\iTop\Portal\Brick\CreateBrick"> zero or more Displays an object creation form.
<modal>true</modal> mandatory Should the form be in a modal dialog or on a whole page. Available values are true|false, default is true.
<class>UserRequest</class> mandatory Class of the object to create.
<rules> mandatory Action rules to apply on the form.
<rule id="*UNIQUE_ID*"> zero or more Declaration of action rule. ID must be unique. See Action Rule section for more informations.
<brick id="UNIQUE_ID" xsi:type="Combodo\iTop\Portal\Brick\FilterBrick"> zero or more Pre-filters another brick.
<search_placeholder_value>eg. install office</search_placeholder_value> optional String to display as a placeholder in the filter field. Default is "Brick:Portal:Filter:SearchInput:Placeholder".
<search_submit_label>Search</search_submit_label> optional Label of the submit button. Default is "Brick:Portal:Filter:SearchInput:Submit".
<search_submit_class>glyphicon glyphicon-search</search_submit_class> optional CSS classes to add on the submit button. This can be used to set a Glyphicon or Font Awesome icon for example.
<target_brick> mandatory Structural node.
<id>ongoing-tickets-for-portal-user</id> mandatory Must correspond to the id of the target brick.
<type>Combodo\iTop\Portal\Brick\ManageBrick</type> mandatory Must correspond to the xsi:type of the target brick. As of today, only BrowseBrick and ManageBrick are supported
<tab>list</tab> mandatory BrowseBrick: The browse mode to use for displaying the results (except mosaic). For ManageBrick: The tab to display (group id).
<brick id="UNIQUE_ID" xsi:type="Combodo\iTop\Portal\Brick\AggregatePageBrick"> zero or more A page with one to many brick tiles.
<aggregate_page_bricks> mandatory List of bricks to include in the page.
<aggregate_page_brick id="PAGE_BRICK_ID"> at least one Brick id to include
<rank>1</rank> optional Rank of the tile in the page

Changes history

The datamodel for the Enhanced Portal became editable with iTop 2.3 released in summer 2016

XML Version 1.4

This version came live with iTop 2.4 beta, released in July 2017.

Bricks

  • Filter Brick: extension was included to the core application.
  • UserProfileBrick: Profile picture, preferences and password forms can be disabled. Available from PHP 5.5.16
   <brick xsi:type=UserProfileBrick>
      <show_picture_form>true</show_picture_form>
      <show_password_form>false</show_password_form>
      <show_preferences_form>true</show_preferences_form>
  • BrowseBrick:
    • “Tree” mode displays a description on top of a name.
    • Default action is the first declared action, an hamburger menu was added proposing all actions
    • New mode “mosaic” convenient on small devices, allowing to display an image associated with the browsed objects.
  <brick xsi:type=BrowseBrick>
     <browse_modes><availables><mode id="mosaic"/></availables></browse_modes>
     <levels><level><image_att>class_field_id</image_att></level></levels>
  • Open a form in the same window, in a tab or a pop-up
 <brick xsi:type=BrowseBrick><levels><level><actions><action><opening_target>new</opening_target>... 
 <brick xsi:type=ManageBrick><opening_target>self</opening_target>... 
  • ManageBrick displays count on tabs
 <brick xsi:type=ManageBrick><groupings><tabs><show_tab_count>true</show_tab_count>... 
  • Create Brick now support abstract class, no specific tag for this

Forms

  • Dictionary entries now usable in forms.
         <legend>{{'Form:Portal:TicketEdit:Fieldset:General'|dict_s}}</legend> 
  • Configurable display mode on forms to change the layout form/properties/display_mode
  • Configurable display mode on fields to change the layout
  <div class="form_field" data-field-id="title" data-field-display-mode="cosy"> 
  • A LinkedSet attribute can now be open by default at form opening.
  <div class="form_field" data-field-id="contacts_list" data-field-opened="true"> 
  • Actions buttons can be added
  • Forms can be defined specifically for a particular transition, so you can decide which fields will be prompted, overwriting the console behavior. Check here for more details

Example, display a read only field:

  <div class="form_field" data-field-id="org_id" data-field-flags="read_only"/>
  • By default the submit button is now hidden on forms when transitions are proposed. If you want to restore the previous behavior use this tag <always_show_submit> on forms defined in XML.
    <module_design id="itop-portal">
      <forms>
        <form id="ticket-create" _delta="if_exists">
          <properties _delta="define_if_not_exists">
            <always_show_submit _delta="force">true</always_show_submit>
          </properties>
        </form>
      </forms>
    </module_design>

Others

  • Configurable opening mode for additional portals
  • You can hide a transition to some portal users, for example, support agent could be allowed to assign a ticket in the portal while all others would not. But as soon as you define a lifecycle tag then you must explicitly describe all denied Events (stimuli), this denial is for all profiles unless denied profiles are explicitly listed.
    <module_design id="itop-portal">
      <classes>
        <class id="Ticket">
          <lifecycle>
            <stimuli>
              <!-- ev_assigned and ev_resolved will NEVER be available in portal -->
              <stimulus id="ev_assign">
              </stimulus>
              <stimulus id="ev_resolved">
              </stimulus>
              <!-- ev_pending will be available in portal to other profiles than Support Agent -->
              <stimulus id="ev_pending">
                <denied_profiles>
                   <denied_profile id="Support Agent"/> 
                </denied_profiles>
              </stimulus>
            </stimuli>
          </lifecycle>
        </class>
      </classes>
    </module_design>

XML Version 1.5

Bricks

  • ManageBrick: this version provides new display types (pie chart, bar chart and badge) to the Manage Brick.
<brick xsi:type="Combodo\iTop\Portal\Brick\ManageBrick">
   <display_modes>
      <availables>
          <mode id="list"/>
          <mode id="pie-chart"/>
          <mode id="bar-chart"/>
      </availables>
      <default>pie-chart</default>
      <tile>badge</tile>
   </display_modes>
  • It also allows to export data to Excel.
  <export><export_default_fields>true</export_default_fields>...
  • The exported fields can be specified:
  <export><fields><field id="title"/>....
  • The number of groups can be limited:
  <grouping><tabs><limit>5</limit>...
  • When the number of groups is limited, the remaining can be aggregated:
  <grouping><tabs><limit>5</limit><show_others>true</show_others>...
  • Aggregated Brick_ : a new brick to display in a page tiles from another bricks
2_5_0/customization/portal_xml.txt · Last modified: 2023/03/15 11:31 by 127.0.0.1
Back to top
Contact us