Cubes & Dimensions

All cubes and dimensions included in the Apliqo UX ContentStore are control objects prefixed with curly brace "}". If the ContentStore is browsed without the "display control objects" option selected then the ContentStore will appear as if empty. This is by design.

All objects in the ContentStore are prefixed with "}APQ UX" except for the }APQ Settings cube which is taken from the Apliqode Framework. The ContentStore is made up of a relatively small and easy to manage number of objects.

It is possible to run the ContentStore inside another TM1 instance such as an instance being reported on via the UX. In theory adopting such a setup where only a single TM1 instance is the source for the reporting or planning application would remove the requirement to sync users and metadata between the instance and ContentStore, simplifying the overall setup. However, this setup is nevertheless not recommended for the following reasons:

  1. Upgradeability; having ContentStore objects inside another instance will make upgrading more difficult and unable to be automated (for example when the upgrade installer automatically shuts down the ContentStore service and replaces objects).
  2. Backups; having the ContentStore objects inside another instance means backups will be orders of magnitude larger and should not be managed by the ContentStore's automated process

It is still possible to run the ContentStore in such a configuration. However, it is not officially supported and you do so at your own risk.

General Settings

The }APQ Settings cube stores general information such as the instance's server name, TM1 version and various other control parameters.

Note: that ContentStore does not use most of the parameters available to configure in the cube. It is only necessary to correctly configure the parameters listed below as mandatory.

It is important to make sure that some key settings are set in order for functions such as file import and export and backups to work properly.

Open the }APQ Settings cube in a cube viewer and go to the "File System Location Parameters" section:  

Mandatory parameters

The parameters marked with * are essential to configure correctly.

  • Instance: this is the root location for the ContentStore. This is the parent directory where the CFG, Data. Log, etc. folders are located
  • Data Dir: data directory for the ContentStore. Usually called "Data"
  • Backup: location for file system backups
  • Export: default location for exports (e.g. when exporting apps for migration)
  • SourceData: default location for imports (e.g. when importing apps for migration)
  • 7Zip: the location of the 7-Zip executable. This is used by the backup job to compress the files in the data directory
  • UXToolkit: the location of the uxtoolkit.exe executable. This is used by the metadata syncing service

The 1 or 0 in the "Numeric" measure is a true/false switch to apply default settings based on the Instance parameter value which assumes a standard naming of sub-directories. To use the standard structure you need only to enter the correct path (with trailing backslash) against the instance parameter (and check that the correct sub-folders exist).

To use custom folder locations enter 0 in the "Numeric" measure and the "String" measure will become writeable.

Most of the mandatory parameters are under the grouping "Backup Params" (except for the Export, Import & UXToolkit locations).

The "TM1 Server Name" parameter is normally set to the name of the TM1 instance configured in tm1s.cfg. For the purposes of the ContentStore configuration, changing this value affects the root of the name used for the backup files.

Optional parameters

For the ContentStore only the stared parameters above are mandatory but it is also possible to enter some additional parameters.

Instance name parameters

These parameters are more for informational purposes.

The only one which has any impact on ContentStore behaviour is TM1 Server Name which is used for file naming when creating  backup archives.

Bedrock control parameters

The ContentStore contains a small collection of bedrock 4 library processes. When calling bedrock processes other processes from the ContentStore look up this location for the delimiters to use.

There is no reason to ever change these values from the standard ones.

Instance Metadata Repository

A number of cubes are used to store metadata information about the instances used for the data source of apps built in the UX. Metadata which can be synced to the ContentStore from other instances is lists of:

  • Clients and Groups, including group membership
  • Cubes present on each instance
  • Dimensions present on each instance, including default member per dimension hierarchy

This information is stored in the following dimensions:

  • }APQ UX Instance
  • }APQ UX Client
  • }APQ UX Group
  • }APQ UX Cube
  • }APQ UX Dimension

And in the following cubes:

  • }APQ UX Instance Attributes
  • }APQ UX Instance Client
  • }APQ UX Instance Group
  • }APQ UX Instance ClientGroups
  • }APQ UX Settings Service Dimensions
Instance Metadata Dimensions

The }APQ UX Instance dimension contains a single hierarchy and a root consolidation "Total Instances". Each instance declared in the instances.json file is a leaf element in }APQ UX Instance and rolls up into Total Instances.

