From 3392f30e6e67349878b8b78032c67bda4b715825 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 25 Jun 2010 16:27:44 +0000 Subject: [PATCH] - Cosmetics: aligning the style in the different pages, make the iTop logo clickable, etc... - Handle persistent user preferences: for now only the menu status (open/closed) and its width. In the future the status of lists (open/closed, which columns to show...) can be considered as well. SVN:trunk[487] --- application/itopwebpage.class.inc.php | 116 +++++++++++---- application/loginwebpage.class.inc.php | 41 +++++- application/menunode.class.inc.php | 2 +- application/user.preferences.class.inc.php | 162 +++++++++++++++++++++ core/config.class.inc.php | 1 + dictionaries/dictionary.itop.ui.php | 3 + dictionaries/fr.dictionary.itop.ui.php | 3 + js/utils.js | 41 +++++- pages/ajax.render.php | 13 ++ setup/setuppage.class.inc.php | 2 +- 10 files changed, 342 insertions(+), 42 deletions(-) create mode 100644 application/user.preferences.class.inc.php diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index fa94cc53b..139d0cb6c 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -26,6 +26,7 @@ require_once("../application/nicewebpage.class.inc.php"); require_once("../application/usercontext.class.inc.php"); require_once("../application/applicationcontext.class.inc.php"); +require_once("../application/user.preferences.class.inc.php"); /** * Web page with some associated CSS and scripts (jquery) for a fancier display */ @@ -95,15 +96,44 @@ class iTopWebPage extends NiceWebPage $(document).ready(function () { // Layout + paneSize = GetUserPreference('menu_size', 300) myLayout = $('body').layout({ - west : { minSize: 200, size: 300 /* TO DO: read from a cookie ?*/, spacing_open: 16, spacing_close: 16, slideTrigger_open: "mouseover", hideTogglerOnSlide: true } + west : { + minSize: 200, size: paneSize, spacing_open: 16, spacing_close: 16, slideTrigger_open: "mouseover", hideTogglerOnSlide: true, + onclose_end: function(name, elt, state, options, layout) + { + if (state.isSliding == false) + { + SetUserPreference('menu_pane', 'closed', true); + } + }, + onresize_end: function(name, elt, state, options, layout) + { + if (state.isSliding == false) + { + SetUserPreference('menu_size', state.size, true); + } + }, + + onopen_end: function(name, elt, state, options, layout) + { + if (state.isSliding == false) + { + SetUserPreference('menu_pane', 'open', true); + } + } + } }); myLayout.addPinBtn( "#tPinMenu", "west" ); //myLayout.open( "west" ); $('.ui-layout-resizer-west').html(''); + if (GetUserPreference('menu_pane', 'open') == 'closed') + { + myLayout.close('west'); + } // Accordion Menu - $("#accordion").accordion({ header: "h3", navigation: true, autoHeight: false }); + $("#accordion").accordion({ header: "h3", navigation: true, autoHeight: false, collapsible: false }); }); //$("div[id^=tabbedContent] > ul").tabs( 1, { fxFade: true, fxSpeed: 'fast' } ); // tabs @@ -128,11 +158,13 @@ class iTopWebPage extends NiceWebPage catch(err) { // Do something with the error ! + alert(err); } //$('.display_block').draggable(); // make the blocks draggable EOF ); + $sUserPrefs = appUserPreferences::GetAsJSON(); $this->add_script(" // For automplete function findValue(li) { @@ -173,6 +205,8 @@ EOF $('#rawOutput').dialog( {autoOpen: true, modal:false}); } } + + var oUserPreferences = $sUserPrefs; "); // Add the standard menus @@ -223,36 +257,56 @@ EOF public function GetSiloSelectionForm() { - $sHtml = '
'; - $sHtml .= '
'; + $sSelected = ($this->m_currentOrganization == '') ? ' selected' : ''; + $sHtml .= ''; + while($oOrg = $oSet->Fetch()) + { + if ($this->m_currentOrganization == $oOrg->GetKey()) + { + $oCurrentOrganization = $oOrg; + $sSelected = " selected"; + + } + else + { + $sSelected = ""; + } + $sHtml .= ''; + } + $sHtml .= ''; + // Add other dimensions/context information to this form + $oAppContext = new ApplicationContext(); + $oAppContext->Reset('org_id'); // Org id is handled above and we want to be able to change it here ! + $sHtml .= $oAppContext->GetForForm(); + $sHtml .= '
'; + $sHtml .= '
'; } - $sHtml .= ''; - // Add other dimensions/context information to this form - $oAppContext = new ApplicationContext(); - $oAppContext->Reset('org_id'); // Org id is handled above and we want to be able to change it here ! - $sHtml .= $oAppContext->GetForForm(); - $sHtml .= ''; - $sHtml .= ''; return $sHtml; } @@ -341,12 +395,12 @@ EOF if (ITOP_REVISION == '$WCREV$') { // This is NOT a version built using the buil system, just display the main version - $sVersionString = "iTop Version ".ITOP_VERSION; + $sVersionString = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION); } else { // This is a build made from SVN, let display the full information - $sVersionString = "iTop Version ".ITOP_VERSION." revision ".ITOP_REVISION.", built on: ".ITOP_BUILD_DATE; + $sVersionString = Dict::Format('UI:iTopVersion:Long', ITOP_VERSION, ITOP_REVISION, ITOP_BUILD_DATE); } // Render the text of the global search form @@ -413,7 +467,7 @@ EOF echo '
'; echo ''; echo ' '; echo '
'; echo '
pin
'; diff --git a/application/loginwebpage.class.inc.php b/application/loginwebpage.class.inc.php index 1fdfb1329..b22190477 100644 --- a/application/loginwebpage.class.inc.php +++ b/application/loginwebpage.class.inc.php @@ -32,33 +32,56 @@ class LoginWebPage extends NiceWebPage public function __construct() { parent::__construct("iTop Login"); - $this->add_style(" + $this->add_style(<<add("
\n"); $this->add("
\n"); $this->add("

".Dict::S('UI:Login:Welcome')."

\n"); if ($bFailedLogin) diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php index bd68f0ca4..66b9ae5a5 100644 --- a/application/menunode.class.inc.php +++ b/application/menunode.class.inc.php @@ -276,7 +276,7 @@ abstract class MenuNode public function GetHyperlink($aExtraParams) { $aExtraParams['menu'] = $this->GetIndex(); - return $this->AddParams('../pages/UI.php?menu=', $aExtraParams); + return $this->AddParams('../pages/UI.php', $aExtraParams); } public abstract function RenderContent(WebPage $oPage, $aExtraParams = array()); diff --git a/application/user.preferences.class.inc.php b/application/user.preferences.class.inc.php new file mode 100644 index 000000000..15bfd9d2e --- /dev/null +++ b/application/user.preferences.class.inc.php @@ -0,0 +1,162 @@ + + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL + */ +require_once('../core/dbobject.class.php'); +require_once('../core/userrights.class.inc.php'); + +/** + * This class is used to store, in a persistent manner, user related settings (preferences) + * For each user, one record in the database will be created, making their preferences permanent and storing a list + * of properties (pairs of name/value strings) + * This overcomes some limitations of cookies: limited number of cookies, maximum size, depends on the browser, etc.. + * This class is used in conjunction with the GetUserPreferences/SetUserPreferences javascript functions (utils.js) + */ +class appUserPreferences extends DBObject +{ + static $oUserPrefs = null; // Local cache + + /** + * Get the value of the given property/preference + * If not set, the default value will be returned + * @param string $sCode Code/Name of the property to set + * @param string $sDefaultValue The default value + * @return string The value of the property for the current user + */ + static function GetPref($sCode, $sDefaultValue) + { + if (self::$oUserPrefs == null) + { + self::Load(); + } + $aPrefs = self::$oUserPrefs->Get('preferences'); + if (isset($aPrefs[$sCode])) + { + return $aPrefs[$sCode]; + } + else + { + return $sDefaultValue; + } + } + + /** + * Set the value for a given preference, and stores it into the database + * @param string $sCode Code/Name of the property/preference to set + * @param string $sValue Value to set + */ + static function SetPref($sCode, $sValue) + { + if (self::$oUserPrefs == null) + { + self::Load(); + } + $aPrefs = self::$oUserPrefs->Get('preferences'); + $aPrefs[$sCode] = $sValue; + self::$oUserPrefs->Set('preferences', $aPrefs); + self::Save(); + } + + /** + * Call this function to get all the preferences for the user, packed as a JSON object + * @return string JSON representation of the preferences + */ + static function GetAsJSON() + { + if (self::$oUserPrefs == null) + { + self::Load(); + } + $aPrefs = self::$oUserPrefs->Get('preferences'); + return json_encode($aPrefs); + } + + /** + * Call this function if the user has changed (like when doing a logoff...) + */ + static public function Reset() + { + self::$oUserPrefs = null; + } + /** + * Call this function to ERASE all the preferences from the current user + */ + static public function ClearPreferences() + { + self::$oUserPrefs = null; + } + + static protected function Save() + { + if (self::$oUserPrefs != null) + { + if (self::$oUserPrefs->IsModified()) + { + self::$oUserPrefs->DBUpdate(); + } + } + } + + /** + * Loads the preferences for the current user, creating the record in the database + * if needed + */ + static protected function Load() + { + if (self::$oUserPrefs != null) return; + $oSearch = new DBObjectSearch('appUserPreferences'); + $oSearch->AddCondition('userid', UserRights::GetUser()); + $oSet = new DBObjectSet($oSearch); + $oObj = $oSet->Fetch(); + if ($oObj == null) + { + // No prefs (yet) for this user, create the object + $oObj = new appUserPreferences(); + $oObj->Set('userid', UserRights::GetUser()); + $oObj->Set('preferences', array()); // Default preferences: an empty array + $oObj->DBInsert(); + } + self::$oUserPrefs = $oObj; + } + + public static function Init() + { + $aParams = array + ( + "category" => "gui", + "key_type" => "autoincrement", + "name_attcode" => "userid", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_app_preferences", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"URP_Users", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributePropertySet("preferences", array("allowed_values"=>null, "sql"=>"preferences", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + } + +} +?> diff --git a/core/config.class.inc.php b/core/config.class.inc.php index cb69269a5..0206af775 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -468,6 +468,7 @@ class Config fwrite($hFile, "\t'application' => array (\n"); fwrite($hFile, "\t\t'../application/transaction.class.inc.php',\n"); fwrite($hFile, "\t\t'../application/menunode.class.inc.php',\n"); + fwrite($hFile, "\t\t'../application/user.preferences.class.inc.php',\n"); fwrite($hFile, "\t\t'../application/audit.rule.class.inc.php',\n"); // Romain - That's dirty, because those 3 classes are in fact part of the core // but I needed those classes to be derived from cmdbAbstractObject diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php index 7a1c33411..510d9bafa 100644 --- a/dictionaries/dictionary.itop.ui.php +++ b/dictionaries/dictionary.itop.ui.php @@ -875,6 +875,9 @@ When associated with a trigger, each action is given an "order" number, specifyi 'UI:UserAccountsMenu' => 'User Accounts', 'UI:UserAccountsMenu+' => 'User Accounts', 'UI:UserAccountsMenu:Title' => 'User Accounts', + + 'UI:iTopVersion:Short' => 'iTop version %1$s', + 'UI:iTopVersion:Long' => 'iTop version %1$s-%2$s built on %3$s', )); diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index 41b52e165..4081a9fbe 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -874,6 +874,9 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé 'UI:UserAccountsMenu+' => 'Comptes Utilisateur', 'UI:UserAccountsMenu:Title' => 'Comptes Utilisateur', + 'UI:iTopVersion:Short' => 'iTop version %1$s', + 'UI:iTopVersion:Long' => 'iTop version %1$s-%2$s du %3$s', + )); ?> diff --git a/js/utils.js b/js/utils.js index 961966705..195716d82 100644 --- a/js/utils.js +++ b/js/utils.js @@ -77,4 +77,43 @@ function ReloadSearchForm(divId, sClassName, sBaseClass) $('#'+divId).unblock(); } ); -} \ No newline at end of file +} + +/** + * Stores - in a persistent way - user specific preferences + * depends on a global variable oUserPreferences created/filled by the iTopWebPage + * that acts as a local -write through- cache + */ +function SetUserPreference(sPreferenceCode, sPrefValue, bPersistent) +{ + sPreviousValue = undefined; + try + { + sPreviousValue = oUserPreferences[sPreferenceCode]; + } + catch(err) + { + sPreviousValue = undefined; + } + oUserPreferences[sPreferenceCode] = sPrefValue; + if (bPersistent && (sPrefValue != sPreviousValue)) + { + ajax_request = $.post('ajax.render.php', + { operation: 'set_pref', code: sPreferenceCode, value: sPrefValue} ); // Make it persistent + } +} + +/** + * Get user specific preferences + * depends on a global variable oUserPreferences created/filled by the iTopWebPage + * that acts as a local -write through- cache + */ +function GetUserPreference(sPreferenceCode, sDefaultValue) +{ + var value = sDefaultValue; + if ( oUserPreferences[sPreferenceCode] != undefined) + { + value = oUserPreferences[sPreferenceCode]; + } + return value; +} diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 953c8346e..aedac3a47 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -30,6 +30,8 @@ require_once('../application/wizardhelper.class.inc.php'); require_once('../application/ui.linkswidget.class.inc.php'); require_once('../application/startup.inc.php'); +require_once('../application/user.preferences.class.inc.php'); + session_start(); if (isset($_SESSION['auth_user'])) { @@ -330,6 +332,17 @@ switch($operation) $sHtml = cmdbAbstractObject::GetSearchForm($oPage, $oSet, array('currentId' => $currentId, 'baseClass' => $sRootClass)); $oPage->add($sHtml); break; + + case 'set_pref': + $sCode = utils::ReadParam('code', '', 'post'); + $sValue = utils::ReadParam('value', '', 'post'); + appUserPreferences::SetPref($sCode, $sValue); + break; + + case 'erase_all_pref': + // Can be useful in case a user got some corrupted prefs... + appUserPreferences::ClearPreferences(); + break; default: $oPage->p("Invalid query."); diff --git a/setup/setuppage.class.inc.php b/setup/setuppage.class.inc.php index a637fbd96..bdfaa1344 100644 --- a/setup/setuppage.class.inc.php +++ b/setup/setuppage.class.inc.php @@ -55,7 +55,7 @@ body { } h1 { - color: #83b217; + color: #1C94C4; font-size: 16pt; } h2 {