Custom Properties Problem DNN has the ability to store custom properties for a variety of entities in the platform. This capability has traditionally only been available to developers through a common API pattern. It should be possible to allow end users to create and manage custom properties as well. Background Custom properties are typically stored in a Settings table which is directly related to a platform entity. A Settings table contains key/value pairs as individual items, and also stores audit information for each item including the user and datetime of when the item was created or last modified. Setting names must be unique for an entity. Setting values are stored as strings ( nvarchar(max) is the optimal database column definition as it does not constrain the size of string, nor the localized format ) and if the data is a non-string datatype or rich object, it is up to the developer to implement their own serialization scheme. Custom properties are serialized during import and export to allow for portability. The API for custom settings is exposed in a consistent manner which allows developers to easily save and retrieve properties using a simple, intuitive, and high performance programming interface. Ie. public Hashtable GetTabSettings(int tabId) public void DeleteTabSetting(int tabId, string settingName) public void UpdateTabSetting(int tabId, string settingName, string settingValue) Goals We do not want to change the public API pattern that developers use to save/retrieve custom properties We want end users to be able to create and manage some custom properties through the user interface Not all properties should be exposed through the user interface We want the solution to be simple, lightweight, and not cause upgrade challenges Solution We need a way to define custom properties. The recommended industry approach is to use the concept of “metadata” to define custom properties. The DNN platform already uses a “metadata” approach in a number of areas. In fact, the existing custom Profile properties matches our business requirements very closely and contains existing code we can leverage for the display and entry of custom properties. A MetaData table can be related to an existing Settings table so that the API for storing and retrieving custom property values are unaffected. The reason for using a separate MetaData table rather than simply adding the MetaData fields to the Settings table is because we want to abstract the definition from the data and because there is a business need to define “global” MetaData which can relate to multiple entities. The MetaData table will contain the following fields: FieldName ID DataType int Nullable no EntityID int yes SettingName nvarchar(50) no CategoryName nvarchar(50) no Title nvarchar(50) no Description nvarchar(255) yes DataType nvarchar(50) no (default=string) DefaultValue nvarchar(255) yes MaxLength int no (default=0) ViewOrder int no Comment Primary Key. Identity. Links to the entity similar to the Settings table. If ID is null it means that the MetaData item is a “global” definition and could be applied to ANY entity Key which corresponds to an item in the Settings table Method to group items in the user interface Title of field to display in the UI Description of field to be used as field level help text Used for input validation in UI – supports a limited number of values ( ie. text, integer, date ) Optional default value for field. Can also be a comma delimited list of allowable values that would be displayed in a dropdownlist in the UI ( ie. Red,White,Blue ) Optional maximum length of the input value Used in conjunction with CategoryName to ReadOnly bit no (default=0) Visible bit no (default=1) CreatedByUserID CreatedOnDate LastModifiedByUserID LastModifiedOnDate int datetime int datetime no no no no enable you to specify the order of items within a category Specifies if the item is read only in the UI Indicates whether the item should be surfaced in the UI or should be hidden. Audit Audit Audit Audit We will not worry about granular permissions related to Settings in the initial implementation. Data entry security will be constrained to those people who can view the specific administrative tab in the user interface which relates to the entity. And publishing will depend on the module or skin object surfacing the Setting value. Some new API methods would need to be created to interact with the new MetaData table. public list GetTabSettingsMetaData(int tabId) public void AddTabSettingMetaData(TabSettingMetaDataInfo TabSettingMetaData) public void UpdateTabSettingMetaData(TabSettingMetaDataInfo TabSettingMetaData) public void DeleteTabSettingMetaData(TabSettingMetaDataInfo TabSettingMetaData) A user interface will need to be created where an administrator can create/modify/remove custom properties, as well as edit/save Setting values: Only those Settings items which have an associated MetaData record that is flagged as Visible would be surfaced through the UI. If a module developer wants their Settings to be manageable through the UI, they would need to create MetaData records during installation. Add – you can add a Metadata record as long as a Metadata with the Name does not already exist. If no Metadata record exists, but a Settings record exists with the Name, it will ask if you wish to associate the MetaData with the Setting. Delete – when you delete a Metadata record it will not delete the related records in the Settings table unless the user specifies that they wish to do so. Setting values are automatically serialized during import/export of site/page templates. We will also need to enhance the platform so that the MetaData info is also serialized during import/export. Localization For consistency, the localized values for UI metadata fields such as Category, Title, and Description will be stored in the core RESX file where the other localized UI values are stored for the administrative user interface. Usage The usage of custom properties will depend on the entity. The storage of Settings values have not changed, so the API for using Settings will stay the same in skins and modules. For example, TabSettings can be used within a skin using the following code: <%= PortalSettings.ActiveTab.TabSettings[“SettingName”] %> If you need more elaborate logic for utilizing Settings then you can build a module or skin object which has the full capabilities of ASP.NET. On upgrade, we will not automatically create any MetaData records, which means that any Settings items that already exist will NOT be surfaced through the UI automatically. Scope We will initially implement this in the Page Settings area. And once we are satisfied with the implementation, we will implement this in Host, Site, Module, and Role management areas. Business Challenge None. Low Risk, High Reward. References http://www.dotnetnuke.com/Community/CommunityVoice/cid/138568.aspx#138568.aspx http://www.dotnetnuke.com/Resources/Forums/forumid/108/threadid/435773/scope/posts /threadpage/1.aspx