Compare commits

...

68 Commits

Author SHA1 Message Date
Denis Flaven
3f80b27044 Fixed a potential XSS vulnerability.
SVN:2.0.2[3666]
2015-07-30 09:23:21 +00:00
Romain Quetiez
51624dd05e #1060 Internal: improved the symptoms when calling MetaModel::GetAttributeDef with an invalid attribute code (feedback on the class name and no more FATAL errors) -retrofit from trunk
SVN:2.0.2[3495]
2015-02-09 13:19:34 +00:00
Romain Quetiez
89b91fa980 #1020 Restrict dashboard/shortcut refresh interval -retrofit from trunk
SVN:2.0.2[3450]
2014-12-03 14:18:28 +00:00
Romain Quetiez
ab65135794 #1007 Unexpected change of the case log when doing massive update of a User Request (+ hide the checkbox for the status because it makes no sense) -retrofit from trunk
SVN:2.0.2[3396]
2014-10-31 15:13:04 +00:00
Denis Flaven
2d7cdafadc #1011 Proper resizing of the dialog box for managing 1:n links - removed a trace - retrofit from trunk.
SVN:2.0.2[3392]
2014-10-30 08:58:07 +00:00
Denis Flaven
c6d6e85b3d #932: fixed a regression introduced by [r3319]... in case a criteria is present several times, retrofit from trunk.
SVN:2.0.2[3346]
2014-09-17 15:44:09 +00:00
Romain Quetiez
d4fbc3dc4f #778 Issue on list sort order when editing an element - fixed a regression -reintegrated from trunk
SVN:2.0.2[3337]
2014-09-15 16:09:12 +00:00
Romain Quetiez
fce3edca40 #778 Issue on list sort order when editing an element -reintegrated from trunk
SVN:2.0.2[3334]
2014-09-15 14:06:58 +00:00
Romain Quetiez
a099db5350 #986 Search form: handle indirect external keys -reintegrated from trunk
SVN:2.0.2[3332]
2014-09-15 14:02:28 +00:00
Romain Quetiez
5e25b349bf #987 Usage login prevents from user deletion -reintegrated from trunk
SVN:2.0.2[3330]
2014-09-15 13:58:20 +00:00
Romain Quetiez
b5793b2129 #932 Search form should be prefilled when running a search "shortcut" - very little progress: fixed the case when several criteria are given
SVN:2.0.2[3328]
2014-09-15 13:51:54 +00:00
Romain Quetiez
6f3583973b #985 Shortcut auto refresh degrading table cosmetics -reintegrated from trunk
SVN:2.0.2[3326]
2014-09-15 13:48:54 +00:00
Romain Quetiez
23f1e27346 #984 Dashboard auto refresh degrading table functionalities like sorting -retrofit from trunk
SVN:2.0.2[3324]
2014-09-15 13:45:54 +00:00
Denis Flaven
ccfc16e296 Protect dashboards against invalid queries in "grouped by" dashlets. (retrofit from trunk)
SVN:2.0.2[3310]
2014-08-28 16:01:53 +00:00
Denis Flaven
01096e232b #966 Prevent duplication of attachments when the form gets reloaded.
SVN:2.0.2[3293]
2014-07-30 08:21:22 +00:00
Denis Flaven
b3ff892508 #966 Prevent duplication of attachments when the form gets reloaded.
SVN:2.0.2[3292]
2014-07-30 08:17:31 +00:00
Denis Flaven
0841367022 #966 (continue'd): Allow attachments of several files to the same ticket (the previous fix breaks after attaching 1 file)
SVN:2.0.2[3288]
2014-07-25 15:18:46 +00:00
Denis Flaven
0fba65d2c5 #965 (Retrofit from trunk) Proper handling of CheckToWrite in ApplyStimulus
SVN:2.0.2[3285]
2014-07-23 15:29:36 +00:00
Denis Flaven
9aaae9b668 #966 Fix for a bug caused by Google Chrome version 36: the selected attachment was added several times (up to 10 or more!).
SVN:2.0.2[3281]
2014-07-21 15:42:59 +00:00
Denis Flaven
c65fb4314b #966 Fix for a bug caused by Google Chrome version 36: the selected attachment was added several times (up to 10 or more!).
SVN:2.0.2[3280]
2014-07-21 15:33:18 +00:00
Romain Quetiez
e9b579fbbd Legacy user rights management: allow the deletion of a profile in one step (it was nearly impossible because of the numerous related records, mainly of type URP_ActionGrant, for which iTop was requesting a manual deletion) -retrofit from trunk
SVN:2.0.2[3278]
2014-07-16 10:51:37 +00:00
Denis Flaven
78d211132d #953 Protect the background processing against corrupted (incomplete) objects.
SVN:2.0.2[3232]
2014-06-30 15:26:22 +00:00
Denis Flaven
1ccb78276a #940 "Close" and "Reopen" buttons were not working on IE (due to extra commas at the end of JS lists)
SVN:2.0.2[3229]
2014-06-27 11:04:39 +00:00
Denis Flaven
72cc38aa4b Oops, fixed a typo in: Add the ability to supply a default "from" email address for the "forgot password" feature, instead of using the same address as for the "to".
SVN:2.0.2[3220]
2014-06-18 15:58:54 +00:00
Denis Flaven
3fb7701015 Proper handling of the re-entrence of the Lock/Unlock within the same PHP page. Previously the code was sometimes performing 2 Unlock() for each Lock().
SVN:2.0.2[3218]
2014-06-18 15:26:26 +00:00
Denis Flaven
91da1583d2 #943: Fix for supporting drop-down lists/auto-completes based on a parametrized query in the portal.
SVN:2.0.2[3215]
2014-06-17 11:26:07 +00:00
Denis Flaven
0e00a6c526 Add the ability to supply a default "from" email address for the "forgot password" feature, instead of using the same address as for the "to".
SVN:2.0.2[3214]
2014-06-16 15:16:43 +00:00
Romain Quetiez
2f1b8d2e71 Added a helper function to get an icon stored as an ormDocument: ormDocument::GetDownloadURL -reintegrated from trunk
SVN:2.0.2[3192]
2014-06-04 13:12:41 +00:00
Denis Flaven
d0280b9672 Fix of a regression introduced by [3146]: due to formatting rules, empty friendlynames may not look like empty strings! -retrofit from trunk.
SVN:2.0.2[3173]
2014-05-23 15:52:27 +00:00
Denis Flaven
f72e1dd542 Properly handle external and basic authentication methods for REST web services.
SVN:2.0.2[3171]
2014-05-23 13:58:37 +00:00
Denis Flaven
6703d5621d Properly optimize the columns to load, when subitems are requested.
SVN:2.0.2[3169]
2014-05-23 13:22:46 +00:00
Denis Flaven
f5e0ff10bb Prevent reloads when using _friendlynames in spreadsheet exports.
SVN:2.0.2[3165]
2014-05-23 08:37:17 +00:00
Denis Flaven
ef7d9659fa #923: prevent XSS injection in forgot password page.
SVN:2.0.2[3140]
2014-05-06 09:53:37 +00:00
Denis Flaven
658664a2b2 Fix for Plugins: if a page uses set_base then JS popup menu items were reloading the page. Still, set_base should not be used! - retrofit from trunk [3072 + 3079]
SVN:2.0.2[3136]
2014-04-24 10:38:18 +00:00
Denis Flaven
7a86487b33 #905 The toolkit menu was visible in the portal for Administrators (but it was not usable). It is now hidden in any case. - retrofit from trunk
SVN:2.0.2[3135]
2014-04-24 10:33:23 +00:00
Denis Flaven
a1d47aac87 #913 Error when searching for child requests and no organization is specified. Still, I could not figure out WHY IT WAS WORKING WHEN AN ORG IS SELECTED as a search filter! - retrofit from trunk
SVN:2.0.2[3134]
2014-04-24 10:31:14 +00:00
Denis Flaven
853a8f0c8f Resetting the stop watch...do clean the first start date when it is not running! - retrofit from trunk
SVN:2.0.2[3133]
2014-04-24 10:29:03 +00:00
Denis Flaven
39d3ce9e47 Finalized the French transalation for some types of "Triggers" - retrofit from trunk
SVN:2.0.2[3132]
2014-04-24 10:25:31 +00:00
Romain Quetiez
145b7a81b7 #918 TTO/TTR status "passed" gets reset when the stop watch is stopped (using the status "triggered" instead) -retrofit from trunk
SVN:2.0.2[3131]
2014-04-24 09:02:39 +00:00
Denis Flaven
1dc59ef7d5 Record the very same installation time for all modules. - merged from thunk
SVN:2.0.2[3129]
2014-04-15 14:41:44 +00:00
Denis Flaven
cf792822f5 Better handling of the default choices in case of upgrade (for some specific configurations of the installation wizard).
SVN:2.0.2[3128]
2014-04-15 13:03:45 +00:00
Romain Quetiez
6cf3758788 Integrated the portal with the module to dispatch incidents or user requests -reintegrated from trunk
SVN:2.0.2[3126]
2014-04-11 14:55:56 +00:00
Denis Flaven
093c8a8506 #878: Missing sscrollbar in "linkset-direct" edition popup dialog
#881: Paginated list in popup dialog is broken
- Missing scrollbar in the popup when using the [+] button

SVN:2.0.2[3122]
2014-04-01 13:12:24 +00:00
Denis Flaven
2b76c17469 Object's edition: keep track of what was typed in the case log fields when reloading the form (for example with a different "initial state")
SVN:2.0.2[3120]
2014-04-01 12:10:11 +00:00
Denis Flaven
b2d6df98ed Protect Bulk Modify against XSS injection!
SVN:2.0.2[3118]
2014-04-01 10:13:58 +00:00
Denis Flaven
3f4db67b40 Allow re-entrance in the same named mutex within the same PHP page.
SVN:2.0.2[3116]
2014-04-01 09:53:17 +00:00
Romain Quetiez
1943b64b35 Fixed issue with 1.x datamodels: dashlets of type "badge" not working (preventing from editing an existing dashboard), since 2.0.2 -retrofitted from trunk
SVN:2.0.2[3114]
2014-04-01 09:16:25 +00:00
Romain Quetiez
9cd41efbcd Aligned the authentication module with the one of 2.x, to enable the feature "Forgot password" for legacy data models -retrofit from trunk
SVN:2.0.2[3112]
2014-04-01 08:35:52 +00:00
Denis Flaven
5b12bd4c07 Bug fix : missing semicolons were causing an error with IE9.
SVN:2.0.2[3108]
2014-03-26 15:00:25 +00:00
Romain Quetiez
46f9a1962a #896 XSS injection on the portal (any search form) -reintegrated from trunk
SVN:2.0.2[3105]
2014-03-21 11:43:19 +00:00
Denis Flaven
d527941707 #870: when a user deletes all her/his shortcuts at once, this was deleting all the shortcuts for all users.
SVN:2.0.2[3100]
2014-03-12 14:07:42 +00:00
Denis Flaven
b795a20e59 Templates processing aligned with "templates-base" 2.1.1: allow template fields with the same name the attribute code of the curent object.
SVN:2.0.2[3098]
2014-03-11 14:05:42 +00:00
Denis Flaven
2833ef0853 #887 short term fix for preventing ToArgs to alter the content of an object...
SVN:2.0.2[3096]
2014-03-04 14:00:01 +00:00
Denis Flaven
1a848fb3cc Trac #890: Dispatch the defines in the proper modules to make sure that the portal works with all possible combinations of tickets.
SVN:2.0.2[3094]
2014-03-03 15:08:26 +00:00
Romain Quetiez
ead3a552e7 Retrofitted three changes from trunk, for the REST/JSON API:
- Enum should be output as the raw value (not the translated label)
- #877 Improved the update of case logs
- #891 Better error reporting when authentication parameters are missing

SVN:2.0.2[3091]
2014-02-27 10:42:02 +00:00
Romain Quetiez
945b9dc8a9 #888 Security on the portal incompatible with customizations (regression introduced in 2.0.2), now requires to define PORTAL_USERREQUEST_DISPLAY_QUERY and PORTAL_USERREQUEST_DISPLAY_POWERUSER_QUERY -reintegrated from trunk
SVN:2.0.2[3087]
2014-02-24 15:56:54 +00:00
Denis Flaven
8850d4c460 Make the Basic Authentication (login_mode=basic) work with non-ASCII characters (in the username as well as in the password), though this may depend on the browser...
SVN:2.0.2[3085]
2014-02-19 17:36:27 +00:00
Denis Flaven
075311b6dd Protect the initialization with a try ... catch, in order to protect that rest of the page in case of trouble.
SVN:2.0.2[3075]
2014-02-05 17:10:44 +00:00
Romain Quetiez
1ad81260ae Reintegrated changes 3058 to 3060 (prerequisiste for templates into the portal) from trunk
SVN:2.0.2[3073]
2014-02-05 16:33:29 +00:00
Romain Quetiez
f8b36a47a6 Optimization: map the extended attribute code to the corresponding external field when this if possible (ex: org_id->name to org_name); this reduces the number of queries, in particular when using the "export CSV" menu on a list. -reintegrated from trunk
SVN:2.0.2[3070]
2014-02-03 14:36:48 +00:00
Romain Quetiez
8b73e2f9bc Optimize the queries for the export page -reintegrated from trunk
SVN:2.0.2[3068]
2014-02-03 14:28:39 +00:00
Romain Quetiez
34ad21b7f5 Allow to reset a running stop watch (without stopping it!) -reintegrated from trunk
SVN:2.0.2[3064]
2014-02-03 11:58:59 +00:00
Denis Flaven
1d2fdb1c23 Compatibility with APCu (For PHP 5.5+), since it is slightly different from APC.
SVN:2.0.2[3062]
2014-01-22 15:42:15 +00:00
Denis Flaven
92baf53c52 #871: eMail validation pattern was too strict: now fully configurable (globally and per attribute).
SVN:2.0.2[3057]
2014-01-17 09:41:21 +00:00
Romain Quetiez
6cc0bf5c5a #869 REST JSON was not outputing case log attributes (implemented in a structured way) -retrofit in branch 2.0.2
SVN:2.0.2[3055]
2014-01-15 11:06:56 +00:00
Romain Quetiez
a0497cf539 Asynchronous emails: added a retry mechanism useful in case your SMTP server restricts the number of emails that can be sent over a period of time (usage: broadcasting a newsletter). The mechanism is not specific to sending email as it is implemented at the AsyncTask level. -reintegrated from trunk
SVN:2.0.2[3048]
2013-12-13 10:40:43 +00:00
Denis Flaven
a72392d0a8 #856: allow asynchronous emails to have an empty 'to' recipient... (not used anyway)
SVN:2.0.2[3044]
2013-12-12 18:05:42 +00:00
Romain Quetiez
d47609ae03 Created branch 2.0.2
SVN:2.0.2[3041]
2013-12-11 13:23:45 +00:00
66 changed files with 1476 additions and 542 deletions

View File

@@ -430,7 +430,7 @@ class URP_ActionGrant extends UserRightsBaseClass
//MetaModel::Init_InheritAttributes();
// Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked)
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
@@ -467,7 +467,7 @@ class URP_StimulusGrant extends UserRightsBaseClass
//MetaModel::Init_InheritAttributes();
// Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked)
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
@@ -503,7 +503,7 @@ class URP_AttributeGrant extends UserRightsBaseClass
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("actiongrantid", array("targetclass"=>"URP_ActionGrant", "jointype"=> "", "allowed_values"=>null, "sql"=>"actiongrantid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("actiongrantid", array("targetclass"=>"URP_ActionGrant", "jointype"=> "", "allowed_values"=>null, "sql"=>"actiongrantid", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("attcode", array("allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists

View File

@@ -450,7 +450,8 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
/** @ignore */
public function GetMenuItem()
{
return array ('label' => $this->GetLabel(), 'onclick' => $this->sJSCode, 'url' => '#');
// Note: the semicolumn is a must here!
return array ('label' => $this->GetLabel(), 'onclick' => $this->sJSCode.'; return false;', 'url' => '#');
}
/** @ignore */
@@ -580,6 +581,14 @@ class RestResult
* Result: the input structure is not a valid JSON string
*/
const INVALID_JSON = 4;
/**
* Result: the parameter 'auth_user' is missing, authentication aborted
*/
const MISSING_AUTH_USER = 5;
/**
* Result: the parameter 'auth_pwd' is missing, authentication aborted
*/
const MISSING_AUTH_PWD = 6;
/**
* Result: no operation is available for the specified version
*/
@@ -944,7 +953,14 @@ class RestUtils
foreach ($aFields as $sAttCode => $value)
{
$realValue = self::MakeValue($sClass, $sAttCode, $value);
$oObject->Set($sAttCode, $realValue);
try
{
$oObject->Set($sAttCode, $realValue);
}
catch (Exception $e)
{
throw new Exception("$sAttCode: ".$e->getMessage(), $e->getCode());
}
}
return $oObject;
}
@@ -964,7 +980,14 @@ class RestUtils
foreach ($aFields as $sAttCode => $value)
{
$realValue = self::MakeValue($sClass, $sAttCode, $value);
$oObject->Set($sAttCode, $realValue);
try
{
$oObject->Set($sAttCode, $realValue);
}
catch (Exception $e)
{
throw new Exception("$sAttCode: ".$e->getMessage(), $e->getCode());
}
}
return $oObject;
}

View File

