');
+ }
}
}
return $bActive;
@@ -298,6 +301,15 @@ abstract class MenuNode
return $this->AddParams('../pages/UI.php', $aExtraParams);
}
+ /**
+ * Tells whether the menu is enabled (i.e. displayed) for the current user
+ * @return bool True if enabled, false otherwise
+ */
+ public function IsEnabled()
+ {
+ return true;
+ }
+
public abstract function RenderContent(WebPage $oPage, $aExtraParams = array());
protected function AddParams($sHyperlink, $aExtraParams)
@@ -394,20 +406,23 @@ class OQLMenuNode extends MenuNode
{
protected $sPageTitle;
protected $sOQL;
+ protected $bSearch;
/**
* Create a menu item based on an OQL query and inserts it into the application's main menu
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
- * @param string $sPageTitle Title displayed into the page's content (will be looked-up in the dictionnary for translation)
+ * @param string $sOQL OQL query defining the set of objects to be displayed
* @param integer $iParentIndex ID of the parent menu
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
+ * @param bool $bSearch Whether or not to display a (collapsed) search frame at the top of the page
* @return MenuNode
*/
- public function __construct($sMenuId, $sOQL, $iParentIndex, $fRank = 0)
+ public function __construct($sMenuId, $sOQL, $iParentIndex, $fRank = 0, $bSearch = false)
{
parent::__construct($sMenuId, $iParentIndex, $fRank);
$this->sPageTitle = "Menu:$sMenuId+";
$this->sOQL = $sOQL;
+ $this->bSearch = $bSearch;
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
@@ -422,8 +437,16 @@ class OQLMenuNode extends MenuNode
$sIcon = '';
}
// The standard template used for all such pages: a (closed) search form at the top and a list of results at the bottom
- $sTemplate = <<bSearch)
+ {
+ $sTemplate .= <<$this->sOQL
+EOF;
+ }
+
+ $sTemplate .= <<$sIcon$this->sPageTitle
$this->sOQL
EOF;
@@ -431,6 +454,40 @@ EOF;
$oTemplate->Render($oPage, $aExtraParams);
}
}
+/**
+ * This class defines a menu item that displays a search form for the given class of objects
+ */
+class SearchMenuNode extends MenuNode
+{
+ protected $sPageTitle;
+ protected $sClass;
+
+ /**
+ * Create a menu item based on an OQL query and inserts it into the application's main menu
+ * @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
+ * @param string $sClass The class of objects to search for
+ * @param string $sPageTitle Title displayed into the page's content (will be looked-up in the dictionnary for translation)
+ * @param integer $iParentIndex ID of the parent menu
+ * @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
+ * @return MenuNode
+ */
+ public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0)
+ {
+ parent::__construct($sMenuId, $iParentIndex, $fRank);
+ $this->sPageTitle = "Menu:$sMenuId+";
+ $this->sClass = $sClass;
+ }
+
+ public function RenderContent(WebPage $oPage, $aExtraParams = array())
+ {
+ // The standard template used for all such pages: an open search form at the top
+ $sTemplate = <<SELECT $this->sClass
+EOF;
+ $oTemplate = new DisplayTemplate($sTemplate);
+ $oTemplate->Render($oPage, $aExtraParams);
+ }
+}
/**
* This class defines a menu that points to any web page. It takes only two parameters:
@@ -468,4 +525,60 @@ class WebPageMenuNode extends MenuNode
assert(false); // Shall never be called, the external web page will handle the display by itself
}
}
+
+/**
+ * This class defines a menu that points to the page for creating a new object of the specified class.
+ * It take only one parameter: the name of the class
+ * Note: the parameter menu=xxx (where xxx is the id of the menu itself) will be added to the hyperlink
+ * in order to make it the active one
+ */
+class NewObjectMenuNode extends MenuNode
+{
+ protected $sClass;
+
+ /**
+ * Create a menu item that points to the URL for creating a new object, the menu will be added only if the current user has enough
+ * rights to create such an object (or an object of a child class)
+ * @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
+ * @param string $sClass URL to the page to load. Use relative URL if you want to keep the application portable !
+ * @param integer $iParentIndex ID of the parent menu
+ * @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
+ * @return MenuNode
+ */
+ public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0)
+ {
+ parent::__construct($sMenuId, $iParentIndex, $fRank);
+ $this->sClass = $sClass;
+ }
+
+ public function GetHyperlink($aExtraParams)
+ {
+ $sHyperlink = '../pages/UI.php?operation=new&class='.$this->sClass;
+ $aExtraParams['menu'] = $this->GetIndex();
+ return $this->AddParams($sHyperlink, $aExtraParams);
+ }
+
+ public function IsEnabled()
+ {
+ // Enable this menu, only if the current user has enough rights to create such an object, or an object of
+ // any child class
+
+ $aSubClasses = MetaModel::EnumChildClasses($this->sClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
+ $bActionIsAllowed = false;
+
+ foreach($aSubClasses as $sCandidateClass)
+ {
+ if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
+ {
+ $bActionIsAllowed = true;
+ break; // Enough for now
+ }
+ }
+ return $bActionIsAllowed;
+ }
+ public function RenderContent(WebPage $oPage, $aExtraParams = array())
+ {
+ assert(false); // Shall never be called, the external web page will handle the display by itself
+ }
+}
?>
diff --git a/application/template.class.inc.php b/application/template.class.inc.php
index 964dc40e3..3fb8ad094 100644
--- a/application/template.class.inc.php
+++ b/application/template.class.inc.php
@@ -170,7 +170,7 @@ class DisplayTemplate
case 'itopcheck':
$sClassName = $aAttributes['class'];
- if (MetaModel::IsValidClass($sClassName))
+ if (MetaModel::IsValidClass($sClassName) && UserRights::IsActionAllowed($sClassName, UR_ACTION_READ))
{
$oTemplate = new DisplayTemplate($sContent);
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
diff --git a/application/ui.passwordwidget.class.inc.php b/application/ui.passwordwidget.class.inc.php
new file mode 100644
index 000000000..43f240ba4
--- /dev/null
+++ b/application/ui.passwordwidget.class.inc.php
@@ -0,0 +1,68 @@
+
+ * @author Romain Quetiez
+ * @author Denis Flaven
+ * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
+ */
+
+require_once('../application/webpage.class.inc.php');
+require_once('../application/displayblock.class.inc.php');
+
+class UIPasswordWidget
+{
+ protected static $iWidgetIndex = 0;
+ protected $sAttCode;
+ protected $sNameSuffix;
+ protected $iId;
+
+ public function __construct($sAttCode, $iInputId, $sNameSuffix = '')
+ {
+ self::$iWidgetIndex++;
+ $this->sAttCode = $sAttCode;
+ $this->sNameSuffix = $sNameSuffix;
+ $this->iId = $iInputId;
+ }
+
+ /**
+ * Get the HTML fragment corresponding to the linkset editing widget
+ * @param WebPage $oP The web page used for all the output
+ * @param Hash $aArgs Extra context arguments
+ * @return string The HTML fragment to be inserted into the page
+ */
+ public function Display(WebPage $oPage, $aArgs = array())
+ {
+ $sCode = $this->sAttCode.$this->sNameSuffix;
+ $iWidgetIndex = self::$iWidgetIndex;
+ $sHtmlValue = '';
+ $sHtmlValue = ' ';
+ $sHtmlValue .= ' '.Dict::S('UI:PasswordConfirm').' ';
+ $sHtmlValue .= '';
+
+ $oPage->add_ready_script("$('#$this->iId').bind('keyup change', function(evt) { return PasswordFieldChanged('$this->iId') } );"); // Bind to a custom event: validate
+ $oPage->add_ready_script("$('#$this->iId').bind('keyup change', function(evt) { return PasswordFieldChanged('$this->iId') } );"); // Bind to a custom event: validate
+ $oPage->add_ready_script("$('#$this->iId').bind('keyup change validate', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate
+ $oPage->add_ready_script("$('#{$this->iId}_confirm').bind('keyup change', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate
+
+ return $sHtmlValue;
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php
index d541eeef6..895ffec48 100644
--- a/core/attributedef.class.inc.php
+++ b/core/attributedef.class.inc.php
@@ -26,6 +26,7 @@
require_once('MyHelpers.class.inc.php');
require_once('ormdocument.class.inc.php');
+require_once('ormpassword.class.inc.php');
/**
* MissingColumnException - sent if an attribute is being created but the column is missing in the row
@@ -377,7 +378,7 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
* @package iTopORM
*/
class AttributeDBFieldVoid extends AttributeDefinition
-{
+{
static protected function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("allowed_values", "depends_on", "sql"));
@@ -465,7 +466,7 @@ class AttributeDBFieldVoid extends AttributeDefinition
* @package iTopORM
*/
class AttributeDBField extends AttributeDBFieldVoid
-{
+{
static protected function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("default_value", "is_null_allowed"));
@@ -861,6 +862,66 @@ class AttributePassword extends AttributeString
}
}
+/**
+ * Map a text column (size < 255) to an attribute that is encrypted in the database
+ * The encryption is based on a key set per iTop instance. Thus if you export your
+ * database (in SQL) to someone else without providing the key at the same time
+ * the encrypted fields will remain encrypted
+ *
+ * @package iTopORM
+ */
+class AttributeEncryptedString extends AttributeString
+{
+ static $sKey = null; // Encryption key used for all encrypted fields
+
+ public function __construct($sCode, $aParams)
+ {
+ parent::__construct($sCode, $aParams);
+ if (self::$sKey == null)
+ {
+ self::$sKey = MetaModel::GetConfig()->GetEncryptionKey();
+ }
+ }
+
+ protected function GetSQLCol() {return "TINYBLOB";}
+
+ public function GetFilterDefinitions()
+ {
+ // Note: due to this, you will get an error if a an encrypted field is declared as a search criteria (see ZLists)
+ // not allowed to search on encrypted fields !
+ return array();
+ }
+
+ public function MakeRealValue($proposedValue)
+ {
+ if (is_null($proposedValue)) return null;
+ return (string)$proposedValue;
+ }
+
+ /**
+ * Decrypt the value when reading from the database
+ */
+ public function FromSQLToValue($aCols, $sPrefix = '')
+ {
+ $oSimpleCrypt = new SimpleCrypt();
+ $sValue = $oSimpleCrypt->Decrypt(self::$sKey, $aCols[$sPrefix]);
+ return $sValue;
+ }
+
+ /**
+ * Encrypt the value before storing it in the database
+ */
+ public function GetSQLValues($value)
+ {
+ $oSimpleCrypt = new SimpleCrypt();
+ $encryptedValue = $oSimpleCrypt->Encrypt(self::$sKey, $value);
+
+ $aValues = array();
+ $aValues[$this->Get("sql")] = $encryptedValue;
+ return $aValues;
+ }
+}
+
/**
* Map a text column (size > ?) to an attribute
*
@@ -1880,6 +1941,140 @@ class AttributeBlob extends AttributeDefinition
return ''; // Not exportable in XML, or as CDATA + some subtags ??
}
}
+/**
+ * One way encrypted (hashed) password
+ */
+class AttributeOneWayPassword extends AttributeDefinition
+{
+ static protected function ListExpectedParams()
+ {
+ return array_merge(parent::ListExpectedParams(), array("depends_on"));
+ }
+
+ public function GetType() {return "One Way Password";}
+ public function GetTypeDesc() {return "One Way Password";}
+ public function GetEditClass() {return "One Way Password";}
+
+ public function IsDirectField() {return true;}
+ public function IsScalar() {return true;}
+ public function IsWritable() {return true;}
+ public function GetDefaultValue() {return "";}
+ public function IsNullAllowed() {return $this->GetOptional("is_null_allowed", false);}
+
+ // Facilitate things: allow the user to Set the value from a string or from an ormPassword (already encrypted)
+ public function MakeRealValue($proposedValue)
+ {
+ $oPassword = $proposedValue;
+ if (!is_object($oPassword))
+ {
+ $oPassword = new ormPassword('', '');
+ $oPassword->SetPassword($proposedValue);
+ }
+ return $oPassword;
+ }
+
+ public function GetSQLExpressions()
+ {
+ $aColumns = array();
+ // Note: to optimize things, the existence of the attribute is determined by the existence of one column with an empty suffix
+ $aColumns[''] = $this->GetCode().'_hash';
+ $aColumns['_salt'] = $this->GetCode().'_salt';
+ return $aColumns;
+ }
+
+ public function FromSQLToValue($aCols, $sPrefix = '')
+ {
+ if (!isset($aCols[$sPrefix]))
+ {
+ $sAvailable = implode(', ', array_keys($aCols));
+ throw new MissingColumnException("Missing column '$sPrefix' from {$sAvailable}");
+ }
+ $hashed = $aCols[$sPrefix];
+
+ if (!isset($aCols[$sPrefix.'_salt']))
+ {
+ $sAvailable = implode(', ', array_keys($aCols));
+ throw new MissingColumnException("Missing column '".$sPrefix."_salt' from {$sAvailable}");
+ }
+ $sSalt = $aCols[$sPrefix.'_salt'];
+
+ $value = new ormPassword($hashed, $sSalt);
+ return $value;
+ }
+
+ public function GetSQLValues($value)
+ {
+ // #@# Optimization: do not load blobs anytime
+ // As per mySQL doc, selecting blob columns will prevent mySQL from
+ // using memory in case a temporary table has to be created
+ // (temporary tables created on disk)
+ // We will have to remove the blobs from the list of attributes when doing the select
+ // then the use of Get() should finalize the load
+ if ($value instanceOf ormPassword)
+ {
+ $aValues = array();
+ $aValues[$this->GetCode().'_hash'] = $value->GetHash();
+ $aValues[$this->GetCode().'_salt'] = $value->GetSalt();
+ }
+ else
+ {
+ $aValues = array();
+ $aValues[$this->GetCode().'_hash'] = '';
+ $aValues[$this->GetCode().'_salt'] = '';
+ echo "Writing an empty password !!!";
+ echo "
\n";
+ print_r($value);
+ echo "
\n";
+ }
+ return $aValues;
+ }
+
+ public function GetSQLColumns()
+ {
+ $aColumns = array();
+ $aColumns[$this->GetCode().'_hash'] = 'TINYBLOB';
+ $aColumns[$this->GetCode().'_salt'] = 'TINYBLOB';
+ return $aColumns;
+ }
+
+ public function GetFilterDefinitions()
+ {
+ return array();
+ // still not working... see later...
+ }
+
+ public function GetBasicFilterOperators()
+ {
+ return array();
+ }
+ public function GetBasicFilterLooseOperator()
+ {
+ return '=';
+ }
+
+ public function GetBasicFilterSQLExpr($sOpCode, $value)
+ {
+ return 'true';
+ }
+
+ public function GetAsHTML($value)
+ {
+ if (is_object($value))
+ {
+ return $value->GetAsHTML();
+ }
+ }
+
+ public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"')
+ {
+ return ''; // Not exportable in CSV
+ }
+
+ public function GetAsXML($value)
+ {
+ return ''; // Not exportable in XML
+ }
+}
// Indexed array having two dimensions
class AttributeTable extends AttributeText
diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php
index ab520fe92..f32dc2bf3 100644
--- a/core/cmdbchangeop.class.inc.php
+++ b/core/cmdbchangeop.class.inc.php
@@ -102,7 +102,7 @@ class CMDBChangeOpCreate extends CMDBChangeOp
*/
public function GetDescription()
{
- return 'Object created';
+ return Dict::S('Change:ObjectCreated');
}
}
@@ -135,7 +135,7 @@ class CMDBChangeOpDelete extends CMDBChangeOp
*/
public function GetDescription()
{
- return 'Object deleted';
+ return Dict::S('Change:ObjectDeleted');
}
}
@@ -228,16 +228,16 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
if (substr($sNewValue,0, strlen($sOldValue)) == $sOldValue) // Text added at the end
{
$sDelta = substr($sNewValue, strlen($sOldValue));
- $sResult = "$sDelta appended to $sAttName";
+ $sResult = Dict::Format('Change:Text_AppendedTo_AttName', $sDelta, $sAttName);
}
else if (substr($sNewValue, -strlen($sOldValue)) == $sOldValue) // Text added at the beginning
{
$sDelta = substr($sNewValue, 0, strlen($sNewValue) - strlen($sOldValue));
- $sResult = "$sDelta appended to $sAttName";
+ $sResult = Dict::Format('Change:Text_AppendedTo_AttName', $sDelta, $sAttName);
}
else
{
- $sResult = "$sAttName set to $sNewValue (previous value: $sOldValue)";
+ $sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sAttName, $sNewValue, $sOldValue);
}
}
elseif($bIsHtml && $oAttDef->IsExternalKey())
@@ -253,7 +253,7 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
}
else
{
- $sResult = "$sAttName set to $sNewValue (previous value: $sOldValue)";
+ $sResult = Dict::Format('Change:Att_SetTo_NewValue_PreviousValue_OldValue', $sAttName, $sNewValue, $sOldValue);
}
}
return $sResult;
@@ -313,7 +313,111 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
$sDocView .= " ".Dict::Format('UI:OpenDocumentInNewWindow_',$oPrevDoc->GetDisplayLink(get_class($this), $this->GetKey(), 'prevdata')).", \n";
$sDocView .= Dict::Format('UI:DownloadDocument_', $oPrevDoc->GetDownloadLink(get_class($this), $this->GetKey(), 'prevdata'))."\n";
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
- $sResult = "$sAttName changed, previous value: $sDocView";
+ $sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sDocView);
+ }
+ return $sResult;
+ }
+}
+/**
+ * Safely record the modification of one way encrypted password
+ */
+class CMDBChangeOpSetAttributeOneWayPassword extends CMDBChangeOpSetAttribute
+{
+ public static function Init()
+ {
+ $aParams = array
+ (
+ "category" => "core/cmdb",
+ "key_type" => "",
+ "name_attcode" => "change",
+ "state_attcode" => "",
+ "reconc_keys" => array(),
+ "db_table" => "priv_changeop_setatt_pwd",
+ "db_key_field" => "id",
+ "db_finalclass_field" => "",
+ );
+ MetaModel::Init_Params($aParams);
+ MetaModel::Init_InheritAttributes();
+ MetaModel::Init_AddAttribute(new AttributeOneWayPassword("prev_pwd", array("sql" => 'data', "default_value" => '', "is_null_allowed"=> true, "allowed_values" => null, "depends_on"=>array())));
+
+ // Display lists
+ MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for the complete details
+ MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for a list
+ }
+
+ /**
+ * Describe (as a text string) the modifications corresponding to this change
+ */
+ public function GetDescription()
+ {
+ // Temporary, until we change the options of GetDescription() -needs a more global revision
+ $bIsHtml = true;
+
+ $sResult = '';
+ $oTargetObjectClass = $this->Get('objclass');
+ $oTargetObjectKey = $this->Get('objkey');
+ $oTargetSearch = new DBObjectSearch($oTargetObjectClass);
+ $oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
+
+ $oMonoObjectSet = new DBObjectSet($oTargetSearch);
+ if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
+ {
+ $oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
+ $sAttName = $oAttDef->GetLabel();
+ $sResult = Dict::Format('Change:AttName_Changed', $sAttName);
+ }
+ return $sResult;
+ }
+}
+
+/**
+ * Safely record the modification of an encrypted field
+ */
+class CMDBChangeOpSetAttributeEncrypted extends CMDBChangeOpSetAttribute
+{
+ public static function Init()
+ {
+ $aParams = array
+ (
+ "category" => "core/cmdb",
+ "key_type" => "",
+ "name_attcode" => "change",
+ "state_attcode" => "",
+ "reconc_keys" => array(),
+ "db_table" => "priv_changeop_setatt_encrypted",
+ "db_key_field" => "id",
+ "db_finalclass_field" => "",
+ );
+ MetaModel::Init_Params($aParams);
+ MetaModel::Init_InheritAttributes();
+ MetaModel::Init_AddAttribute(new AttributeEncryptedString("prevstring", array("sql" => 'data', "default_value" => '', "is_null_allowed"=> true, "allowed_values" => null, "depends_on"=>array())));
+
+ // Display lists
+ MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for the complete details
+ MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for a list
+ }
+
+ /**
+ * Describe (as a text string) the modifications corresponding to this change
+ */
+ public function GetDescription()
+ {
+ // Temporary, until we change the options of GetDescription() -needs a more global revision
+ $bIsHtml = true;
+
+ $sResult = '';
+ $oTargetObjectClass = $this->Get('objclass');
+ $oTargetObjectKey = $this->Get('objkey');
+ $oTargetSearch = new DBObjectSearch($oTargetObjectClass);
+ $oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
+
+ $oMonoObjectSet = new DBObjectSet($oTargetSearch);
+ if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
+ {
+ $oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
+ $sAttName = $oAttDef->GetLabel();
+ $sPrevString = $this->Get('prevstring');
+ $sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sPrevString);
}
return $sResult;
}
@@ -370,7 +474,7 @@ class CMDBChangeOpSetAttributeText extends CMDBChangeOpSetAttribute
$sTextView = '
'.$this->GetAsHtml('prevdata').'
';
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
- $sResult = "$sAttName changed, previous value: $sTextView";
+ $sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sTextView);
}
return $sResult;
}
diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php
index 3a8de347c..50ba69a6b 100644
--- a/core/cmdbobject.class.inc.php
+++ b/core/cmdbobject.class.inc.php
@@ -187,7 +187,47 @@ abstract class CMDBObject extends DBObject
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if ($oAttDef->IsLinkSet()) continue; // #@# temporary
- if ($oAttDef instanceOf AttributeBlob)
+ if ($oAttDef instanceOf AttributeOneWayPassword)
+ {
+ // One Way encrypted passwords' history is stored -one way- encrypted
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeOneWayPassword");
+ $oMyChangeOp->Set("change", $oChange->GetKey());
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+
+ if (array_key_exists($sAttCode, $aOrigValues))
+ {
+ $original = $aOrigValues[$sAttCode];
+ }
+ else
+ {
+ $original = '';
+ }
+ $oMyChangeOp->Set("prev_pwd", $original);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ elseif ($oAttDef instanceOf AttributeEncryptedString)
+ {
+ // Encrypted string history is stored encrypted
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeEncrypted");
+ $oMyChangeOp->Set("change", $oChange->GetKey());
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+
+ if (array_key_exists($sAttCode, $aOrigValues))
+ {
+ $original = $aOrigValues[$sAttCode];
+ }
+ else
+ {
+ $original = '';
+ }
+ $oMyChangeOp->Set("prevdata", $original);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ elseif ($oAttDef instanceOf AttributeBlob)
{
// Data blobs
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeBlob");
diff --git a/core/config.class.inc.php b/core/config.class.inc.php
index 8b83ed22b..1afedc700 100644
--- a/core/config.class.inc.php
+++ b/core/config.class.inc.php
@@ -46,6 +46,7 @@ define ('DEFAULT_SECURE_CONNECTION_REQUIRED', false);
define ('DEFAULT_HTTPS_HYPERLINKS', false);
define ('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|basic|external');
define ('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
+define ('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random value, later...
/**
* Config
@@ -126,6 +127,13 @@ class Config
*/
protected $m_sExtAuthVariable;
+ /**
+ * @var string Encryption key used for all attributes of type "encrypted string". Can be set to a random value
+ * unless you want to import a database from another iTop instance, in which case you must use
+ * the same encryption key in order to properly decode the encrypted fields
+ */
+ protected $m_sEncryptionKey;
+
public function __construct($sConfigFile, $bLoadConfig = true)
{
$this->m_sFile = $sConfigFile;
@@ -177,6 +185,7 @@ class Config
$this->m_sDefaultLanguage = 'EN US';
$this->m_sAllowedLoginTypes = DEFAULT_ALLOWED_LOGIN_TYPES;
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
+ $this->m_sEncryptionKey = DEFAULT_ENCRYPTION_KEY;
$this->m_aModuleSettings = array();
@@ -278,6 +287,7 @@ class Config
$this->m_sDefaultLanguage = isset($MySettings['default_language']) ? trim($MySettings['default_language']) : 'EN US';
$this->m_sAllowedLoginTypes = isset($MySettings['allowed_login_types']) ? trim($MySettings['allowed_login_types']) : DEFAULT_ALLOWED_LOGIN_TYPES;
$this->m_sExtAuthVariable = isset($MySettings['ext_auth_variable']) ? trim($MySettings['ext_auth_variable']) : DEFAULT_EXT_AUTH_VARIABLE;
+ $this->m_sEncryptionKey = isset($MySettings['encryption_key']) ? trim($MySettings['encryption_key']) : DEFAULT_ENCRYPTION_KEY;
}
protected function Verify()
@@ -430,6 +440,10 @@ class Config
return $this->m_sDefaultLanguage;
}
+ public function GetEncryptionKey()
+ {
+ return $this->m_sEncryptionKey;
+ }
public function GetAllowedLoginTypes()
{
@@ -531,6 +545,11 @@ class Config
$this->m_sExtAuthVariable = $sExtAuthVariable;
}
+ public function SetEncryptionKey($sKey)
+ {
+ $this->m_sEncryptionKey = $sKey;
+ }
+
public function FileIsWritable()
{
return is_writable($this->m_sFile);
@@ -580,6 +599,7 @@ class Config
fwrite($hFile, "\t'https_hyperlinks' => ".($this->m_bHttpsHyperlinks ? 'true' : 'false').",\n");
fwrite($hFile, "\t'default_language' => '{$this->m_sDefaultLanguage}',\n");
fwrite($hFile, "\t'allowed_login_types' => '{$this->m_sAllowedLoginTypes}',\n");
+ fwrite($hFile, "\t'encryption_key' => '{$this->m_sEncryptionKey}',\n");
fwrite($hFile, ");\n");
fwrite($hFile, "\n");
diff --git a/core/metamodel.class.php b/core/metamodel.class.php
index 6b9ab63e6..4c242009d 100644
--- a/core/metamodel.class.php
+++ b/core/metamodel.class.php
@@ -3216,6 +3216,11 @@ abstract class MetaModel
return self::$m_oConfig->GetModuleSetting($sModule, $sProperty, $defaultvalue);
}
+ public static function GetConfig()
+ {
+ return self::$m_oConfig;
+ }
+
protected static $m_aPlugins = array();
public static function RegisterPlugin($sType, $sName, $aInitCallSpec = array())
{
diff --git a/core/ormpassword.class.inc.php b/core/ormpassword.class.inc.php
new file mode 100644
index 000000000..217076480
--- /dev/null
+++ b/core/ormpassword.class.inc.php
@@ -0,0 +1,119 @@
+
+ * @author Romain Quetiez
+ * @author Denis Flaven
+ * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
+ * @package itopORM
+ */
+
+class ormPassword
+{
+ protected $m_sHashed;
+ protected $m_sSalt;
+
+ /**
+ * Constructor, initializes the password from the encrypted values
+ */
+ public function __construct($sHash = '', $sSalt = '')
+ {
+ $this->m_sHashed = $sHash;
+ $this->m_sSalt = $sSalt;
+ }
+
+ /**
+ * Encrypts the clear text password, with a unique salt
+ */
+ public function SetPassword($sClearTextPassword)
+ {
+ $this->m_sSalt = SimpleCrypt::GetNewSalt();
+ $this->m_sHashed = $this->ComputeHash($sClearTextPassword);
+ }
+
+ /**
+ * Print the password: displays some stars
+ * @return string
+ */
+ public function __toString()
+ {
+ return '*****'; // Password can not be read
+ }
+
+ public function IsEmpty()
+ {
+ return ($this->m_hashed == null);
+ }
+
+ public function GetHash()
+ {
+ return $this->m_sHashed;
+ }
+
+ public function GetSalt()
+ {
+ return $this->m_sSalt;
+ }
+
+ /**
+ * Displays the password: displays some stars
+ * @return string
+ */
+ public function GetAsHTML()
+ {
+ return '*****'; // Password can not be read
+ }
+
+ /**
+ * Check if the supplied clear text password matches the encrypted one
+ * @param string $sClearTextPassword
+ * @return boolean True if it matches, false otherwise
+ */
+ public function CheckPassword($sClearTextPassword)
+ {
+ $bResult = false;
+ $sHashedPwd = $this->ComputeHash($sClearTextPassword);
+ if ($this->m_sHashed == $sHashedPwd)
+ {
+ $bResult = true;
+ }
+ return $bResult;
+ }
+
+ /**
+ * Computes the hashed version of a password using a unique salt
+ * for this password. A unique salt is generated if needed
+ * @return string
+ */
+ protected function ComputeHash($sClearTextPwd)
+ {
+ if ($this->m_sSalt == null)
+ {
+ $this->m_sSalt = SimpleCrypt::GetNewSalt();
+ }
+ return hash('sha256', $this->m_sSalt.$sClearTextPwd);
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/simplecrypt.class.inc.php b/core/simplecrypt.class.inc.php
new file mode 100644
index 000000000..f2bf00a8d
--- /dev/null
+++ b/core/simplecrypt.class.inc.php
@@ -0,0 +1,235 @@
+encrypt('a_key','the_text');
+ * $sClearText = $oSimpleCrypt->decrypt('a_key',$encrypted);
+ *
+ * The result is $plain equals to 'the_text'
+ *
+ * You can use a different engine if you don't have Mcrypt:
+ * $oSimpleCrypt = new SimpleCrypt('Simple');
+ *
+ * A string encrypted with one engine can't be decrypted with
+ * a different one even if the key is the same.
+ *
+ * @author Miguel Ros
+ * @author Erwan Taloc
+ * @author Romain Quetiez
+ * @author Denis Flaven
+ * @version 0.3
+ * @license GPL
+ */
+
+class SimpleCrypt
+{
+ /**
+ * Constructor
+ * @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt
+ */
+ function __construct($sEngineName = 'Mcrypt')
+ {
+ if (($sEngineName == 'Mcrypt') && (!function_exists('mcrypt_module_open')))
+ {
+ // Defaults to Simple encryption if the mcrypt module is not present
+ $sEngineName = 'Simple';
+ }
+ $sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
+ $this->oEngine = new $sEngineName;
+ }
+
+ /**
+ * Encrypts the string with the given key
+ * @param string $key
+ * @param string $sString Plaintext string
+ * @return string Ciphered string
+ */
+ function Encrypt($key, $sString)
+ {
+ return $this->oEngine->Encrypt($key,$sString);
+ }
+
+
+ /**
+ * Decrypts the string by the given key
+ * @param string $key
+ * @param string $string Ciphered string
+ * @return string Plaintext string
+ */
+ function Decrypt($key, $string)
+ {
+ return $this->oEngine->Decrypt($key,$string);
+ }
+
+ /**
+ * Returns a random "salt" value, to be used when "hashing" a password
+ * using a one-way encryption algorithm, to prevent an attack using a "rainbow table"
+ * Tryes to use the best available random number generator
+ * @return string The generated random "salt"
+ */
+ static function GetNewSalt()
+ {
+ // Copied from http://www.php.net/manual/en/function.mt-rand.php#83655
+ // get 128 pseudorandom bits in a string of 16 bytes
+
+ $sRandomBits = null;
+
+ // Unix/Linux platform?
+ $fp = @fopen('/dev/urandom','rb');
+ if ($fp !== FALSE)
+ {
+ //echo "Random bits pulled from /dev/urandom \n";
+ $sRandomBits .= @fread($fp,16);
+ @fclose($fp);
+ }
+ else
+ {
+ // MS-Windows platform?
+ if (@class_exists('COM'))
+ {
+ // http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx
+ try
+ {
+ $CAPI_Util = new COM('CAPICOM.Utilities.1');
+ $sBase64RandomBits = ''.$CAPI_Util->GetRandom(16,0);
+
+ // if we ask for binary data PHP munges it, so we
+ // request base64 return value. We squeeze out the
+ // redundancy and useless ==CRLF by hashing...
+ if ($sBase64RandomBits)
+ {
+ //echo "Random bits got from CAPICOM.Utilities.1 \n";
+ $sRandomBits = md5($sBase64RandomBits, TRUE);
+ }
+ }
+ catch (Exception $ex)
+ {
+ // echo 'Exception: ' . $ex->getMessage();
+ }
+ }
+ }
+ if ($sRandomBits == null)
+ {
+ // No "strong" random generator available, use PHP's built-in mechanism
+ //echo "Random bits generated from mt_rand \n";
+ mt_srand(crc32(microtime()));
+ $sRandomBits = '';
+ for($i = 0; $i < 4; $i++)
+ {
+ $sRandomBits .= sprintf('%04x', mt_rand(0, 65535));
+ }
+
+
+ }
+ return $sRandomBits;
+ }
+}
+
+/**
+ * Interface for encryption engines
+ */
+interface CryptEngine
+{
+ function Encrypt($key, $sString);
+ function Decrypt($key, $encrypted_data);
+}
+
+/**
+ * Simple Engine doesn't need any PHP extension.
+ * Every encryption of the same string with the same key
+ * will return the same encrypted string
+ */
+class SimpleCryptSimpleEngine implements CryptEngine
+{
+ public function Encrypt($key, $sString)
+ {
+ $result = '';
+ for($i=1; $i<=strlen($sString); $i++)
+ {
+ $char = substr($sString, $i-1, 1);
+ $keychar = substr($key, ($i % strlen($key))-1, 1);
+ $char = chr(ord($char)+ord($keychar));
+ $result.=$char;
+ }
+ return $result;
+ }
+
+ public function Decrypt($key, $encrypted_data)
+ {
+ $result = '';
+ for($i=1; $i<=strlen($encrypted_data); $i++)
+ {
+ $char = substr($encrypted_data, $i-1, 1);
+ $keychar = substr($key, ($i % strlen($key))-1, 1);
+ $char = chr(ord($char)-ord($keychar));
+ $result.=$char;
+ }
+ return $result;
+ }
+}
+
+/**
+ * McryptEngine requires Mcrypt extension
+ * Every encryption of the same string with the same key
+ * will return a different encrypted string.
+ */
+class SimpleCryptMcryptEngine implements CryptEngine
+{
+ var $alg = MCRYPT_BLOWFISH;
+ var $td = null;
+
+ public function __construct()
+ {
+ $this->td = mcrypt_module_open($this->alg,'','cbc','');
+ }
+
+ public function Encrypt($key, $sString)
+ {
+ $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($this->td), MCRYPT_RAND); // MCRYPT_RAND is the only choice on Windows prior to PHP 5.3
+ mcrypt_generic_init($this->td, $key, $iv);
+ if (empty($sString))
+ {
+ $sString = str_repeat("\0", 8);
+ }
+ $encrypted_data = mcrypt_generic($this->td, $sString);
+ mcrypt_generic_deinit($this->td);
+ return $iv.$encrypted_data;
+ }
+
+ public function Decrypt($key, $encrypted_data)
+ {
+ $iv = substr($encrypted_data, 0, mcrypt_enc_get_iv_size($this->td));
+ $string = substr($encrypted_data, mcrypt_enc_get_iv_size($this->td));
+ mcrypt_generic_init($this->td, $key, $iv);
+ $decrypted_data = rtrim(mdecrypt_generic($this->td, $string), "\0");
+ mcrypt_generic_deinit($this->td);
+ return $decrypted_data;
+ }
+
+ public function __destruct()
+ {
+ mcrypt_module_close($this->td);
+ }
+}
+?>
\ No newline at end of file
diff --git a/dictionaries/dictionary.itop.core.php b/dictionaries/dictionary.itop.core.php
index e72642336..5a7afaa8a 100644
--- a/dictionaries/dictionary.itop.core.php
+++ b/dictionaries/dictionary.itop.core.php
@@ -104,6 +104,15 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:CMDBChangeOpSetAttributeScalar/Attribute:newvalue' => 'New value',
'Class:CMDBChangeOpSetAttributeScalar/Attribute:newvalue+' => 'new value of the attribute',
));
+// Used by CMDBChangeOp... & derived classes
+Dict::Add('EN US', 'English', 'English', array(
+ 'Change:ObjectCreated' => 'Object created',
+ 'Change:ObjectDeleted' => 'Object deleted',
+ 'Change:AttName_SetTo_NewValue_PreviousValue_OldValue' => '%1$s set to %2$s (previous value: %3$s)',
+ 'Change:Text_AppendedTo_AttName' => '%1$s appended to %2$s',
+ 'Change:AttName_Changed_PreviousValue_OldValue' => '%1$s modified, previous value: %2$s',
+ 'Change:AttName_Changed' => '%1$s modified',
+));
//
// Class: CMDBChangeOpSetAttributeBlob
diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php
index eddc4b8d4..ac399abf0 100644
--- a/dictionaries/dictionary.itop.ui.php
+++ b/dictionaries/dictionary.itop.ui.php
@@ -315,7 +315,7 @@ Dict::Add('EN US', 'English', 'English', array(
',
- 'UI:WelcomeMenu:MyCalls' => 'User Requests assigned to me',
+ 'UI:WelcomeMenu:MyCalls' => 'My requests',
'UI:WelcomeMenu:MyIncidents' => 'Incidents assigned to me',
'UI:AllOrganizations' => ' All Organizations ',
'UI:YourSearch' => 'Your Search',
@@ -345,9 +345,11 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Button:Create' => ' Create ',
'UI:Button:Delete' => ' Delete ! ',
'UI:Button:ChangePassword' => ' Change Password ',
-
+ 'UI:Button:ResetPassword' => ' Reset Password ',
+
'UI:SearchToggle' => 'Search',
'UI:ClickToCreateNew' => 'Click here to create a new %1$s',
+ 'UI:SearchFor_Class' => 'Search for %1$s objects',
'UI:NoObjectToDisplay' => 'No object to display.',
'UI:Error:MandatoryTemplateParameter_object_id' => 'Parameter object_id is mandatory when link_attr is specified. Check the definition of the display template.',
'UI:Error:MandatoryTemplateParameter_target_attr' => 'Parameter target_attr is mandatory when link_attr is specified. Check the definition of the display template.',
@@ -383,6 +385,7 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:GroupBy:Count' => 'Count',
'UI:GroupBy:Count+' => 'Number of elements',
'UI:CountOfObjects' => '%1$d objects matching the criteria.',
+ 'UI_CountOfObjectsShort' => '%1$d objects.',
'UI:NoObject_Class_ToDisplay' => 'No %1$s to display',
'UI:History:LastModified_On_By' => 'Last modified on %1$s by %2$s.',
'UI:HistoryTab' => 'History',
@@ -818,6 +821,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:Deadline_Hours_Minutes' => '%1$dh %2$dmin',
'UI:Deadline_Days_Hours_Minutes' => '%1$dd %2$dh %3$dmin',
'UI:Help' => 'Help',
+ 'UI:PasswordConfirm' => '(Confirm)',
));
diff --git a/dictionaries/es_cr.dictionary.itop.core.php b/dictionaries/es_cr.dictionary.itop.core.php
index e56d1c89a..e6abb9f8a 100644
--- a/dictionaries/es_cr.dictionary.itop.core.php
+++ b/dictionaries/es_cr.dictionary.itop.core.php
@@ -104,6 +104,13 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:CMDBChangeOpSetAttributeScalar/Attribute:newvalue' => 'Nuevo valor',
'Class:CMDBChangeOpSetAttributeScalar/Attribute:newvalue+' => 'nuevo valor del atributo',
));
+// Used by CMDBChangeOp... & derived classes
+Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
+ 'Change:AttName_SetTo_NewValue_PreviousValue_OldValue' => '%1$s modificado en %2$s (valor anterior: %3$s)',
+ 'Change:Text_AppendedTo_AttName' => '%1$s añadido a %2$s',
+ 'Change:AttName_Changed_PreviousValue_OldValue' => '%1$s modificado, valor anterior: %2$s',
+ 'Change:AttName_Changed' => '%1$s modificado',
+));
//
// Class: CMDBChangeOpSetAttributeBlob
diff --git a/dictionaries/es_cr.dictionary.itop.ui.php b/dictionaries/es_cr.dictionary.itop.ui.php
index 433ade8d2..a6fa853ba 100644
--- a/dictionaries/es_cr.dictionary.itop.ui.php
+++ b/dictionaries/es_cr.dictionary.itop.ui.php
@@ -331,7 +331,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
',
- 'UI:WelcomeMenu:MyCalls' => 'User Requests assigned to me',
+ 'UI:WelcomeMenu:MyCalls' => 'My requests',
'UI:WelcomeMenu:MyIncidents' => 'Incidents assigned to me',
'UI:AllOrganizations' => ' All Organizations ',
'UI:YourSearch' => 'Your Search',
@@ -361,6 +361,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'UI:Button:Create' => ' Create ',
'UI:Button:Delete' => ' Delete ! ',
'UI:Button:ChangePassword' => ' Change Password ',
+ 'UI:Button:ResetPassword' => ' Reset Password ',
'UI:SearchToggle' => 'Search',
'UI:ClickToCreateNew' => 'Click here to create a new %1$s',
@@ -399,6 +400,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'UI:GroupBy:Count' => 'Count',
'UI:GroupBy:Count+' => 'Number of elements',
'UI:CountOfObjects' => '%1$d objects matching the criteria.',
+ 'UI_CountOfObjectsShort' => '%1$d objects.',
'UI:NoObject_Class_ToDisplay' => 'No %1$s to display',
'UI:History:LastModified_On_By' => 'Last modified on %1$s by %2$s.',
'UI:HistoryTab' => 'History',
@@ -829,6 +831,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:Deadline_Hours_Minutes' => '%1$dh %2$dmin',
'UI:Deadline_Days_Hours_Minutes' => '%1$dd %2$dh %3$dmin',
'UI:Help' => 'Ayuda',
+ 'UI:PasswordConfirm' => '(Confirm)',
));
?>
diff --git a/dictionaries/fr.dictionary.itop.core.php b/dictionaries/fr.dictionary.itop.core.php
index 742e75ec8..468f307c0 100644
--- a/dictionaries/fr.dictionary.itop.core.php
+++ b/dictionaries/fr.dictionary.itop.core.php
@@ -33,7 +33,7 @@
// Class: CMDBChange
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:CMDBChange' => 'change',
'Class:CMDBChange+' => 'Changes tracking',
'Class:CMDBChange/Attribute:date' => 'date',
@@ -46,7 +46,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: CMDBChangeOp
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:CMDBChangeOp' => 'change operation',
'Class:CMDBChangeOp+' => 'Change operations tracking',
'Class:CMDBChangeOp/Attribute:change' => 'change',
@@ -67,7 +67,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: CMDBChangeOpCreate
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:CMDBChangeOpCreate' => 'object creation',
'Class:CMDBChangeOpCreate+' => 'Object creation tracking',
));
@@ -76,7 +76,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: CMDBChangeOpDelete
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:CMDBChangeOpDelete' => 'object deletion',
'Class:CMDBChangeOpDelete+' => 'Object deletion tracking',
));
@@ -85,7 +85,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: CMDBChangeOpSetAttribute
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:CMDBChangeOpSetAttribute' => 'object change',
'Class:CMDBChangeOpSetAttribute+' => 'Object properties change tracking',
'Class:CMDBChangeOpSetAttribute/Attribute:attcode' => 'Attribute',
@@ -96,7 +96,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: CMDBChangeOpSetAttributeScalar
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:CMDBChangeOpSetAttributeScalar' => 'property change',
'Class:CMDBChangeOpSetAttributeScalar+' => 'Object scalar properties change tracking',
'Class:CMDBChangeOpSetAttributeScalar/Attribute:oldvalue' => 'Previous value',
@@ -104,12 +104,21 @@ Dict::Add('EN US', 'French', 'Français', array(
'Class:CMDBChangeOpSetAttributeScalar/Attribute:newvalue' => 'New value',
'Class:CMDBChangeOpSetAttributeScalar/Attribute:newvalue+' => 'new value of the attribute',
));
+// Used by CMDBChangeOp... & derived classes
+Dict::Add('FR FR', 'French', 'Français', array(
+ 'Change:ObjectCreated' => 'Elément créé',
+ 'Change:ObjectDeleted' => 'Elément effacé',
+ 'Change:AttName_SetTo_NewValue_PreviousValue_OldValue' => '%1$s modifié en %2$s (ancienne valeur: %3$s)',
+ 'Change:Text_AppendedTo_AttName' => '%1$s ajouté à %2$s',
+ 'Change:AttName_Changed_PreviousValue_OldValue' => '%1$s modifié, ancienne valeur: %2$s',
+ 'Change:AttName_Changed' => '%1$s modifié',
+));
//
// Class: CMDBChangeOpSetAttributeBlob
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:CMDBChangeOpSetAttributeBlob' => 'data change',
'Class:CMDBChangeOpSetAttributeBlob+' => 'data change tracking',
'Class:CMDBChangeOpSetAttributeBlob/Attribute:prevdata' => 'Previous data',
@@ -120,7 +129,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: CMDBChangeOpSetAttributeText
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:CMDBChangeOpSetAttributeText' => 'text change',
'Class:CMDBChangeOpSetAttributeText+' => 'text change tracking',
'Class:CMDBChangeOpSetAttributeText/Attribute:prevdata' => 'Previous data',
@@ -131,7 +140,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: Event
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:Event' => 'Log Event',
'Class:Event+' => 'An application internal event',
'Class:Event/Attribute:message' => 'message',
@@ -148,7 +157,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: EventNotification
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:EventNotification' => 'Notification event',
'Class:EventNotification+' => 'Trace of a notification that has been sent',
'Class:EventNotification/Attribute:trigger_id' => 'Trigger',
@@ -163,7 +172,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: EventNotificationEmail
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:EventNotificationEmail' => 'Email emission event',
'Class:EventNotificationEmail+' => 'Trace of an email that has been sent',
'Class:EventNotificationEmail/Attribute:to' => 'TO',
@@ -184,7 +193,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: EventIssue
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:EventIssue' => 'Issue event',
'Class:EventIssue+' => 'Trace of an issue (warning, error, etc.)',
'Class:EventIssue/Attribute:issue' => 'Issue',
@@ -207,7 +216,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: EventWebService
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:EventWebService' => 'Web service event',
'Class:EventWebService+' => 'Trace of an web service call',
'Class:EventWebService/Attribute:verb' => 'Verb',
@@ -228,7 +237,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: Action
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:Action' => 'action',
'Class:Action+' => 'Custom action',
'Class:Action/Attribute:name' => 'Name',
@@ -253,7 +262,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: ActionNotification
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:ActionNotification' => 'notification',
'Class:ActionNotification+' => 'Notification (abstract)',
));
@@ -262,7 +271,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: ActionEmail
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:ActionEmail' => 'email notification',
'Class:ActionEmail+' => 'Action: Email notification',
'Class:ActionEmail/Attribute:test_recipient' => 'Test recipient',
@@ -295,7 +304,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: Trigger
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:Trigger' => 'trigger',
'Class:Trigger+' => 'Custom event handler',
'Class:Trigger/Attribute:description' => 'Description',
@@ -310,7 +319,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: TriggerOnObject
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:TriggerOnObject' => 'Trigger on a class of objects',
'Class:TriggerOnObject+' => 'Trigger on a given class of objects',
'Class:TriggerOnObject/Attribute:target_class' => 'Target class',
@@ -321,7 +330,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: TriggerOnStateChange
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:TriggerOnStateChange' => 'Trigger on object state change',
'Class:TriggerOnStateChange+' => 'Trigger on object state change',
'Class:TriggerOnStateChange/Attribute:state' => 'State',
@@ -332,7 +341,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: TriggerOnStateEnter
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:TriggerOnStateEnter' => 'Trigger on object entering a state',
'Class:TriggerOnStateEnter+' => 'Trigger on object state change - entering',
));
@@ -341,7 +350,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: TriggerOnStateLeave
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:TriggerOnStateLeave' => 'Trigger on object leaving a state',
'Class:TriggerOnStateLeave+' => 'Trigger on object state change - leaving',
));
@@ -350,7 +359,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: TriggerOnObjectCreate
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:TriggerOnObjectCreate' => 'Trigger on object creation',
'Class:TriggerOnObjectCreate+' => 'Trigger on object creation of [a child class of] the given class',
));
@@ -359,7 +368,7 @@ Dict::Add('EN US', 'French', 'Français', array(
// Class: lnkTriggerAction
//
-Dict::Add('EN US', 'French', 'Français', array(
+Dict::Add('FR FR', 'French', 'Français', array(
'Class:lnkTriggerAction' => 'Actions-Trigger',
'Class:lnkTriggerAction+' => 'Link between a trigger and an action',
'Class:lnkTriggerAction/Attribute:action_id' => 'Action',
diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php
index 7693f56b5..cbf576f00 100644
--- a/dictionaries/fr.dictionary.itop.ui.php
+++ b/dictionaries/fr.dictionary.itop.ui.php
@@ -346,6 +346,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI:Button:Create' => ' Créer ',
'UI:Button:Delete' => ' Supprimer ! ',
'UI:Button:ChangePassword' => ' Changer ! ',
+ 'UI:Button:ResetPassword' => ' Ràz du mot de passe ',
'UI:SearchToggle' => 'Recherche',
@@ -384,6 +385,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI:GroupBy:Count' => 'Nombre',
'UI:GroupBy:Count+' => 'Nombre d\'éléments',
'UI:CountOfObjects' => '%1$d objets correspondants aux critères.',
+ 'UI_CountOfObjectsShort' => '%1$d objets.',
'UI:NoObject_Class_ToDisplay' => 'Aucun objet %1$s à afficher',
'UI:History:LastModified_On_By' => 'Dernière modification par %2$s le %1$s.',
'UI:HistoryTab' => 'Historique',
@@ -829,6 +831,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:Deadline_Hours_Minutes' => '%1$dh %2$dmin',
'UI:Deadline_Days_Hours_Minutes' => '%1$dj %2$dh %3$dmin',
'UI:Help' => 'Aide',
+ 'UI:PasswordConfirm' => '(Confirmer)',
));
?>
diff --git a/js/forms-json-utils.js b/js/forms-json-utils.js
index 2588c61d6..f0f059768 100644
--- a/js/forms-json-utils.js
+++ b/js/forms-json-utils.js
@@ -207,3 +207,45 @@ function UpdateDependentFields(aFieldNames)
}
oWizardHelper.AjaxQueryServer();
}
+
+function ResetPwd(id)
+{
+ // Reset the values of the password fields
+ $('#'+id).val('*****');
+ $('#'+id+'_confirm').val('*****');
+ // And reset the flag, to tell it that the password remains unchanged
+ $('#'+id+'_changed').val(0);
+ // Visual feedback, None when it's Ok
+ $('#v_'+id).html('');
+}
+
+// Called whenever the content of a one way encrypted password changes
+function PasswordFieldChanged(id)
+{
+ // Set the flag, to tell that the password changed
+ console.log('Password changed');
+ $('#'+id+'_changed').val(1);
+}
+
+// Special validation function for one way encrypted password fields
+function ValidatePasswordField(id, sFormId)
+{
+ var bChanged = $('#'+id+'_changed').val();
+ if (bChanged)
+ {
+ if ($('#'+id).val() != $('#'+id+'_confirm').val())
+ {
+ oFormErrors['err_'+sFormId]++;
+ if (oFormErrors['input_'+sFormId] == null)
+ {
+ // Let's remember the first input with an error, so that we can put back the focus on it later
+ oFormErrors['input_'+sFormId] = id;
+ }
+ // Visual feedback
+ $('#v_'+id).html('');
+ return false;
+ }
+ }
+ $('#v_'+id).html(''); //');
+ return true;
+}
\ No newline at end of file
diff --git a/modules/authent-local/model.authent-local.php b/modules/authent-local/model.authent-local.php
index 1ac7f6371..3da9c9ca1 100644
--- a/modules/authent-local/model.authent-local.php
+++ b/modules/authent-local/model.authent-local.php
@@ -44,8 +44,7 @@ class UserLocal extends UserInternal
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
- MetaModel::Init_AddAttribute(new AttributePassword("password", array("allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
- MetaModel::Init_AddAttribute(new AttributeEncryptedString("encrypted_password", array("allowed_values"=>null, "sql"=>"encrypted_pwd", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
+ MetaModel::Init_AddAttribute(new AttributeOneWayPassword("password", array("allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'password', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
@@ -57,7 +56,14 @@ class UserLocal extends UserInternal
public function CheckCredentials($sPassword)
{
- if ($this->Get('password') == $sPassword)
+// if ($this->Get('password') == $sPassword)
+// {
+// return true;
+// }
+ $oPassword = $this->Get('password'); // ormPassword object
+ // Cannot compare directly the values since they are hashed, so
+ // Let's ask the password to compare the hashed values
+ if ($oPassword->CheckPassword($sPassword))
{
return true;
}
diff --git a/modules/itop-change-mgmt-1.0.0/en.dict.itop-change-mgmt.php b/modules/itop-change-mgmt-1.0.0/en.dict.itop-change-mgmt.php
index 6e983436f..9e79444b0 100644
--- a/modules/itop-change-mgmt-1.0.0/en.dict.itop-change-mgmt.php
+++ b/modules/itop-change-mgmt-1.0.0/en.dict.itop-change-mgmt.php
@@ -27,6 +27,12 @@ Dict::Add('EN US', 'English', 'English', array(
'Menu:ChangeManagement' => 'Change management',
'Menu:Change:Overview' => 'Overview',
'Menu:Change:Overview+' => '',
+ 'Menu:NewChange' => 'New Change',
+ 'Menu:NewChange+' => 'Create a new Change ticket',
+ 'Menu:SearchChanges' => 'Search for Changes',
+ 'Menu:SearchChanges+' => 'Search for Change tickets',
+ 'Menu:Change:Shortcuts' => 'Shortcuts',
+ 'Menu:Change:Shortcuts+' => '',
'Menu:WaitingAcceptance' => 'Changes awaiting acceptance',
'Menu:WaitingAcceptance+' => '',
'Menu:WaitingApproval' => 'Changes awaiting approval',
diff --git a/modules/itop-change-mgmt-1.0.0/es_cr.dict.itop-change-mgmt.php b/modules/itop-change-mgmt-1.0.0/es_cr.dict.itop-change-mgmt.php
index 68a72e3e3..526d30237 100644
--- a/modules/itop-change-mgmt-1.0.0/es_cr.dict.itop-change-mgmt.php
+++ b/modules/itop-change-mgmt-1.0.0/es_cr.dict.itop-change-mgmt.php
@@ -27,6 +27,12 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Menu:ChangeManagement' => 'Gestión del cambio',
'Menu:Change:Overview' => 'Visión General',
'Menu:Change:Overview+' => '',
+ 'Menu:NewChange' => 'New Change',
+ 'Menu:NewChange+' => 'Create a new Change ticket',
+ 'Menu:SearchChanges' => 'Search for Changes',
+ 'Menu:SearchChanges+' => 'Search for Change tickets',
+ 'Menu:Change:Shortcuts' => 'Shortcuts',
+ 'Menu:Change:Shortcuts+' => '',
'Menu:WaitingAcceptance' => 'Cambios esperando ser aceptados',
'Menu:WaitingAcceptance+' => '',
'Menu:WaitingApproval' => 'Cambios esperando ser aprovados',
diff --git a/modules/itop-change-mgmt-1.0.0/fr.dict.itop-change-mgmt.php b/modules/itop-change-mgmt-1.0.0/fr.dict.itop-change-mgmt.php
index 3ead4d25a..8d5fdb271 100644
--- a/modules/itop-change-mgmt-1.0.0/fr.dict.itop-change-mgmt.php
+++ b/modules/itop-change-mgmt-1.0.0/fr.dict.itop-change-mgmt.php
@@ -27,6 +27,12 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Menu:ChangeManagement' => 'Gestion des changements',
'Menu:Change:Overview' => 'Vue d\'ensemble',
'Menu:Change:Overview+' => '',
+ 'Menu:NewChange' => 'Nouveau changement',
+ 'Menu:NewChange+' => 'Créer un nouveau ticket de changement',
+ 'Menu:SearchChanges' => 'Rechercher des changements',
+ 'Menu:SearchChanges+' => 'Rechercher parmi les tickets de changement',
+ 'Menu:Change:Shortcuts' => 'Raccourcis',
+ 'Menu:Change:Shortcuts+' => '',
'Menu:WaitingAcceptance' => 'Tickets en attente d\'acceptance',
'Menu:WaitingAcceptance+' => '',
'Menu:WaitingApproval' => 'Tickets en attente d\'approbation',
diff --git a/modules/itop-change-mgmt-1.0.0/model.itop-change-mgmt.php b/modules/itop-change-mgmt-1.0.0/model.itop-change-mgmt.php
index c7cb5e766..3d7c8cda0 100644
--- a/modules/itop-change-mgmt-1.0.0/model.itop-change-mgmt.php
+++ b/modules/itop-change-mgmt-1.0.0/model.itop-change-mgmt.php
@@ -483,9 +483,12 @@ class EmergencyChange extends ApprovedChange
$oMyMenuGroup = new MenuGroup('ChangeManagement', 50 /* fRank */);
new TemplateMenuNode('Change:Overview', '../modules/itop-change-mgmt-1.0.0/overview.html', $oMyMenuGroup->GetIndex() /* oParent */, 0 /* fRank */);
-new OQLMenuNode('MyChanges', 'SELECT Change WHERE agent_id = :current_contact_id', $oMyMenuGroup->GetIndex(), 1 /* fRank */);
-new OQLMenuNode('Changes', 'SELECT Change WHERE status != "closed"', $oMyMenuGroup->GetIndex(), 2 /* fRank */);
-new OQLMenuNode('WaitingApproval', 'SELECT ApprovedChange WHERE status IN ("plannedscheduled")', $oMyMenuGroup->GetIndex(), 3 /* fRank */);
-new OQLMenuNode('WaitingAcceptance', 'SELECT NormalChange WHERE status IN ("new")', $oMyMenuGroup->GetIndex(), 4 /* fRank */);
+new NewObjectMenuNode('NewChange', 'Change', $oMyMenuGroup->GetIndex(), 1 /* fRank */);
+new SearchMenuNode('SearchChanges', 'Change', $oMyMenuGroup->GetIndex(), 2 /* fRank */);
+$oShortcutNode = new TemplateMenuNode('Change:Shortcuts', '', $oMyMenuGroup->GetIndex(), 3 /* fRank */);
+new OQLMenuNode('MyChanges', 'SELECT Change WHERE agent_id = :current_contact_id', $oShortcutNode->GetIndex(), 1 /* fRank */);
+new OQLMenuNode('Changes', 'SELECT Change WHERE status != "closed"', $oShortcutNode->GetIndex(), 2 /* fRank */);
+new OQLMenuNode('WaitingApproval', 'SELECT ApprovedChange WHERE status IN ("plannedscheduled")', $oShortcutNode->GetIndex(), 3 /* fRank */);
+new OQLMenuNode('WaitingAcceptance', 'SELECT NormalChange WHERE status IN ("new")', $oShortcutNode->GetIndex(), 4 /* fRank */);
?>
diff --git a/modules/itop-config-mgmt-1.0.0/fr.dict.itop-config-mgmt.php b/modules/itop-config-mgmt-1.0.0/fr.dict.itop-config-mgmt.php
index 30ec9f9ed..bc041280a 100644
--- a/modules/itop-config-mgmt-1.0.0/fr.dict.itop-config-mgmt.php
+++ b/modules/itop-config-mgmt-1.0.0/fr.dict.itop-config-mgmt.php
@@ -31,7 +31,10 @@
Dict::Add('FR FR', 'French', 'Français', array(
'Relation:impacts/Description' => 'Eléments impactés par',
'Relation:impacts/VerbUp' => 'Impacte...',
- 'Relation:impacts/VerbDown' => 'Eléments impactés par...',
+ 'Relation:impacts/VerbDown' => 'Dépend de...',
+ 'Relation:depends on/Description' => 'Eléments dont dépend cet élément',
+ 'Relation:depends on/VerbUp' => 'Dépend de...',
+ 'Relation:depends on/VerbDown' => 'Impacte...',
));
diff --git a/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php b/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php
index 8cf7e05c8..dc984320a 100644
--- a/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php
+++ b/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php
@@ -1474,7 +1474,7 @@ new WebPageMenuNode('Audit', '../pages/audit.php', $iAdminGroup, 33 /* fRank */)
$oTypologyNode = new TemplateMenuNode('Catalogs', '', $iAdminGroup, 50 /* fRank */);
$iTopology = $oTypologyNode->GetIndex();
-new OQLMenuNode('Organization', 'SELECT Organization', $iTopology, 10 /* fRank */);
+new OQLMenuNode('Organization', 'SELECT Organization', $iTopology, 10 /* fRank */, true /* bSearch */);
new OQLMenuNode('Application', 'SELECT Application', $iTopology, 20 /* fRank */);
new OQLMenuNode('DBServer', 'SELECT DBServer', $iTopology, 40 /* fRank */);
@@ -1486,24 +1486,28 @@ new TemplateMenuNode('ConfigManagementOverview', '../modules/itop-config-mgmt-1.
$oContactNode = new TemplateMenuNode('Contact', '../modules/itop-config-mgmt-1.0.0/contacts_menu.html', $oConfigManagementGroup->GetIndex(), 1 /* fRank */);
-new OQLMenuNode('Person', 'SELECT Person', $oContactNode->GetIndex(), 1 /* fRank */);
-new OQLMenuNode('Team', 'SELECT Team', $oContactNode->GetIndex(), 2 /* fRank */);
+new NewObjectMenuNode('NewContact', 'Contact', $oContactNode->GetIndex(), 1 /* fRank */);
+new SearchMenuNode('SearchContacts', 'Contact', $oContactNode->GetIndex(), 2 /* fRank */);
+new OQLMenuNode('Person', 'SELECT Person', $oContactNode->GetIndex(), 3 /* fRank */);
+new OQLMenuNode('Team', 'SELECT Team', $oContactNode->GetIndex(), 4 /* fRank */);
-new OQLMenuNode('Document', 'SELECT Document', $oConfigManagementGroup->GetIndex(), 2 /* fRank */);
-new OQLMenuNode('Location', 'SELECT Location', $oConfigManagementGroup->GetIndex(), 3 /* fRank */);
+new OQLMenuNode('Document', 'SELECT Document', $oConfigManagementGroup->GetIndex(), 2 /* fRank */, true /* bSearch */);
+new OQLMenuNode('Location', 'SELECT Location', $oConfigManagementGroup->GetIndex(), 3 /* fRank */, true /* bSearch */);
$oCINode = new TemplateMenuNode('ConfigManagementCI', '../modules/itop-config-mgmt-1.0.0/cis_menu.html', $oConfigManagementGroup->GetIndex(), 4 /* fRank */);
+new NewObjectMenuNode('NewCI', 'FunctionalCI', $oCINode->GetIndex(), 0 /* fRank */);
+new SearchMenuNode('SearchCIs', 'FunctionalCI', $oCINode->GetIndex(), 1 /* fRank */);
-new OQLMenuNode('BusinessProcess', 'SELECT BusinessProcess', $oCINode->GetIndex(), 0 /* fRank */);
-new OQLMenuNode('ApplicationSolution', 'SELECT ApplicationSolution', $oCINode->GetIndex(), 1 /* fRank */);
+new OQLMenuNode('BusinessProcess', 'SELECT BusinessProcess', $oCINode->GetIndex(), 2 /* fRank */);
+new OQLMenuNode('ApplicationSolution', 'SELECT ApplicationSolution', $oCINode->GetIndex(), 3 /* fRank */);
-$oSWNode = new TemplateMenuNode('ConfigManagementSoftware', '', $oCINode->GetIndex(), 2 /* fRank */);
+$oSWNode = new TemplateMenuNode('ConfigManagementSoftware', '', $oCINode->GetIndex(), 4 /* fRank */);
new OQLMenuNode('Licence', 'SELECT Licence', $oSWNode->GetIndex(), 0 /* fRank */);
new OQLMenuNode('Patch', 'SELECT Patch', $oSWNode->GetIndex(), 1 /* fRank */);
new OQLMenuNode('ApplicationInstance', 'SELECT SoftwareInstance', $oSWNode->GetIndex(), 2 /* fRank */);
-$oHWNode = new TemplateMenuNode('ConfigManagementHardware', '', $oCINode->GetIndex(), 3 /* fRank */);
+$oHWNode = new TemplateMenuNode('ConfigManagementHardware', '', $oCINode->GetIndex(), 5 /* fRank */);
new OQLMenuNode('Subnet', 'SELECT Subnet', $oHWNode->GetIndex(), 0 /* fRank */);
new OQLMenuNode('NetworkDevice', 'SELECT NetworkDevice', $oHWNode->GetIndex(), 1 /* fRank */);
new OQLMenuNode('Server', 'SELECT Server', $oHWNode->GetIndex(), 2 /* fRank */);
diff --git a/modules/itop-incident-mgmt-1.0.0/en.dict.itop-incident-mgmt.php b/modules/itop-incident-mgmt-1.0.0/en.dict.itop-incident-mgmt.php
index 7352570ae..837fa7267 100644
--- a/modules/itop-incident-mgmt-1.0.0/en.dict.itop-incident-mgmt.php
+++ b/modules/itop-incident-mgmt-1.0.0/en.dict.itop-incident-mgmt.php
@@ -28,6 +28,12 @@ Dict::Add('EN US', 'English', 'English', array(
'Menu:IncidentManagement+' => 'Incident Management',
'Menu:Incident:Overview' => 'Overview',
'Menu:Incident:Overview+' => 'Overview',
+ 'Menu:NewIncident' => 'New Incident',
+ 'Menu:NewIncident+' => 'Create a new Incident ticket',
+ 'Menu:SearchIncidents' => 'Search for Incidents',
+ 'Menu:SearchIncidents+' => 'Search for Incident tickets',
+ 'Menu:Incident:Shortcuts' => 'Shortcuts',
+ 'Menu:Incident:Shortcuts+' => '',
'Menu:Incident:MyIncidents' => 'Incidents assigned to me',
'Menu:Incident:MyIncidents+' => 'Incidents assigned to me (as Agent)',
'Menu:Incident:EscalatedIncidents' => 'Escalated Incidents',
diff --git a/modules/itop-incident-mgmt-1.0.0/es_cr.dict.itop-incident-mgmt.php b/modules/itop-incident-mgmt-1.0.0/es_cr.dict.itop-incident-mgmt.php
index abe09149f..d16e982af 100644
--- a/modules/itop-incident-mgmt-1.0.0/es_cr.dict.itop-incident-mgmt.php
+++ b/modules/itop-incident-mgmt-1.0.0/es_cr.dict.itop-incident-mgmt.php
@@ -28,6 +28,12 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Menu:IncidentManagement+' => 'Gestión de Incidentes',
'Menu:Incident:Overview' => 'Visión General',
'Menu:Incident:Overview+' => 'Visión General',
+ 'Menu:NewIncident' => 'New Incident',
+ 'Menu:NewIncident+' => 'Create a new Incident ticket',
+ 'Menu:SearchIncidents' => 'Search for Incidents',
+ 'Menu:SearchIncidents+' => 'Search for Incident tickets',
+ 'Menu:Incident:Shortcuts' => 'Shortcuts',
+ 'Menu:Incident:Shortcuts+' => '',
'Menu:Incident:MyIncidents' => 'Incidentes asignados a mí',
'Menu:Incident:MyIncidents+' => 'Incidentes asignados a mí (como Agente)',
'Menu:Incident:EscalatedIncidents' => 'Incidentes Escalados',
diff --git a/modules/itop-incident-mgmt-1.0.0/fr.dict.itop-incident-mgmt.php b/modules/itop-incident-mgmt-1.0.0/fr.dict.itop-incident-mgmt.php
index 2ea56673b..831c4efb6 100644
--- a/modules/itop-incident-mgmt-1.0.0/fr.dict.itop-incident-mgmt.php
+++ b/modules/itop-incident-mgmt-1.0.0/fr.dict.itop-incident-mgmt.php
@@ -28,6 +28,12 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Menu:IncidentManagement+' => 'Gestion des incidents',
'Menu:Incident:Overview' => 'Vue d\'ensemble',
'Menu:Incident:Overview+' => 'Vue d\'ensemble',
+ 'Menu:NewIncident' => 'Nouvel Incident',
+ 'Menu:NewIncident+' => 'Créer un nouveau ticket d\'incident',
+ 'Menu:SearchIncidents' => 'Rechercher des incidents',
+ 'Menu:SearchIncidents+' => 'Rechercher parmi les tickets d\'incidents',
+ 'Menu:Incident:Shortcuts' => 'Raccourcis',
+ 'Menu:Incident:Shortcuts+' => '',
'Menu:Incident:MyIncidents' => 'Mes tickets',
'Menu:Incident:MyIncidents+' => 'Tickets d\'incident qui me sont assignés',
'Menu:Incident:EscalatedIncidents' => 'Ticket en cours d\'escalade',
diff --git a/modules/itop-incident-mgmt-1.0.0/model.itop-incident-mgmt.php b/modules/itop-incident-mgmt-1.0.0/model.itop-incident-mgmt.php
index d62989f9b..2db0f4a15 100644
--- a/modules/itop-incident-mgmt-1.0.0/model.itop-incident-mgmt.php
+++ b/modules/itop-incident-mgmt-1.0.0/model.itop-incident-mgmt.php
@@ -96,8 +96,11 @@ class Incident extends ResponseTicket
$oMyMenuGroup = new MenuGroup('IncidentManagement', 40 /* fRank */);
new TemplateMenuNode('Incident:Overview', '../modules/itop-incident-mgmt-1.0.0/overview.html', $oMyMenuGroup->GetIndex() /* oParent */, 0 /* fRank */);
-new OQLMenuNode('Incident:MyIncidents', 'SELECT Incident WHERE agent_id = :current_contact_id', $oMyMenuGroup->GetIndex(), 1 /* fRank */);
-new OQLMenuNode('Incident:EscalatedIncidents', 'SELECT Incident WHERE status IN ("escalated_tto", "escalated_ttr")', $oMyMenuGroup->GetIndex(), 2 /* fRank */);
-new OQLMenuNode('Incident:OpenIncidents', 'SELECT Incident WHERE status IN ("new", "assigned", "escalated_tto", "escalated_ttr", "resolved")', $oMyMenuGroup->GetIndex(), 3 /* fRank */);
+new NewObjectMenuNode('NewIncident', 'Incident', $oMyMenuGroup->GetIndex(), 1 /* fRank */);
+new SearchMenuNode('SearchIncidents', 'Incident', $oMyMenuGroup->GetIndex(), 2 /* fRank */);
+$oShortcutNode = new TemplateMenuNode('Incident:Shortcuts', '', $oMyMenuGroup->GetIndex(), 3 /* fRank */);
+new OQLMenuNode('Incident:MyIncidents', 'SELECT Incident WHERE agent_id = :current_contact_id', $oShortcutNode->GetIndex(), 1 /* fRank */);
+new OQLMenuNode('Incident:EscalatedIncidents', 'SELECT Incident WHERE status IN ("escalated_tto", "escalated_ttr")', $oShortcutNode->GetIndex(), 2 /* fRank */);
+new OQLMenuNode('Incident:OpenIncidents', 'SELECT Incident WHERE status IN ("new", "assigned", "escalated_tto", "escalated_ttr", "resolved")', $oShortcutNode->GetIndex(), 3 /* fRank */);
?>
diff --git a/modules/itop-request-mgmt-1.0.0/en.dict.itop-request-mgmt.php b/modules/itop-request-mgmt-1.0.0/en.dict.itop-request-mgmt.php
index 72631ce4e..e62a91605 100644
--- a/modules/itop-request-mgmt-1.0.0/en.dict.itop-request-mgmt.php
+++ b/modules/itop-request-mgmt-1.0.0/en.dict.itop-request-mgmt.php
@@ -28,6 +28,12 @@ Dict::Add('EN US', 'English', 'English', array(
'Menu:RequestManagement+' => 'Helpdesk',
'Menu:UserRequest:Overview' => 'Overview',
'Menu:UserRequest:Overview+' => 'Overview',
+ 'Menu:NewUserRequest' => 'New User Request',
+ 'Menu:NewUserRequest+' => 'Create a new User Request ticket',
+ 'Menu:SearchUserRequests' => 'Search for User Requests',
+ 'Menu:SearchUserRequests+' => 'Search for User Request tickets',
+ 'Menu:UserRequest:Shortcuts' => 'Shortcuts',
+ 'Menu:UserRequest:Shortcuts+' => '',
'Menu:UserRequest:MyRequests' => 'Requests assigned to me',
'Menu:UserRequest:MyRequests+' => 'Requests assigned to me (as Agent)',
'Menu:UserRequest:EscalatedRequests' => 'Escalated Requests',
diff --git a/modules/itop-request-mgmt-1.0.0/es_cr.dict.itop-request-mgmt.php b/modules/itop-request-mgmt-1.0.0/es_cr.dict.itop-request-mgmt.php
index c55e62d6a..b8d5c1a48 100644
--- a/modules/itop-request-mgmt-1.0.0/es_cr.dict.itop-request-mgmt.php
+++ b/modules/itop-request-mgmt-1.0.0/es_cr.dict.itop-request-mgmt.php
@@ -28,6 +28,12 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Menu:RequestManagement+' => 'Servicio de ayuda',
'Menu:UserRequest:Overview' => 'Visión General',
'Menu:UserRequest:Overview+' => 'Visión General',
+ 'Menu:NewUserRequest' => 'New User Request',
+ 'Menu:NewUserRequest+' => 'Create a new User Request ticket',
+ 'Menu:SearchUserRequests' => 'Search for User Requests',
+ 'Menu:SearchUserRequests+' => 'Search for User Request tickets',
+ 'Menu:UserRequest:Shortcuts' => 'Shortcuts',
+ 'Menu:UserRequest:Shortcuts+' => '',
'Menu:UserRequest:MyRequests' => 'Solicitudes asignadas a mí',
'Menu:UserRequest:MyRequests+' => 'Solicitudes asignadas a mí (como Agente)',
'Menu:UserRequest:EscalatedRequests' => 'Solicitudes Escaladas',
diff --git a/modules/itop-request-mgmt-1.0.0/fr.dict.itop-request-mgmt.php b/modules/itop-request-mgmt-1.0.0/fr.dict.itop-request-mgmt.php
index de8db84b4..9f63ff409 100644
--- a/modules/itop-request-mgmt-1.0.0/fr.dict.itop-request-mgmt.php
+++ b/modules/itop-request-mgmt-1.0.0/fr.dict.itop-request-mgmt.php
@@ -28,6 +28,12 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Menu:RequestManagement+' => 'Gestion des demandes utilisateurs',
'Menu:UserRequest:Overview' => 'Vue d\'ensemble',
'Menu:UserRequest:Overview+' => 'Vue d\'ensemble des demandes utilisateurs',
+ 'Menu:NewUserRequest' => 'Nouvelle demande utilisateur',
+ 'Menu:NewUserRequest+' => 'Créer un nouveau ticket de demande utilisateur',
+ 'Menu:SearchUserRequests' => 'Rechercher des demandes utilisateur',
+ 'Menu:SearchUserRequests+' => 'Rechercher parmi les demandes utilisateur',
+ 'Menu:UserRequest:Shortcuts' => 'Raccourcis',
+ 'Menu:UserRequest:Shortcuts+' => '',
'Menu:UserRequest:MyRequests' => 'Mes demandes',
'Menu:UserRequest:MyRequests+' => 'Demandes utilisateurs qui me sont assignées',
'Menu:UserRequest:EscalatedRequests' => 'Demandes en escalade',
diff --git a/modules/itop-request-mgmt-1.0.0/model.itop-request-mgmt.php b/modules/itop-request-mgmt-1.0.0/model.itop-request-mgmt.php
index 08bec2255..8f6602881 100644
--- a/modules/itop-request-mgmt-1.0.0/model.itop-request-mgmt.php
+++ b/modules/itop-request-mgmt-1.0.0/model.itop-request-mgmt.php
@@ -86,8 +86,11 @@ class UserRequest extends ResponseTicket
$oMyMenuGroup = new MenuGroup('RequestManagement', 30 /* fRank */);
new TemplateMenuNode('UserRequest:Overview', '../modules/itop-request-mgmt-1.0.0/overview.html', $oMyMenuGroup->GetIndex() /* oParent */, 0 /* fRank */);
-new OQLMenuNode('UserRequest:MyRequests', 'SELECT UserRequest WHERE agent_id = :current_contact_id', $oMyMenuGroup->GetIndex(), 1 /* fRank */);
-new OQLMenuNode('UserRequest:EscalatedRequests', 'SELECT UserRequest WHERE status IN ("escalated_tto", "escalated_ttr")', $oMyMenuGroup->GetIndex(), 2 /* fRank */);
-new OQLMenuNode('UserRequest:OpenRequests', 'SELECT UserRequest WHERE status IN ("new", "assigned", "escalated_tto", "escalated_ttr", "frozen", "resolved")', $oMyMenuGroup->GetIndex(), 3 /* fRank */);
+new NewObjectMenuNode('NewUserRequest', 'UserRequest', $oMyMenuGroup->GetIndex(), 1 /* fRank */);
+new SearchMenuNode('SearchUserRequests', 'UserRequest', $oMyMenuGroup->GetIndex(), 2 /* fRank */);
+$oShortcutNode = new TemplateMenuNode('UserRequest:Shortcuts', '', $oMyMenuGroup->GetIndex(), 3 /* fRank */);
+new OQLMenuNode('UserRequest:MyRequests', 'SELECT UserRequest WHERE agent_id = :current_contact_id', $oShortcutNode->GetIndex(), 1 /* fRank */);
+new OQLMenuNode('UserRequest:EscalatedRequests', 'SELECT UserRequest WHERE status IN ("escalated_tto", "escalated_ttr")', $oShortcutNode->GetIndex(), 2 /* fRank */);
+new OQLMenuNode('UserRequest:OpenRequests', 'SELECT UserRequest WHERE status IN ("new", "assigned", "escalated_tto", "escalated_ttr", "frozen", "resolved")', $oShortcutNode->GetIndex(), 3 /* fRank */);
?>
diff --git a/pages/UI.php b/pages/UI.php
index a246d632e..116223594 100644
--- a/pages/UI.php
+++ b/pages/UI.php
@@ -407,7 +407,7 @@ function UpdateObject(&$oObj)
{
// Non-visible, or read-only attribute, do nothing
}
- else if ($oAttDef->GetEditClass() == 'Document')
+ elseif ($oAttDef->GetEditClass() == 'Document')
{
// There should be an uploaded file with the named attr_
$oDocument = utils::ReadPostedDocument('file_'.$sAttCode);
@@ -417,6 +417,17 @@ function UpdateObject(&$oObj)
$oObj->Set($sAttCode, $oDocument);
}
}
+ elseif ($oAttDef->GetEditClass() == 'One Way Password')
+ {
+ // Check if the password was typed/changed
+ $bChanged = utils::ReadPostedParam("attr_{$sAttCode}_changed", false);
+ if ($bChanged)
+ {
+ // The password has been changed or set
+ $rawValue = utils::ReadPostedParam("attr_$sAttCode", null);
+ $oObj->Set($sAttCode, $rawValue);
+ }
+ }
else
{
$rawValue = utils::ReadPostedParam("attr_$sAttCode", null);
@@ -769,7 +780,7 @@ try
{
foreach($aSubClasses as $sCandidateClass)
{
- if (!MetaModel::IsAbstract($sCandidateClass))
+ if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
{
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
}