All other metadata dimensions contain a root consolidation in the same named hierarchy which contain as children the individual objects which exist across all instances. In addition, each dimension contains an additional hierarchy named for each instance. Each alternate hierarchy contains only leaves and only the elements which exist in each instance.

For example a ContentStore contains leaf elements in }APQ UX Instance dimension UX_Demo, SData and ContentStore. The }APQ UX Dimension dimension contains a root consolidation "Total Dimensions" in the same named hierarchy. The rollup contains as children elements corresponding to the union of all dimension names across all three instances. As well, the dimension contains four additional hierarchies:

  • UX_Demo : contains leaf elements only of all dimension names in the UX_Demo instance
  • SData : contains leaf elements only of all dimension names in the SData instance
  • ContentStore : contains leaf elements only of all dimension names in the ContentStore instance (ContentStore is itself also an instance)
  • Leaves : contains leaf elements of all dimension names from all instances. The only difference between Leaves and the same named hierarchy }APQ UX Dimension is the absence of the "Total Dimensions" consolidation.

Exactly the same pattern is followed for the remaining metadata dimensions for listing of cubes, clients and groups per instance.

The }APQ UX Client and }APQ UX Group dimensions in addition have special "placeholder" elements "ALL USERS PLACEHOLDER" and "ALL GROUPS PLACEHOLDER" respectively. These elements are used for storage of data entry which applies to all Clients (or Groups) for a given instance.

Instance Metadata Cubes
Connected Instances ( }APQ UX Instance Attributes )

The }APQ UX Instance Attributes is a 2-dimensional cube. The reason that it exists at all versus the information being stored in attributes of the }APQ UX Instance dimension is for security as only members of APQ Admin or ADMIN should have access to the contents as this contains information on the admin user used to connect to the database instances for the metadata syncing as well as IP and rest port of the database instances which could well not be exposed or know to users.

Required attributes/parameters:

  • DNS/IP
  • HTTPPortNumber
  • UseSSL
  • CAMNamespace (if CAM is used for authentication)
  • ServiceAccount
  • ServiceAccountPwd

These parameters are used by the metadata syncing service to connect to the reporting instances to compile the metadata for each instance.

Note: that all required parameters are read fro the instances.json file and populated automatically. With the exception of ServiceAccount and ServiceAccountPwd. The user connection properties for each instance only need to be supplied if different from the ContentStore. If the fields are left empty then the metadata syncing service will attempt to connect with ContentStore credentials.

Note: the ServiceAccountPwd attribute can be stored manually as plain text (this cube is available only to admin users) or as an encrypted string. When the password value is set by the metadata syncing service it is stored automatically as a encrypted string.

User Repository ( }APQ UX Instance Client )

The }APQ UX Instance Client cube in combination with the }APQ UX Client dimension stores a record of which users exist in each instance connected to a Apliqo UX ContentStore and serves as a repository of which users are to be imported from a source instance and used as users of Apliqo UX applications.

Description of measures:

  • IsUsed is a Boolean 1/0 value which marks is the User exists in the selected instance
  • ContentStoreImport is a Boolean 1/0 value which indicates whether the user should also be created as a TM1 user in the ContentStores own }Clients dimension
  • ContentStoreCheck for users marked with ContentStoreImport=1, ContentStoreCheck is a rule calculated Boolean 1/0 value which indicates whether the user exists in the ContentStore }Clients dimension
  • EncryptedPassword holds the encrypted byte string value of the user password (in the case of TM1 native security) on the selected instance

In the example above the value of 1 in the intersection of Client:Demo1 / Measure:ContentStoreCheck indicates an exception error. The user has been marked to be created in the ContentStore in order that the user be able to use Apliqo UX. However the user does not exist in the ContentStore }Clients dimension. (Security syncing process has not yet been run).

Note: users can be individually flagged whether to be "imported" (created in the ContentStore) or not. However, in normal use cases you would enter a 1 against the intersection of ALL USERS PLACEHOLDER / ContentStoreImport. This will enforce a rule defined value of 1 against all users existing on the instance. Selecting this option ensures lowest maintenance as all users on the connected instance will then automatically be flagged to be created in the ContentStore.

Above you can see the effect of entering 1 against ALL USERS PLACEHOLDER. All users existing on the instance will now automatically be flagged to be created in the ContentStore as soon as they are detected.