@@ -286,6 +286,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
{
$sComment = (isset($aExtraParams['fieldsComments'][$sAttCode])) ? $aExtraParams['fieldsComments'][$sAttCode] : '';
$this->DisplayCaseLog($oPage, $sAttCode, $sComment, $sPrefix, $bEditMode);
$aFieldsMap[$sAttCode] = $this->m_iFormId.'_'.$sAttCode;
}
}
@@ -1225,6 +1226,8 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
else
{
$rawValue = $oObj->Get($sAttCodeEx);
// Due to custom formatting rules, empty friendlynames may be rendered as non-empty strings
// let's fix this and make sure we render an empty string if the key == 0
if ($oAttDef instanceof AttributeFriendlyName)
{
$sKeyAttCode = $oAttDef->GetKeyAttCode();
@@ -1430,20 +1433,24 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
}
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sFilterCode);
if ($oAttDef->IsExternalKey())
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
$sTargetClass = $oAttDef->GetTargetClass();
$oKeyAttDef = $oAttDef->GetFinalAttDef();
$sKeyAttClass = $oKeyAttDef->GetHostClass();
$sKeyAttCode = $oKeyAttDef->GetCode();
$sTargetClass = $oKeyAttDef->GetTargetClass();
$oSearch = new DBObjectSearch($sTargetClass);
$oSearch->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oAllowedValues = new DBObjectSet($oSearch);
$iFieldSize = $oAttDef->GetMaxSize();
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
$sHtml .= "<label>".MetaModel::GetFilterLabel($sClassName, $sFilterCode).":</label>&nbsp;";
$iFieldSize = $oKeyAttDef->GetMaxSize();
$iMaxComboLength = $oKeyAttDef->GetMaximumComboLength();
$sHtml .= "<label>".MetaModel::GetFilterLabel($sKeyAttClass, $sKeyAttCode).":</label>&nbsp;";
$aExtKeyParams = $aExtraParams;
$aExtKeyParams['iFieldSize'] = $oAttDef->GetMaxSize();
$aExtKeyParams['iMinChars'] = $oAttDef->GetMinAutoCompleteChars();
$sHtml .= UIExtKeyWidget::DisplayFromAttCode($oPage, $sFilterCode, $sClassName, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sSearchFormId.'search_'.$sFilterCode, false, $sFilterCode, '', $aExtKeyParams, true);
$aExtKeyParams['iFieldSize'] = $oKeyAttDef->GetMaxSize();
$aExtKeyParams['iMinChars'] = $oKeyAttDef->GetMinAutoCompleteChars();
$sHtml .= UIExtKeyWidget::DisplayFromAttCode($oPage, $sKeyAttCode, $sKeyAttClass, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sSearchFormId.'search_'.$sFilterCode, false, $sFilterCode, '', $aExtKeyParams, true);
}
else
{
@@ -2118,6 +2125,138 @@ EOF
}
return $oObj->DisplayModifyForm( $oPage, $aExtraParams);
}
public function DisplayStimulusForm(WebPage $oPage, $sStimulus)
{
$sClass = get_class($this);
$aTransitions = $this->EnumTransitions();
$aStimuli = MetaModel::EnumStimuli($sClass);
if (!isset($aTransitions[$sStimulus]))
{
// Invalid stimulus
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $this->GetName(), $this->GetStateLabel()));
}
$sActionLabel = $aStimuli[$sStimulus]->GetLabel();
$sActionDetails = $aStimuli[$sStimulus]->GetDescription();
$aTransition = $aTransitions[$sStimulus];
$sTargetState = $aTransition['target_state'];
$aTargetStates = MetaModel::EnumStates($sClass);
$oPage->add("<div class=\"page_header\">\n");
$oPage->add("<h1>$sActionLabel - <span class=\"hilite\">{$this->GetName()}</span></h1>\n");
$oPage->set_title($sActionLabel);
$oPage->add("</div>\n");
$aTargetState = $aTargetStates[$sTargetState];
$aExpectedAttributes = $aTargetState['attribute_list'];
$oPage->add("<h1>$sActionDetails</h1>\n");
$sButtonsPosition = MetaModel::GetConfig()->Get('buttons_position');
if ($sButtonsPosition == 'bottom')
{
// bottom: Displays the ticket details BEFORE the actions
$oPage->add('<div class="ui-widget-content">');
$this->DisplayBareProperties($oPage);
$oPage->add('</div>');
}
$oPage->add("<div class=\"wizContainer\">\n");
$oPage->add("<form id=\"apply_stimulus\" method=\"post\" onSubmit=\"return OnSubmit('apply_stimulus');\">\n");
$aDetails = array();
$iFieldIndex = 0;
$aFieldsMap = array();
$aDetailsList =$this->FlattenZList(MetaModel::GetZListItems($sClass, 'details'));
// Order the fields based on their dependencies, set the fields for which there is only one possible value
// and perform this in the order of dependencies to avoid dead-ends
$aDeps = array();
foreach($aDetailsList as $sAttCode)
{
$aDeps[$sAttCode] = MetaModel::GetPrequisiteAttributes($sClass, $sAttCode);
}
$aList =$this->OrderDependentFields($aDeps);
foreach($aList as $sAttCode)
{
// Consider only the "expected" fields for the target state
if (array_key_exists($sAttCode, $aExpectedAttributes))
{
$iExpectCode = $aExpectedAttributes[$sAttCode];
// Prompt for an attribute if
// - the attribute must be changed or must be displayed to the user for confirmation
// - or the field is mandatory and currently empty
if ( ($iExpectCode & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) ||
(($iExpectCode & OPT_ATT_MANDATORY) && ($this->Get($sAttCode) == '')) )
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$aArgs = array('this' => $this);
// If the field is mandatory, set it to the only possible value
if ((!$oAttDef->IsNullAllowed()) || ($iExpectCode & OPT_ATT_MANDATORY))
{
if ($oAttDef->IsExternalKey())
{
$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs);
if ($oAllowedValues->Count() == 1)
{
$oRemoteObj = $oAllowedValues->Fetch();
$this->Set($sAttCode, $oRemoteObj->GetKey());
}
}
else
{
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs);
if (count($aAllowedValues) == 1)
{
$aValues = array_keys($aAllowedValues);
$this->Set($sAttCode, $aValues[0]);
}
}
}
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef,$this->Get($sAttCode),$this->GetEditValue($sAttCode), 'att_'.$iFieldIndex, '', $iExpectCode, $aArgs);
$aDetails[] = array('label' => '<span>'.$oAttDef->GetLabel().'</span>', 'value' => "<span id=\"field_att_$iFieldIndex\">$sHTMLValue</span>");
$aFieldsMap[$sAttCode] = 'att_'.$iFieldIndex;
$iFieldIndex++;
}
}
}
$oPage->add('<table><tr><td>');
$oPage->details($aDetails);
$oPage->add('</td></tr></table>');
$oPage->add("<input type=\"hidden\" name=\"id\" value=\"".$this->GetKey()."\" id=\"id\">\n");
$aFieldsMap['id'] = 'id';
$oPage->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">\n");
$oPage->add("<input type=\"hidden\" name=\"operation\" value=\"apply_stimulus\">\n");
$oPage->add("<input type=\"hidden\" name=\"stimulus\" value=\"$sStimulus\">\n");
$oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">\n");
$oAppContext = new ApplicationContext();
$oPage->add($oAppContext->GetForForm());
$oPage->add("<button type=\"button\" class=\"action\" onClick=\"BackToDetails('$sClass', ".$this->GetKey().")\"><span>".Dict::S('UI:Button:Cancel')."</span></button>&nbsp;&nbsp;&nbsp;&nbsp;\n");
$oPage->add("<button type=\"submit\" class=\"action\"><span>$sActionLabel</span></button>\n");
$oPage->add("</form>\n");
$oPage->add("</div>\n");
if ($sButtonsPosition != 'top')
{
// bottom or both: Displays the ticket details AFTER the actions
$oPage->add('<div class="ui-widget-content">');
$this->DisplayBareProperties($oPage);
$oPage->add('</div>');
}
$iFieldsCount = count($aFieldsMap);
$sJsonFieldsMap = json_encode($aFieldsMap);
$oPage->add_script(
<<<EOF
// Initializes the object once at the beginning of the page...
var oWizardHelper = new WizardHelper('$sClass', '', '$sTargetState');
oWizardHelper.SetFieldsMap($sJsonFieldsMap);
oWizardHelper.SetFieldsCount($iFieldsCount);
EOF
);
$oPage->add_ready_script(
<<<EOF
// Starts the validation when the page is ready
CheckFields('apply_stimulus', false);
EOF
);
}
public static function ProcessZlist($aList, $aDetails, $sCurrentTab, $sCurrentCol, $sCurrentSet)
{
@@ -2709,7 +2848,7 @@ EOF
$aFinalValues[$sAttCode] = $aValues[$sAttCode];
}
$this->UpdateObjectFromArray($aFinalValues);
// Invoke extensions after the update of the object from the form
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
@@ -2930,7 +3069,7 @@ EOF
// Attribute is read-only
$sHTMLValue = $this->GetAsHTML($sAttCode);
$sHTMLValue .= '<input type="hidden" id="'.$sInputId.'" name="attr_'.$sPrefix.$sAttCode.'" value="'.htmlentities($this->Get($sAttCode), ENT_QUOTES, 'UTF-8').'"/>';
$sHTMLValue .= '<input type="hidden" id="'.$sInputId.'" name="attr_'.$sPrefix.$sAttCode.'" value="'.htmlentities($this->GetEditValue($sAttCode), ENT_QUOTES, 'UTF-8').'"/>';
$aFieldsMap[$sAttCode] = $sInputId;
$sComment .= $sSynchroIcon;
}
@@ -2962,7 +3101,7 @@ EOF
if (!isset($aTransitions[$sStimulus]))
{
// Invalid stimulus
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel()));
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus,$this->GetName(),$this->GetStateLabel()));
}
$aTransition = $aTransitions[$sStimulus];
$sTargetState = $aTransition['target_state'];
@@ -3107,7 +3246,11 @@ EOF
$currValue = $aKeys[0]; // The only value is the first key
//echo "<p>current value for $sAttCode : $currValue</p>";
$oDummyObj->Set($sAttCode, $currValue);
$aComments[$sAttCode] = '<input type="checkbox" checked id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
$aComments[$sAttCode] = '';
if ($sAttCode != MetaModel::GetStateAttributeCode($sClass))
{
$aComments[$sAttCode] .= '<input type="checkbox" checked id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
}
$aComments[$sAttCode] .= '<div class="mono_value">1</div>';
}
else
@@ -3134,7 +3277,11 @@ EOF
$sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );";
$oDummyObj->Set($sAttCode, null);
$aComments[$sAttCode] = '<input type="checkbox" id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
$aComments[$sAttCode] = '';
if ($sAttCode != MetaModel::GetStateAttributeCode($sClass))
{
$aComments[$sAttCode] .= '<input type="checkbox" id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
}
$aComments[$sAttCode] .= '<div class="multi_values" id="multi_values_'.$sAttCode.'">'.$iCount.'</div>';
}
$sReadyScript .= 'ToogleField('.(($iCount == 1) ? 'true': 'false').', \''.$iFormId.'_'.$sAttCode.'\');'."\n";
@@ -3284,12 +3431,12 @@ EOF
{
foreach($value as $vKey => $vValue)
{
$oP->add("<input type=\"hidden\" name=\"{$sKey}[$vKey]\" value=\"$vValue\">\n");
$oP->add("<input type=\"hidden\" name=\"{$sKey}[$vKey]\" value=\"".htmlentities($vValue, ENT_QUOTES, 'UTF-8')."\">\n");
}
}
else
{
$oP->add("<input type=\"hidden\" name=\"$sKey\" value=\"$value\">\n");
$oP->add("<input type=\"hidden\" name=\"$sKey\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\">\n");
}
}
}

View File