Group Repository ( }APQ UX Instance Group )

The }APQ UX Instance Group cube performs exactly the same function as }APQ UX Instance Client except for security groups.

Description of measures:

  • IsUsed is a Boolean 1/0 value which marks is the group exists in the selected instance
  • ContentStoreImport is a Boolean 1/0 value which indicates whether the group  should also be created as a TM1 group in the ContentStores own }Groups dimension
  • ContentStoreCheck for groups marked with ContentStoreImport=1, ContentStoreCheck is a rule calculated Boolean 1/0 value which indicates whether the group exists in the ContentStore }Groups dimension

Note that zero suppression on rows is switched off in the above view in order to show the ALL GROUPS PLACEHOLDER element. Thus groups which belong to other instances but do not exist in the selected instance are also displayed on rows. For such groups the ContentStoreImport measure is greyed out as it cannot be available for import if it does not exist on the selected instance.

Unlike for users the usual use case for creation of groups on the ContentStore is probably not to automatically create all groups from the connected instance as most will probably not be relevant for the ContentStore. Only groups which are required for ContentStore purposes (that is for element security of the Apps dimension) should be marked to be imported.     

Note: that marking a group to be imported not only creates the group in the ContentStore but also inserts all users to the group on the ContentStore who are members of the same group in the source instance (and also marked to be imported). This means group memberships don't need to be maintained twice.

Group Membership ( }APQ UX Instance ClientGroups )

The }APQ UX Instance ClientGroups cube has no user data entry but is used as a repository by the metadata syncing service to store the client / group memberships from all synced instances (including the ContentStore itself). A value of 1 at the intersection of instance, user and group indicates the user is assigned as a member of the group on that instance.

Should a group have been marked to be imported/synced to the ContentStore in the }APQ UX Instance Group cube then the measure "ContentStoreImport" is rule calculated and determines whether the user will be added to the same group in the ContentStore TM1 instance based on:

  1. the group is marked as to be imported in the }APQ UX Instance Group cube  
  2. the user is a member of the group in the source instance as indicated by the "Assignment" measure
  3. the user is also marked as to be imported in the }APQ UX Instance Client cube  
Dimensions ( }APQ UX Settings Service Dimensions )

The "settings service" is a component of Apliqo UX which keeps track per instance per hierarchy, of all the filter selections used in the application or the "point of view". The settings service records the last selection of the principal name of the selected element as well as all attribute values associated with each element. This is a critical component for persisting the point of view as users navigate around the application and saves users from reselecting filters when navigating to each screen.

The }APQ UX Settings Service Dimensions cube allows the Apliqo UX administrator to set default values for each instance/hierarchy combination of the element which will be pre-selected by default as the "point of view" for that hierarchy when each user logs in.

The  }APQ UX Settings Service Dimensions cube is largely populated by the metadata syncing service but also allows for input by the ContentStore administrator.

Description of measures:

  • IsUsed is a Boolean 1/0 value which marks if the dimension (or dimension:hierarchy) exists in the selected instance. This value is populated by the metadata syncing service.
  • IsUsedSettingService is a Boolean 1/0 value which indicates whether a default element value should be loaded for the instance / hierarchy combination upon login by the user or whether the initial settings service value for the hierarchy is empty. This value is data entry by the UX administrator.
  • DefaultMember indicates the default member for the hierarchy on the source instance as defined by the }HierarchyProperties cube on the source instance. If the Default_Member property for the hierarchy is not defined then the 1st element in the hierarchy is assumed to be the default member. This value is populated by the metadata syncing service.
  • DefaultOverride is used should the administrator of the UX application with to define another element as the default member for Apliqo UX versus the one defined on the source instance itself. This value is data entry by the UX administrator.
  • SettingServiceDefault is rule calculated measure which takes the DefaultOverride (if provided) otherwise the DefaultMember value.
  • DisplayAttribute indicates the display attribute to present to users when displaying hierarchy default member values. This is usefule for dimensions with technical element names which may not be recognized by users. This value is data entry by the UX administrator.

Apliqo UX has two distinct types of "point of view" dimensions

  1. Filters : display as drop down lists in the FilterBar and are selectable/changeable by end users on each screen. The element list to display can be set in a number of ways (e.g. subset, MDX, element comparisson, element children)
  2. Fixed : fixed dimensions can be displaed in the FilterBar but appear as a text box and are not selectable/changeable by end users. As only a single element selection is possible for a fixed dimension there is no concept of list type. (Note: fixed dimensions can also be hidden from the FilterBar and not displayed to end users.

When creating Apliqo UX screens the "selected element" value for both filter and fixed dimensions should typically deliberately be left empty. Not defining an element indicates to Apliqo UX to substitute the current value from the settings service into the point of view. Defining a selected element in the view definition means Apliqo UX will replace the settings service value with the defined value.

For filters, if no value has been set for the instance / hierarchy combination then the first element in the defined list will be used.

Views ( }APQ UX ViewDefinition Defaults )

The }APQ UX ViewDefinition Defaults cube stores default dimension axis layout for cubes in the different possible layout configurations per object type. Due to the way views and dashboards are composed in Apliqo UX the axis coordinates are not stored in a single object but are broken into components:

  • View: dimension coordinates stores for Fixed, Filter and Rows
  • SubView: dimension coordinates stored for Columns
  • Dashboard: dimension coordinates stored for Fixed and Filter
  • Widget: dimension coordinates stored for Rows, Columns and Internal Filter

This data storage requirement is reflected in the measure dimension of the cube. Description of measures:

  • IsUsed is a Boolean 1/0 value which marks if the cube exists in the selected instance. This value is populated by the metadata syncing service.
  • Default View JSON is a string which holds the default value of the viewDef JSON string attribute for views
  • Default SubView JSON is a string which holds the default value of the viewDef JSON string attribute for subviews
  • Default Dashboard JSON is a string which holds the default value of the viewDef JSON string attribute for dashboards
  • Default Widget JSON is a string which holds the default value of the viewDef JSON string attribute for widgets

When creating new objects from scratch (as opposed to duplicating an existing object) this cube is checked for the instance/cube combination and if a default layout has been set for the object type then this is used for the new object. This allows the new widget to be immediately useable (that is with no red undefined dimensions which must first be dragged to axis positions before a MDX query can be built and data queried for the widget.)

It is therefore advisable to set default axis layout for all object types for all commonly used cubes as this greatly accelerates application building (especially for cubes with a large number of dimensions). This is also described in the ContentStore configuration guide.

Localization

The Apliqo UX Content Store has been pre-configured to support application localization should you have users from multiple regions requiring access to the application in their own preferred language. All user facing objects or potentially user facing objects have been pre-configured to support localization. Localized attribute cubes already exist for these objects. In order to support localization all you need to do is enter attribute values in the languages you wish to support. (Any users with non-supported language preference will see the default attribute values).

  • }Cubes : display caption rather than technical name
  • }Dimensions : display caption rather than technical name
  • }APQ UX App : display name, info text (tooltip) and text box support localization
  • }APQ UX AppType : display alternate language captions for main components; Dashboard, View, Widget, Wizard, PopUp
  • }APQ UX AppSubType : display alternate language captions for widget types (e.g. line chart, column chart, waterfall)
  • UserPreference Measure : alternate captions for Homepage selection, etc.
  • Settings Service UserPreference Measure : alternate captions for "default member"
  • UserFavorite Measure : alternate captions for favorites
User Preferences

General Preferences ( }APQ UX UserPreference )

The user preferences cube stores general preferences such as language and homepage.

The currently functional measures in the preferences cube are:

  • Language: selected supported language. If empty then the browser locale will be used.
  • HomepageSelection: is a PickList showing code and description for dashboards in the }APQ UX App dimension to which the user has at least READ access
  • Homepage: is the principal name of the user's assigned homepage. Note that is the user has not made a default homepage selection then the homepage assigned to the user defined as the "Default User" in the }APQ Settings cube will be used. (If no default user is defined then the Admin user will be used as the default).

In addition the user preferences cube also caters for additional measures which are not currently functional! (Values can be stored but they currently have no effect on application behaviour).

  • DefaultInstance: stores a string of a user's default instance. Where the user is creating a new view and does not select a data source this setting would dictate the instance to use
  • DecimalSeparator: will be used when localization is fully supported including localized number format. Currently number format is set centrally as an application constant
  • ThousandSeparator: will be used when localization is fully supported including localized number format. Currently number format is set centrally as an application constant
  • DateFormat: will be used when localization is fully supported including localized date format. Currently date format is set centrally as an application constant