@@ -91,7 +91,7 @@ abstract class Dashboard
}
if ($oAutoReloadInterval = $oAutoReloadNode->getElementsByTagName('interval')->item(0))
{
$this->iAutoReloadSec = max(5, (int)$oAutoReloadInterval->textContent);
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$oAutoReloadInterval->textContent);
}
}
@@ -235,7 +235,7 @@ abstract class Dashboard
$this->sLayoutClass = $aParams['layout_class'];
$this->sTitle = $aParams['title'];
$this->bAutoReload = $aParams['auto_reload'] == 'true';
$this->iAutoReloadSec = max(5, (int) $aParams['auto_reload_sec']);
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']);
foreach($aParams['cells'] as $aCell)
{
@@ -300,7 +300,7 @@ abstract class Dashboard
public function SetAutoReloadInterval($iAutoReloadSec)
{
$this->iAutoReloadSec = max(5, (int)$iAutoReloadSec);
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec);
}
public function AddDashlet($oDashlet)
@@ -312,7 +312,7 @@ abstract class Dashboard
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$oPage->add('<h1>'.Dict::S($this->sTitle).'</h1>');
$oPage->add('<h1>'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</h1>');
$oLayout = new $this->sLayoutClass;
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
if (!$bEditMode)
@@ -357,16 +357,17 @@ abstract class Dashboard
$oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload);
$oForm->AddField($oField);
$oField = new DesignerTextField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
$oField->SetValidationPattern('^$|^0*([5-9]|[1-9][0-9]+)$'); // Can be empty, or a number > 4
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
$oForm->AddField($oField);
$this->SetFormParams($oForm);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$oPage->add('</div>');
$sRateTitle = addslashes(Dict::S('UI:DashboardEdit:AutoReloadSec+'));
$sRateTitle = addslashes(Dict::Format('UI:DashboardEdit:AutoReloadSec+', MetaModel::GetConfig()->Get('min_reload_interval')));
$oPage->add_ready_script(
<<<EOF
// Note: the title gets deleted by the validation mechanism

View File

@@ -506,11 +506,19 @@ abstract class DashletGroupBy extends Dashlet
$sGroupBy = $this->aProperties['group_by'];
$sStyle = $this->aProperties['style'];
// First perform the query - if the OQL is not ok, it will generate an exception : no need to go further
$oQuery = $this->oModelReflection->GetQuery($sQuery);
$sClass = $oQuery->GetClass();
$sClassAlias = $oQuery->GetClassAlias();
// First perform the query - if the OQL is not ok, it will generate an exception : no need to go further
try
{
$oQuery = $this->oModelReflection->GetQuery($sQuery);
$sClass = $oQuery->GetClass();
$sClassAlias = $oQuery->GetClassAlias();
}
catch(Exception $e)
{
// Invalid query, let the user edit the dashlet/dashboard anyhow
$sClass = '';
$sClassAlias = '';
}
// Check groupby... it can be wrong at this stage
if (preg_match('/^(.*):(.*)$/', $sGroupBy, $aMatches))
{

View File

@@ -551,6 +551,7 @@ EOF;
$oPage->add_ready_script(
<<<EOF
var oTable = $('#{$this->iListId} table.listResults');
oTable.tableHover();
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
EOF
);

View File

@@ -226,7 +226,7 @@ class DisplayBlock
if (is_numeric($aExtraParams['auto_reload']) && ($aExtraParams['auto_reload'] > 0))
{
$bAutoReload = true;
$iReloadInterval = max(5, $aExtraParams['auto_reload'])*1000;
$iReloadInterval = max(MetaModel::GetConfig()->Get('min_reload_interval'), $aExtraParams['auto_reload'])*1000;
}
else
{

View File

@@ -790,6 +790,74 @@ EOF
}
}
class DesignerIntegerField extends DesignerFormField
{
protected $iMin; // Lower boundary, inclusive
protected $iMax; // Higher boundary, inclusive
public function __construct($sCode, $sLabel = '', $defaultValue = '')
{
parent::__construct($sCode, $sLabel, $defaultValue);
$this->iMin = 0; // Positive integer is the default
$this->iMax = null;
}
public function SetBoundaries($iMin = null, $iMax = null)
{
$this->iMin = $iMin;
$this->iMax = $iMax;
}
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
if ($this->IsReadOnly())
{
$sHtmlValue = "<span>".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
}
else
{
$sMin = json_encode($this->iMin);
$sMax = json_encode($this->iMax);
$sMandatory = $this->bMandatory ? 'true' : 'false';
$oP->add_ready_script(
<<<EOF
$('#$sId').bind('change keyup validate', function() { ValidateInteger('$sId', $sMandatory, '$sFormId', $sMin, $sMax); } );
{
var myTimer = null;
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
}
EOF
);
$sCSSClasses = '';
if (count($this->aCSSClasses) > 0)
{
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
}
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">";
}
return array('label' => $this->sLabel, 'value' => $sHtmlValue);
}
public function ReadParam(&$aValues)
{
parent::ReadParam($aValues);
if (!is_null($this->iMin) && ($aValues[$this->sCode] < $this->iMin))
{
// Reject the value...
$aValues[$this->sCode] = $this->defaultValue;
}
if (!is_null($this->iMax) && ($aValues[$this->sCode] > $this->iMax))
{
// Reject the value...
$aValues[$this->sCode] = $this->defaultValue;
}
}
}
class DesignerComboField extends DesignerFormField
{
protected $aAllowedValues;

View File

@@ -33,6 +33,7 @@ class LoginWebPage extends NiceWebPage
{
const EXIT_PROMPT = 0;
const EXIT_HTTP_401 = 1;
const EXIT_RETURN_FALSE = 2;
protected static $sHandlerClass = __class__;
public static function RegisterHandler($sClass)
@@ -105,6 +106,7 @@ class LoginWebPage extends NiceWebPage
case 'url':
$this->add_header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
$this->add_header('HTTP/1.0 401 Unauthorized');
$this->add_header('Content-type: text/html; charset=iso-8859-1');
// Note: displayed when the user will click on Cancel
$this->add('<p><strong>'.Dict::S('UI:Login:Error:AccessRestricted').'</strong></p>');
break;
@@ -190,7 +192,7 @@ class LoginWebPage extends NiceWebPage
$this->add("<p>".Dict::S('UI:Login:ForgotPwdForm+')."</p>\n");
if ($bFailedToReset)
{
$this->add("<p class=\"hilite\">".Dict::Format('UI:Login:ResetPwdFailed', $sFailureReason)."</p>\n");
$this->add("<p class=\"hilite\">".Dict::Format('UI:Login:ResetPwdFailed', htmlentities($sFailureReason, ENT_QUOTES, 'UTF-8'))."</p>\n");
}
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
$this->add("<form method=\"post\">\n");
@@ -238,7 +240,12 @@ class LoginWebPage extends NiceWebPage
$oEmail = new Email();
$oEmail->SetRecipientTO($sTo);
$oEmail->SetRecipientFrom($sTo);
$sFrom = MetaModel::GetConfig()->Get('forgot_password_from');
if ($sFrom == '')
{
$sFrom = $sTo;
}
$oEmail->SetRecipientFrom($sFrom);
$oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject'));
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);
$oEmail->SetBody(Dict::Format('UI:ResetPwd-EmailBody', $sResetUrl));
@@ -419,6 +426,24 @@ EOF
return MetaModel::GetConfig()->GetSecureConnectionRequired();
}
/**
* Guess if a string looks like an UTF-8 string based on some ranges of multi-bytes encoding
* @param string $sString
* @return bool True if the string contains some typical UTF-8 multi-byte sequences
*/
static function LooksLikeUTF8($sString)
{
return preg_match('%(?:
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
|\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
|\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)+%xs', $sString);
}
/**
* Attempt a login
*
@@ -484,7 +509,22 @@ EOF
else if (isset($_SERVER['PHP_AUTH_USER']))
{
$sAuthUser = $_SERVER['PHP_AUTH_USER'];
// Unfortunately, the RFC is not clear about the encoding...
// IE and FF supply the user and password encoded in ISO-8859-1 whereas Chrome provides them encoded in UTF-8
// So let's try to guess if it's an UTF-8 string or not... fortunately all encodings share the same ASCII base
if (!self::LooksLikeUTF8($sAuthUser))
{
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthUser = iconv('iso-8859-1', 'utf-8', $sAuthUser);
}
$sAuthPwd = $_SERVER['PHP_AUTH_PW'];
if (!self::LooksLikeUTF8($sAuthPwd))
{
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthPwd = iconv('iso-8859-1', 'utf-8', $sAuthPwd);
}
$sLoginMode = 'basic';
}
break;
@@ -527,11 +567,17 @@ EOF
{
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
}
if ($iOnExit == self::EXIT_HTTP_401)
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
{
header("HTTP/1.0 401 Unauthorized");
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
header('HTTP/1.0 401 Unauthorized');
header('Content-type: text/html; charset=iso-8859-1');
exit;
}
else if($iOnExit == self::EXIT_RETURN_FALSE)
{
return false;
}
else
{
$oPage = self::NewLoginWebPage();
@@ -546,11 +592,17 @@ EOF
{
//echo "Check Credentials returned false for user $sAuthUser!";
self::ResetSession();
if ($iOnExit == self::EXIT_HTTP_401)
if (($iOnExit == self::EXIT_HTTP_401))
{
header("HTTP/1.0 401 Unauthorized");
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
header('HTTP/1.0 401 Unauthorized');
header('Content-type: text/html; charset=iso-8859-1');
exit;
}
else if($iOnExit == self::EXIT_RETURN_FALSE)
{
return false;
}
else
{
$oPage = self::NewLoginWebPage();
@@ -578,6 +630,7 @@ EOF
}
}
}
return true;
}
/**
@@ -684,7 +737,7 @@ EOF
$sMessage = Dict::S('UI:Login:PasswordChanged');
}
self::Login($iOnExit);
$bRet = self::Login($iOnExit);
if ($bMustBeAdmin && !UserRights::IsAdministrator())
{
@@ -696,6 +749,13 @@ EOF
exit;
}
call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $bIsAllowedToPortalUsers);
return $sMessage;
if ($iOnExit == self::EXIT_RETURN_FALSE)
{
return $bRet;
}
else
{
return $sMessage;
}
}
} // End of class

View File

@@ -324,7 +324,7 @@ EOF
$sUniqueId = $sClass.$this->GetUniqueId();
$this->add("<div id=\"$sUniqueId\">\n"); // The id here MUST be the same as currentId, otherwise the pagination will be broken
cmdbAbstractObject::DisplaySet($this, $oSet, array('currentId' => $sUniqueId, 'menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList)));
cmdbAbstractObject::DisplaySet($this, $oSet, array('currentId' => $sUniqueId, 'menu' => false, 'toolkit_menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList)));
$this->add("</div>\n");
}
else
@@ -420,7 +420,7 @@ EOF
}
$oObjSearch->AddCondition_ReferencedBy($oLinkSet->GetFilter(), $sRemoteAttCode);
$aExtraParams = array('menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList));
$aExtraParams = array('menu' => false, 'toolkit_menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList));
$oBlock = new DisplayBlock($oObjSearch, 'list', false);
$oBlock->Display($this, 1, $aExtraParams);
}
@@ -480,7 +480,9 @@ EOF
{
try
{
$oAllowedValues = new DBObjectSet(DBObjectSearch::FromOQL(constant($sFilterDefName)), array(), $aFilterParams);
$oFitlerWithParams = DBObjectSearch::FromOQL(constant($sFilterDefName));
$sFilterOQL = $oFitlerWithParams->ToOQL(true, $aFilterParams);
$oAllowedValues = new DBObjectSet(DBObjectSearch::FromOQL($sFilterOQL), array(), $aFilterParams);
}
catch(OQLException $e)
{
@@ -510,7 +512,8 @@ EOF
if (is_null($aAllowedValues))
{
// Any value is possible, display an input box
$this->add("<label>".MetaModel::GetFilterLabel($sClass, $sAttSpec).":</label>&nbsp;<input class=\"textSearch\" name=\"$sPrefix$sFieldName\" value=\"$sFilterValue\"/>\n");
$sSanitizedValue = htmlentities($sFilterValue, ENT_QUOTES, 'UTF-8');
$this->add("<label>".MetaModel::GetFilterLabel($sClass, $sAttSpec).":</label>&nbsp;<input class=\"textSearch\" name=\"$sPrefix$sFieldName\" value=\"$sSanitizedValue\"/>\n");
}
else
{
@@ -793,24 +796,7 @@ EOF
}
}
$sOQL = "SELECT $sClass WHERE org_id = :org_id";
$oSearch = DBObjectSearch::FromOQL($sOQL);
$iUser = UserRights::GetContactId();
if ($iUser > 0 && !IsPowerUser())
{
$oSearch->AddCondition('caller_id', $iUser);
}
$oSearch->AddCondition('id', $iId);
$oContact = MetaModel::GetObject('Contact', $iUser, false); // false => Can fail
if (!is_object($oContact))
{
throw new Exception(Dict::S('Portal:ErrorNoContactForThisUser'));
}
$oSet = new DBObjectSet($oSearch, array(), array('org_id' => $oContact->Get('org_id')));
$oObj = $oSet->Fetch();
$oObj = MetaModel::GetObject($sClass, $iId, false);
if (!is_object($oObj))
{
throw new Exception("Could not find the object $sClass/$iId");

View File

@@ -254,8 +254,8 @@ class ShortcutOQL extends Shortcut
$oField = new DesignerBooleanField('auto_reload', Dict::S('Class:ShortcutOQL/Attribute:auto_reload'), false);
$oForm->AddField($oField);
$oField = new DesignerTextField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
$oField->SetValidationPattern('^$|^0*([5-9]|[1-9][0-9]+)$'); // Can be empty, or a number > 4
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
$oField->SetMandatory(false);
$oForm->AddField($oField);
@@ -284,7 +284,7 @@ class ShortcutOQL extends Shortcut
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sRateTitle = addslashes(Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec+'));
$sRateTitle = addslashes(Dict::Format('Class:ShortcutOQL/Attribute:auto_reload_sec/tip', MetaModel::GetConfig()->Get('min_reload_interval')));
$oPage->add_ready_script(
<<<EOF

View File

@@ -453,7 +453,7 @@ EOF
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true));
$oPage->add('</div></div></div>');
// $oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: $(window).width()*0.8, height: 'auto', autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("$('#dcr_{$this->iId} form').removeAttr('onsubmit');");
$oPage->add_ready_script("$('#dcr_{$this->iId} form').bind('submit.uilinksWizard', oACWidget_{$this->iId}.DoCreateObject);");
}

View File

@@ -318,12 +318,11 @@ class UILinksWidgetDirect
{
$oFilter->AddCondition('id', $aAlreadyLinked, 'NOTIN');
}
$aArgs = array();
if ($oCurrentObj != null)
{
$aArgs = $oCurrentObj->ToArgs('this');
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
$oFilter->SetInternalParams($aArgs);
}
$oFilter->SetInternalParams($aArgs);
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->sInputid}", array('menu' => false, 'cssCount'=> '#count_'.$this->sInputid , 'selection_mode' => true, 'table_id' => 'add_'.$this->sInputid)); // Don't display the 'Actions' menu on the results
}

View File

@@ -239,7 +239,7 @@ EOF
*/
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData)
{
$sHtml = '';
$sHtml = "<input type=\"hidden\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\">";
$sHtml .= "<table class=\"listResults\">\n";
// Header
$sHtml .= "<thead>\n";
@@ -259,11 +259,11 @@ EOF
$sEmptyRowStyle = 'style="display:none;"';
}
$sHtml .= "<tr $sEmptyRowStyle id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."<input type=\"hidden\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\"></td></td>";
foreach($aData as $iRowId => $aRow)
{
$sHtml .= $this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId);
}
$sHtml .= "<tr $sEmptyRowStyle id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."</td></tr>";
$sHtml .= "</tbody>\n";
// Footer

View File

@@ -34,18 +34,65 @@ class ExecAsyncTask implements iBackgroundProcess
public function Process($iTimeLimit)
{
$sOQL = "SELECT AsyncTask WHERE ISNULL(started) AND (ISNULL(planned) OR (planned < NOW()))";
$sNow = date('Y-m-d H:i:s');
$sOQL = "SELECT AsyncTask WHERE ISNULL(started) AND (ISNULL(planned) OR (planned < '$sNow'))";
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array());
$iProcessed = 0;
while ((time() < $iTimeLimit) && ($oTask = $oSet->Fetch()))
{
$oTask->Set('started', time());
$oTask->DBUpdate();
try
{
$oTask->Set('started', time());
$oTask->DBUpdate();
}
catch(Exception $e)
{
// Corrupted task !! (for example: "Failed to reload object")
IssueLog::Error('Failed to process async task #'.$oTask->GetKey().' - reason: '.$e->getMessage().' - fatal error, deleting the task.');
if ($oTask->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $oTask->Get('event_id'));
$oEventLog->Set('message', 'Failed, corrupted data: '.$e->getMessage());
$oEventLog->DBUpdate();
}
$oTask->DBDelete();
continue; // end of processing for this task
}
$oTask->Process();
$iProcessed++;
try
{
$oTask->Process();
$iProcessed++;
$oTask->DBDelete();
$oTask->DBDelete();
}
catch(Exception $e)
{
$iRemaining = $oTask->Get('remaining_retries');
if ($iRemaining > 0)
{
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
if (is_array($aRetries) && array_key_exists(get_class($oTask), $aRetries))
{
$aConfig = $aRetries[get_class($oTask)];
$iRetryDelay = $aConfig['retry_delay'];
}
else
{
$iRetryDelay = 600;
}
IssueLog::Info('Failed to process async task #'.$oTask->GetKey().' - reason: '.$e->getMessage().' - remaining retries: '.$iRemaining.' - next retry in '.$iRetryDelay.'s');
$oTask->Set('remaining_retries', $iRemaining - 1);
$oTask->Set('started', null);
$oTask->Set('planned', time() + $iRetryDelay);
$oTask->DBUpdate();
}
else
{
IssueLog::Error('Failed to process async task #'.$oTask->GetKey().' - reason: '.$e->getMessage());
}
}
}
if ($iProcessed == $oSet->Count())
{
@@ -88,6 +135,8 @@ abstract class AsyncTask extends DBObject
MetaModel::Init_AddAttribute(new AttributeDateTime("planned", array("allowed_values"=>null, "sql"=>"planned", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("event_id", array("targetclass"=>"Event", "jointype"=> "", "allowed_values"=>null, "sql"=>"event_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("remaining_retries", array("allowed_values"=>null, "sql"=>"remaining_retries", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
// MetaModel::Init_SetZListItems('details', array()); // Attributes to be displayed for the complete details
// MetaModel::Init_SetZListItems('list', array()); // Attributes to be displayed for a list
@@ -99,6 +148,14 @@ abstract class AsyncTask extends DBObject
protected function OnInsert()
{
$this->Set('created', time());
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$iRetries = $aConfig['max_retries'];
$this->Set('remaining_retries', $iRetries);
}
}
public function Process()
@@ -140,7 +197,7 @@ class AsyncSendEmail extends AsyncTask
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeInteger("version", array("allowed_values"=>null, "sql"=>"version", "default_value"=>Email::ORIGINAL_FORMAT, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));

View File

@@ -1915,7 +1915,12 @@ class AttributeCaseLog extends AttributeLongText
// Facilitate things: allow the user to Set the value from a string
public function MakeRealValue($proposedValue, $oHostObj)
{
if (!($proposedValue instanceof ormCaseLog))
if ($proposedValue instanceof ormCaseLog)
{
// Passthrough
$ret = $proposedValue;
}
else
{
// Append the new value if an instance of the object is supplied
//
@@ -1937,13 +1942,21 @@ class AttributeCaseLog extends AttributeLongText
{
$oCaseLog = new ormCaseLog();
}
if (strlen($proposedValue) > 0)
if ($proposedValue instanceof stdClass)
{
$oCaseLog->AddLogEntry(parent::MakeRealValue($proposedValue, $oHostObj));
$oCaseLog->AddLogEntryFromJSON($proposedValue);
}
return $oCaseLog;
else
{
if (strlen($proposedValue) > 0)
{
$oCaseLog->AddLogEntry(parent::MakeRealValue($proposedValue, $oHostObj));
}
}
$ret = $oCaseLog;
}
return $proposedValue;
return $ret;
}
public function GetSQLExpressions($sPrefix = '')
@@ -2061,6 +2074,45 @@ class AttributeCaseLog extends AttributeLongText
return '';
}
}
/**
* Helper to get a value that will be JSON encoded
* The operation is the opposite to FromJSONToValue
*/
public function GetForJSON($value)
{
return $value->GetForJSON();
}
/**
* Helper to form a value, given JSON decoded data
* The operation is the opposite to GetForJSON
*/
public function FromJSONToValue($json)
{
if (is_string($json))
{
// Will be correctly handled in MakeRealValue
$ret = $json;
}
else
{
if (isset($json->add_item))
{
// Will be correctly handled in MakeRealValue
$ret = $json->add_item;
if (!isset($ret->message))
{
throw new Exception("Missing mandatory entry: 'message'");
}
}
else
{
$ret = ormCaseLog::FromJSON($json);
}
}
return $ret;
}
}
/**
@@ -2087,8 +2139,7 @@ class AttributeEmailAddress extends AttributeString
{
public function GetValidationPattern()
{
// return "^([0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\\w]*[0-9a-zA-Z]\\.)+[a-zA-Z]{2,9})$";
return "^[a-zA-Z0-9._&-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]{2,}$";
return $this->GetOptional('validation_pattern', '^'.utils::GetConfig()->Get('email_validation_pattern').'$');
}
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
@@ -2347,6 +2398,15 @@ class AttributeEnum extends AttributeString
}
}
/**
* Helper to get a value that will be JSON encoded
* The operation is the opposite to FromJSONToValue
*/
public function GetForJSON($value)
{
return $value;
}
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
$aRawValues = parent::GetAllowedValues($aArgs, $sContains);

View File

@@ -332,6 +332,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'async_task_retries' => array(
'type' => 'array',
'description' => 'Automatic retries of asynchronous tasks in case of failure (per class)',
'default' => array('AsyncSendEmail' => array('max_retries' => 0, 'retry_delay' => 600)),
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'email_asynchronous' => array(
'type' => 'bool',
'description' => 'If set, the emails are sent off line, which requires cron.php to be activated. Exception: some features like the email test utility will force the serialized mode',
@@ -540,6 +548,15 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'forgot_password_from' => array(
'type' => 'string',
'description' => 'Sender email address for the "forgot password" feature. If empty, defaults to the recipient\'s email address.',
// examples... not used (nor 'description')
'default' => '',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'deadline_format' => array(
'type' => 'string',
'description' => 'The format used for displaying "deadline" attributes: any string with the following placeholders: $date$, $difference$',
@@ -632,6 +649,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'email_validation_pattern' => array(
'type' => 'string',
'description' => 'Regular expression to validate/detect the format of an eMail address',
'default' => "[a-zA-Z0-9._&'-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]{2,}",
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'log_kpi_duration' => array(
'type' => 'integer',
'description' => 'Level of logging for troubleshooting performance issues (1 to enable, 2 +blame callers)',
@@ -684,6 +709,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'min_reload_interval' => array(
'type' => 'integer',
'description' => 'Minimum refresh interval (seconds) for dashboards, shortcuts, etc. Even if the interval is set programmatically, it is forced to that minimum',
'default' => 5, // In iTop 2.0.3, this was the hardcoded value
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
);
public function IsProperty($sPropCode)
@@ -712,6 +745,8 @@ class Config
case 'float':
$value = (float) $value;
break;
case 'array':
break;
default:
throw new CoreException('Unknown type for setting', array('property' => $sPropCode, 'type' => $sType));
}
@@ -968,7 +1003,14 @@ class Config
{
if ($this->IsProperty($sPropCode))
{
$value = trim($rawvalue);
if (is_string($rawvalue))
{
$value = trim($rawvalue);
}
else
{
$value = $rawvalue;
}
$this->Set($sPropCode, $value, $sConfigFile);
}
}

View File

@@ -400,16 +400,25 @@ abstract class DBObject
{
throw new CoreException("Unknown external key '$sExtKeyAttCode' for the class ".get_class($this));
}
$oKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode);
$sRemoteClass = $oKeyAttDef->GetTargetClass();
$oRemoteObj = MetaModel::GetObject($sRemoteClass, $this->GetStrict($sExtKeyAttCode), false);
if (is_null($oRemoteObj))
$oExtFieldAtt = MetaModel::FindExternalField(get_class($this), $sExtKeyAttCode, $sRemoteAttCode);
if (!is_null($oExtFieldAtt))
{
return '';
return $this->GetStrict($oExtFieldAtt->GetCode());
}
else
{
return $oRemoteObj->Get($sRemoteAttCode);
$oKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode);
$sRemoteClass = $oKeyAttDef->GetTargetClass();
$oRemoteObj = MetaModel::GetObject($sRemoteClass, $this->GetStrict($sExtKeyAttCode), false);
if (is_null($oRemoteObj))
{
return '';
}
else
{
return $oRemoteObj->Get($sRemoteAttCode);
}
}
}
}
@@ -2042,7 +2051,7 @@ abstract class DBObject
{
$sRemoteName = $oAttDef->IsIndirect() ? $oAttDef->GetExtKeyToRemote().'_friendlyname' : 'friendlyname';
$oLinkSet = $this->Get($sAttCode);
$oLinkSet = clone $this->Get($sAttCode); // Workaround/Safety net for Trac #887
$iLimit = MetaModel::GetConfig()->Get('max_linkset_output');
if ($iLimit > 0)
{

View File

@@ -418,7 +418,7 @@ class DBObjectSet
CMDBSource::FreeResult($resQuery);
$this->m_iCount = $aRow['COUNT'];
}
return $this->m_iCount;
return $this->m_iCount; // WARNING this value can be wrong, see Trac #887
}
}
@@ -741,8 +741,11 @@ class DBObjectSet
{
foreach($aVals as $sCode => $oExpr)
{
$oScalarExpr = $oExpr->GetAsScalar($aScalarArgs);
$aConst[$sClassAlias][$sCode] = $oScalarExpr->GetValue();
if (is_object($oExpr)) // Array_merge_recursive creates an array when the same key is present multiple times... ignore them
{
$oScalarExpr = $oExpr->GetAsScalar($aScalarArgs);
$aConst[$sClassAlias][$sCode] = $oScalarExpr->GetValue();
}
}
}
return $aConst;

View File

@@ -351,7 +351,7 @@ class EventLoginUsage extends Event
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
$aZList = array('date', 'user_id');
if (MetaModel::IsValidAttCode('Contact', 'name'))
{

View File

@@ -299,14 +299,11 @@ class BinaryExpression extends Expression
{
$aResult[$this->m_oRightExpr->GetParent()][$this->m_oRightExpr->GetName()] = $this->m_oLeftExpr;
}
else
{
$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
}
}
else
else if ($this->m_sOperator == 'AND')
{
$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
// Strictly, this should be done only for the AND operator
$aResult = array_merge_recursive($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields());
}
return $aResult;
}

View File

@@ -840,6 +840,10 @@ abstract class MetaModel
final static public function GetAttributeDef($sClass, $sAttCode)
{
self::_check_subclass($sClass);
if (!isset(self::$m_aAttribDefs[$sClass][$sAttCode]))
{
throw new Exception("Unknown attribute $sAttCode from class $sClass");
}
return self::$m_aAttribDefs[$sClass][$sAttCode];
}
@@ -871,15 +875,32 @@ abstract class MetaModel
final static public function GetExternalFields($sClass, $sKeyAttCode)
{
$aExtFields = array();
foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAtt)
static $aExtFields = array();
if (!isset($aExtFields[$sClass][$sKeyAttCode]))
{
if ($oAtt->IsExternalField() && ($oAtt->GetKeyAttCode() == $sKeyAttCode))
$aExtFields[$sClass][$sKeyAttCode] = array();
foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAtt)
{
$aExtFields[] = $oAtt;
if ($oAtt->IsExternalField() && ($oAtt->GetKeyAttCode() == $sKeyAttCode))
{
$aExtFields[$sClass][$sKeyAttCode][$oAtt->GetExtAttCode()] = $oAtt;
}
}
}
return $aExtFields;
return $aExtFields[$sClass][$sKeyAttCode];
}
final static public function FindExternalField($sClass, $sKeyAttCode, $sRemoteAttCode)
{
$aExtFields = self::GetExternalFields($sClass, $sKeyAttCode);
if (isset($aExtFields[$sRemoteAttCode]))
{
return $aExtFields[$sRemoteAttCode];
}
else
{
return null;
}
}
final static public function GetExtKeyFriends($sClass, $sExtKeyAttCode)
@@ -5121,6 +5142,12 @@ abstract class MetaModel
}
return $aResult;
}
/**
* It is not recommended to use this function: call GetLinkClasses instead
* Return classes having at least to external keys (thus too many classes as compared to GetLinkClasses)
* The only difference with EnumLinkingClasses is the output format
*/
public static function EnumLinksClasses()
{
// Returns a flat array of classes having at least two external keys
@@ -5143,6 +5170,11 @@ abstract class MetaModel
}
return $aResult;
}
/**
* It is not recommended to use this function: call GetLinkClasses instead
* Return classes having at least to external keys (thus too many classes as compared to GetLinkClasses)
* The only difference with EnumLinksClasses is the output format
*/
public static function EnumLinkingClasses($sClass = "")
{
// N-N links, array of sLinkClass => (array of sAttCode=>sClass)
@@ -5179,6 +5211,38 @@ abstract class MetaModel
return $aResult;
}
/**
* This function has two siblings that will be soon deprecated:
* EnumLinkingClasses and EnumLinkClasses
*
* Using GetLinkClasses is the recommended way to determine if a class is
* actually an N-N relation because it is based on the decision made by the
* designer the data model
*/
public static function GetLinkClasses()
{
$aRet = array();
foreach(self::GetClasses() as $sClass)
{
if (isset(self::$m_aClassParams[$sClass]["is_link"]))
{
if (self::$m_aClassParams[$sClass]["is_link"])
{
$aExtKeys = array();
foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
if ($oAttDef->IsExternalKey())
{
$aExtKeys[$sAttCode] = $oAttDef->GetTargetClass();
}
}
$aRet[$sClass] = $aExtKeys;
}
}
}
return $aRet;
}
public static function GetLinkLabel($sLinkClass, $sAttCode)
{
self::_check_subclass($sLinkClass);
@@ -5248,18 +5312,27 @@ abstract class MetaModel
$sEnvironment = MetaModel::GetEnvironmentId();
}
$aEntries = array();
$aCacheUserData = @apc_cache_info('user');
if (extension_loaded('apcu'))
{
// Beware: APCu behaves slightly differently from APC !!
$aCacheUserData = @apc_cache_info();
}
else
{
$aCacheUserData = @apc_cache_info('user');
}
if (is_array($aCacheUserData) && isset($aCacheUserData['cache_list']))
{
$sPrefix = 'itop-'.$sEnvironment.'-';
foreach($aCacheUserData['cache_list'] as $i => $aEntry)
{
$sEntryKey = $aEntry['info'];
$sEntryKey = array_key_exists('info', $aEntry) ? $aEntry['info'] : $aEntry['key'];
if (strpos($sEntryKey, $sPrefix) === 0)
{
$sCleanKey = substr($sEntryKey, strlen($sPrefix));
$aEntries[$sCleanKey] = $aEntry;
$aEntries[$sCleanKey]['info'] = $sEntryKey;
}
}
}

View File

@@ -178,7 +178,7 @@ class ModelReflectionRuntime extends ModelReflection
$aClasses = MetaModel::GetClasses($sCategories);
if ($bExcludeLinks)
{
$aExcluded = ProfilesConfig::GetLinkClasses(); // table computed at compile time
$aExcluded = MetaModel::GetLinkClasses();
$aRes = array();
foreach ($aClasses as $sClass)
{

View File

@@ -31,12 +31,20 @@ class iTopMutex
{
protected $sName;
protected $hDBLink;
protected $bLocked; // Whether or not this instance of the Mutex is locked
static protected $aAcquiredLocks = array(); // Number of instances of the Mutex, having the lock, in this page
public function __construct($sName)
{
// Compute the name of a lock for mysql
// Note: the name is server-wide!!!
$this->sName = 'itop.'.$sName;
$this->bLocked = false; // Not yet locked
if (!array_key_exists($this->sName, self::$aAcquiredLocks))
{
self::$aAcquiredLocks[$this->sName] = 0;
}
// It is a MUST to create a dedicated session each time a lock is required, because
// using GET_LOCK anytime on the same session will RELEASE the current and unique session lock (known issue)
@@ -46,7 +54,10 @@ class iTopMutex
public function __destruct()
{
$this->Unlock();
if ($this->bLocked)
{
$this->Unlock();
}
mysqli_close($this->hDBLink);
}
@@ -55,17 +66,27 @@ class iTopMutex
*/
public function Lock()
{
do
if ($this->bLocked)
{
$res = $this->QueryToScalar("SELECT GET_LOCK('".$this->sName."', 3600)");
if (is_null($res))
{
throw new Exception("Failed to acquire the lock '".$this->sName."'");
}
// $res === '1' means I hold the lock
// $res === '0' means it timed out
// Lock already acquired
return;
}
while ($res !== '1');
if (self::$aAcquiredLocks[$this->sName] == 0)
{
do
{
$res = $this->QueryToScalar("SELECT GET_LOCK('".$this->sName."', 3600)");
if (is_null($res))
{
throw new Exception("Failed to acquire the lock '".$this->sName."'");
}
// $res === '1' means I hold the lock
// $res === '0' means it timed out
}
while ($res !== '1');
}
$this->bLocked = true;
self::$aAcquiredLocks[$this->sName]++;
}
/**
@@ -74,6 +95,17 @@ class iTopMutex
*/
public function TryLock()
{
if ($this->bLocked)
{
return true; // Already acquired
}
if (self::$aAcquiredLocks[$this->sName] > 0)
{
self::$aAcquiredLocks[$this->sName]++;
$this->bLocked = true;
return true;
}
$res = $this->QueryToScalar("SELECT GET_LOCK('".$this->sName."', 0)");
if (is_null($res))
{
@@ -81,6 +113,11 @@ class iTopMutex
}
// $res === '1' means I hold the lock
// $res === '0' means it timed out
if ($res === '1')
{
$this->bLocked = true;
self::$aAcquiredLocks[$this->sName]++;
}
return ($res === '1');
}
@@ -89,7 +126,22 @@ class iTopMutex
*/
public function Unlock()
{
$res = $this->QueryToScalar("SELECT RELEASE_LOCK('".$this->sName."')");
if (!$this->bLocked)
{
// ??? the lock is not acquired, exit
return;
}
if (self::$aAcquiredLocks[$this->sName] == 0)
{
return; // Safety net
}
if (self::$aAcquiredLocks[$this->sName] == 1)
{
$res = $this->QueryToScalar("SELECT RELEASE_LOCK('".$this->sName."')");
}
$this->bLocked = false;
self::$aAcquiredLocks[$this->sName]--;
}

View File

@@ -48,6 +48,80 @@ class ormCaseLog {
return $this->m_sLog;
}
public static function FromJSON($oJson)
{
if (!isset($oJson->items))
{
throw new Exception("Missing 'items' elements");
}
$oCaseLog = new ormCaseLog();
foreach($oJson->items as $oItem)
{
$oCaseLog->AddLogEntryFromJSON($oItem);
}
return $oCaseLog;
}
/**
* Return a value that will be further JSON encoded
*/
public function GetForJSON()
{
$aEntries = array();
$iPos = 0;
for($index=count($this->m_aIndex)-1 ; $index >= 0 ; $index--)
{
$iPos += $this->m_aIndex[$index]['separator_length'];
$sTextEntry = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
$iPos += $this->m_aIndex[$index]['text_length'];
// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
// therefore we have changed the format. To preserve the compatibility with existing
// installations of iTop, both format are allowed:
// the 'date' item is either a DateTime object, or a unix timestamp
if (is_int($this->m_aIndex[$index]['date']))
{
// Unix timestamp
$sDate = date(Dict::S('UI:CaseLog:DateFormat'),$this->m_aIndex[$index]['date']);
}
elseif (is_object($this->m_aIndex[$index]['date']))
{
if (version_compare(phpversion(), '5.3.0', '>='))
{
// DateTime
$sDate = $this->m_aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat'));
}
else
{
// No Warning... but the date is unknown
$sDate = '';
}
}
$aEntries[] = array(
'date' => $sDate,
'user_login' => $this->m_aIndex[$index]['user_name'],
'user_id' => $this->m_aIndex[$index]['user_id'],
'message' => $sTextEntry
);
}
// Process the case of an eventual remainder (quick migration of AttributeText fields)
if ($iPos < (strlen($this->m_sLog) - 1))
{
$sTextEntry = substr($this->m_sLog, $iPos);
$aEntries[] = array(
'date' => '',
'user_login' => '',
'message' => $sTextEntry
);
}
// Order by ascending date
$aRet = array('entries' => array_reverse($aEntries));
return $aRet;
}
public function GetIndex()
{
return $this->m_aIndex;
@@ -230,7 +304,62 @@ class ormCaseLog {
}
$this->m_bModified = true;
}
public function AddLogEntryFromJSON($oJson)
{
$sText = isset($oJson->message) ? $oJson->message : '';
if (isset($oJson->user_id))
{
if (!UserRights::IsAdministrator())
{
throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED);
}
try
{
$oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
}
catch(Exception $e)
{
throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
}
$iUserId = $oUser->GetKey();
$sOnBehalfOf = $oUser->GetFriendlyName();
}
else
{
$iUserId = UserRights::GetUserId();
$sOnBehalfOf = UserRights::GetUserFriendlyName();
}
if (isset($oJson->date))
{
$oDate = new DateTime($oJson->date);
$iDate = (int) $oDate->format('U');
}
else
{
$iDate = time();
}
$sDate = date(Dict::S('UI:CaseLog:DateFormat'), $iDate);
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
$iSepLength = strlen($sSeparator);
$iTextlength = strlen($sText);
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
$this->m_aIndex[] = array(
'user_name' => $sOnBehalfOf,
'user_id' => $iUserId,
'date' => $iDate,
'text_length' => $iTextlength,
'separator_length' => $iSepLength,
);
$this->m_bModified = true;
}
public function GetModifiedEntry()
{
$sModifiedEntry = '';

View File

@@ -117,7 +117,18 @@ class ormDocument
{
return "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode\">".$this->GetFileName()."</a>\n";
}
/**
* Returns an URL to download a document like an image (uses HTTP caching)
* @return string
*/
public function GetDownloadURL($sClass, $Id, $sAttCode)
{
// Compute a signature to reset the cache anytime the data changes (this is acceptable if used only with icon files)
$sSignature = md5($this->GetData());
return utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400";
}
public function IsPreviewAvailable()
{

View File

@@ -258,16 +258,22 @@ class ormStopWatch
public function Reset($oObject, $oAttDef)
{
$this->iTimeSpent = 0;
$this->iStarted = null;
$this->iLastStart = null;
$this->iStopped = null;
$this->iStarted = null;
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
{
$aThresholdData['triggered'] = false;
$aThresholdData['deadline'] = null;
$aThresholdData['overrun'] = null;
}
if (!is_null($this->iLastStart))
{
// Currently running... starting again from now!
$this->iStarted = time();
$this->iLastStart = time();
$this->ComputeDeadlines($oObject, $oAttDef);
}
}
/**

View File

@@ -16,36 +16,17 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Localized data
*
* @author Stephan Rosenke <stephan.rosenke@itomig.de>
* @author Stephan Rosenke <stephan.rosenke@itomig.de>
* @author David M. Gümbel <david.guembel@itomig.de>
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @licence http://opensource.org/licenses/AGPL-3.0
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// Class:<class_name>/Attribute:<attribute_code>+
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserLocal
//
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:UserLocal' => 'iTop-Benutzer',
'Class:UserLocal+' => 'Benutzer von iTop authentifiziert',
'Class:UserLocal/Attribute:password' => 'Passwort',
'Class:UserLocal/Attribute:password+' => 'Benutzerpasswort',
));
?>
?>

View File

@@ -0,0 +1,51 @@
<?php
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Localized data
*
* @copyright Copyright (C) 2010-2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// Class:<class_name>/Attribute:<attribute_code>+
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserLocal
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserLocal' => 'Usuario de iTop',
'Class:UserLocal+' => 'Usuario Autenticado vía iTop',
'Class:UserLocal/Attribute:password' => 'Contrase&ntilde;a',
'Class:UserLocal/Attribute:password+' => 'Contrase&ntilde;a',
));
?>

View File

@@ -1,51 +1,32 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Localized data
*
* @author Hirofumi Kosaka <kosaka@rworks.jp>
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// Class:<class_name>/Attribute:<attribute_code>+
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserLocal
//
Dict::Add('JA JP', 'Japanese', '日本語', array(
'Class:UserLocal' => 'iTopユーザー', // 'iTop user',
'Class:UserLocal+' => 'iTopローカル認証ユーザー', // 'User authentified by iTop',
'Class:UserLocal/Attribute:password' => 'パスワード', // 'Password',
'Class:UserLocal/Attribute:password+' => '認証文字列', // 'user authentication string',
));
?>
<?php
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* @author Hirofumi Kosaka <kosaka@rworks.jp>
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @licence http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('JA JP', 'Japanese', '日本語', array(
'Class:UserLocal' => 'iTopユーザー',
'Class:UserLocal+' => 'iTopローカル認証ユーザー',
'Class:UserLocal/Attribute:password' => 'パスワード',
'Class:UserLocal/Attribute:password+' => '認証文字列',
));
?>

View File

@@ -74,7 +74,10 @@ class UserLocal extends UserInternal
public function CanChangePassword()
{
// For now everyone can change their password..
if (MetaModel::GetConfig()->Get('demo_mode'))
{
return false;
}
return true;
}
@@ -85,18 +88,47 @@ class UserLocal extends UserInternal
// Let's ask the password to compare the hashed values
if ($oPassword->CheckPassword($sOldPassword))
{
$this->Set('password', $sNewPassword);
$oChange = MetaModel::NewObject("CMDBChange");
$oChange->Set("date", time());
$sUserString = CMDBChange::GetCurrentUserName();
$oChange->Set("userinfo", $sUserString);
$oChange->DBInsert();
$this->DBUpdateTracked($oChange, true);
$this->SetPassword($sNewPassword);
return true;
}
return false;
}
/**
* Use with care!
*/
public function SetPassword($sNewPassword)
{
$this->Set('password', $sNewPassword);
$oChange = MetaModel::NewObject("CMDBChange");
$oChange->Set("date", time());
$sUserString = CMDBChange::GetCurrentUserName();
$oChange->Set("userinfo", $sUserString);
$oChange->DBInsert();
$this->DBUpdateTracked($oChange, true);
}
/**
* Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
* for the given attribute in the current state of the object
* @param $sAttCode string $sAttCode The code of the attribute
* @param $aReasons array To store the reasons why the attribute is read-only (info about the synchro replicas)
* @param $sTargetState string The target state in which to evalutate the flags, if empty the current state will be used
* @return integer Flags: the binary combination of the flags applicable to this attribute
*/
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
$iFlags = parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
if (MetaModel::GetConfig()->Get('demo_mode'))
{
if (strpos('contactid,login,language,password,profile_list,allowed_org_list', $sAttCode) !== false)
{
// contactid and allowed_org_list are disabled to make sure the portal remains accessible
$aReasons[] = 'Sorry, this attribute is read-only in the demonstration mode!';
$iFlags |= OPT_ATT_READONLY;
}
}
return $iFlags;
}
}
?>

View File

@@ -17,32 +17,16 @@
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Localized data
*
* @author Vladimir Shilov <shilow@ukr.net>
* @author Vladimir Shilov <shilow@ukr.net>
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @licence http://opensource.org/licenses/AGPL-3.0
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// Class:<class_name>/Attribute:<attribute_code>+
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserLocal
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:UserLocal' => 'Пользователь iTop',
'Class:UserLocal+' => 'Пользователь аутентифицированный iTop',
'Class:UserLocal/Attribute:password' => 'Пароль',
'Class:UserLocal/Attribute:password+' => 'строка аутентификации пользователя',
));
?>
?>

View File

@@ -350,7 +350,8 @@ EOF
$oPage->add('</span>');
$oPage->add('<div style="clear:both"></div>');
$sMaxUpload = $this->GetMaxUpload();
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file" onChange="ajaxFileUpload();"><span style="display:none;" id="attachment_loading">&nbsp;<img src="../images/indicator.gif"></span> '.$sMaxUpload);
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file"><span style="display:none;" id="attachment_loading">&nbsp;<img src="../images/indicator.gif"></span> '.$sMaxUpload);
$oPage->add_ready_script('$("#file").off("change.itop-attachments").on("change.itop-attachments", function() {ajaxFileUpload();});'); // Workaround for a Chrome 36 bug causing multiple (12!) times the same upload. See http://www.redmine.org/issues/17151
$oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>');
$oPage->p('<input type="hidden" id="attachment_plugin" name="attachment_plugin"/>');
$oPage->add('</fieldset>');

View File

@@ -352,7 +352,8 @@ EOF
$oPage->add('</span>');
$oPage->add('<div style="clear:both"></div>');
$sMaxUpload = $this->GetMaxUpload();
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file" onChange="ajaxFileUpload();"><span style="display:none;" id="attachment_loading">&nbsp;<img src="../images/indicator.gif"></span> '.$sMaxUpload);
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file"><span style="display:none;" id="attachment_loading">&nbsp;<img src="../images/indicator.gif"></span> '.$sMaxUpload);
$oPage->add_ready_script('$("#file").off("change.itop-attachments").on("change.itop-attachments", function() {ajaxFileUpload();});'); // Workaround for a Chrome 36 bug causing multiple (12!) times the same upload. See http://www.redmine.org/issues/17151
$oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>');
$oPage->p('<input type="hidden" id="attachment_plugin" name="attachment_plugin"/>');
$oPage->add('</fieldset>');

View File

@@ -1,5 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<constants>
<constant id="PORTAL_TYPE_TO_CLASS" xsi:type="string" _delta="redefine"><![CDATA[{"service_request":"UserRequest","incident":"Incident"}]]></constant>
<constant id="PORTAL_INCIDENT_PUBLIC_LOG" xsi:type="string" _delta="define"><![CDATA[public_log]]></constant>
<constant id="PORTAL_INCIDENT_USER_COMMENT" xsi:type="string" _delta="define"><![CDATA[user_comment]]></constant>
<constant id="PORTAL_INCIDENT_FORM_ATTRIBUTES" xsi:type="string" _delta="define"><![CDATA[title,description,impact,urgency]]></constant>
<constant id="PORTAL_INCIDENT_TYPE" xsi:type="string" _delta="define"><![CDATA[]]></constant>
<constant id="PORTAL_INCIDENT_LIST_ZLIST" xsi:type="string" _delta="define"><![CDATA[finalclass,title,start_date,status,servicesubcategory_id,priority,caller_id]]></constant>
<constant id="PORTAL_INCIDENT_CLOSED_ZLIST" xsi:type="string" _delta="define"><![CDATA[title,start_date,close_date,servicesubcategory_id]]></constant>
<constant id="PORTAL_INCIDENT_DETAILS_ZLIST" xsi:type="string" _delta="define"><![CDATA[{"col:left":["ref","caller_id","servicesubcategory_id","title","description","solution"],"col:right":["status","priority","start_date","resolution_date","last_update","agent_id"]}]]></constant>
<constant id="PORTAL_INCIDENT_DISPLAY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT Incident WHERE org_id = :contact->org_id AND caller_id = :contact->id]]></constant>
<constant id="PORTAL_INCIDENT_DISPLAY_POWERUSER_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT Incident WHERE org_id = :contact->org_id]]></constant>
</constants>
<classes>
<class id="Incident" _delta="define">
<parent>Ticket</parent>
@@ -227,7 +239,7 @@
</field>
<field id="sla_tto_passed" xsi:type="AttributeSubItem">
<target_attcode>tto</target_attcode>
<item_code>100_passed</item_code>
<item_code>100_triggered</item_code>
</field>
<field id="sla_tto_over" xsi:type="AttributeSubItem">
<target_attcode>tto</target_attcode>
@@ -239,7 +251,7 @@
</field>
<field id="sla_ttr_passed" xsi:type="AttributeSubItem">
<target_attcode>ttr</target_attcode>
<item_code>100_passed</item_code>
<item_code>100_triggered</item_code>
</field>
<field id="sla_ttr_over" xsi:type="AttributeSubItem">
<target_attcode>ttr</target_attcode>

View File

@@ -1,15 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<constants>
<constant id="PORTAL_POWER_USER_PROFILE" xsi:type="string" _delta="define"><![CDATA[Portal power user]]></constant>
<constant id="PORTAL_SERVICECATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :org_id AND s.status != 'obsolete']]></constant>
<constant id="PORTAL_SERVICE_SUBCATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT ServiceSubcategory WHERE service_id = :svc_id AND ServiceSubcategory.status != "obsolete"]]></constant>
<constant id="PORTAL_VALIDATE_SERVICECATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :org_id AND s.id = :id AND s.status != 'obsolete']]></constant>
<constant id="PORTAL_VALIDATE_SERVICESUBCATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT ServiceSubcategory AS Sub JOIN Service AS Svc ON Sub.service_id = Svc.id WHERE Sub.id=:id AND Sub.status != 'obsolete']]></constant>
<constant id="PORTAL_ALL_PARAMS" xsi:type="string" _delta="define"><![CDATA[from_service_id,org_id,caller_id,service_id,servicesubcategory_id,title,description,impact,emergency,moreinfo,caller_id,start_date,end_date,duration,impact_duration]]></constant>
<constant id="PORTAL_SET_TYPE_FROM" xsi:type="string" _delta="define"><![CDATA[request_type]]></constant>
<constant id="PORTAL_TYPE_TO_CLASS" xsi:type="string" _delta="define"><![CDATA[{"service_request":"UserRequest","incident":"Incident"}]]></constant>
<constant id="PORTAL_TICKETS_SEARCH_CRITERIA" xsi:type="string" _delta="define"><![CDATA[ref,start_date,close_date,service_id,caller_id]]></constant>
<constant id="PORTAL_TYPE_TO_CLASS" xsi:type="string" _delta="redefine"><![CDATA[{"service_request":"UserRequest","incident":"Incident"}]]></constant>
<constant id="PORTAL_USERREQUEST_PUBLIC_LOG" xsi:type="string" _delta="define"><![CDATA[public_log]]></constant>
<constant id="PORTAL_USERREQUEST_USER_COMMENT" xsi:type="string" _delta="define"><![CDATA[user_comment]]></constant>
<constant id="PORTAL_USERREQUEST_FORM_ATTRIBUTES" xsi:type="string" _delta="define"><![CDATA[title,description,impact,urgency]]></constant>
@@ -17,15 +9,8 @@
<constant id="PORTAL_USERREQUEST_LIST_ZLIST" xsi:type="string" _delta="define"><![CDATA[finalclass,title,start_date,status,servicesubcategory_id,priority,caller_id]]></constant>
<constant id="PORTAL_USERREQUEST_CLOSED_ZLIST" xsi:type="string" _delta="define"><![CDATA[title,start_date,close_date,servicesubcategory_id]]></constant>
<constant id="PORTAL_USERREQUEST_DETAILS_ZLIST" xsi:type="string" _delta="define"><![CDATA[{"col:left":["ref","caller_id","servicesubcategory_id","title","description","solution"],"col:right":["status","priority","start_date","resolution_date","last_update","agent_id"]}]]></constant>
<constant id="PORTAL_INCIDENT_PUBLIC_LOG" xsi:type="string" _delta="define"><![CDATA[public_log]]></constant>
<constant id="PORTAL_INCIDENT_USER_COMMENT" xsi:type="string" _delta="define"><![CDATA[user_comment]]></constant>
<constant id="PORTAL_INCIDENT_FORM_ATTRIBUTES" xsi:type="string" _delta="define"><![CDATA[title,description,impact,urgency]]></constant>
<constant id="PORTAL_INCIDENT_TYPE" xsi:type="string" _delta="define"><![CDATA[]]></constant>
<constant id="PORTAL_INCIDENT_LIST_ZLIST" xsi:type="string" _delta="define"><![CDATA[finalclass,title,start_date,status,servicesubcategory_id,priority,caller_id]]></constant>
<constant id="PORTAL_INCIDENT_CLOSED_ZLIST" xsi:type="string" _delta="define"><![CDATA[title,start_date,close_date,servicesubcategory_id]]></constant>
<constant id="PORTAL_INCIDENT_DETAILS_ZLIST" xsi:type="string" _delta="define"><![CDATA[{"col:left":["ref","caller_id","servicesubcategory_id","title","description","solution"],"col:right":["status","priority","start_date","resolution_date","last_update","agent_id"]}]]></constant>
<constant id="PORTAL_TICKETS_SEARCH_FILTER_service_id" xsi:type="string" _delta="define"><![CDATA[SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :org_id AND s.status != 'obsolete']]></constant>
<constant id="PORTAL_TICKETS_SEARCH_FILTER_caller_id" xsi:type="string" _delta="define"><![CDATA[SELECT Person WHERE org_id = :org_id]]></constant>
<constant id="PORTAL_USERREQUEST_DISPLAY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT UserRequest WHERE org_id = :contact->org_id AND caller_id = :contact->id]]></constant>
<constant id="PORTAL_USERREQUEST_DISPLAY_POWERUSER_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT UserRequest WHERE org_id = :contact->org_id]]></constant>
</constants>
<classes>
<class id="UserRequest" _delta="define">
@@ -281,7 +266,7 @@
</field>
<field id="sla_tto_passed" xsi:type="AttributeSubItem">
<target_attcode>tto</target_attcode>
<item_code>100_passed</item_code>
<item_code>100_triggered</item_code>
</field>
<field id="sla_tto_over" xsi:type="AttributeSubItem">
<target_attcode>tto</target_attcode>
@@ -293,7 +278,7 @@
</field>
<field id="sla_ttr_passed" xsi:type="AttributeSubItem">
<target_attcode>ttr</target_attcode>
<item_code>100_passed</item_code>
<item_code>100_triggered</item_code>
</field>
<field id="sla_ttr_over" xsi:type="AttributeSubItem">
<target_attcode>ttr</target_attcode>

View File

@@ -1,24 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<constants>
<constant id="PORTAL_POWER_USER_PROFILE" xsi:type="string" _delta="define"><![CDATA[Portal power user]]></constant>
<constant id="PORTAL_SERVICECATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :org_id AND s.status != 'obsolete']]></constant>
<constant id="PORTAL_SERVICE_SUBCATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT ServiceSubcategory WHERE service_id = :svc_id AND ServiceSubcategory.status != 'obsolete']]></constant>
<constant id="PORTAL_VALIDATE_SERVICECATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :org_id AND s.id = :id AND s.status != 'obsolete']]></constant>
<constant id="PORTAL_VALIDATE_SERVICESUBCATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT ServiceSubcategory AS Sub JOIN Service AS Svc ON Sub.service_id = Svc.id WHERE Sub.id=:id AND Sub.status != 'obsolete']]></constant>
<constant id="PORTAL_ALL_PARAMS" xsi:type="string" _delta="define"><![CDATA[from_service_id,org_id,caller_id,service_id,servicesubcategory_id,title,description,impact,emergency,moreinfo,caller_id,start_date,end_date,duration,impact_duration]]></constant>
<constant id="PORTAL_SET_TYPE_FROM" xsi:type="string" _delta="define"><![CDATA[request_type]]></constant>
<constant id="PORTAL_TYPE_TO_CLASS" xsi:type="string" _delta="define"><![CDATA[]]></constant>
<constant id="PORTAL_USERREQUEST_PUBLIC_LOG" xsi:type="string" _delta="define"><![CDATA[public_log]]></constant>
<constant id="PORTAL_USERREQUEST_USER_COMMENT" xsi:type="string" _delta="define"><![CDATA[user_comment]]></constant>
<constant id="PORTAL_USERREQUEST_FORM_ATTRIBUTES" xsi:type="string" _delta="define"><![CDATA[title,description,impact,urgency]]></constant>
<constant id="PORTAL_USERREQUEST_TYPE" xsi:type="string" _delta="define"><![CDATA[request_type]]></constant>
<constant id="PORTAL_USERREQUEST_LIST_ZLIST" xsi:type="string" _delta="define"><![CDATA[finalclass,title,start_date,status,servicesubcategory_id,priority,caller_id]]></constant>
<constant id="PORTAL_TICKETS_SEARCH_CRITERIA" xsi:type="string" _delta="define"><![CDATA[ref,start_date,close_date,service_id,caller_id]]></constant>
<constant id="PORTAL_USERREQUEST_CLOSED_ZLIST" xsi:type="string" _delta="define"><![CDATA[title,start_date,close_date,servicesubcategory_id]]></constant>
<constant id="PORTAL_USERREQUEST_DETAILS_ZLIST" xsi:type="string" _delta="define"><![CDATA[{"col:left":["ref","caller_id","servicesubcategory_id","title","description","solution"],"col:right":["status","priority","start_date","resolution_date","last_update","agent_id"]}]]></constant>
<constant id="PORTAL_TICKETS_SEARCH_FILTER_service_id" xsi:type="string" _delta="define"><![CDATA[SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :org_id AND s.status != 'obsolete']]></constant>
<constant id="PORTAL_TICKETS_SEARCH_FILTER_caller_id" xsi:type="string" _delta="define"><![CDATA[SELECT Person WHERE org_id = :org_id]]></constant>
<constant id="PORTAL_USERREQUEST_DISPLAY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT UserRequest WHERE org_id = :contact->org_id AND caller_id = :contact->id]]></constant>
<constant id="PORTAL_USERREQUEST_DISPLAY_POWERUSER_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT UserRequest WHERE org_id = :contact->org_id]]></constant>
</constants>
<classes>
<class id="UserRequest" _delta="define">
@@ -280,7 +271,7 @@
</field>
<field id="sla_tto_passed" xsi:type="AttributeSubItem">
<target_attcode>tto</target_attcode>
<item_code>100_passed</item_code>
<item_code>100_triggered</item_code>
</field>
<field id="sla_tto_over" xsi:type="AttributeSubItem">
<target_attcode>tto</target_attcode>
@@ -292,7 +283,7 @@
</field>
<field id="sla_ttr_passed" xsi:type="AttributeSubItem">
<target_attcode>ttr</target_attcode>
<item_code>100_passed</item_code>
<item_code>100_triggered</item_code>
</field>
<field id="sla_ttr_over" xsi:type="AttributeSubItem">
<target_attcode>ttr</target_attcode>

View File

@@ -253,7 +253,6 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Portal:ShowServices' => 'Service-Katalog',
'Portal:SelectRequestType' => 'Wählen Sie den Typ der Anfrage',
'Portal:SelectServiceElementFrom_Service' => 'Wählen Sie ein Service-Element für %1$s',
'Portal:SelectRequestTemplate' => 'Wählen Sie eine Template für %1$s',
'Portal:ListServices' => 'Liste der Services',
'Portal:TitleDetailsFor_Service' => 'Details für Service',
'Portal:Button:CreateRequestFromService' => 'EIne Benutzeranfrage betreffend dieses Diensts erzeugen',

View File

@@ -275,7 +275,6 @@ Dict::Add('EN US', 'English', 'English', array(
'Portal:ShowServices' => 'Service catalogue',
'Portal:SelectRequestType' => 'Select a type of request',
'Portal:SelectServiceElementFrom_Service' => 'Select a service element for %1$s',
'Portal:SelectRequestTemplate' => 'Select a template for %1$s',
'Portal:ListServices' => 'List of services',
'Portal:TitleDetailsFor_Service' => 'Details for Service',
'Portal:Button:CreateRequestFromService' => 'Create a Request for this service',

View File

@@ -279,7 +279,6 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Portal:ShowServices' => 'Catálogo de Servicios',
'Portal:SelectRequestType' => 'Seleccione un Tipo de Solicitud',
'Portal:SelectServiceElementFrom_Service' => 'Seleccione un Elemento de Servicio para %1$s',
'Portal:SelectRequestTemplate' => 'Seleccione una Plantilla para %1$s',
'Portal:ListServices' => 'Lista de Servicios',
'Portal:TitleDetailsFor_Service' => 'Detalles para el Servicio',
'Portal:Button:CreateRequestFromService' => 'Crear una Solicitud para este Servicio',

View File

@@ -264,7 +264,6 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Portal:ShowServices' => 'Catalogue de service',
'Portal:SelectRequestType' => 'Sélectionnez un type de requête',
'Portal:SelectServiceElementFrom_Service' => 'Sélectionnez un élément de service pour %1$s',
'Portal:SelectRequestTemplate' => 'Sélectionnez un modèle de requête pour %1$s',
'Portal:ListServices' => 'Liste des services',
'Portal:TitleDetailsFor_Service' => 'Détail d\'un service',
'Portal:Button:CreateRequestFromService' => 'Créer une requête pour ce service',

View File

@@ -250,7 +250,6 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'Portal:ShowServices' => 'サービスカタログ',
'Portal:SelectRequestType' => '要求のタイプを選択',
'Portal:SelectServiceElementFrom_Service' => '%1$s のサービス要素を選択',
'Portal:SelectRequestTemplate' => 'Select a template for %1$s のテンプレートを選択',
'Portal:ListServices' => 'サービスのリスト',
'Portal:TitleDetailsFor_Service' => 'サービスの詳細',
'Portal:Button:CreateRequestFromService' => 'このサービスへの要求を作成',

View File

@@ -2,6 +2,17 @@
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<constants>
<constant id="RESPONSE_TICKET_SLT_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT SLT AS slt JOIN lnkSLAToSLT AS l1 ON l1.slt_id=slt.id JOIN SLA AS sla ON l1.sla_id=sla.id JOIN lnkCustomerContractToService AS l2 ON l2.sla_id=sla.id JOIN CustomerContract AS sc ON l2.customercontract_id=sc.id WHERE slt.metric = :metric AND l2.service_id = :this->service_id AND sc.org_id = :this->org_id AND slt.request_type = :request_type AND slt.priority = :this->priority]]></constant>
<constant id="PORTAL_POWER_USER_PROFILE" xsi:type="string" _delta="define"><![CDATA[Portal power user]]></constant>
<constant id="PORTAL_SERVICECATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :org_id AND s.status != 'obsolete']]></constant>
<constant id="PORTAL_SERVICE_SUBCATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT ServiceSubcategory WHERE service_id = :svc_id AND ServiceSubcategory.status != 'obsolete']]></constant>
<constant id="PORTAL_VALIDATE_SERVICECATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :org_id AND s.id = :id AND s.status != 'obsolete']]></constant>
<constant id="PORTAL_VALIDATE_SERVICESUBCATEGORY_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT ServiceSubcategory AS Sub JOIN Service AS Svc ON Sub.service_id = Svc.id WHERE Sub.id=:id AND Sub.status != 'obsolete']]></constant>
<constant id="PORTAL_ALL_PARAMS" xsi:type="string" _delta="define"><![CDATA[from_service_id,org_id,caller_id,service_id,servicesubcategory_id,title,description,impact,emergency,moreinfo,caller_id,start_date,end_date,duration,impact_duration]]></constant>
<constant id="PORTAL_SET_TYPE_FROM" xsi:type="string" _delta="define"><![CDATA[request_type]]></constant>
<constant id="PORTAL_TYPE_TO_CLASS" xsi:type="string" _delta="define"><![CDATA[]]></constant>
<constant id="PORTAL_TICKETS_SEARCH_CRITERIA" xsi:type="string" _delta="define"><![CDATA[ref,start_date,close_date,service_id,caller_id]]></constant>
<constant id="PORTAL_TICKETS_SEARCH_FILTER_service_id" xsi:type="string" _delta="define"><![CDATA[SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :org_id AND s.status != 'obsolete']]></constant>
<constant id="PORTAL_TICKETS_SEARCH_FILTER_caller_id" xsi:type="string" _delta="define"><![CDATA[SELECT Person WHERE org_id = :org_id]]></constant>
</constants>
<classes>
<class id="Ticket" _delta="define">

View File

@@ -810,6 +810,7 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm
'Portal:AddAttachment' => ' Attachment hinzufügen',
'Portal:RemoveAttachment' => 'Attachment entfernen',
'Portal:Attachment_No_To_Ticket_Name' => 'Attachment #%1$d an %2$s (%3$s)',
'Portal:SelectRequestTemplate' => 'Wählen Sie eine Template für %1$s',
'Enum:Undefined' => 'Nicht definiert',
'UI:DurationForm_Days_Hours_Minutes_Seconds' => '%1$s Tage %2$s Stunden %3$s Minuten %4$s Sekunden',
'UI:ModifyAllPageTitle' => 'Alle modifizieren',

View File

@@ -996,6 +996,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'Portal:AddAttachment' => ' Add Attachment ',
'Portal:RemoveAttachment' => ' Remove Attachment ',
'Portal:Attachment_No_To_Ticket_Name' => 'Attachment #%1$d to %2$s (%3$s)',
'Portal:SelectRequestTemplate' => 'Select a template for %1$s',
'Enum:Undefined' => 'Undefined',
'UI:DurationForm_Days_Hours_Minutes_Seconds' => '%1$s Days %2$s Hours %3$s Minutes %4$s Seconds',
'UI:ModifyAllPageTitle' => 'Modify All',
@@ -1085,7 +1086,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:DashboardEdit:DashboardTitle' => 'Title',
'UI:DashboardEdit:AutoReload' => 'Automatic refresh',
'UI:DashboardEdit:AutoReloadSec' => 'Automatic refresh interval (seconds)',
'UI:DashboardEdit:AutoReloadSec+' => 'The minimum allowed is 5 seconds',
'UI:DashboardEdit:AutoReloadSec+' => 'The minimum allowed is %1$d seconds',
'UI:DashboardEdit:Layout' => 'Layout',
'UI:DashboardEdit:Properties' => 'Dashboard Properties',
@@ -1185,7 +1186,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => 'Disabled',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Custom rate',
'Class:ShortcutOQL/Attribute:auto_reload_sec' => 'Automatic refresh interval (seconds)',
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'The minimum allowed is 5 seconds',
'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => 'The minimum allowed is %1$d seconds',
'UI:FillAllMandatoryFields' => 'Please fill all mandatory fields.',

View File

@@ -995,6 +995,7 @@ Cuando se asocien con un disparador, cada acción recibe un número de "orden",
'Portal:AddAttachment' => 'Agregar Anexo',
'Portal:RemoveAttachment' => 'Borrar Anexo',
'Portal:Attachment_No_To_Ticket_Name' => 'Anexo #%1$d to %2$s (%3$s)',
'Portal:SelectRequestTemplate' => 'Seleccione una Plantilla para %1$s',
'Enum:Undefined' => 'No Definido',
'UI:DurationForm_Days_Hours_Minutes_Seconds' => '%1$s Días %2$s Hrs. %3$s Mins. %4$s Segs.',
'UI:ModifyAllPageTitle' => 'Modificar Todos',
@@ -1084,7 +1085,7 @@ Cuando se asocien con un disparador, cada acción recibe un número de "orden",
'UI:DashboardEdit:DashboardTitle' => 'Título',
'UI:DashboardEdit:AutoReload' => 'Actualización Automática',
'UI:DashboardEdit:AutoReloadSec' => 'Interválo de Actualización Automática (segundos)',
'UI:DashboardEdit:AutoReloadSec+' => 'El interválo mínimo es de 5 segundos',
'UI:DashboardEdit:AutoReloadSec+' => 'El interválo mínimo es de %1$d segundos',
'UI:DashboardEdit:Layout' => 'Distribución',
'UI:DashboardEdit:Properties' => 'Propiedades',
'UI:DashboardEdit:Dashlets' => 'Dashlets disponibles',
@@ -1183,7 +1184,7 @@ Cuando se asocien con un disparador, cada acción recibe un número de "orden",
'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => 'Deshabilitado',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Frecuencia configurable',
'Class:ShortcutOQL/Attribute:auto_reload_sec' => 'Interválo de Actualización Automática (segundos)',
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'El interválo mínimo es de 5 segundos',
'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => 'El interválo mínimo es de %1$d segundos',
'UI:FillAllMandatoryFields' => 'Por favor llenar los campos obligatorios.',
'UI:CSVImportConfirmTitle' => 'Por favor confirme la operación',
'UI:CSVImportConfirmMessage' => '¿Está seguro?',

View File

@@ -544,11 +544,11 @@ Opérateurs :<br/>
'Class:Trigger/Attribute:action_list+' => '',
'Class:Trigger/Attribute:finalclass' => 'Type',
'Class:Trigger/Attribute:finalclass+' => '',
'Class:TriggerOnObject' => 'Trigger on a class of objects',
'Class:TriggerOnObject' => 'Déclencheur sur modification de données',
'Class:TriggerOnObject+' => '',
'Class:TriggerOnObject/Attribute:target_class' => 'Classe cible',
'Class:TriggerOnObject/Attribute:target_class+' => 'label',
'Class:TriggerOnStateChange' => 'Trigger on object state change',
'Class:TriggerOnStateChange' => 'Déclencheur sur changement d\'état',
'Class:TriggerOnStateChange+' => '',
'Class:TriggerOnStateChange/Attribute:state' => 'Etat',
'Class:TriggerOnStateChange/Attribute:state+' => 'label',

View File

@@ -839,6 +839,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'Portal:AddAttachment' => ' Ajouter une pièce jointe ',
'Portal:RemoveAttachment' => ' Enlever la pièce jointe ',
'Portal:Attachment_No_To_Ticket_Name' => 'Pièce jointe #%1$d à %2$s (%3$s)',
'Portal:SelectRequestTemplate' => 'Sélectionnez un modèle de requête pour %1$s',
'Enum:Undefined' => 'Non défini',
'UI:DurationForm_Days_Hours_Minutes_Seconds' => '%1$s J %2$s H %3$s min %4$s s',
'UI:ModifyAllPageTitle' => 'Modification par lots',
@@ -927,7 +928,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:DashboardEdit:DashboardTitle' => 'Titre',
'UI:DashboardEdit:AutoReload' => 'Réactualisation automatique',
'UI:DashboardEdit:AutoReloadSec' => 'Réactualisation toutes les (secondes)',
'UI:DashboardEdit:AutoReloadSec+' => 'Le minimum permis est de 5 secondes',
'UI:DashboardEdit:AutoReloadSec+' => 'Le minimum permis est de %1$d secondes',
'UI:DashboardEdit:Layout' => 'Mise en page',
'UI:DashboardEdit:Properties' => 'Propriétés du tableau de bord',
'UI:DashboardEdit:Dashlets' => 'Indicateurs',
@@ -1026,7 +1027,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => 'Désactivée',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Personnalisée',
'Class:ShortcutOQL/Attribute:auto_reload_sec' => 'Réactualisation toutes les (secondes)',
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'Le minimum permis est de 5 secondes',
'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => 'Le minimum permis est de %1$d secondes',
'UI:FillAllMandatoryFields' => 'Veuillez remplir tous les champs obligatoires.',

View File

@@ -816,6 +816,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'Portal:AddAttachment' => ' 添付を追加 ',
'Portal:RemoveAttachment' => ' 添付を削除 ',
'Portal:Attachment_No_To_Ticket_Name' => '$2$s ($3$s)への添付 #%1$d',
'Portal:SelectRequestTemplate' => 'Select a template for %1$s のテンプレートを選択',
'Enum:Undefined' => '未定義',
'UI:DurationForm_Days_Hours_Minutes_Seconds' => '%1$s 日 %2$s 時 %3$s 分 %4$s 秒',
'UI:ModifyAllPageTitle' => '全てを修正',

View File

@@ -1084,7 +1084,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:DashboardEdit:DashboardTitle' => 'Título',
'UI:DashboardEdit:AutoReload' => 'Atualizar automaticamente',
'UI:DashboardEdit:AutoReloadSec' => 'Intervalo atualização automática (segundos)',
'UI:DashboardEdit:AutoReloadSec+' => 'O mínimo permitido é 5 segundos',
'UI:DashboardEdit:AutoReloadSec+' => 'O mínimo permitido é %1$d segundos',
'UI:DashboardEdit:Layout' => 'Layout',
'UI:DashboardEdit:Properties' => 'Propriedades',
@@ -1184,7 +1184,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => 'Desabilitado',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Avaliar',
'Class:ShortcutOQL/Attribute:auto_reload_sec' => 'Intervalo atualização automática (segundos)',
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'O mínimo permitido é 5 sgundos',
'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => 'O mínimo permitido é %1$d sgundos',
'UI:FillAllMandatoryFields' => 'Por favor, preencha todos os campos obrigatórios.',
'UI:CSVImportConfirmTitle' => 'Por favor, confirme a operação',

View File

@@ -891,11 +891,16 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'UI:DashletCreation:EditNow' => 'Edit the Dashboard~~',
'UI:DashboardEdit:Title' => 'Dashboard Editor~~',
'UI:DashboardEdit:DashboardTitle' => 'Заголовок',
'UI:DashboardEdit:Layout' => 'Слой',
'UI:DashboardEdit:Properties' => 'Dashboard Properties~~',
'UI:DashboardEdit:Dashlets' => 'Available Dashlets~~',
'UI:DashboardEdit:DashletProperties' => 'Dashlet Properties~~',
'UI:Form:Property' => 'Свойства',
'UI:DashboardEdit:AutoReload' => 'Обновлять автоматически',
'UI:DashboardEdit:AutoReloadSec' => 'Интервал обновления (секунды)',
'UI:DashboardEdit:AutoReloadSec+' => 'Минимальный интервал %1$d секунд',
'UI:DashboardEdit:Layout' => 'Макет',
'UI:DashboardEdit:Properties' => 'Свойства дашборда',
'UI:DashboardEdit:Dashlets' => 'Доступные дашлеты',
'UI:DashboardEdit:DashletProperties' => 'Свойства дашлета',
'UI:Form:Property' => 'Свойство',
'UI:Form:Value' => 'Значение',
'UI:DashletPlainText:Label' => 'Текст',
'UI:DashletPlainText:Description' => 'Plain text (no formatting)~~',
@@ -950,5 +955,56 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'DayOfWeek-Thursday' => 'Четверг',
'DayOfWeek-Friday' => 'Пятница',
'DayOfWeek-Saturday' => 'Суббота',
'Month-01' => 'Январь',
'Month-02' => 'Февраль',
'Month-03' => 'Март',
'Month-04' => 'Апрель',
'Month-05' => 'Май',
'Month-06' => 'Июнь',
'Month-07' => 'Июль',
'Month-08' => 'Август',
'Month-09' => 'Сентябрь',
'Month-10' => 'Октябрь',
'Month-11' => 'Ноябрь',
'Month-12' => 'Декабрь',
'UI:Menu:ShortcutList' => 'Добавить в избранное...',
'UI:ShortcutRenameDlg:Title' => 'Переименовать ссылку',
'UI:ShortcutListDlg:Title' => 'Добавить в избранное ссылку на список',
'UI:ShortcutDelete:Confirm' => 'Подтвердите удаление ссылки (ссылок).',
'Menu:MyShortcuts' => 'Избранное',
'Class:Shortcut' => 'Ссылка',
'Class:Shortcut+' => '',
'Class:Shortcut/Attribute:name' => 'Название',
'Class:Shortcut/Attribute:name+' => 'Label used in the menu and page title',
'Class:ShortcutOQL' => 'Search result shortcut~~',
'Class:ShortcutOQL+' => '',
'Class:ShortcutOQL/Attribute:oql' => 'Запрос',
'Class:ShortcutOQL/Attribute:oql+' => 'OQL defining the list of objects to search for',
'Class:ShortcutOQL/Attribute:auto_reload' => 'Automatic refresh',
'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => 'Disabled',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Custom rate',
'Class:ShortcutOQL/Attribute:auto_reload_sec' => 'Automatic refresh interval (seconds)',
'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => 'The minimum allowed is %1$d seconds',
'UI:FillAllMandatoryFields' => 'Пожалуйста, заполните все обязательные поля.',
'UI:CSVImportConfirmTitle' => 'Please confirm the operation',
'UI:CSVImportConfirmMessage' => 'Are you sure you want to do this?',
'UI:CSVImportError_items' => 'Errors: %1$d',
'UI:CSVImportCreated_items' => 'Created: %1$d',
'UI:CSVImportModified_items' => 'Modified: %1$d',
'UI:CSVImportUnchanged_items' => 'Unchanged: %1$d',
'UI:Button:Remove' => 'Remove',
'UI:AddAnExisting_Class' => 'Add objects of type %1$s...',
'UI:SelectionOf_Class' => 'Selection of objects of type %1$s',
'UI:AboutBox' => 'Об этом iTop...',
'UI:About:Title' => 'Об этом iTop',
'UI:About:DataModel' => 'Модель данных',
'UI:About:Support' => 'Информация для технической поддержки',
'UI:About:Licenses' => 'Лицензии',
'UI:About:Modules' => 'Установленные модули',
));
?>

View File

@@ -29,7 +29,7 @@ jQuery.extend(
var fileId = 'jUploadFile' + id;
var form = jQuery('<form action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>');
var oldElement = jQuery('#' + fileElementId);
var newElement = jQuery(oldElement).clone();
var newElement = jQuery(oldElement).clone(true);
jQuery(oldElement).attr('id', fileId);
jQuery(oldElement).before(newElement);
jQuery(oldElement).appendTo(form);
@@ -131,12 +131,12 @@ jQuery.extend(
jQuery.handleError(s, xml, null, e);
}
}, 100)
}, 100);
xml = null
xml = null;
}
}
};
// Timeout checker
if ( s.timeout > 0 )
{
@@ -197,7 +197,9 @@ jQuery.extend({
s.error( xhr, status, e );
// If we have some XML response text (e.g. from an AJAX call) then log it in the console
else if(xhr.responseText)
console.log(xhr.responseText);
{
//console.log(xhr.responseText);
}
}
});