Default Point of View ( }APQ UX Settings Service UserPreference )

From an application behaviour perspective the most important user preference cube is }APQ UX Settings Service UserPreference. This cube allows individual users to store their own default member preferences rather than use the global defaults defined in the }APQ UX Settings Service Dimensions cube.

For all dimensions or hierarchies marked in }APQ UX Settings Service Dimensions with IsUsedSettingService=1 a per user override is possible. In the example below the "Admin" user has entered a personal default in the account dimension of "6000". When the user logs in this value will be used in preference to the global default value of "4" and will be used to pre-set filter or fixed dimension values where the Account dimension is present in the FilterBar.

A view of this cube is what the user sees when the user clicks on "my preferences" in the top right of the Apliqo UX application.

In the example above the "Demo1" user has overridden the default filter selection of "Jan" in the Month dimension and selected "Year". Also note that the Region dimension will use the "Description" attribute in the subset picklist. The default value still displays as "1" (World) but if the user browses the available selections the description attribute will display and they will see "World" and not "1".

Note: until the metadata syncing service is run AND at least one dimension has been marked with IsUsedSettingService=1 then nothing will be displayed to users when they click on "my preferences".

Favorites ( }APQ UX UserFavorite )

The user favorites cube stores a Boolean 1/0 against the IsFavorite measure for elements of the }APQ UX App dimension to indicate that the apps are members of a user's favorites collection. Unlike the default homepage selection which only accepts dashboards, favorites can be either dashboards, views or wizards.

The IsHomepage measure is a rule derived Boolean 1/0 value which indicates the user's selected homepage.

The Content Store

The real content store within the ContentStore is the attribute cube of the }APQ UX App dimension. The structure of the application is mirrored in the structure of the UX App dimension and the definition of every screen in Apliqo UX is stored in the attributes of the dimension. In fact the real "content store" is the }ElementAttributes_}APQ UX App cube. Everything else in the content store instance is really just for management purposes.

The UX App dimension follows a naming convention for the elements with a single letter indicating the object type followed by a numeric identifier.

  • a : for "app" level. The root elements of each main menu drawer or app launcher icon
  • f : for folders
  • v : for dashboards or views
  • z : for wizards
  • w : for widgets

There are over 30 different attributes which are used. However, by far the most important are 3 main attributes which store json objects which define the structure of each screen:

  • Settings : defines the data source. The instance, the cube, and the widget type. For dashboards using the new layout mode, the (x,y) coordinate and widget height & width are also saved in the settings json object.
  • ViewDef : defines the layout of the query to the data source. Which dimensions are on filter, which are fixed, which are rows and which columns. As well as the element selections and the subsets, MDX or other list types for any axes containing a set of elements
  • Options : modifies the behavior, layout, or appearance of the screen. For examples whether to show or hide toolbars or buttons, column widths, conditional formatting rules, etc.

Other attributes encode the caption, info text, whether a screen is protected, the timestamp and user of the last update, etc.

Element security applied to the }APQ UX App dimension determines which users have access to which screens, and whether users have the ability to edit public apps. (In the sense of editing the metadata of the report definition. Security of the data itself, including whether a user can write back is always determined by security on the reporting instances.)

In addition to the main "content store" }ElementAttributes_}APQ UX App there are 2 additional cubes which are used in case the content store is localized with text display in different languages depending on the user's browser locale or language preference. These are:

  • }LocalizedElementAttributes_}APQ UX App
  • }APQ UX App Localized Attr Rule Calc

The attributes which support localization are Description, CalcDescription, Caption, Code and Description, Info and TextBox. The bolded attributes support data entry. Description and Info are entered in the "cube settings" GUI and used for the object display name and info text tooltip (CalcDescription, Caption & Code and Description are rule calculated and derived from the Description input). The 2nd cube is required for performance reasons as a rule cannot be used to derive the localized Code and Description alias without significant performance impact (the rule is in this cube and values are transferred to the localized attributes cube via TI process should the content store support localization.)

Security Management

You should also refer to the initial security configuration section in the setup guide.

The ContentStore contains 4 cubes used to control security within the Apliqo UX application.

  • }APQ UX Instance Client : is used to mark which users to import from connected instances (including the option to import all users from an instance). This cube is already described in the section above.
  • }APQ UX Instance Group : is used to mark which groups to import from connected instances (including the option to import all groups from an instance). Note that importing a group not only creates the group but also syncs the group members, providing the users are also marked to be imported to the ContentStore. This cube is already described in the section above.
  • }APQ UX App Security Access : is used to set access to "apps" within Apliqo UX. As described above the application structure and all view definitions are stored in the }APQ UX App dimension and setting element security on this dimension is how access is managed in Apliqo UX. How this cube functions will be described in more detail below.
  • }APQ UX Security Manage Client Groups : is used to manage the assignment of users to groups within the ContentStore. This will de described in more detail below.

Managing access to apps ( }APQ UX App Security Access )

The }APQ UX App Security Access cube has 4 measures:

  • Permission Level: this is the primary input measure. Note that the entered permission will be inherited to the descendants.
  • Permission Level Override: any input in this measure will be forced to the Permission Level measure and stop the inheritance (and in turn be inherited to the descendants of the inputted node).
  • Permission Numeric: this measure is rule calculated and informational. A numeric representation of the security level, 0=NONE, 1=READ, 2=WRITE, 3=RESERVE, 4=LOCK, 5=ADMIN.
  • Current Permission Level: this measure is rule calculated. A direct feed via rule from the element security cube of the current security. This allows a comparison in the cube to see if the assigned security matches the current access.

Element security for the apps is assigned here and then applied to the ContentStore security model via a TI process  }APQ.UX.Security.Dim.App.LoadAccessRights which is described in more detail in the section on ContentStore TI processes.

Managing group memberships ( APQ UX Security Manage Client Groups )

The }APQ UX Security Manage Client Groups cube has 3 measures:

  • Assignment: Boolean 1/0. This is the primary input measure, with a value of 1 indicating that the user will be assigned to the group.
  • Current Assignment: this measure is rule calculated. A direct feed via rule from the }ClientGroups security cube of the current security group memberships.
  • Current Assignment Validation: this measure is rule calculated. This allows a comparison in the cube to see if the assigned security matches the current access.

The process }APQ.UX.Security.AssignClientGroups applies the assigned group memberships to the ContentStore security model. (After running this process Assignment=CurrentAssignment and CurrentAssignmentValidation=0). The process is described in more detail in the section on ContentStore TI processes.

Note: this cube can also be written to by the process }APQ.UX.Security.ClientGroups.Import.FromInstances which reads from the }APQ UX Instance Group, }APQ UX Instance Client and }APQ UX Instance ClientGroups cubes which are described above. In the case where group memberships are managed via a connected instance only this process needs to be run. Where there is a mixture of synced group membership and manually maintained in the ContentStore then both processes should be run in tandem.  

Logging

The Apliqo UX application comes with built-in logging covering the following areas:

  • Process logging :  Each TI process in the ContentStore has a standard logging snippet which is always executed by default. The snippet writes to the }APQ UX Process Execution Log cube and records start and end time, process status, the requesting user, and the runtime parameters, etc.
  • App activity logging : On each navigation to a screen and on each data refresh request the Apliqo UX web application writes to the  }APQ UX App Log cube in the ContentStore. This allows capture of the number of refresh requests per screen which allows administrators to know which areas of the application are the most used, and conversely which screens are not used. (Note this feature is disabled by default and must be enabled in the application default constants in order to track page impressions by user).
  • User properties logging : On each login request the Apliqo UX web application writes to the }APQ UX Connection Log cube which captures the operating system, browser, screen size and device type. This is important information for administrators for planning purposes. The }APQ UX Connection Analysis cube is a transformation of the same information from a "flat" form to a multi-dimensional OLAP form which can be used for analysis and reporting.
Process execution log

The process logging cube captures each TI process execution and acts as an audit log of all actions on the server.

App usage tracking

The app log stores page refresh events per screen in Apliqo UX. Individual refreshes are captured at up to the granularity of per user per second and can be aggregated to unique login per day or per month. This is critical for supporting analysis of which parts of the application are most used.

Connection log

The connection log captures unique login by user by day and also has measures which are reported for browser, device, screen size and operating system which can be transformed to dimension elements for more detailed analysis and reporting in the connection analysis cube.

Note that both the app log and connection log are available from release 2.5.2.

0 Comments

Add your comment

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.