View File

@@ -447,59 +447,70 @@ function sprintf(format, etc) {
this.construct = function(settings) {
return this.each(function() {
config = $.extend(this.config, $.tablesorterPager.defaults, settings);
return this.each(function() {
var table = this, pager = config.container;
try
{
config = $.extend(this.config, $.tablesorterPager.defaults, settings);
var table = this, pager = config.container;
this.ajax_request = null;
this.ajax_request = null;
config.selectedSize = parseInt($(".pagesize",pager).val());
setPageSize(table,config.selectedSize, false);
restoreParams(table, config);
//$(this).trigger("appendCache"); // Load the data
//console.log($.tablesorterPager);
applySelection(table);
config.selectedSize = parseInt($(".pagesize",pager).val());
$('.gotopage',pager).click(function() {
var idx = $(this).attr('page');
table.config.page = idx;
moveToPage(table);
});
$(config.cssFirst,pager).click(function() {
moveToFirstPage(table);
return false;
});
$(config.cssNext,pager).click(function() {
moveToNextPage(table);
return false;
});
$(config.cssPrev,pager).click(function() {
moveToPrevPage(table);
return false;
});
$(config.cssLast,pager).click(function() {
moveToLastPage(table);
return false;
});
$(config.cssPageSize,pager).change(function() {
setPageSize(table,parseInt($(this).val()), true);
return false;
});
$(table).find(':checkbox.checkAll').removeAttr('onclick').click(function() {
return checkAll(table, pager, this.checked);
});
setPageSize(table,config.selectedSize, false);
restoreParams(table, config);
$(table).bind('load_selection', function() {
loadSelection(table, pager);
//$(this).trigger("appendCache"); // Load the data
//console.log($.tablesorterPager);
applySelection(table);
});
$(table).bind('check_all', function() {
checkAll(table, pager, true);
});
$('.gotopage',pager).click(function() {
var idx = $(this).attr('page');
table.config.page = idx;
moveToPage(table);
});
$(config.cssFirst,pager).click(function() {
moveToFirstPage(table);
return false;
});
$(config.cssNext,pager).click(function() {
moveToNextPage(table);
return false;
});
$(config.cssPrev,pager).click(function() {
moveToPrevPage(table);
return false;
});
$(config.cssLast,pager).click(function() {
moveToLastPage(table);
return false;
});
$(config.cssPageSize,pager).change(function() {
setPageSize(table,parseInt($(this).val()), true);
return false;
});
$(table).find(':checkbox.checkAll').removeAttr('onclick').click(function() {
return checkAll(table, pager, this.checked);
});
$(table).bind('load_selection', function() {
loadSelection(table, pager);
applySelection(table);
});
$(table).bind('check_all', function() {
checkAll(table, pager, true);
});
}
catch(err)
{
if (console && console.log)
{
console.log(err);
}
}
});
};
}

View File

@@ -139,7 +139,7 @@ $(function()
this.datatable.tableHover();
this.datatable.find('.selectList'+this.id).bind('change', function() { me._updateButtons(); });
},
_updateDlgSize: function()
_updateDlgPosition: function()
{
this.oDlg.dialog('option', { position: { my: "center", at: "center", of: window }});
},
@@ -171,7 +171,7 @@ $(function()
});
me.indicator.html('');
me.oButtons['create'].removeAttr('disabled');
me._updateDlgSize();
me._updateDlgPosition();
});
},
_selectToAdd: function()
@@ -197,19 +197,23 @@ $(function()
me.oDlg.find('form').removeAttr('onsubmit').bind('submit', function() { me._onSearchToAdd(); return false; } );
me.oDlg.find('button.cancel').unbind('click').click( function() { me.oDlg.dialog('close'); } );
me.oDlg.find('button.ok').unbind('click').click( function() { me._onDoAdd(); } );
$('#SearchFormToAdd_'+me.id).resize(function() { me._onSearchDlgUpdateSize(); });
me.oDlg.dialog({
title: me.options.labels['selection_title'],
modal: true,
width: 'auto',
height: 'auto',
width: $(window).width()*0.8,
height: $(window).height()*0.8,
maxHeight: $(window).height() - 50,
position: { my: "center", at: "center", of: window },
close: function() { me._onDlgClose(); }
close: function() { me._onDlgClose(); },
resizeStop: function() { me._onSearchDlgUpdateSize(); }
});
me.indicator.html('');
me.oButtons['add'].removeAttr('disabled');
me._onSearchToAdd();
me._updateDlgSize();
me._updateDlgPosition();
me._onSearchDlgUpdateSize();
});
},
_onSearchToAdd: function()
@@ -254,8 +258,8 @@ $(function()
me._onUpdateDlgButtons(c);
});
$('#SearchResultsToAdd_'+me.id).unblock();
me._onSearchDlgUpdateSize();
});
//alert("C'est parti mon kiki !");
return false; // Stay on the page, no submit
},
_getSelection: function(sName)
@@ -372,7 +376,7 @@ $(function()
me.oDlg.html(data);
me.oDlg.find('form').removeAttr('onsubmit').bind('submit', function() { me._onCreateRow(); return false; } );
me.oDlg.find('button.cancel').unbind('click').click( function() { me.oDlg.dialog('close'); } );
me._updateDlgSize();
me._updateDlgPosition();
});
},
_onCreateRow: function()
@@ -425,6 +429,13 @@ $(function()
this.oDlg.remove();
this.oDlg = null;
},
_onSearchDlgUpdateSize: function()
{
var searchHeight = $('#SearchFormToAdd_'+this.id).outerHeight();
var dlgHeight = this.oDlg.height();
$('.wizContainer', this.oDlg).height(dlgHeight - 20);
$('#SearchResultsToAdd_'+this.id).height(dlgHeight - 50 - searchHeight);
},
_deleteRow: function(oCheckbox)
{
var iObjKey = parseInt(oCheckbox.val(), 10); // Number in base 10

View File

@@ -242,7 +242,7 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
if (data != '')
{
$('#'+me.id+'_empty_row').hide();
$('#linkedset_'+me.id+' .listResults tbody').append(data);
$('#linkedset_'+me.id+' .listResults tbody').prepend(data);
$('#linkedset_'+me.id+' .listResults').trigger('update');
$('#linkedset_'+me.id+' .listResults').tableHover();
$('#linkedset_'+me.id+' .listResults').trigger('update').trigger("applyWidgets"); // table is already sortable, just refresh it

View File

@@ -272,6 +272,77 @@ function ValidateWithPattern(sFieldId, bMandatory, sPattern, sFormId, aForbidden
}
}
function ValidateInteger(sFieldId, bMandatory, sFormId, iMin, iMax, sExplainFormat)
{
var currentVal = $('#'+sFieldId).val();
var bValid = true;
var sMessage = null;
if (bMandatory && (currentVal == ''))
{
bValid = false;
}
re = new RegExp('^$|^-?[0-9]+$');
bValid = re.test(currentVal);
if (bValid && (currentVal != ''))
{
// It is a valid number, let's check the boundaries
var iValue = parseInt(currentVal, 10);
if ((iMin != null) && (iValue < iMin))
{
bValid = false;
}
if ((iMax != null) && (iValue > iMax))
{
bValid = false;
}
if (!bValid && (sExplainFormat != undefined))
{
sMessage = sExplainFormat;
}
}
if (oFormValidation[sFormId] == undefined) oFormValidation[sFormId] = [];
if (!bValid)
{
$('#v_'+sFieldId).addClass('ui-state-error');
iFieldIdPos = jQuery.inArray(sFieldId, oFormValidation[sFormId]);
if (iFieldIdPos == -1)
{
oFormValidation[sFormId].push(sFieldId);
}
if (sMessage)
{
$('#'+sFieldId).attr('title', sMessage).tooltip();
if ($('#'+sFieldId).is(":focus"))
{
$('#'+sFieldId).tooltip('open');
}
}
}
else
{
$('#v_'+sFieldId).removeClass('ui-state-error');
if ($('#'+sFieldId).data('uiTooltip'))
{
$('#'+sFieldId).tooltip('close');
}
$('#'+sFieldId).removeAttr('title');
// Remove the element from the array
iFieldIdPos = jQuery.inArray(sFieldId, oFormValidation[sFormId]);
if (iFieldIdPos > -1)
{
oFormValidation[sFormId].splice(iFieldIdPos, 1);
}
}
}
function ValidateForm(sFormId, bValidateAll)
{
oFormValidation[sFormId] = [];

View File

@@ -398,6 +398,7 @@ try
case 'form_for_modify_all': // Form to modify multiple objects (bulk modify)
case 'bulk_stimulus': // For to apply a stimulus to multiple objects
case 'stimulus': // Form displayed when applying a stimulus (state change)
case 'apply_stimulus': // Form displayed when applying a stimulus (state change)
$oP->add_linked_script("../js/json.js");
$oP->add_linked_script("../js/forms-json-utils.js");
$oP->add_linked_script("../js/wizardhelper.js");
@@ -1381,132 +1382,7 @@ EOF
$oObj = MetaModel::GetObject($sClass, $id, false);
if ($oObj != null)
{
$aTransitions = $oObj->EnumTransitions();
$aStimuli = MetaModel::EnumStimuli($sClass);
if (!isset($aTransitions[$sStimulus]))
{
// Invalid stimulus
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel()));
}
$sActionLabel = $aStimuli[$sStimulus]->GetLabel();
$sActionDetails = $aStimuli[$sStimulus]->GetDescription();
$aTransition = $aTransitions[$sStimulus];
$sTargetState = $aTransition['target_state'];
$aTargetStates = MetaModel::EnumStates($sClass);
$oP->add("<div class=\"page_header\">\n");
$oP->add("<h1>$sActionLabel - <span class=\"hilite\">{$oObj->GetName()}</span></h1>\n");
$oP->set_title($sActionLabel);
$oP->add("</div>\n");
$aTargetState = $aTargetStates[$sTargetState];
$aExpectedAttributes = $aTargetState['attribute_list'];
$oP->add("<h1>$sActionDetails</h1>\n");
$sButtonsPosition = MetaModel::GetConfig()->Get('buttons_position');
if ($sButtonsPosition == 'bottom')
{
// bottom: Displays the ticket details BEFORE the actions
$oP->add('<div class="ui-widget-content">');
$oObj->DisplayBareProperties($oP);
$oP->add('</div>');
}
$oP->add("<div class=\"wizContainer\">\n");
$oP->add("<form id=\"apply_stimulus\" method=\"post\" onSubmit=\"return OnSubmit('apply_stimulus');\">\n");
$aDetails = array();
$iFieldIndex = 0;
$aFieldsMap = array();
$aDetailsList = $oObj->FlattenZList(MetaModel::GetZListItems($sClass, 'details'));
// Order the fields based on their dependencies, set the fields for which there is only one possible value
// and perform this in the order of dependencies to avoid dead-ends
$aDeps = array();
foreach($aDetailsList as $sAttCode)
{
$aDeps[$sAttCode] = MetaModel::GetPrequisiteAttributes($sClass, $sAttCode);
}
$aList = $oObj->OrderDependentFields($aDeps);
foreach($aList as $sAttCode)
{
// Consider only the "expected" fields for the target state
if (array_key_exists($sAttCode, $aExpectedAttributes))
{
$iExpectCode = $aExpectedAttributes[$sAttCode];
// Prompt for an attribute if
// - the attribute must be changed or must be displayed to the user for confirmation
// - or the field is mandatory and currently empty
if ( ($iExpectCode & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) ||
(($iExpectCode & OPT_ATT_MANDATORY) && ($oObj->Get($sAttCode) == '')) )
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$aArgs = array('this' => $oObj);
// If the field is mandatory, set it to the only possible value
if ((!$oAttDef->IsNullAllowed()) || ($iExpectCode & OPT_ATT_MANDATORY))
{
if ($oAttDef->IsExternalKey())
{
$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs);
if ($oAllowedValues->Count() == 1)
{
$oRemoteObj = $oAllowedValues->Fetch();
$oObj->Set($sAttCode, $oRemoteObj->GetKey());
}
}
else
{
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs);
if (count($aAllowedValues) == 1)
{
$aValues = array_keys($aAllowedValues);
$oObj->Set($sAttCode, $aValues[0]);
}
}
}
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $oObj->Get($sAttCode), $oObj->GetEditValue($sAttCode), 'att_'.$iFieldIndex, '', $iExpectCode, $aArgs);
$aDetails[] = array('label' => '<span>'.$oAttDef->GetLabel().'</span>', 'value' => "<span id=\"field_att_$iFieldIndex\">$sHTMLValue</span>");
$aFieldsMap[$sAttCode] = 'att_'.$iFieldIndex;
$iFieldIndex++;
}
}
}
$oP->add('<table><tr><td>');
$oP->details($aDetails);
$oP->add('</td></tr></table>');
$oP->add("<input type=\"hidden\" name=\"id\" value=\"$id\" id=\"id\">\n");
$aFieldsMap['id'] = 'id';
$oP->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">\n");
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"apply_stimulus\">\n");
$oP->add("<input type=\"hidden\" name=\"stimulus\" value=\"$sStimulus\">\n");
$oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">\n");
$oP->add($oAppContext->GetForForm());
$oP->add("<button type=\"button\" class=\"action\" onClick=\"BackToDetails('$sClass', $id)\"><span>".Dict::S('UI:Button:Cancel')."</span></button>&nbsp;&nbsp;&nbsp;&nbsp;\n");
$oP->add("<button type=\"submit\" class=\"action\"><span>$sActionLabel</span></button>\n");
$oP->add("</form>\n");
$oP->add("</div>\n");
if ($sButtonsPosition != 'top')
{
// bottom or both: Displays the ticket details AFTER the actions
$oP->add('<div class="ui-widget-content">');
$oObj->DisplayBareProperties($oP);
$oP->add('</div>');
}
$iFieldsCount = count($aFieldsMap);
$sJsonFieldsMap = json_encode($aFieldsMap);
$oP->add_script(
<<<EOF
// Initializes the object once at the beginning of the page...
var oWizardHelper = new WizardHelper('$sClass', '', '$sTargetState');
oWizardHelper.SetFieldsMap($sJsonFieldsMap);
oWizardHelper.SetFieldsCount($iFieldsCount);
EOF
);
$oP->add_ready_script(
<<<EOF
// Starts the validation when the page is ready
CheckFields('apply_stimulus', false);
EOF
);
$oObj->DisplayStimulusForm($oP, $sStimulus);
}
else
{
@@ -1533,6 +1409,7 @@ EOF
$aStimuli = MetaModel::EnumStimuli($sClass);
$sMessage = '';
$sSeverity = 'ok';
$bDisplayDetails = true;
if (!isset($aTransitions[$sStimulus]))
{
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel()));
@@ -1568,20 +1445,52 @@ EOF
}
}
$oObj->UpdateObjectFromPostedForm('', array_keys($aExpectedAttributes), $sTargetState);
$oObj->UpdateObjectFromPostedForm('', array_keys($aExpectedAttributes), $sTargetState);
if (count($aErrors) == 0)
{
if ($oObj->ApplyStimulus($sStimulus))
$sIssues = '';
$bApplyStimulus = true;
list($bRes, $aIssues) = $oObj->CheckToWrite(); // Check before trying to write the object
if ($bRes)
{
$oObj->DBUpdate();
$sMessage = Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
$sSeverity = 'ok';
try
{
$bApplyStimulus = $oObj->ApplyStimulus($sStimulus); // will write the object in the DB
}
catch(CoreException $e)
{
// Rollback to the previous state... by reloading the object from the database and applying the modifications again
$oObj = MetaModel::GetObject(get_class($oObj), $oObj->GetKey());
$oObj->UpdateObjectFromPostedForm('', array_keys($aExpectedAttributes), $sTargetState);
$aData = $e->getContextData();
$sIssues = (array_key_exists('issues', $aData)) ? $aData['issues'] : 'Unknown error...';
}
}
else
{
$sIssues = implode(' ', $aIssues);
}
if (!$bApplyStimulus)
{
$sMessage = Dict::S('UI:FailedToApplyStimuli');
$sSeverity = 'error';
$sSeverity = 'error';
}
else if ($sIssues != '')
{
$bDisplayDetails = false;
// Found issues, explain and give the user a second chance
//
$oObj->DisplayStimulusForm($oP, $sStimulus);
$sIssueDesc = Dict::Format('UI:ObjectCouldNotBeWritten',$sIssues);
$oP->add_ready_script("alert('".addslashes($sIssueDesc)."');");
}
else
{
$sMessage = Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
$sSeverity = 'ok';
utils::RemoveTransaction($sTransactionId);
}
}
else
@@ -1590,7 +1499,10 @@ EOF
$sSeverity = 'error';
}
}
ReloadAndDisplay($oP, $oObj, 'apply_stimulus', $sMessage, $sSeverity);
if ($bDisplayDetails)
{
ReloadAndDisplay($oP, $oObj, 'apply_stimulus', $sMessage, $sSeverity);
}
}
else
{

View File

@@ -57,6 +57,7 @@ try
case 'pagination':
$oPage->SetContentType('text/html');
$extraParams = utils::ReadParam('extra_param', '', false, 'raw_data');
$aExtraParams = array();
if (is_array($extraParams))
{
$aExtraParams = $extraParams;
@@ -64,10 +65,13 @@ try
else
{
$sExtraParams = stripslashes($extraParams);
$aExtraParams = array();
if (!empty($sExtraParams))
{
$aExtraParams = json_decode(str_replace("'", '"', $sExtraParams), true /* associative array */);
$val = json_decode(str_replace("'", '"', $sExtraParams), true /* associative array */);
if ($val !== null)
{
$aExtraParams = $val;
}
}
}
if ($sEncoding == 'oql')
@@ -766,9 +770,15 @@ try
case 'download_document':
$id = utils::ReadParam('id', '');
$sField = utils::ReadParam('field', '');
$iCacheSec = (int) utils::ReadParam('cache', 0);
if (!empty($sClass) && !empty($id) && !empty($sField))
{
DownloadDocument($oPage, $sClass, $id, $sField, 'attachment');
if ($iCacheSec > 0)
{
$oPage->add_header("Expires: "); // Reset the value set in ajax_page
$oPage->add_header("Cache-Control: no-transform,public,max-age=$iCacheSec,s-maxage=$iCacheSec");
}
}
break;
@@ -816,6 +826,7 @@ try
$oMenu = ApplicationMenu::GetMenuNode($idx);
$oDashboard = $oMenu->GetDashboard();
$oDashboard->Render($oPage, false, $aExtraParams);
$oPage->add_ready_script("$('.dashboard_contents table.listResults').tableHover(); $('.dashboard_contents table.listResults').tablesorter( { widgets: ['myZebra', 'truncatedList']} );");
break;
case 'dashboard_editor':
@@ -1001,7 +1012,7 @@ EOF
$iAutoReload = (int)$aValues['auto_reload_sec'];
if (($aValues['auto_reload']) && ($iAutoReload > 0))
{
$oShortcut->Set("auto_reload_sec", max(5, $iAutoReload));
$oShortcut->Set("auto_reload_sec", max(MetaModel::GetConfig()->Get('min_reload_interval'), $iAutoReload));
$oShortcut->Set("auto_reload", 'custom');
}
$iId = $oShortcut->DBInsertNoReload();
@@ -1042,6 +1053,7 @@ EOF
case 'shortcut_delete_go':
$oSearch = new DBObjectSearch('Shortcut');
$oSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$aShortcuts = utils::ReadMultipleSelection($oSearch);
foreach ($aShortcuts as $iShortcut)
{

View File

@@ -50,6 +50,46 @@ function GetTicketClasses()
return $aClasses;
}
/**
* Helper to protect the portal against malicious usages
* Throws an exception if the current user is not allowed to view the object details
*/
function ValidateObject($oObject)
{
if (IsPowerUser())
{
$sValidationDefine = 'PORTAL_'.strtoupper(get_class($oObject)).'_DISPLAY_POWERUSER_QUERY';
}
else
{
$sValidationDefine = 'PORTAL_'.strtoupper(get_class($oObject)).'_DISPLAY_QUERY';
}
if (defined($sValidationDefine))
{
$sValidationOql = constant($sValidationDefine);
$oSearch = DBObjectSearch::FromOQL($sValidationOql);
$oSearch->AddCondition('id', $oObject->GetKey());
if ($iUser = UserRights::GetContactId())
{
$oContact = MetaModel::GetObject('Contact', $iUser);
$aArgs = $oContact->ToArgs('contact');
}
else
{
$aArgs = array();
}
$oSet = new DBObjectSet($oSearch, array(), $aArgs);
if ($oSet->Count() == 0)
{
throw new SecurityException('You are not allowed to access the object '.get_class($oObject).'::'.$oObject->GetKey());
}
}
}
/**
* Helper to get the relevant constant
*/
@@ -481,22 +521,19 @@ function RequestCreationForm($oP, $oUserOrg, $iSvcId = null, $iSubSvcId = null,
$sValue = "<span id=\"field_{$sInputId}\">".$oRequest->GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $value, '', 'attr_'.$sAttCode, '', $iFlags, $aArgs).'</span>';
$aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sValue);
}
$aHidden = array();
if (!empty($aTemplateFields))
{
foreach ($aTemplateFields as $sAttCode => $oField)
{
if (!in_array($sAttCode, $aList))
$sValue = $oField->GetFormElement($oP, $sClass);
if ($oField->Get('input_type') == 'hidden')
{
$sValue = $oField->GetFormElement($oP, $sClass);
if ($oField->Get('input_type') == 'hidden')
{
$aHidden[] = $sValue;
}
else
{
$aDetails[] = array('label' => $oField->GetAsHTML('label'), 'value' => $sValue);
}
$aHidden[] = $sValue;
}
else
{
$aDetails[] = array('label' => $oField->GetAsHTML('label'), 'value' => $sValue);
}
}
}
@@ -536,7 +573,11 @@ EOF
}
}
}
if (isset($aParameters['template_id']) && ($aParameters['template_id'] != 0))
{
$oP->add("<input type=\"hidden\" name=\"attr_template_id\" value=\"{$aParameters['template_id']}\">");
}
$oP->add(implode("\n", $aHidden));
$oAttPlugin = new AttachmentPlugIn();
$oAttPlugin->OnDisplayRelations($oRequest, $oP, true /* edit */);
@@ -610,9 +651,11 @@ function DoCreateRequest($oP, $oUserOrg)
$sClass = ComputeClass($oServiceSubCategory->GetKey());
$oRequest = MetaModel::NewObject($sClass);
$aAttList = array_merge(explode(',', GetConstant($sClass, 'FORM_ATTRIBUTES')), array('service_id', 'servicesubcategory_id'));
$oRequest->UpdateObjectFromPostedForm('' /* form prefix */, $aAttList);
$oRequest->Set('org_id', $oUserOrg->GetKey());
$oRequest->Set('caller_id', UserRights::GetContactId());
$oRequest->UpdateObjectFromPostedForm();
if (isset($aParameters['moreinfo']))
{
// There is a template, insert it into the description
@@ -872,6 +915,8 @@ function ShowDetailsRequest(WebPage $oP, $oObj)
case 'assigned':
case 'frozen':
case 'pending':
case 'dispatched':
case 'redispatched':
$aEditAtt = array(
$sLogAttCode => '????'
);
@@ -1119,9 +1164,17 @@ function MakeStimulusForm(WebPage $oP, $oObj, $sStimulusCode, $aEditAtt)
function RunStimulusDialog(sStimulusCode, sTitle, sOkButtonLabel)
{
var sWidth = 'auto';
if (sStimulusCode == 'ev_reopen')
{
// Avoid having a dialog spanning the complete width of the window
// just because it contains a CaseLog entry
sWidth = '80%';
}
$('#'+sStimulusCode+'_dialog').dialog({
height: 'auto',
width: 'auto',
width: sWidth,
modal: true,
title: sTitle,
buttons: [
@@ -1130,8 +1183,13 @@ function RunStimulusDialog(sStimulusCode, sTitle, sOkButtonLabel)
} },
{ text: "$sCancelButtonLabel", click: function() {
$(this).dialog( "close" );
} },
],
} }
]
});
// Start the validation
CheckFields(sStimulusCode+'_form', false);
$('#'+sStimulusCode+'_form').submit( function() {
return OnSubmit(sStimulusCode+'_form');
});
}
EOF
@@ -1243,6 +1301,7 @@ try
$oP->set_title(Dict::S('Portal:TitleDetailsFor_Request'));
DisplayMainMenu($oP);
$oObj = $oP->FindObjectFromArgs(GetTicketClasses());
ValidateObject($oObj);
DisplayObject($oP, $oObj, $oUserOrg);
break;
@@ -1252,6 +1311,7 @@ try
if (!MetaModel::DBIsReadOnly())
{
$oObj = $oP->FindObjectFromArgs(GetTicketClasses());
ValidateObject($oObj);
$aAttList = array(
GetConstant(get_class($oObj), 'PUBLIC_LOG'),
'user_satisfaction',

View File

@@ -41,6 +41,8 @@ PORTAL_<TICKET-CLASS>_TYPE: optional attribute to be set with the value of "requ
PORTAL_<TICKET-CLASS>_LIST_ZLIST: list of attribute displayed in the lists (opened and resolved)
PORTAL_<TICKET-CLASS>_CLOSED_ZLIST: list of attribute displayed in the list of closed tickets
PORTAL_<TICKET-CLASS>_DETAILS_ZLIST: selection and presentation of attributes in the page that shows their details
PORTAL_<TICKET-CLASS>_DISPLAY_QUERY: selection of displayable objects (use parameters contact->attcode to check things against the user/contact)
PORTAL_<TICKET-CLASS>_DISPLAY_POWERUSER_QUERY: selection of displayable objects for power users (use parameters contact->attcode to check things against the user/contact)
How to add a type of ticket (example: Incident)

View File

@@ -617,7 +617,11 @@ EOF;
$aClassParams = array();
$aClassParams['category'] = $this->GetPropString($oProperties, 'category', '');
$aClassParams['key_type'] = "'autoincrement'";
if ((bool) $this->GetPropNumber($oProperties, 'is_link', 0))
{
$aClassParams['is_link'] = 'true';
}
if ($oNaming = $oProperties->GetOptionalElement('naming'))
{
$oNameAttributes = $oNaming->GetUniqueElement('attributes');
@@ -1462,6 +1466,8 @@ class ProfilesConfig
protected static \$aLINKTOCLASSES = $sLinkToClasses;
// Now replaced by MetaModel::GetLinkClasses (working with 1.x)
// This function could be deprecated
public static function GetLinkClasses()
{
return self::\$aLINKTOCLASSES;

View File

@@ -57,12 +57,6 @@ class ModuleInstallation extends cmdbAbstractObject
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
protected function OnInsert()
{
$this->Set('installed', time());
parent::OnInsert();
}
}
?>

View File

@@ -466,11 +466,13 @@ class RunTimeEnvironment
$aData = array(
'source_dir' => $oConfig->Get('source_dir'),
);
$iInstallationTime = time(); // Make sure that all modules record the same installation time
$oInstallRec = new ModuleInstallation();
$oInstallRec->Set('name', DATAMODEL_MODULE);
$oInstallRec->Set('version', $sDataModelVersion);
$oInstallRec->Set('comment', json_encode($aData));
$oInstallRec->Set('parent_id', 0); // root module
$oInstallRec->Set('installed', $iInstallationTime);
$iMainItopRecord = $oInstallRec->DBInsertNoReload();
// Record main installation
@@ -479,6 +481,7 @@ class RunTimeEnvironment
$oInstallRec->Set('version', ITOP_VERSION.'.'.ITOP_REVISION);
$oInstallRec->Set('comment', "Done by the setup program\nBuilt on ".ITOP_BUILD_DATE);
$oInstallRec->Set('parent_id', 0); // root module
$oInstallRec->Set('installed', $iInstallationTime);
$iMainItopRecord = $oInstallRec->DBInsertNoReload();
@@ -519,6 +522,7 @@ class RunTimeEnvironment
$oInstallRec->Set('version', $sVersion);
$oInstallRec->Set('comment', $sComment);
$oInstallRec->Set('parent_id', $iMainItopRecord);
$oInstallRec->Set('installed', $iInstallationTime);
$oInstallRec->DBInsertNoReload();
}
// Database is created, installation has been tracked into it

View File

@@ -1205,13 +1205,14 @@ EOF
protected function GetDefaults($aInfo, &$aDefaults, $aModules, $sParentId = '')
{
$iScore = 0;
$aRetScore = array();
$aScores = array();
$aOptions = isset($aInfo['options']) ? $aInfo['options'] : array();
foreach($aOptions as $index => $aChoice)
{
$sChoiceId = $sParentId.self::$SEP.$index;
$aScores[$sChoiceId] = 0;
$aScores[$sChoiceId] = array();
if (!$this->bUpgrade && isset($aChoice['default']) && $aChoice['default'])
{
$aDefaults[$sChoiceId] = $sChoiceId;
@@ -1223,34 +1224,32 @@ EOF
{
if ($aModules[$sModuleId]['version_db'] != '')
{
// A module corresponding to this choice is installed, the whole choice is selected
if (!isset($aScores[$sChoiceId])) $aScores[$sChoiceId] = 0;
$aScores[$sChoiceId]++;
// A module corresponding to this choice is installed
$aScores[$sChoiceId][$sModuleId] = true;
}
}
if ($aScores[$sChoiceId] == count($aChoice['modules']))
if (count($aScores[$sChoiceId]) == count($aChoice['modules']))
{
$iScore += 100; // Bonus for the parent when the whole choice is selected
// All the modules are installed, this choice is selected
$aDefaults[$sChoiceId] = $sChoiceId;
}
$iScore += $aScores[$sChoiceId];
$aRetScore = array_merge($aRetScore, $aScores[$sChoiceId]);
}
if (isset($aChoice['sub_options']))
{
$aScores[$sChoiceId] = $this->GetDefaults($aChoice['sub_options'], $aDefaults, $sChoiceId);
$aScores[$sChoiceId] = array_merge($aScores[$sChoiceId], $this->GetDefaults($aChoice['sub_options'], $aDefaults, $sChoiceId));
}
$index++;
}
$aAlternatives = isset($aInfo['alternatives']) ? $aInfo['alternatives'] : array();
$sChoiceName = null;
$aScores = array();
$sChoiceIdNone = null;
foreach($aAlternatives as $index => $aChoice)
{
$sChoiceId = $sParentId.self::$SEP.$index;
$aScores[$sChoiceId] = 0;
$aScores[$sChoiceId] = array();
if ($sChoiceName == null)
{
$sChoiceName = $sChoiceId;
@@ -1288,30 +1287,36 @@ EOF
if ($aModules[$sModuleId]['version_db'] != '')
{
// A module corresponding to this choice is installed, increase the score of this choice
if (!isset($aScores[$sChoiceId])) $aScores[$sChoiceId] = 0;
$aScores[$sChoiceId]++;
$iMaxScore = max($iMaxScore, $aScores[$sChoiceId]);
if (!isset($aScores[$sChoiceId])) $aScores[$sChoiceId] = array();
$aScores[$sChoiceId][$sModuleId] = true;
$iMaxScore = max($iMaxScore, count($aScores[$sChoiceId]));
}
}
if ($aScores[$sChoiceId] == count($aChoice['modules']))
{
$iScore += 100; // Bonus for the parent when a choice is complete
}
$iScore += $aScores[$sChoiceId];
//if (count($aScores[$sChoiceId]) == count($aChoice['modules']))
//{
// $iScore += 100; // Bonus for the parent when a choice is complete
//}
$aRetScore = array_merge($aRetScore, $aScores[$sChoiceId]);
}
$iMaxScore = max($iMaxScore, isset($aScores[$sChoiceId]) ? $aScores[$sChoiceId] : 0);
$iMaxScore = max($iMaxScore, isset($aScores[$sChoiceId]) ? count($aScores[$sChoiceId]) : 0);
}
}
if ($iMaxScore > 0)
{
//echo "Scores: <pre>".print_r($aScores, true)."</pre><br/>";
$aNumericScores = array();
foreach($aScores as $sChoiceId => $aModules)
{
$aNumericScores[$sChoiceId] = count($aModules);
}
// The choice with the bigger score wins !
asort($aScores, SORT_NUMERIC);
$aKeys = array_keys($aScores);
asort($aNumericScores, SORT_NUMERIC);
$aKeys = array_keys($aNumericScores);
$sBetterChoiceId = array_pop($aKeys);
$aDefaults[$sChoiceName] = $sBetterChoiceId;
}
return $iScore;
// echo "Scores: <pre>".print_r($aScores, true)."</pre><br/>";
// echo "Defaults: <pre>".print_r($aDefaults, true)."</pre><br/>";
return $aRetScore;
}
/**

View File

@@ -125,6 +125,7 @@ if (!empty($sExpression))
// Check and adjust column names
//
$aAliasToFields = array();
foreach($aFields as $index => $sField)
{
if (preg_match('/^(.*)\.(.*)$/', $sField, $aMatches))
@@ -136,13 +137,25 @@ if (!empty($sExpression))
{
$sClassAlias = $oFilter->GetClassAlias();
$sAttCode = $sField;
// Disambiguate the class alias
$aFields[$index] = $sClassAlias.'.'.$sAttCode;
}
$aAliasToFields[$sClassAlias][] = $sAttCode;
$sClass = $oFilter->GetClassName($sClassAlias);
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
{
throw new CoreException("Invalid field specification $sField: $sAttCode is not a valid attribute for $sClass");
}
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef instanceof AttributeSubItem)
{
$aAliasToFields[$sClassAlias][] = $oAttDef->GetParentAttCode();
}
else if($oAttDef instanceof AttributeFriendlyname)
{
$aAliasToFields[$sClassAlias][] = $oAttDef->GetKeyAttCode();
}
}
// Read query parameters
@@ -161,6 +174,7 @@ if (!empty($sExpression))
if ($oFilter)
{
$oSet = new CMDBObjectSet($oFilter, array(), $aArgs);
$oSet->OptimizeColumnLoad($aAliasToFields);
switch($sFormat)
{
case 'html':

View File

@@ -61,6 +61,7 @@
if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
require_once(__DIR__.'/../approot.inc.php');
require_once(APPROOT.'/application/application.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
require_once(APPROOT.'/application/ajaxwebpage.class.inc.php');
require_once(APPROOT.'/application/startup.inc.php');
@@ -95,15 +96,26 @@ try
{
utils::UseParamFile();
$sAuthUser = utils::ReadParam('auth_user', null, false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
if (!LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN_FALSE))
{
UserRights::Login($sAuthUser); // Login & set the user's language
}
else
{
throw new Exception("Invalid login '$sAuthUser'", RestResult::UNAUTHORIZED);
$sAuthUser = utils::ReadParam('auth_user', null, false, 'raw_data');
if ($sAuthUser === null)
{
throw new Exception("Missing parameter 'auth_user'", RestResult::MISSING_AUTH_USER);
}
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if ($sAuthPwd === null)
{
throw new Exception("Missing parameter 'auth_pwd'", RestResult::MISSING_AUTH_PWD);
}
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
{
UserRights::Login($sAuthUser); // Login & set the user's language
}
else
{
throw new Exception("Invalid login '$sAuthUser'", RestResult::UNAUTHORIZED);
}
}
$sVersion = utils::ReadParam('version', null, false, 'raw_data');