diff --git a/.gitignore b/.gitignore index 47a71b8c1..bc4aef17f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,11 @@ vendor/* test/vendor/* +# all conf but listing prevention +/conf/** +!/conf/.htaccess +!/conf/web.config + # all datas but listing prevention /data/** !/data/.htaccess diff --git a/.idea/inspectionProfiles/Combodo.xml b/.idea/inspectionProfiles/Combodo.xml index 763f7a53d..f28cfada7 100644 --- a/.idea/inspectionProfiles/Combodo.xml +++ b/.idea/inspectionProfiles/Combodo.xml @@ -2,6 +2,12 @@ + + @@ -167,5 +180,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.jenkins/bin/tests/phpunit.sh b/.jenkins/bin/tests/phpunit.sh index 319e977c4..17fa52827 100755 --- a/.jenkins/bin/tests/phpunit.sh +++ b/.jenkins/bin/tests/phpunit.sh @@ -6,6 +6,8 @@ cd test export DEBUG_UNIT_TEST=0 RUN_NONREG_TESTS=0 +#USAGE ${debugMode} ${runNonRegOQLTests} "${coverture}" "${testFile}" + if [ $# -ge 1 -a "x$1" == "xtrue" ] then export DEBUG_UNIT_TEST=1 @@ -13,10 +15,27 @@ else export DEBUG_UNIT_TEST=0 fi +set -x +OPTION="" +if [ $# -ge 3 -a "x$3" == "xtrue" ] +then + ##coverture + OPTION="-dxdebug.coverage_enable=1 --coverage-clover ../var/test/coverage.xml" +fi + +TESTFILE="$4" +if [ "x$TESTFILE" != "x" ] +then + # shellcheck disable=SC2001 + TESTFILE=$(echo "$TESTFILE" | sed 's|test/||1') + php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml $OPTION $TESTFILE --teamcity + exit 0 +fi + if [ $# -ge 2 -a "x$2" == "xtrue" ] then - php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --teamcity + php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml $OPTION --teamcity else #echo php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --teamcity - php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --exclude-group OQL --teamcity + php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml $OPTION --exclude-group OQL --teamcity fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c8d1a454b..646fbafdb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,7 +60,7 @@ For example, if no version is currently prepared for shipping we could have: In this example, when 2.8.0-beta is shipped that will become: - `develop`: future 2.9.0 version -- `release/2.8`: 2.8.0-beta +- `release/2.8.0`: 2.8.0-beta - `support/2.7`: 2.7.x maintenance version - `support/2.6`: 2.6.x maintenance version - `support/2.5`: 2.5.x maintenance version diff --git a/Jenkinsfile b/Jenkinsfile index 0e04b54dd..32c49f9b7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,6 +2,8 @@ pipeline { agent any parameters { booleanParam(name: 'debugMode', defaultValue: 'false', description: 'Debug mode?') + string(name: 'testFile', defaultValue: '', description: 'Provide test file to execute. Example: test/core/LogAPITest.php') + booleanParam(name: 'coverture', defaultValue: 'false', description: 'Test coverture?') booleanParam(name: 'runNonRegOQLTests', defaultValue: 'false', description: 'Do You want to run legacy OQL regression tests?') } stages { @@ -40,7 +42,7 @@ pipeline { parallel { stage('phpunit') { steps { - sh './.jenkins/bin/tests/phpunit.sh ${debugMode} ${runNonRegOQLTests}' + sh './.jenkins/bin/tests/phpunit.sh ${debugMode} ${runNonRegOQLTests} "${coverture}" "${testFile}"' } } } @@ -50,18 +52,20 @@ pipeline { post { always { + archiveArtifacts allowEmptyArchive:true, excludes: '.gitkeep', artifacts: 'var/test/*.xml' junit 'var/test/phpunit-log.junit.xml' } failure { - slackSend(channel: "#jenkins-itop", color: '#FF0000', message: "Ho no! Build failed! (${currentBuild.result}), Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})") + slackSend(channel: "#jenkins-itop", color: '#FF0000', message: "Ho no! Build failed! (${currentBuild.result}), Job '${env.JOB_NAME_UNESCAPED} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})") } fixed { - slackSend(channel: "#jenkins-itop", color: '#FFa500', message: "Yes! Build repaired! (${currentBuild.result}), Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})") + slackSend(channel: "#jenkins-itop", color: '#FFa500', message: "Yes! Build repaired! (${currentBuild.result}), Job '${env.JOB_NAME_UNESCAPED} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})") } } environment { DEBUG_UNIT_TEST = '0' + JOB_NAME_UNESCAPED = env.JOB_NAME.replaceAll("%2F", "/") } options { timeout(time: 20, unit: 'MINUTES') diff --git a/README.md b/README.md index 3deb3aa28..cfb243b02 100644 --- a/README.md +++ b/README.md @@ -21,21 +21,35 @@ iTop also offers mass import tools and web services to integrate with your IT - [Data synchronization][18] (for data federation) +## Latest release + + - [Changes since the previous version][62] + - [New features][63] + - [Installation notes][64] + - [Download][65] + +[62]: https://www.itophub.io/wiki/page?id=latest:release:change_log +[63]: https://www.itophub.io/wiki/page?id=latest:release:start +[64]: https://www.itophub.io/wiki/page?id=latest:install:start +[65]: https://sourceforge.net/projects/itop/files/latest/download + + ## Resources - [iTop Forums][1]: community support - [iTop Tickets][2]: for feature requests and bug reports - [Releases download][3] - - [Documentation][4] covering both iTop and its official extensions - - [iTop Hub][5] : discover and install extensions ! - + - [Software requirements][4] + - [Documentation][5] covering both iTop and its official extensions + - [iTop Hub][6] : discover and install extensions ! [1]: https://sourceforge.net/p/itop/discussion/ [2]: https://sourceforge.net/p/itop/tickets/ [3]: https://sourceforge.net/projects/itop/files/itop/ -[4]: https://www.itophub.io/wiki -[5]: https://store.itophub.io/en_US/ +[4]: https://www.itophub.io/wiki/page?id=latest:install:upgrading_itop +[5]: https://www.itophub.io/wiki +[6]: https://store.itophub.io/en_US/ [10]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#configuration_management_cmdb [11]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#ticketing @@ -49,47 +63,6 @@ iTop also offers mass import tools and web services to integrate with your IT -## Last releases - -### Versions 2.7.* - - 2.7.0-1 published on April 8, 2020 - - [Changes since the previous version][62] - - [New features][63] - - [Migration notes][64] - - [Download iTop 2.7.0-beta2][65] - -[62]: https://www.itophub.io/wiki/page?id=2_7_0:release:change_log -[63]: https://www.itophub.io/wiki/page?id=2_7_0:release:2_7_whats_new -[64]: https://www.itophub.io/wiki/page?id=2_7_0:install:260_to_270_migration_notes -[65]: https://sourceforge.net/projects/itop/files/itop/2.7.0-beta2 - - -### Versions 2.6.* - - 2.6.0 published on January 9, 2019 - - [Changes since the previous version][58] - - [New features][59] - - [Migration notes][60] - - [Download iTop 2.6.3][61] - -[58]: https://www.itophub.io/wiki/page?id=2_6_0:release:change_log -[59]: https://www.itophub.io/wiki/page?id=2_6_0:release:2_6_whats_new -[60]: https://www.itophub.io/wiki/page?id=2_6_0:install:250_to_260_migration_notes -[61]: https://sourceforge.net/projects/itop/files/itop/2.6.3 - - -### Versions 2.5.* - - 2.5.0 published on July 11, 2018 - - [Changes since the previous version][54] - - [New features][55] - - [Migration notes][56] - - [Download iTop 2.5.1][57] - -[54]: https://www.itophub.io/wiki/page?id=2_5_0:release:change_log -[55]: https://www.itophub.io/wiki/page?id=2_5_0:release:2_5_whats_new -[56]: https://www.itophub.io/wiki/page?id=2_5_0:install:240_to_250_migration_notes -[57]: https://sourceforge.net/projects/itop/files/itop/2.5.1 - - ## About Us iTop development is sponsored, led and supported by [Combodo][0]. @@ -127,7 +100,6 @@ We would like to give a special thank you to the people from the community who c - Rosenke, Stephan - Seki, Shoji - Shilov, Vladimir - - Tahri, Ahmed R. (Ousret) - Tulio, Marco - Turrubiates, Miguel diff --git a/application/clipage.class.inc.php b/application/clipage.class.inc.php index 25153d17e..db348322f 100644 --- a/application/clipage.class.inc.php +++ b/application/clipage.class.inc.php @@ -29,9 +29,13 @@ require_once(APPROOT."/application/webpage.class.inc.php"); class CLIPage implements Page { - function __construct($s_title) + /** @var string */ + public $s_title; + + function __construct($s_title) { - } + $this->s_title = $s_title; + } public function output() { @@ -48,22 +52,22 @@ class CLIPage implements Page public function add($sText) { echo $sText; - } + } public function p($sText) { echo $sText."\n"; - } + } public function pre($sText) { echo $sText."\n"; - } + } public function add_comment($sText) { echo "#".$sText."\n"; - } + } public function table($aConfig, $aData, $aParams = array()) { @@ -93,5 +97,3 @@ class CLIPage implements Page } } } - -?> diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 5e100852e..20873b740 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -239,8 +239,11 @@ EOF foreach($_SESSION['obj_messages'][$sMessageKey] as $sMessageId => $aMessageData) { $sMsgClass = 'message_'.$aMessageData['severity']; - $aMessages[] = "
".$aMessageData['message']."
"; - $aRanks[] = $aMessageData['rank']; + if(!in_array("
".$aMessageData['message']."
",$aMessages)) + { + $aMessages[] = "
".$aMessageData['message']."
"; + $aRanks[] = $aMessageData['rank']; + } } unset($_SESSION['obj_messages'][$sMessageKey]); } @@ -660,7 +663,30 @@ EOF { // n:n links $oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote()); + $sLinkingAttCode = $oLinkingAttDef->GetCode(); $sTargetClass = $oLinkingAttDef->GetTargetClass(); + + // N°2334 fields to display for n:n relations + $aLnkAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($sClass, $sAttCode); + $aRemoteAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($sTargetClass); + $aLnkAttCodesToDisplay = array_map(function ($oLnkAttDef) { + return ormLinkSet::LINK_ALIAS.'.'.$oLnkAttDef->GetCode(); + }, + $aLnkAttDefsToDisplay + ); + if (!in_array(ormLinkSet::LINK_ALIAS.'.'.$sLinkingAttCode, $aLnkAttCodesToDisplay)) + { + // we need to display a link to the remote class instance ! + $aLnkAttCodesToDisplay[] = ormLinkSet::LINK_ALIAS.'.'.$sLinkingAttCode; + } + $aRemoteAttCodesToDisplay = array_map(function ($oRemoteAttDef) { + return ormLinkSet::REMOTE_ALIAS.'.'.$oRemoteAttDef->GetCode(); + }, + $aRemoteAttDefsToDisplay + ); + $aAttCodesToDisplay = array_merge($aLnkAttCodesToDisplay, $aRemoteAttCodesToDisplay); + $sAttCodesToDisplay = implode(',', $aAttCodesToDisplay); + $aParams = array( 'link_attr' => $oAttDef->GetExtKeyToMe(), 'object_id' => $this->GetKey(), @@ -668,8 +694,12 @@ EOF 'view_link' => false, 'menu' => false, //'menu_actions_target' => '_blank', - 'display_limit' => true, // By default limit the list to speed up the initial load & display + // By default limit the list to speed up the initial load & display + 'display_limit' => true, 'table_id' => $sClass.'_'.$sAttCode, + // N°2334 specify fields to display for n:n relations + 'zlist' => false, + 'extra_fields' => $sAttCodesToDisplay, ); } $oPage->p(MetaModel::GetClassIcon($sTargetClass)." ".$oAttDef->GetDescription()); @@ -1283,7 +1313,14 @@ HTML /** * @param \WebPage $oPage * @param \CMDBObjectSet $oSet - * @param array $aExtraParams + * @param array $aExtraParams key used : + * * * @return string * @throws \CoreException @@ -1362,7 +1399,7 @@ HTML } // Filter the list to removed linked set since we are not able to display them here - foreach($aList[$sAlias] as $index => $sAttCode) + foreach ($aList[$sAlias] as $index => $sAttCode) { $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); if ($oAttDef instanceof AttributeLinkedSet) @@ -1371,6 +1408,11 @@ HTML unset($aList[$sAlias][$index]); } } + + if (empty($aList[$sAlias])) + { + unset($aList[$sAlias], $aAuthorizedClasses[$sAlias]); + } } $sSelectMode = 'none'; @@ -3012,10 +3054,38 @@ HTML $sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $this->Get($sAttCode), $this->GetEditValue($sAttCode), 'att_'.$iFieldIndex, '', $iExpectCode, $aArgs); - $aDetails[] = array( + $aAttrib = array( 'label' => ''.$oAttDef->GetLabel().'', 'value' => "$sHTMLValue", ); + + //add attrib for data-attribute + // Prepare metadata attributes + $sAttCode = $oAttDef->GetCode(); + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + $sAttDefClass = get_class($oAttDef); + $sAttLabel = MetaModel::GetLabel($sClass, $sAttCode); + + $aAttrib['attcode'] = $sAttCode; + $aAttrib['atttype'] = $sAttDefClass; + $aAttrib['attlabel'] = $sAttLabel; + // - Attribute flags + $aAttrib['attflags'] = $this->GetFormAttributeFlags($sAttCode) ; + // - How the field should be rendered + $aAttrib['layout'] = (in_array($oAttDef->GetEditClass(), static::GetAttEditClassesToRenderAsLargeField())) ? 'large' : 'small'; + // - For simple fields, we get the raw (stored) value as well + $bExcludeRawValue = false; + foreach (static::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude) + { + if (is_a($sAttDefClass, $sAttDefClassToExclude, true)) + { + $bExcludeRawValue = true; + break; + } + } + $aAttrib['value_raw'] = ($bExcludeRawValue === false) ? $this->Get($sAttCode) : ''; + + $aDetails[] = $aAttrib; $aFieldsMap[$sAttCode] = 'att_'.$iFieldIndex; $iFieldIndex++; } @@ -3080,7 +3150,7 @@ EOF $this->GetOwnershipJSHandler($oPage, $sOwnershipToken); } - // Note: This part (inline images activation) is duplicated in self::DisplayModifyForm and several other places. Maybe it should be refactored so it automatically activates when an HTML field is present, or be an option of the attribute. See bug n°1240. + // Note: This part (inline images activation) is duplicated in self::DisplayModifyForm and several other places. Maybe it should be refactored so it automatically activates when an HTML field is present, or be an option of the attribute. See bug N°1240. $sTempId = utils::GetUploadTempId($iTransactionId); $oPage->add_ready_script(InlineImage::EnableCKEditorImageUpload($this, $sTempId)); } diff --git a/application/dashboard.class.inc.php b/application/dashboard.class.inc.php index 35b9833e6..322e68e38 100644 --- a/application/dashboard.class.inc.php +++ b/application/dashboard.class.inc.php @@ -1574,9 +1574,16 @@ JS private function GetDashletObjectListAppUserPreferencesPrefix(DashletObjectList $oDashlet, $aExtraParams, $sDashletId) { $sDataTableId = Dashlet::APPUSERPREFERENCES_PREFIX.$sDashletId; - $oFilter = $oDashlet->GetDBSearch($aExtraParams); - $aClassAliases = $oFilter->GetSelectedClasses(); - + $aClassAliases = array(); + try{ + $oFilter = $oDashlet->GetDBSearch($aExtraParams); + $aClassAliases = $oFilter->GetSelectedClasses(); + } + catch (Exception $e) + { + //on error, return default value + return null; + } return DataTableSettings::GetAppUserPreferenceKey($aClassAliases, $sDataTableId); } } diff --git a/application/dashlet.class.inc.php b/application/dashlet.class.inc.php index db1cd7596..f43f232bb 100644 --- a/application/dashlet.class.inc.php +++ b/application/dashlet.class.inc.php @@ -922,7 +922,7 @@ class DashletObjectList extends Dashlet $sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block if ($sHtmlTitle != '') { - $oPage->add('

'.$sHtmlTitle.'

'); + $oPage->add('

 '.$sHtmlTitle.'

'); } $oFilter = $this->GetDBSearch($aExtraParams); $oBlock = new DisplayBlock($oFilter, 'list'); @@ -1121,7 +1121,7 @@ abstract class DashletGroupBy extends Dashlet $this->sFunction = null; } - if (empty($this->aProperties['order_direction'])) + if ((!is_null($this->sClass)) && empty($this->aProperties['order_direction'])) { $aAttributeTypes = $this->oModelReflection->ListAttributes($this->sClass); if (isset($aAttributeTypes[$this->sGroupByAttCode])) @@ -1272,10 +1272,10 @@ abstract class DashletGroupBy extends Dashlet break; } - $oPage->add('
'); + $oPage->add('
'); if ($sHtmlTitle != '') { - $oPage->add('

'.$sHtmlTitle.'

'); + $oPage->add('

 '.$sHtmlTitle.'

'); } $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM) $oBlock = new DisplayBlock($oFilter, $sType); @@ -1868,11 +1868,11 @@ class DashletGroupByTable extends DashletGroupBy $iTotal += $aDisplayData['value']; } - $oPage->add('
'); $sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM) $oPage->add('
'); + $oPage->add('
'); $oPage->add('

'.Dict::Format('UI:Pagination:HeaderNoSelection', $iTotal).'

'); $oPage->add(''); $oPage->add(''); @@ -1926,7 +1926,7 @@ class DashletHeaderStatic extends Dashlet $oPage->add('
'); $oPage->add(''); - $oPage->add('

'.$this->oModelReflection->DictString($sTitle).'

'); + $oPage->add('

 '.$this->oModelReflection->DictString($sTitle).'

'); $oPage->add('
'); $oPage->add(''); diff --git a/application/datamodel.application.xml b/application/datamodel.application.xml index f2910b821..b8c634773 100644 --- a/application/datamodel.application.xml +++ b/application/datamodel.application.xml @@ -1,5 +1,46 @@ + + + cmdbAbstractObject + + /* Resource access control abstraction. Can be herited by abstract resource access control classes. Generaly controlled using UR_ACTION_MODIFY access right. */ + true + + + + + + AbstractResource + + /* AdminTools menu access control. */ + true + grant_by_profile + + + + + + AbstractResource + + /* RunQueriesMenu menu access control. */ + true + grant_by_profile + + + + + + AbstractResource + + /* System menu access control. */ + true + grant_by_profile + + + + + pages/UI.php @@ -12,6 +53,117 @@ + + 10 + + + 10 + WelcomeMenu + + DashboardLayoutOneCol + + <cells> + <cell id="0"> + <rank>0</rank> + <dashlets> + </dashlets> + </cell> + </cells> + </definition> + </menu> + <menu id="MyShortcuts" xsi:type="ShortcutContainerMenuNode" _delta="define"> + <rank>20</rank> + <parent>WelcomeMenu</parent> + </menu> + <menu id="UserManagement" xsi:type="TemplateMenuNode" _delta="define"> + <rank>10</rank> + <parent>AdminTools</parent> + <template_file/> + </menu> + <menu id="UserAccountsMenu" xsi:type="OQLMenuNode" _delta="define"> + <rank>11</rank> + <parent>UserManagement</parent> + <oql><![CDATA[SELECT User]]></oql> + <do_search>1</do_search> + <search_form_open>1</search_form_open> + <enable_class>User</enable_class> + <enable_action>UR_ACTION_MODIFY</enable_action> + </menu> + <menu id="ProfilesMenu" xsi:type="OQLMenuNode" _delta="define"> + <rank>12</rank> + <parent>UserManagement</parent> + <oql><![CDATA[SELECT URP_Profiles]]></oql> + <do_search>1</do_search> + <enable_class>URP_Profiles</enable_class> + <enable_action>UR_ACTION_MODIFY</enable_action> + </menu> + <menu id="AuditCategories" xsi:type="OQLMenuNode" _delta="define"> + <rank>20</rank> + <parent>AdminTools</parent> + <oql><![CDATA[SELECT AuditCategory]]></oql> + <do_search>1</do_search> + <enable_class>AuditCategory</enable_class> + <enable_action>UR_ACTION_MODIFY</enable_action> + </menu> + <menu id="Queries" xsi:type="TemplateMenuNode" _delta="define"> + <rank>30</rank> + <parent>AdminTools</parent> + <template_file/> + </menu> + <menu id="RunQueriesMenu" xsi:type="WebPageMenuNode" _delta="define"> + <rank>31</rank> + <parent>Queries</parent> + <url>$pages/run_query.php</url> + <enable_class>ResourceRunQueriesMenu</enable_class> + <enable_action>UR_ACTION_MODIFY</enable_action> + </menu> + <menu id="QueryMenu" xsi:type="OQLMenuNode" _delta="define"> + <rank>32</rank> + <parent>Queries</parent> + <oql><![CDATA[SELECT Query]]></oql> + <do_search>1</do_search> + <enable_class>Query</enable_class> + <enable_action>UR_ACTION_MODIFY</enable_action> + </menu> + <menu id="ExportMenu" xsi:type="WebPageMenuNode" _delta="define"> + <rank>33</rank> + <parent>Queries</parent> + <url>$webservices/export-v2.php?interactive=1</url> + <enable_class>ResourceAdminMenu</enable_class> + <enable_action>UR_ACTION_MODIFY</enable_action> + </menu> + <menu id="DataModelMenu" xsi:type="WebPageMenuNode" _delta="define"> + <rank>40</rank> + <parent>AdminTools</parent> + <url>$pages/schema.php</url> + <enable_class>ResourceRunQueriesMenu</enable_class> + <enable_action>UR_ACTION_MODIFY</enable_action> + </menu> + <menu id="UniversalSearchMenu" xsi:type="WebPageMenuNode" _delta="define"> + <rank>35</rank> + <parent>Queries</parent> + <url>$pages/UniversalSearch.php</url> + <enable_class>ResourceAdminMenu</enable_class> + <enable_action>UR_ACTION_MODIFY</enable_action> + </menu> + <menu id="ConfigurationTools" xsi:type="MenuGroup" _delta="define_if_not_exists"> + <rank>90</rank> + </menu> + <menu id="DataSources" xsi:type="OQLMenuNode" _delta="define"> + <rank>20</rank> + <parent>ConfigurationTools</parent> + <oql><![CDATA[SELECT SynchroDataSource]]></oql> + <do_search>1</do_search> + <enable_class>SynchroDataSource</enable_class> + <enable_action>UR_ACTION_MODIFY</enable_action> + </menu> + <menu id="NotificationsMenu" xsi:type="WebPageMenuNode" _delta="define"> + <rank>40</rank> + <parent>ConfigurationTools</parent> + <url>$pages/notifications.php</url> + <enable_class>Trigger</enable_class> + <enable_action>UR_ACTION_MODIFY</enable_action> + </menu> <menu id="AdminTools" xsi:type="MenuGroup" _delta="define"> <rank>80</rank> </menu> @@ -21,4 +173,219 @@ <enable_action>UR_ACTION_MODIFY</enable_action> </menu> </menus> + <meta> + <classes> + <class id="cmdbAbstractObject" _delta="define"> + <methods> + <method id="Set"> + <arguments> + <argument id="1"> + <type>attcode</type> + <mandatory>true</mandatory> + <type_restrictions> + <operation>deny</operation> + <types> + <type id="AttributeStopWatch"/> + <type id="AttributeSubItem"/> + <type id="AttributeExternalField"/> + <type id="AttributeLinkedSetIndirect"/> + <type id="AttributeLinkedSet"/> + <type id="AttributeImage"/> + <type id="AttributeBlob"/> + </types> + </type_restrictions> + </argument> + <argument id="2"> + <type>string</type> + <mandatory>true</mandatory> + </argument> + </arguments> + </method> + <method id="SetIfNull"> + <arguments> + <argument id="1"> + <type>attcode</type> + <mandatory>true</mandatory> + <type_restrictions> + <operation>deny</operation> + <types> + <type id="AttributeStopWatch"/> + <type id="AttributeSubItem"/> + <type id="AttributeExternalField"/> + <type id="AttributeLinkedSetIndirect"/> + <type id="AttributeLinkedSet"/> + <type id="AttributeImage"/> + <type id="AttributeBlob"/> + </types> + </type_restrictions> + </argument> + <argument id="2"> + <type>string</type> + <mandatory>true</mandatory> + </argument> + </arguments> + </method> + <method id="SetCurrentDate"> + <arguments> + <argument id="1"> + <type>attcode</type> + <mandatory>true</mandatory> + <type_restrictions> + <operation>allow</operation> + <types> + <type id="AttributeDate"/> + <type id="AttributeDateTime"/> + <type id="AttributeString"/> + </types> + </type_restrictions> + </argument> + </arguments> + </method> + <method id="SetCurrentUser"> + <arguments> + <argument id="1"> + <type>attcode</type> + <mandatory>true</mandatory> + <type_restrictions> + <operation>allow</operation> + <types> + <type id="AttributeExternalKey"/> + <type id="AttributeInteger"/> + <type id="AttributeString"/> + </types> + </type_restrictions> + </argument> + </arguments> + </method> + <method id="SetCurrentPerson"> + <arguments> + <argument id="1"> + <type>attcode</type> + <mandatory>true</mandatory> + <type_restrictions> + <operation>allow</operation> + <types> + <type id="AttributeExternalKey"/> + <type id="AttributeInteger"/> + <type id="AttributeString"/> + </types> + </type_restrictions> + </argument> + </arguments> + </method> + <method id="SetElapsedTime"> + <arguments> + <argument id="1"> + <type>attcode</type> + <mandatory>true</mandatory> + <type_restrictions> + <operation>allow</operation> + <types> + <type id="AttributeDuration"/> + </types> + </type_restrictions> + </argument> + <argument id="2"> + <type>attcode</type> + <mandatory>true</mandatory> + <type_restrictions> + <operation>allow</operation> + <types> + <type id="AttributeDate"/> + <type id="AttributeDateTime"/> + </types> + </type_restrictions> + </argument> + <argument id="3"> + <type>string</type> + <mandatory>false</mandatory> + </argument> + </arguments> + </method> + <method id="Reset"> + <arguments> + <argument id="1"> + <type>attcode</type> + <mandatory>true</mandatory> + <type_restrictions> + <operation>deny</operation> + <types> + <type id="AttributeStopWatch"/> + <type id="AttributeSubItem"/> + <type id="AttributeExternalField"/> + </types> + </type_restrictions> + </argument> + </arguments> + </method> + <method id="ResetStopWatch"> + <arguments> + <argument id="1"> + <type>attcode</type> + <mandatory>true</mandatory> + <type_restrictions> + <operation>allow</operation> + <types> + <type id="AttributeStopWatch"/> + </types> + </type_restrictions> + </argument> + </arguments> + </method> + <method id="Copy"> + <arguments> + <argument id="1"> + <type>attcode</type> + <mandatory>true</mandatory> + <type_restrictions> + <operation>deny</operation> + <types> + <type id="AttributeStopWatch"/> + <type id="AttributeSubItem"/> + <type id="AttributeExternalField"/> + </types> + </type_restrictions> + </argument> + <argument id="2"> + <type>attcode</type> + <mandatory>true</mandatory> + </argument> + </arguments> + </method> + <method id="ApplyStimulus"> + <arguments> + <argument id="1"> + <type>string</type> + <mandatory>true</mandatory> + </argument> + </arguments> + </method> + <method id="PrefillCreationForm"> + <arguments> + <argument id="1"> + <type>reference</type> + <mandatory>true</mandatory> + </argument> + </arguments> + </method> + <method id="PrefillTransitionForm"> + <arguments> + <argument id="1"> + <type>reference</type> + <mandatory>true</mandatory> + </argument> + </arguments> + </method> + <method id="PrefillSearchForm"> + <arguments> + <argument id="1"> + <type>reference</type> + <mandatory>true</mandatory> + </argument> + </arguments> + </method> + </methods> + </class> + </classes> + </meta> </itop_design> diff --git a/application/datatable.class.inc.php b/application/datatable.class.inc.php index adbeaecae..226e1d614 100644 --- a/application/datatable.class.inc.php +++ b/application/datatable.class.inc.php @@ -971,13 +971,13 @@ class DataTableSettings implements Serializable * @throws \CoreException * @throws \DictExceptionMissingString */ - static public function GetDataModelSettings($aClassAliases, $bViewLink, $aDefaultLists) + public static function GetDataModelSettings($aClassAliases, $bViewLink, $aDefaultLists) { $oSettings = new DataTableSettings($aClassAliases); // Retrieve the class specific settings for each class/alias based on the 'list' ZList //TODO let the caller pass some other default settings (another Zlist, extre fields...) $aColumns = array(); - foreach($aClassAliases as $sAlias => $sClass) + foreach ($aClassAliases as $sAlias => $sClass) { if ($aDefaultLists == null) { diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 2114a8dfb..6159282fb 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -24,15 +24,6 @@ require_once(APPROOT.'/application/utils.inc.php'); * * Each block is actually rendered as a <div></div> tag that can be rendered synchronously * or as a piece of Javascript/JQuery/Ajax that will get its content from another page (ajax.render.php). - * The list of cmdbObjects to be displayed into the block is defined by a filter - * Right now the type of display is either: list, count, bare_details, details, csv, modify or search - * - list produces a table listing the objects - * - count produces a paragraphs with a sentence saying 'cont' objects found - * - bare_details displays just the details of the attributes of the object (best if only one) - * - details display the full details of each object found using its template (best if only one) - * - csv displays a textarea with the CSV export of the list of objects - * - modify displays the form to modify an object (best if only one) - * - search displays a search form with the criteria of the filter set */ class DisplayBlock { @@ -45,7 +36,23 @@ class DisplayBlock protected $m_aParams; protected $m_oSet; protected $m_bShowObsoleteData = null; - + + /** + * @param \DBSearch $oFilter list of cmdbObjects to be displayed into the block + * @param string $sStyle one of : + * <ul> + * <li>list : produces a table listing the objects</li> + * <li>count : produces a paragraphs with a sentence saying 'cont' objects found</li> + * <li>bare_details : displays just the details of the attributes of the object (best if only one)</li> + * <li>details : display the full details of each object found using its template (best if only one)</li> + * <li>csv : displays a textarea with the CSV export of the list of objects</li> + * <li>modify : displays the form to modify an object (best if only one)</li> + * <li>search : displays a search form with the criteria of the filter set</li> + * </ul> + * @param bool $bAsynchronous + * @param array $aParams + * @param \DBObjectSet $oSet + */ public function __construct(DBSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null) { $this->m_oFilter = $oFilter->DeepClone(); diff --git a/application/errorpage.class.inc.php b/application/errorpage.class.inc.php new file mode 100644 index 000000000..603bc2515 --- /dev/null +++ b/application/errorpage.class.inc.php @@ -0,0 +1,82 @@ +<?php +/** + * @copyright Copyright (C) 2010-2020 Combodo SARL + * @license http://opensource.org/licenses/AGPL-3.0 + */ + + +class ErrorPage extends NiceWebPage +{ + public function __construct($sTitle) + { + parent::__construct($sTitle); + $this->add_linked_script("../js/jquery.blockUI.js"); + $this->add_linked_script("../setup/setup.js"); + $this->add_saas("css/setup.scss"); + } + + public function info($sText) + { + $this->add("<p class=\"info\">$sText</p>\n"); + $this->log_info($sText); + } + + public function ok($sText) + { + $this->add("<div class=\"message message-valid\"><span class=\"message-title\">Success:</span>$sText</div>"); + $this->log_ok($sText); + } + + public function warning($sText) + { + $this->add("<div class=\"message message-warning\"><span class=\"message-title\">Warning:</span>$sText</div>"); + $this->log_warning($sText); + } + + public function error($sText) + { + $this->add("<div class=\"message message-error\">$sText</div>"); + $this->log_error($sText); + } + + public function output() + { + $sLogo = utils::GetAbsoluteUrlAppRoot().'/images/itop-logo.png'; + $sTimeStamp = utils::GetCacheBusterTimestamp(); + $sTitle = utils::HtmlEntities($this->s_title); + $this->s_content = <<<HTML +<div id="header" class="error_page"> + <h1><a href="http://www.combodo.com/itop" target="_blank"><img title="iTop by Combodo" alt=" " src="{$sLogo}?t={$sTimeStamp}"></a> {$sTitle}</h1> +</div> +<div id="setup" class="error_page"> + {$this->s_content} +</div> +HTML; + return parent::output(); + } + + public static function log_error($sText) + { + IssueLog::Error($sText); + } + + public static function log_warning($sText) + { + IssueLog::Warning($sText); + } + + public static function log_info($sText) + { + IssueLog::Info($sText); + } + + public static function log_ok($sText) + { + IssueLog::Ok($sText); + } + + public static function log($sText) + { + IssueLog::Ok($sText); + } +} diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 576779140..330f2aef5 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -73,19 +73,16 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage $this->add_header("Content-type: text/html; charset=".self::PAGES_CHARSET); $this->add_header("Cache-control: no-cache"); $this->add_linked_stylesheet("../css/jquery.treeview.css"); - $this->add_linked_stylesheet("../css/jquery.autocomplete.css"); $this->add_linked_stylesheet("../css/jquery-ui-timepicker-addon.css"); $this->add_linked_stylesheet("../css/jquery.multiselect.css"); $this->add_linked_stylesheet("../css/magnific-popup.css"); $this->add_linked_stylesheet("../css/c3.min.css"); $this->add_linked_stylesheet("../css/font-awesome/css/all.min.css"); - $this->add_linked_stylesheet("../css/font-awesome/css/v4-shims.min.css"); $this->add_linked_stylesheet("../js/ckeditor/plugins/codesnippet/lib/highlight/styles/obsidian.css"); $this->add_linked_script('../js/jquery.layout.min.js'); $this->add_linked_script('../js/jquery.ba-bbq.min.js'); $this->add_linked_script("../js/jquery.treeview.js"); - $this->add_linked_script("../js/jquery.autocomplete.js"); $this->add_linked_script("../js/date.js"); $this->add_linked_script("../js/jquery-ui-timepicker-addon.js"); $this->add_linked_script("../js/jquery-ui-timepicker-addon-i18n.min.js"); diff --git a/application/loginbasic.class.inc.php b/application/loginbasic.class.inc.php index 6460dad92..660b45cba 100644 --- a/application/loginbasic.class.inc.php +++ b/application/loginbasic.class.inc.php @@ -26,6 +26,10 @@ class LoginBasic extends AbstractLoginFSMExtension { $_SESSION['login_mode'] = 'basic'; } + elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) + { + $_SESSION['login_mode'] = 'basic'; + } elseif (isset($_SERVER['PHP_AUTH_USER'])) { $_SESSION['login_mode'] = 'basic'; @@ -36,7 +40,7 @@ class LoginBasic extends AbstractLoginFSMExtension protected function OnReadCredentials(&$iErrorCode) { - if ($_SESSION['login_mode'] == 'basic') + if (!isset($_SESSION['login_mode']) || $_SESSION['login_mode'] == 'basic') { list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword(); $_SESSION['login_temp_auth_user'] = $sAuthUser; @@ -92,9 +96,19 @@ class LoginBasic extends AbstractLoginFSMExtension { $sAuthUser = ''; $sAuthPwd = null; + $sAuthorization = ''; if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])) { - list($sAuthUser, $sAuthPwd) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6))); + $sAuthorization = $_SERVER['HTTP_AUTHORIZATION']; + } + elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) + { + $sAuthorization = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; + } + + if (!empty($sAuthorization)) + { + list($sAuthUser, $sAuthPwd) = explode(':', base64_decode(substr($sAuthorization, 6))); } else { @@ -121,4 +135,4 @@ class LoginBasic extends AbstractLoginFSMExtension } return array($sAuthUser, $sAuthPwd); } -} \ No newline at end of file +} diff --git a/application/loginexternal.class.inc.php b/application/loginexternal.class.inc.php index 04ed7f1d4..d4fcb7182 100644 --- a/application/loginexternal.class.inc.php +++ b/application/loginexternal.class.inc.php @@ -67,6 +67,15 @@ class LoginExternal extends AbstractLoginFSMExtension return LoginWebPage::LOGIN_FSM_CONTINUE; } + protected function OnError(&$iErrorCode) + { + if ($_SESSION['login_mode'] == 'external') + { + LoginWebPage::HTTP401Error(); + } + return LoginWebPage::LOGIN_FSM_CONTINUE; + } + /** * @return bool */ diff --git a/application/loginwebpage.class.inc.php b/application/loginwebpage.class.inc.php index 09eaa9073..ab594b21d 100644 --- a/application/loginwebpage.class.inc.php +++ b/application/loginwebpage.class.inc.php @@ -92,7 +92,6 @@ class LoginWebPage extends NiceWebPage { $this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/login.css'); $this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/all.min.css'); - $this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/v4-shims.min.css'); } public static function SetLoginFailedMessage($sMessage) @@ -100,6 +99,44 @@ class LoginWebPage extends NiceWebPage self::$m_sLoginFailedMessage = $sMessage; } + /** + * @param $oUser + * @param array $aProfiles + * + * @return array + * @throws \CoreException + * @throws \CoreUnexpectedValue + */ + public static function SynchroniseProfiles(&$oUser, array $aProfiles, $sOrigin) + { + $oProfilesSet = $oUser->Get(‘profile_list’); + //delete old profiles + $aExistingProfiles = []; + while ($oProfile = $oProfilesSet->Fetch()) + { + array_push($aExistingProfiles, $oProfile->Get('profileid')); + $iArrayKey = array_search($oProfile->Get('profileid'), $aProfiles); + if (!$iArrayKey) + { + $oProfilesSet->RemoveItem($oProfile->Get('profileid')); + } + else + { + unset($aProfiles[$iArrayKey]); + } + } + //add profiles not already linked with user + foreach ($aProfiles as $iProfileId) + { + $oLink = new URP_UserProfile(); + $oLink->Set('profileid', $iProfileId); + $oLink->Set('reason', $sOrigin); + + $oProfilesSet->AddItem(MetaModel::NewObject('URP_UserProfile', array('profileid' => $iProfileId, 'reason' => $sOrigin))); + } + $oUser->Set('profile_list', $oProfilesSet); + } + public function DisplayLoginHeader($bMainAppLogo = false) { $sLogo = 'itop-logo-external.png'; @@ -790,12 +827,13 @@ class LoginWebPage extends NiceWebPage $oPerson = null; try { - $sOrigin = 'External User provisioning'; + CMDBObject::SetTrackOrigin('custom-extension'); + $sInfo = 'External User provisioning'; if (isset($_SESSION['login_mode'])) { - $sOrigin .= " ({$_SESSION['login_mode']})"; + $sInfo .= " ({$_SESSION['login_mode']})"; } - CMDBObject::SetTrackOrigin($sOrigin); + CMDBObject::SetTrackInfo($sInfo); $oPerson = MetaModel::NewObject('Person'); $oPerson->Set('first_name', $sFirstName); @@ -843,6 +881,14 @@ class LoginWebPage extends NiceWebPage $oUser = null; try { + CMDBObject::SetTrackOrigin('custom-extension'); + $sInfo = 'External User provisioning'; + if (isset($_SESSION['login_mode'])) + { + $sInfo .= " ({$_SESSION['login_mode']})"; + } + CMDBObject::SetTrackInfo($sInfo); + $oUser = MetaModel::GetObjectByName('UserExternal', $sAuthUser, false); if (is_null($oUser)) { @@ -877,20 +923,12 @@ class LoginWebPage extends NiceWebPage } // Now synchronize the profiles - $oProfilesSet = DBObjectSet::FromScratch('URP_UserProfile'); $sOrigin = 'External User provisioning'; if (isset($_SESSION['login_mode'])) { $sOrigin .= " ({$_SESSION['login_mode']})"; } - foreach ($aProfiles as $iProfileId) - { - $oLink = new URP_UserProfile(); - $oLink->Set('profileid', $iProfileId); - $oLink->Set('reason', $sOrigin); - $oProfilesSet->AddObject($oLink); - } - $oUser->Set('profile_list', $oProfilesSet); + $aExistingProfiles = self::SynchroniseProfiles($oUser, $aProfiles, $sOrigin); if ($oUser->IsModified()) { $oUser->DBWrite(); @@ -985,7 +1023,7 @@ class LoginWebPage extends NiceWebPage else { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n"); $oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>"); $oP->output(); diff --git a/application/maintenancemsg.php b/application/maintenancemsg.php index f91339b13..8b346b7b6 100644 --- a/application/maintenancemsg.php +++ b/application/maintenancemsg.php @@ -34,7 +34,7 @@ function _MaintenanceSetupPageMessage($sTitle, $sMessage) @include_once(APPROOT.'setup/setuppage.class.inc.php'); if (class_exists('SetupPage')) { - $oP = new SetupPage($sTitle); + $oP = new ErrorPage($sTitle); $oP->p("<h2 class=\"center\">$sMessage</h2>"); $oP->add_ready_script( <<<JS diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php index 96a4e91ca..0f08ceeb4 100644 --- a/application/menunode.class.inc.php +++ b/application/menunode.class.inc.php @@ -128,7 +128,7 @@ class ApplicationMenu if (is_null($oMenuNode) || !$oMenuNode->IsEnabled()) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:Login:Error:AccessRestricted')."</h1>\n"); $oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>"); $oP->output(); diff --git a/application/ui.extkeywidget.class.inc.php b/application/ui.extkeywidget.class.inc.php index 6426ddf51..6a40e2052 100644 --- a/application/ui.extkeywidget.class.inc.php +++ b/application/ui.extkeywidget.class.inc.php @@ -239,13 +239,13 @@ EOF $oPage->add_ready_script("$('.multiselect').multiselect($sJSOptions);"); } $oPage->add_ready_script( -<<<EOF +<<<JS oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch); oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>"; $('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } ); $('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } ); -EOF +JS ); } // Switch } @@ -269,10 +269,10 @@ EOF { $sDisplayValue = $this->GetObjectName($value); } - $iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars(); + $iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 2; //@@@ $this->oAttDef->GetMinAutoCompleteChars(); // the input for the auto-complete - $sHTMLValue .= "<input class=\"field_autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>"; + $sHTMLValue .= "<input class=\"field_autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>"; $sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\"><i class=\"fas fa-search\"></i></div></span>"; // another hidden input to store & pass the object's Id @@ -281,30 +281,27 @@ EOF $JSSearchMode = $this->bSearchMode ? 'true' : 'false'; // Scripts to start the autocomplete and bind some events to it $oPage->add_ready_script( -<<<EOF +<<<JS oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch); oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>"; - $('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter',bSearchMode:$JSSearchMode, json: function() { return $sWizHelperJSON; } }}); - $('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly ! - $('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('{$this->iId}', event, data, formatted); } ); - $('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } ); + oACWidget_{$this->iId}.AddAutocomplete($iMinChars, $sWizHelperJSON); if ($('#ac_dlg_{$this->iId}').length == 0) { $('body').append('<div id="ac_dlg_{$this->iId}"></div>'); } -EOF +JS ); } if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false) { $sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_tree_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"><i class=\"fas fa-sitemap\"></i></div></span>"; $oPage->add_ready_script( -<<<EOF +<<<JS if ($('#ac_tree_{$this->iId}').length == 0) { $('body').append('<div id="ac_tree_{$this->iId}"></div>'); } -EOF +JS ); } if ($bCreate && $bExtensions) @@ -313,12 +310,12 @@ EOF $sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_add_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\"><i class=\"fas fa-plus\"></i></div></span>"; $oPage->add_ready_script( -<<<EOF +<<<JS if ($('#ajax_{$this->iId}').length == 0) { $('body').append('<div id="ajax_{$this->iId}"></div>'); } -EOF +JS ); } $sHTMLValue .= "</div>"; @@ -443,16 +440,31 @@ EOF $oValuesSet->SetSort(false); $oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode); $oValuesSet->SetLimit($iMax); - $aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains'); + $aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'start_with'); asort($aValuesContains); - $aValues = array(); - foreach($aValuesContains as $sKey => $sFriendlyName) + $aValues = $aValuesContains; + if (sizeof($aValues) < $iMax) { - if (!isset($aValues[$sKey])) + $aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains'); + asort($aValuesContains); + $iSize = sizeof($aValuesContains); + foreach ($aValuesContains as $sKey => $sFriendlyName) { - $aValues[$sKey] = $sFriendlyName; + if (!isset($aValues[$sKey])) + { + $aValues[$sKey] = $sFriendlyName; + if (++$iSize >= $iMax) + { + break; + } + } } } + elseif (!in_array($sContains, $aValues)) + { + $aValuesEquals = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals'); + $aValues = array_merge($aValuesEquals, $aValues); + } switch($sOutputFormat) { @@ -652,7 +664,6 @@ HTML $oPage->add('</div></div>'); $oPage->add_ready_script("\$('#tree_$this->iId ul').treeview({ control: '#treecontrolid', persist: 'false'});\n"); - $oPage->add_ready_script("\$('#tree_$this->iId ul').treeview();\n"); $oPage->add_ready_script("\$('#dlg_tree_$this->iId').dialog({ width: 'auto', height: 'auto', autoOpen: true, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.OnHKResize, close: oACWidget_{$this->iId}.OnHKClose });\n"); } diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index 2d709ae94..02804fc5d 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -46,7 +46,7 @@ class UILinksWidget * UILinksWidget constructor. * * @param string $sClass - * @param string $sAttCode + * @param string $sAttCode AttributeLinkedSetIndirect attcode * @param int $iInputId * @param string $sNameSuffix * @param bool $bDuplicatesAllowed @@ -73,41 +73,35 @@ class UILinksWidget /** @var AttributeExternalKey $oLinkingAttDef */ $oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote); $this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass(); - $sExtKeyToMe = $oAttDef->GetExtKeyToMe(); - $sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass); - $sDefaultState = MetaModel::GetDefaultState($this->m_sClass); $this->m_aEditableFields = array(); $this->m_aTableConfig = array(); - $this->m_aTableConfig['form::checkbox'] = array( 'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+')); + $this->m_aTableConfig['form::checkbox'] = array( + 'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">", + 'description' => Dict::S('UI:SelectAllToggle+'), + ); - foreach(MetaModel::FlattenZList(MetaModel::GetZListItems($this->m_sLinkedClass, 'list')) as $sAttCode) + $aLnkAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($sClass, $sAttCode); + foreach ($aLnkAttDefsToDisplay as $oLnkAttDef) { - $oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sAttCode); - if ($sStateAttCode == $sAttCode) - { - // State attribute is always hidden from the UI - } - else if ($oAttDef->IsWritable() && ($sAttCode != $sExtKeyToMe) && ($sAttCode != $this->m_sExtKeyToRemote) && ($sAttCode != 'finalclass')) - { - $iFlags = MetaModel::GetAttributeFlags($this->m_sLinkedClass, $sDefaultState, $sAttCode); - if ( !($iFlags & OPT_ATT_HIDDEN) && !($iFlags & OPT_ATT_READONLY) ) - { - $this->m_aEditableFields[] = $sAttCode; - $this->m_aTableConfig[$sAttCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription()); - } - } + $sLnkAttCode = $oLnkAttDef->GetCode(); + $this->m_aEditableFields[] = $sLnkAttCode; + $this->m_aTableConfig[$sLnkAttCode] = array('label' => $oLnkAttDef->GetLabel(), 'description' => $oLnkAttDef->GetDescription()); } - - $this->m_aTableConfig['static::key'] = array( 'label' => MetaModel::GetName($this->m_sRemoteClass), 'description' => MetaModel::GetClassDescription($this->m_sRemoteClass)); - foreach(MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode) + + $this->m_aTableConfig['static::key'] = array( + 'label' => MetaModel::GetName($this->m_sRemoteClass), + 'description' => MetaModel::GetClassDescription($this->m_sRemoteClass), + ); + + $aRemoteAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($this->m_sRemoteClass); + foreach ($aRemoteAttDefsToDisplay as $oRemoteAttDef) { - // TO DO: check the state of the attribute: hidden or visible ? - if ($sFieldCode != 'finalclass') - { - $oAttDef = MetaModel::GetAttributeDef($this->m_sRemoteClass, $sFieldCode); - $this->m_aTableConfig['static::'.$sFieldCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription()); - } + $sRemoteAttCode = $oRemoteAttDef->GetCode(); + $this->m_aTableConfig['static::'.$sRemoteAttCode] = array( + 'label' => $oRemoteAttDef->GetLabel(), + 'description' => $oRemoteAttDef->GetDescription(), + ); } } diff --git a/application/utils.inc.php b/application/utils.inc.php index 9cf115dd8..53e65814e 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -283,6 +283,8 @@ class utils * * @since 2.5.2 2.6.0 new 'transaction_id' filter * @since 2.7.0 new 'element_identifier' filter + * + * @throws \CoreException */ protected static function Sanitize_Internal($value, $sSanitizationFilter) { @@ -296,7 +298,7 @@ class utils $retValue = $value; if (!MetaModel::IsValidClass($value)) { - $retValue = false; + throw new CoreException(Dict::Format('UI:OQL:UnknownClassNoFix', utils::HtmlEntities($value))); } break; @@ -2190,7 +2192,7 @@ class utils * * not contained in base path * Otherwise return the real path (see realpath()) * - * @since 2.7.0 N°2538 + * @since 2.6.5 2.7.0 N°2538 */ final public static function RealPath($sPath, $sBasePath) { diff --git a/composer.json b/composer.json index 240f68395..651c3e86d 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "type": "project", "license": "AGPLv3", "require": { - "php": ">=5.6.0", + "php": ">=7.1.3", "ext-ctype": "*", "ext-dom": "*", "ext-gd": "*", @@ -10,7 +10,7 @@ "ext-json": "*", "ext-mysqli": "*", "ext-soap": "*", - "combodo/tcpdf": "6.3.4", + "combodo/tcpdf": "6.3.5", "nikic/php-parser": "^3.1", "pear/archive_tar": "1.4.9", "pelago/emogrifier": "2.1.0", @@ -37,7 +37,7 @@ }, "config": { "platform": { - "php": "5.6.0" + "php": "7.2.0" }, "vendor-dir": "lib", "preferred-install": { diff --git a/composer.lock b/composer.lock index 40179fbc2..3a08b7f7d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b29eb2767d269b9ec2cf4d148dc083bc", + "content-hash": "ad359769d05acd25a9fc31d69acbe43a", "packages": [ { "name": "combodo/tcpdf", - "version": "6.3.4", + "version": "6.3.5", "source": { "type": "git", "url": "https://github.com/combodo-itop-libs/TCPDF.git", - "reference": "fe1c625d33e8f7d872d6fb69fb0255fd0e5cee2d" + "reference": "abbfedb8ca59843dec11c97ca3f308742265c3fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/combodo-itop-libs/TCPDF/zipball/fe1c625d33e8f7d872d6fb69fb0255fd0e5cee2d", - "reference": "fe1c625d33e8f7d872d6fb69fb0255fd0e5cee2d", + "url": "https://api.github.com/repos/combodo-itop-libs/TCPDF/zipball/abbfedb8ca59843dec11c97ca3f308742265c3fc", + "reference": "abbfedb8ca59843dec11c97ca3f308742265c3fc", "shasum": "" }, "require": { @@ -64,7 +64,7 @@ ], "description": "TCPDF fork adding requirements for iTop: Specific fonts.", "homepage": "https://github.com/combodo-itop-libs/TCPDF", - "time": "2020-02-12T14:16:56+00:00" + "time": "2020-06-05T13:06:44+00:00" }, { "name": "nikic/php-parser", diff --git a/conf/web.config b/conf/web.config index 599a5f260..58c9c3ac3 100644 --- a/conf/web.config +++ b/conf/web.config @@ -1,8 +1,13 @@ -<?xml version="1.0" encoding="utf-8" ?> +<?xml version="1.0" encoding="utf-8"?> <configuration> - <system.web> - <authorization> - <deny users="*" /> <!-- Denies all users --> - </authorization> - </system.web> + <system.webServer> + <security> + <requestFiltering> + <fileExtensions applyToWebDAV="false" allowUnlisted="false"></fileExtensions> + </requestFiltering> + <authorization> + <deny users="*" /> <!-- Denies all users --> + </authorization> + </security> + </system.webServer> </configuration> \ No newline at end of file diff --git a/core/action.class.inc.php b/core/action.class.inc.php index d7ff9d367..cb9dd281d 100644 --- a/core/action.class.inc.php +++ b/core/action.class.inc.php @@ -35,6 +35,10 @@ require_once(APPROOT.'/core/email.class.inc.php'); */ abstract class Action extends cmdbAbstractObject { + /** + * @throws \CoreException + * @throws \Exception + */ public static function Init() { $aParams = array @@ -57,15 +61,32 @@ abstract class Action extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("trigger_list", array("linked_class"=>"lnkTriggerAction", "ext_key_to_me"=>"action_id", "ext_key_to_remote"=>"trigger_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status')); // Attributes to be displayed for a list + // - Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list')); + // - Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status')); // Search criteria - MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status')); // Criteria of the std search form -// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + // - Criteria of the std search form + MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status')); + // - Criteria of the advanced search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); } + /** + * Encapsulate the execution of the action and handle failure & logging + * + * @param \Trigger $oTrigger + * @param array $aContextArgs + * + * @return mixed + */ abstract public function DoExecute($oTrigger, $aContextArgs); + /** + * @return bool + * @throws \ArchivedObjectException + * @throws \CoreException + */ public function IsActive() { switch($this->Get('status')) @@ -79,6 +100,13 @@ abstract class Action extends cmdbAbstractObject } } + /** + * Return true if the current action status is set on "test" + * + * @return bool + * @throws \ArchivedObjectException + * @throws \CoreException + */ public function IsBeingTested() { switch($this->Get('status')) @@ -99,6 +127,10 @@ abstract class Action extends cmdbAbstractObject */ abstract class ActionNotification extends Action { + /** + * @inheritDoc + * @throws \CoreException + */ public static function Init() { $aParams = array @@ -117,11 +149,15 @@ abstract class ActionNotification extends Action MetaModel::Init_InheritAttributes(); // Display lists - MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status')); // Attributes to be displayed for a list + // - Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list')); + // - Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status')); // Search criteria -// 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 + // - Criteria of the std search form +// MetaModel::Init_SetZListItems('standard_search', array('name')); + // - Criteria of the advanced search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); } } @@ -132,6 +168,9 @@ abstract class ActionNotification extends Action */ class ActionEmail extends ActionNotification { + /** + * @inheritDoc + */ public static function Init() { $aParams = array @@ -161,11 +200,15 @@ class ActionEmail extends ActionNotification MetaModel::Init_AddAttribute(new AttributeEnum("importance", array("allowed_values"=>new ValueSetEnum('low,normal,high'), "sql"=>"importance", "default_value"=>'normal', "is_null_allowed"=>false, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance', 'trigger_list')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('name', 'status', 'to', 'subject')); // Attributes to be displayed for a list + // - Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance', 'trigger_list')); + // - Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('name', 'status', 'to', 'subject')); // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('name','description', 'status', 'subject')); // Criteria of the std search form -// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + // - Criteria of the std search form + MetaModel::Init_SetZListItems('standard_search', array('name','description', 'status', 'subject')); + // - Criteria of the advanced search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); } // count the recipients found @@ -175,7 +218,18 @@ class ActionEmail extends ActionNotification // executed in the background, while making sure that any issue would be reported clearly protected $m_aMailErrors; //array of strings explaining the issue - // returns a the list of emails as a string, or a detailed error description + /** + * Return a the list of emails as a string, or a detailed error description + * + * @param string $sRecipAttCode + * @param array $aArgs + * + * @return string + * @throws \ArchivedObjectException + * @throws \CoreException + * @throws \CoreUnexpectedValue + * @throws \MySQLException + */ protected function FindRecipients($sRecipAttCode, $aArgs) { $sOQL = $this->Get($sRecipAttCode); @@ -224,9 +278,7 @@ class ActionEmail extends ActionNotification } /** - * @param \Trigger $oTrigger - * @param array $aContextArgs - * + * @inheritDoc * @throws \CoreException * @throws \CoreUnexpectedValue * @throws \CoreWarning @@ -306,6 +358,7 @@ class ActionEmail extends ActionNotification * * @return string * @throws \CoreException + * @throws \Exception */ protected function _DoExecute($oTrigger, $aContextArgs, &$oLog) { @@ -316,7 +369,7 @@ class ActionEmail extends ActionNotification $this->m_aMailErrors = array(); $bRes = false; // until we do succeed in sending the email - // Determine recicipients + // Determine recipients // $sTo = $this->FindRecipients('to', $aContextArgs); $sCC = $this->FindRecipients('cc', $aContextArgs); @@ -439,4 +492,3 @@ class ActionEmail extends ActionNotification } } } -?> diff --git a/core/asynctask.class.inc.php b/core/asynctask.class.inc.php index 73e33dd1d..0562b26b3 100644 --- a/core/asynctask.class.inc.php +++ b/core/asynctask.class.inc.php @@ -65,6 +65,10 @@ class ExecAsyncTask implements iBackgroundProcess */ abstract class AsyncTask extends DBObject { + /** + * @throws \CoreException + * @throws \Exception + */ public static function Init() { $aParams = array @@ -285,11 +289,13 @@ abstract class AsyncTask extends DBObject /** * Throws an exception (message and code) + * + * @return string */ abstract public function DoProcess(); /** - * Describes the error codes that DoProcess can return by the mean of exceptions + * Describes the error codes that DoProcess can return by the mean of exceptions */ static public function EnumErrorCodes() { @@ -352,6 +358,11 @@ class AsyncSendEmail extends AsyncTask $oNew->DBInsert(); } + /** + * @inheritDoc + * @throws \ArchivedObjectException + * @throws \CoreException + */ public function DoProcess() { $sMessage = $this->Get('message'); diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index e86e84e4f..ce105e2a8 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -156,7 +156,7 @@ abstract class AttributeDefinition */ public function IsSearchable() { - return static::SEARCH_WIDGET_TYPE != static::SEARCH_WIDGET_TYPE_RAW; + return $this->GetSearchType() != static::SEARCH_WIDGET_TYPE_RAW; } /** @var string */ @@ -6653,6 +6653,23 @@ class AttributeExternalKey extends AttributeDBFieldVoid return (int)$proposedValue; } + public function GetPrerequisiteAttributes($sClass = null) + { + $aAttributes = parent::GetPrerequisiteAttributes($sClass); + $oExpression = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression())->GetCriteria(); + foreach ($oExpression->GetParameters('this') as $sAttCode) + { + // Skip the id as it cannot change anyway + if ($sAttCode =='id') continue; + + if (!in_array($sAttCode, $aAttributes)) + { + $aAttributes[] = $sAttCode; + } + } + return $aAttributes; + } + public function GetMaximumComboLength() { return $this->GetOptional('max_combo_length', MetaModel::GetConfig()->Get('max_combo_length')); @@ -7015,6 +7032,15 @@ class AttributeExternalField extends AttributeDefinition return self::SEARCH_WIDGET_TYPE_RAW; } + function IsSearchable() + { + if ($this->IsFriendlyName()) + { + return true; + } + return parent::IsSearchable(); + } + public static function ListExpectedParams() { return array_merge(parent::ListExpectedParams(), array("extkey_attcode", "target_attcode")); @@ -7936,18 +7962,6 @@ class AttributeImage extends AttributeBlob return "Image"; } - /** - * {@inheritDoc} - * @see AttributeBlob::MakeRealValue() - */ - public function MakeRealValue($proposedValue, $oHostObj) - { - $oDoc = parent::MakeRealValue($proposedValue, $oHostObj); - - // The validation of the MIME Type is done by CheckFormat below - return $oDoc; - } - /** * Check that the supplied ormDocument actually contains an image * {@inheritDoc} @@ -9632,7 +9646,7 @@ class AttributePropertySet extends AttributeTable $sValue = '*****'; } $sRes .= "<TR>"; - $sCell = str_replace("\n", "<br>\n", Str::pure2html((string)$sValue)); + $sCell = str_replace("\n", "<br>\n", Str::pure2html(@(string)$sValue)); $sRes .= "<TD class=\"label\">$sProperty</TD><TD>$sCell</TD>"; $sRes .= "</TR>"; } @@ -11564,7 +11578,17 @@ class AttributeFriendlyName extends AttributeDefinition public function GetPrerequisiteAttributes($sClass = null) { - return $this->GetOptional("depends_on", array()); + // Code duplicated with AttributeObsolescenceFlag + $aAttributes = $this->GetOptional("depends_on", array()); + $oExpression = $this->GetOQLExpression(); + foreach ($oExpression->ListRequiredFields() as $sClass => $sAttCode) + { + if (!in_array($sAttCode, $aAttributes)) + { + $aAttributes[] = $sAttCode; + } + } + return $aAttributes; } public static function IsScalar() @@ -12796,7 +12820,17 @@ class AttributeObsolescenceFlag extends AttributeBoolean public function GetPrerequisiteAttributes($sClass = null) { - return $this->GetOptional("depends_on", array()); + // Code duplicated with AttributeFriendlyName + $aAttributes = $this->GetOptional("depends_on", array()); + $oExpression = $this->GetOQLExpression(); + foreach ($oExpression->ListRequiredFields() as $sClass => $sAttCode) + { + if (!in_array($sAttCode, $aAttributes)) + { + $aAttributes[] = $sAttCode; + } + } + return $aAttributes; } public function IsDirectField() diff --git a/core/backgroundprocess.inc.php b/core/backgroundprocess.inc.php index ba5c78891..fde1605c3 100644 --- a/core/backgroundprocess.inc.php +++ b/core/backgroundprocess.inc.php @@ -1,29 +1,23 @@ <?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/> - - /** - * interface iProcess - * Something that can be executed + * Copyright (C) 2010-2020 Combodo SARL * - * @copyright Copyright (C) 2010-2012 Combodo SARL - * @license http://opensource.org/licenses/AGPL-3.0 + * 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 */ + + interface iProcess { /** @@ -78,21 +72,27 @@ interface iScheduledProcess extends iProcess * * week_days * * time * - * Param names and some of their default values are in constant that can be overriden. + * Param names and some of their default values are in constant that can be overridden. * * Other info (module name and time default value) should be provided using a method that needs to be implemented. * - * @since 2.7.0 + * @since 2.7.0 PR #89 + * @since 2.7.0-2 N°2580 Fix {@link GetNextOccurrence} returning wrong value */ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess { - // param have default names/values but can be overriden + // param have default names/values but can be overridden const MODULE_SETTING_ENABLED = 'enabled'; const DEFAULT_MODULE_SETTING_ENABLED = true; const MODULE_SETTING_WEEKDAYS = 'week_days'; const DEFAULT_MODULE_SETTING_WEEKDAYS = 'monday, tuesday, wednesday, thursday, friday, saturday, sunday'; const MODULE_SETTING_TIME = 'time'; + /** + * @var Config can be used to mock config for tests + */ + protected $oConfig; + /** * Module must be declared in each implementation * @@ -106,6 +106,20 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess */ abstract protected function GetDefaultModuleSettingTime(); + /** + * @return \Config + */ + public function getOConfig() + { + if (!isset($this->oConfig)) + { + $this->oConfig = MetaModel::GetConfig(); + } + + return $this->oConfig; + } + + /** * Interpret current setting for the week days * @@ -124,7 +138,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess 'sunday' => 7, ); $aDays = array(); - $sWeekDays = MetaModel::GetConfig()->GetModuleSetting( + $sWeekDays = $this->getOConfig()->GetModuleSetting( $this->GetModuleName(), static::MODULE_SETTING_WEEKDAYS, static::DEFAULT_MODULE_SETTING_WEEKDAYS @@ -157,21 +171,26 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess } /** - * Gives the exact time at which the process must be run next time + * @param string $sCurrentTime Date string to extract time dependency + * this parameter is not present in the interface but as it is optional it's ok * - * @return DateTime - * @throws Exception + * @return DateTime the exact time at which the process must be run next time + * @throws \ProcessInvalidConfigException */ - public function GetNextOccurrence() + public function GetNextOccurrence($sCurrentTime = 'now') { - $bEnabled = MetaModel::GetConfig()->GetModuleSetting( + $bEnabled = $this->getOConfig()->GetModuleSetting( $this->GetModuleName(), static::MODULE_SETTING_ENABLED, static::DEFAULT_MODULE_SETTING_ENABLED ); + + $sItopTimeZone = $this->getOConfig()->Get('timezone'); + $timezone = new DateTimeZone($sItopTimeZone); + if (!$bEnabled) { - return new DateTime('3000-01-01'); + return new DateTime('3000-01-01', $timezone); } // 1st - Interpret the list of days as ordered numbers (monday = 1) @@ -180,7 +199,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess // 2nd - Find the next active week day // - $sProcessTime = MetaModel::GetConfig()->GetModuleSetting( + $sProcessTime = $this->getOConfig()->GetModuleSetting( $this->GetModuleName(), static::MODULE_SETTING_TIME, static::GetDefaultModuleSettingTime() @@ -189,9 +208,11 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess { throw new ProcessInvalidConfigException($this->GetModuleName().": wrong format for setting '".static::MODULE_SETTING_TIME."' (found '$sProcessTime')"); } - $oNow = new DateTime(); + + $oNow = new DateTime($sCurrentTime, $timezone); $iNextPos = false; - for ($iDay = $oNow->format('N'); $iDay <= 7; $iDay++) + $sDay = $oNow->format('N'); + for ($iDay = (int) $sDay; $iDay <= 7; $iDay++) { $iNextPos = array_search($iDay, $aDays, true); if ($iNextPos !== false) @@ -223,6 +244,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess $oRet->modify('+'.$iMove.' days'); } list($sHours, $sMinutes) = explode(':', $sProcessTime); + /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection non used new parameter in PHP 7.1 */ $oRet->setTime((int)$sHours, (int)$sMinutes); return $oRet; @@ -241,13 +263,14 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess /** * Exception for {@link iProcess} implementations.<br> * An error happened during the processing but we can go on with the next implementations. + * @since 2.5.0 N°1195 */ class ProcessException extends CoreException { } /** - * @since 2.7.0 + * @since 2.7.0 PR #89 */ class ProcessInvalidConfigException extends ProcessException { @@ -257,6 +280,7 @@ class ProcessInvalidConfigException extends ProcessException * Class ProcessFatalException * Exception for iProcess implementations.<br> * A big error occurred, we have to stop the iProcess processing. + * @since 2.5.0 N°1195 */ class ProcessFatalException extends CoreException { diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php index b222707e5..b1b015516 100644 --- a/core/cmdbchangeop.class.inc.php +++ b/core/cmdbchangeop.class.inc.php @@ -544,7 +544,7 @@ class CMDBChangeOpSetAttributeEncrypted extends CMDBChangeOpSetAttribute // The attribute was renamed or removed from the object ? $sAttName = $this->Get('attcode'); } - $sPrevString = $this->Get('prevstring'); + $sPrevString = $this->GetAsHTML('prevstring'); $sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sPrevString); } return $sResult; diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php index 76525316f..dbdab2d58 100644 --- a/core/cmdbobject.class.inc.php +++ b/core/cmdbobject.class.inc.php @@ -513,83 +513,6 @@ abstract class CMDBObject extends DBObject } } - /** - * @deprecated 2.7.0 N°2361 simply use {@link DBInsert} instead, that will automatically create and persist a CMDBChange object. - * If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before. - * - * @param \CMDBChange $oChange - * @param null $bSkipStrongSecurity - * - * @return int|null - * @throws \ArchivedObjectException - * @throws \CoreCannotSaveObjectException - * @throws \CoreException - * @throws \CoreUnexpectedValue - * @throws \CoreWarning - * @throws \MySQLException - * @throws \OQLException - * @throws \SecurityException - */ - public function DBInsertTracked(CMDBChange $oChange, $bSkipStrongSecurity = null) - { - self::SetCurrentChange($oChange); - $this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY); - $ret = $this->DBInsertTracked_Internal(); - return $ret; - } - - /** - * @deprecated 2.7.0 N°2361 simply use {@link DBInsertNoReload} instead, that will automatically create and persist a CMDBChange object. - * If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before. - * - * @param \CMDBChange $oChange - * @param null $bSkipStrongSecurity - * - * @return int - * @throws \ArchivedObjectException - * @throws \CoreCannotSaveObjectException - * @throws \CoreException - * @throws \CoreUnexpectedValue - * @throws \CoreWarning - * @throws \MySQLException - * @throws \OQLException - * @throws \SecurityException - */ - public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null) - { - self::SetCurrentChange($oChange); - $this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY); - $ret = $this->DBInsertTracked_Internal(true); - return $ret; - } - - /** - * @deprecated 2.7.0 N°2361 simply use {@link DBInsert} or {@link DBInsertNoReload} instead - * - * @param bool $bDoNotReload - * - * @return integer Identifier of the created object - * @throws \ArchivedObjectException - * @throws \CoreCannotSaveObjectException - * @throws \CoreException - * @throws \CoreUnexpectedValue - * @throws \CoreWarning - * @throws \MySQLException - * @throws \OQLException - */ - protected function DBInsertTracked_Internal($bDoNotReload = false) - { - if ($bDoNotReload) - { - $ret = $this->DBInsertNoReload(); - } - else - { - $ret = $this->DBInsert(); - } - return $ret; - } - public function DBClone($newKey = null) { return $this->DBCloneTracked_Internal(); @@ -622,24 +545,6 @@ abstract class CMDBObject extends DBObject return $ret; } - /** - * @deprecated 2.7.0 N°2361 simply use {@link DBUpdate} instead, that will automatically create and persist a CMDBChange object. - * If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before. - * - * @param \CMDBChange $oChange - * @param null $bSkipStrongSecurity - * - * @return int|void - * @throws \CoreCannotSaveObjectException - * @throws \CoreException - * @throws \SecurityException - */ - public function DBUpdateTracked(CMDBChange $oChange, $bSkipStrongSecurity = null) - { - self::SetCurrentChange($oChange); - $this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY); - $this->DBUpdate(); - } /** * @param null $oDeletionPlan @@ -659,31 +564,6 @@ abstract class CMDBObject extends DBObject return $this->DBDeleteTracked_Internal($oDeletionPlan); } - /** - * @deprecated 2.7.0 N°2361 simply use {@link DBDelete} instead, that will automatically create and persist a CMDBChange object. - * If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before. - * - * @param \CMDBChange $oChange - * @param null $bSkipStrongSecurity - * @param null $oDeletionPlan - * - * @throws \ArchivedObjectException - * @throws \CoreCannotSaveObjectException - * @throws \CoreException - * @throws \CoreUnexpectedValue - * @throws \DeleteException - * @throws \MySQLException - * @throws \MySQLHasGoneAwayException - * @throws \OQLException - * @throws \SecurityException - */ - public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null) - { - self::SetCurrentChange($oChange); - $this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_DELETE); - $this->DBDeleteTracked_Internal($oDeletionPlan); - } - /** * @param null $oDeletionPlan * diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index 632e9caae..2ccd70d2e 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -119,7 +119,18 @@ class CMDBSource const ENUM_DB_VENDOR_MYSQL = 'MySQL'; const ENUM_DB_VENDOR_MARIADB = 'MariaDB'; const ENUM_DB_VENDOR_PERCONA = 'Percona'; - + + /** + * Error: 1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT) + * Message: Lock wait timeout exceeded; try restarting transaction + */ + const MYSQL_ERRNO_WAIT_TIMEOUT = 1205; + /** + * Error: 1213 SQLSTATE: 40001 (ER_LOCK_DEADLOCK) + * Message: Deadlock found when trying to get lock; try restarting transaction + */ + const MYSQL_ERRNO_DEADLOCK = 1213; + protected static $m_sDBHost; protected static $m_sDBUser; protected static $m_sDBPwd; @@ -661,6 +672,7 @@ class CMDBSource } catch (mysqli_sql_exception $e) { + self::LogDeadLock($e); throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e)); } $oKPI->ComputeStats('Query exec (mySQL)', $sSql); @@ -674,13 +686,55 @@ class CMDBSource { throw new MySQLHasGoneAwayException(self::GetError(), $aContext); } - - throw new MySQLException('Failed to issue SQL query', $aContext); + $e = new MySQLException('Failed to issue SQL query', $aContext); + self::LogDeadLock($e); + throw $e; } return $oResult; } + /** + * @param \Exception $e + * + * @since 2.7.1 + */ + private static function LogDeadLock(Exception $e) + { + // checks MySQL error code + $iMySqlErrorNo = self::$m_oMysqli->errno; + if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) + { + return; + } + + // Get error info + $sUser = UserRights::GetUser(); + $oError = self::$m_oMysqli->query('SHOW ENGINE INNODB STATUS'); + if ($oError !== false) + { + $aData = $oError->fetch_all(MYSQLI_ASSOC); + $sInnodbStatus = $aData[0]; + } + else + { + $sInnodbStatus = 'Get status query cannot execute'; + } + + // log ! + $sMessage = "deadlock detected: user= $sUser; errno=$iMySqlErrorNo"; + $aLogContext = array( + 'userinfo' => $sUser, + 'errno' => $iMySqlErrorNo, + 'ex_msg' => $e->getMessage(), + 'callstack' => $e->getTraceAsString(), + 'data' => $sInnodbStatus, + ); + DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext); + + IssueLog::Error($sMessage, 'DeadLock', $e->getMessage()); + } + /** * If nested transaction, we are not starting a new one : only one global transaction will exist. * @@ -805,25 +859,6 @@ class CMDBSource self::$m_iTransactionLevel = 0; } - /** - * - * @deprecated 2.7.0 N°1627 use ItopCounter instead - * - * @param string $sTable - * - * @return int - * @throws \MySQLException - * @throws \MySQLHasGoneAwayException - */ - public static function GetNextInsertId($sTable) - { - $sSQL = "SHOW TABLE STATUS LIKE '$sTable'"; - $oResult = self::Query($sSQL); - $aRow = $oResult->fetch_assoc(); - - return $aRow['Auto_increment']; - } - public static function GetInsertId() { $iRes = self::$m_oMysqli->insert_id; @@ -1194,9 +1229,9 @@ class CMDBSource */ private static function GetFieldDataTypeAndOptions($sCompleteFieldType) { - preg_match('/^([a-zA-Z]+)(\(([^\)]+)\))?( .+)?$/', $sCompleteFieldType, $aMatches); + preg_match('/^([a-zA-Z]+)(\(([^\)]+)\))?( .+)?/', $sCompleteFieldType, $aMatches); - $sDataType = $aMatches[1]; + $sDataType = isset($aMatches[1]) ? $aMatches[1] : ''; $sTypeOptions = isset($aMatches[2]) ? $aMatches[3] : ''; $sOtherOptions = isset($aMatches[4]) ? $aMatches[4] : ''; diff --git a/core/config.class.inc.php b/core/config.class.inc.php index f6677e43e..c007c5548 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -22,7 +22,7 @@ define('ITOP_APPLICATION', 'iTop'); define('ITOP_APPLICATION_SHORT', 'iTop'); -define('ITOP_VERSION', '2.7.0-dev'); +define('ITOP_VERSION', '2.8.0-dev'); define('ITOP_REVISION', 'svn'); define('ITOP_BUILD_DATE', '$WCNOW$'); define('ITOP_VERSION_FULL', ITOP_VERSION.'-'.ITOP_REVISION); diff --git a/core/dbobject.class.php b/core/dbobject.class.php index f7fb06af6..6b1745a12 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -569,43 +569,41 @@ abstract class DBObject implements iDisplay $this->Reload(); } - if ($oAttDef->IsExternalKey()) + if ($oAttDef->IsExternalKey() && is_object($value)) { - if (is_object($value)) + // Setting an external key with a whole object (instead of just an ID) + // let's initialize also the external fields that depend on it + // (useful when building objects in memory and not from a query) + /** @var \AttributeExternalKey $oAttDef */ + if ((get_class($value) != $oAttDef->GetTargetClass()) && (!is_subclass_of($value, $oAttDef->GetTargetClass()))) { - // Setting an external key with a whole object (instead of just an ID) - // let's initialize also the external fields that depend on it - // (useful when building objects in memory and not from a query) - /** @var \AttributeExternalKey $oAttDef */ - if ( (get_class($value) != $oAttDef->GetTargetClass()) && (!is_subclass_of($value, $oAttDef->GetTargetClass()))) - { - throw new CoreUnexpectedValue("Trying to set the value of '$sAttCode', to an object of class '".get_class($value)."', whereas it's an ExtKey to '".$oAttDef->GetTargetClass()."'. Ignored"); - } + throw new CoreUnexpectedValue("Trying to set the value of '$sAttCode', to an object of class '".get_class($value)."', whereas it's an ExtKey to '".$oAttDef->GetTargetClass()."'. Ignored"); + } - foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef) + foreach (MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef) + { + /** @var \AttributeExternalField $oDef */ + if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode)) { - /** @var \AttributeExternalField $oDef */ - if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode)) - { - /** @var \DBObject $value */ - $this->m_aCurrValues[$sCode] = $value->Get($oDef->GetExtAttCode()); - $this->m_aLoadedAtt[$sCode] = true; - } + /** @var \DBObject $value */ + $this->m_aCurrValues[$sCode] = $value->Get($oDef->GetExtAttCode()); + $this->m_aLoadedAtt[$sCode] = true; + } + elseif (in_array($sAttCode, $oDef->GetPrerequisiteAttributes(get_class($this)))) + { + $this->m_aCurrValues[$sCode] = $this->GetDefaultValue($sCode); + unset($this->m_aLoadedAtt[$sCode]); } } - else if ($this->m_aCurrValues[$sAttCode] != $value) + } + else if ($this->m_aCurrValues[$sAttCode] !== $value) + { + // Invalidate dependent fields so that they get reloaded in case they are needed (See Get()) + // + foreach (MetaModel::GetDependentAttributes(get_class($this), $sAttCode) as $sCode) { - // Setting an external key, but no any other information is available... - // Invalidate the corresponding fields so that they get reloaded in case they are needed (See Get()) - foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef) - { - /** @var \AttributeExternalKey $oDef */ - if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode)) - { - $this->m_aCurrValues[$sCode] = $this->GetDefaultValue($sCode); - unset($this->m_aLoadedAtt[$sCode]); - } - } + $this->m_aCurrValues[$sCode] = $this->GetDefaultValue($sCode); + unset($this->m_aLoadedAtt[$sCode]); } } if ($oAttDef->IsLinkSet() && ($value != null)) @@ -787,20 +785,20 @@ abstract class DBObject implements iDisplay { // Standard case... we have the information directly } - elseif ($this->m_bIsInDB && !$this->m_bDirty) + elseif ($this->m_bIsInDB && !$this->m_bFullyLoaded && !$this->m_bDirty) { // Lazy load (polymorphism): complete by reloading the entire object - // #@# non-scalar attributes.... handle that differently? $oKPI = new ExecutionKPI(); $this->Reload(); $oKPI->ComputeStats('Reload', get_class($this).'/'.$sAttCode); } - elseif ($sAttCode == 'friendlyname') + elseif ($oAttDef->IsBasedOnOQLExpression()) { - // The friendly name is not computed and the object is dirty - // Todo: implement the computation of the friendly name based on sprintf() - // - $this->m_aCurrValues[$sAttCode] = ''; + // Recompute -which is likely to call Get() + // + /** @var AttributeFriendlyName|\AttributeObsolescenceFlag $oAttDef */ + $this->m_aCurrValues[$sAttCode] = $this->EvaluateExpression($oAttDef->GetOQLExpression()); + $this->m_aLoadedAtt[$sAttCode] = true; } else { @@ -2947,46 +2945,6 @@ abstract class DBObject implements iDisplay return $this->m_iKey; } - /** - * @internal - * - * @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBInsert()} instead, that will automatically create and persist a CMDBChange object. - * If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before. - * - * @param CMDBChange $oChange - * - * @return int|null - * @throws CoreException - */ - public function DBInsertTracked(CMDBChange $oChange) - { - CMDBObject::SetCurrentChange($oChange); - return $this->DBInsert(); - } - - /** - * @internal - * - * @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBInsertNoReload()} instead, that will automatically create and persist a CMDBChange object. - * If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before. - * - * @param CMDBChange $oChange - * - * @return int - * @throws ArchivedObjectException - * @throws CoreCannotSaveObjectException - * @throws CoreException - * @throws CoreUnexpectedValue - * @throws CoreWarning - * @throws MySQLException - * @throws OQLException - */ - public function DBInsertTrackedNoReload(CMDBChange $oChange) - { - CMDBObject::SetCurrentChange($oChange); - return $this->DBInsertNoReload(); - } - /** * Creates a copy of the current object into the database * @@ -3338,25 +3296,6 @@ abstract class DBObject implements iDisplay $this->m_aPreviousValuesForUpdatedAttributes = $aPreviousValuesForUpdatedAttributes; } - /** - * - * @internal - * - * @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBUpdate()} instead, that will automatically create and persist a CMDBChange object. - * If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before. - * - * @param CMDBChange $oChange - * - * @return int - * @throws CoreCannotSaveObjectException - * @throws CoreException - */ - public function DBUpdateTracked(CMDBChange $oChange) - { - CMDBObject::SetCurrentChange($oChange); - return $this->DBUpdate(); - } - /** * Make the current changes persistent - clever wrapper for Insert or Update * @@ -3611,32 +3550,7 @@ abstract class DBObject implements iDisplay return $oDeletionPlan; } - /** - * @internal - * - * @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBDelete()} instead. - * If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before. - * - * @param CMDBChange $oChange - * @param boolean $bSkipStrongSecurity - * @param \DeletionPlan $oDeletionPlan - * - * @throws ArchivedObjectException - * @throws CoreCannotSaveObjectException - * @throws CoreException - * @throws CoreUnexpectedValue - * @throws DeleteException - * @throws MySQLException - * @throws MySQLHasGoneAwayException - * @throws OQLException - */ - public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null) - { - CMDBObject::SetCurrentChange($oChange); - $this->DBDelete($oDeletionPlan); - } - - /** + /** * @internal * * @return array @@ -3695,21 +3609,38 @@ abstract class DBObject implements iDisplay */ public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false) { - $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this)); + $sClass = get_class($this); + $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); if (empty($sStateAttCode)) { - throw new CoreException('No lifecycle for the class '.get_class($this)); + throw new CoreException('No lifecycle for the class '.$sClass); } - MyHelpers::CheckKeyInArray('object lifecycle stimulus', $sStimulusCode, MetaModel::EnumStimuli(get_class($this))); + MyHelpers::CheckKeyInArray('object lifecycle stimulus', $sStimulusCode, MetaModel::EnumStimuli($sClass)); $aStateTransitions = $this->EnumTransitions(); if (!array_key_exists($sStimulusCode, $aStateTransitions)) { - // This simulus has no effect in the current state... do nothing - IssueLog::Error(get_class($this).": Transition $sStimulusCode is not allowed in ".$this->Get($sStateAttCode)); + // This stimulus has no effect in the current state... do nothing + IssueLog::Error("$sClass: Transition $sStimulusCode is not allowed in ".$this->Get($sStateAttCode)); return false; } + + // save current object values in case of an action failure (in memory rollback) + $aBackupValues = array(); + foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + { + $value = $this->m_aCurrValues[$sAttCode]; + if (is_object($value)) + { + $aBackupValues[$sAttCode] = clone $value; + } + else + { + $aBackupValues[$sAttCode] = $value; + } + } + $aTransitionDef = $aStateTransitions[$sStimulusCode]; // Change the state before proceeding to the actions, this is necessary because an action might @@ -3728,11 +3659,11 @@ abstract class DBObject implements iDisplay { // Old (pre-2.1.0 modules) action definition without any parameter $aActionCallSpec = array($this, $actionHandler); - $sActionDesc = get_class($this).'::'.$actionHandler; + $sActionDesc = $sClass.'::'.$actionHandler; if (!is_callable($aActionCallSpec)) { - throw new CoreException("Unable to call action: ".get_class($this)."::$actionHandler"); + throw new CoreException("Unable to call action: $sClass::$actionHandler"); } $bRet = call_user_func($aActionCallSpec, $sStimulusCode); } @@ -3740,7 +3671,7 @@ abstract class DBObject implements iDisplay { // New syntax: 'verb' and typed parameters $sAction = $actionHandler['verb']; - $sActionDesc = get_class($this).'::'.$sAction; + $sActionDesc = "$sClass::$sAction"; $aParams = array(); foreach($actionHandler['params'] as $aDefinition) { @@ -3776,14 +3707,12 @@ abstract class DBObject implements iDisplay // (in case there is no returned value, null is obtained and means "ok") if ($bRet === false) { - IssueLog::Info("Lifecycle action $sActionDesc returned false on object #".$this->GetKey()); + IssueLog::Info("Lifecycle action $sActionDesc returned false on object #$sClass:".$this->GetKey()); $bSuccess = false; } } if ($bSuccess) { - $sClass = get_class($this); - // Stop watches foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { @@ -3826,6 +3755,14 @@ abstract class DBObject implements iDisplay $oTrigger->DoActivate($this->ToArgs('this')); } } + else + { + // At least one action failed, rollback the object value to its previous value + foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + { + $this->m_aCurrValues[$sAttCode] = $aBackupValues[$sAttCode]; + } + } return $bSuccess; } @@ -4397,22 +4334,7 @@ abstract class DBObject implements iDisplay } - /** - * implement relations - * - * Return an empty set for the parent of all - * - * this way of implementing the relations suffers limitations (not handling the redundancy) - * and you should consider defining those things in XML - * - * @internal - * @deprecated - */ - public static function GetRelationQueries($sRelCode) - { - return array(); - } - + /** * Reserved: do not overload * @@ -4423,72 +4345,6 @@ abstract class DBObject implements iDisplay return array(); } - /** - * Use GetRelatedObjectsDown/Up instead to take redundancy into account - * - * @internal - * @deprecated - */ - public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array()) - { - // Temporary patch: until the impact analysis GUI gets rewritten, - // let's consider that "depends on" is equivalent to "impacts/up" - // The current patch has been implemented in DBObject and MetaModel - $sHackedRelCode = $sRelCode; - $bDown = true; - if ($sRelCode == 'depends on') - { - $sHackedRelCode = 'impacts'; - $bDown = false; - } - foreach (MetaModel::EnumRelationQueries(get_class($this), $sHackedRelCode, $bDown) as $sDummy => $aQueryInfo) - { - $sQuery = $bDown ? $aQueryInfo['sQueryDown'] : $aQueryInfo['sQueryUp']; - //$bPropagate = $aQueryInfo["bPropagate"]; - //$iDepth = $bPropagate ? $iMaxDepth - 1 : 0; - $iDepth = $iMaxDepth - 1; - - // Note: the loop over the result set has been written in an unusual way for error reporting purposes - // In the case of a wrong query parameter name, the error occurs on the first call to Fetch, - // thus we need to have this first call into the try/catch, but - // we do NOT want to nest the try/catch for the error message to be clear - try - { - $oFlt = DBObjectSearch::FromOQL($sQuery); - $oObjSet = new DBObjectSet($oFlt, array(), $this->ToArgsForQuery()); - $oObj = $oObjSet->Fetch(); - } - catch (Exception $e) - { - $sClassOfDefinition = $aQueryInfo['_legacy_'] ? get_class($this).'(or a parent)::GetRelationQueries()' : $aQueryInfo['sDefinedInClass']; - throw new Exception("Wrong query for the relation $sRelCode/$sClassOfDefinition/{$aQueryInfo['sNeighbour']}: ".$e->getMessage()); - } - if ($oObj) - { - do - { - $sRootClass = MetaModel::GetRootClass(get_class($oObj)); - $sObjKey = $oObj->GetKey(); - if (array_key_exists($sRootClass, $aResults)) - { - if (array_key_exists($sObjKey, $aResults[$sRootClass])) - { - continue; // already visited, skip - } - } - - $aResults[$sRootClass][$sObjKey] = $oObj; - if ($iDepth > 0) - { - $oObj->GetRelatedObjects($sRelCode, $iDepth, $aResults); - } - } - while ($oObj = $oObjSet->Fetch()); - } - } - return $aResults; - } - /** * Compute the "RelatedObjects" (forward or "down" direction) for the object * for the specified relation @@ -5234,10 +5090,6 @@ abstract class DBObject implements iDisplay throw new Exception('Missing argument #1: stimulus'); } $sStimulus = $aParams[0]; - if (!in_array($sStimulus, MetaModel::EnumStimuli(get_class($this)))) - { - throw new Exception("Unknown stimulus ".get_class($this)."::".$sStimulus); - } $this->ApplyStimulus($sStimulus); break; @@ -5504,5 +5356,33 @@ abstract class DBObject implements iDisplay break; } } + + public function EvaluateExpression(Expression $oExpression) + { + $aFields = $oExpression->ListRequiredFields(); + $aArgs = array(); + foreach ($aFields as $sFieldDesc) + { + $aFieldParts = explode('.', $sFieldDesc); + if (count($aFieldParts) == 2) + { + $sClass = $aFieldParts[0]; + $sAttCode = $aFieldParts[1]; + } + else + { + $sClass = get_class($this); + $sAttCode = $aFieldParts[0]; + } + if (get_class($this) != $sClass) continue; + if (!MetaModel::IsValidAttCode(get_class($this), $sAttCode)) continue; + + $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode); + $aSQLValues = $oAttDef->GetSQLValues($this->m_aCurrValues[$sAttCode]); + $value = reset($aSQLValues); + $aArgs[$sFieldDesc] = $value; + } + return $oExpression->Evaluate($aArgs); + } } diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index bff609535..ed54e4f5a 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -223,9 +223,9 @@ class DBObjectSearch extends DBSearch public function RenameAlias($sOldName, $sNewName) { $bFound = false; - if (array_key_exists($sOldName, $this->m_aClasses)) + if (!array_key_exists($sOldName, $this->m_aClasses)) { - $bFound = true; + return false; } if (array_key_exists($sNewName, $this->m_aClasses)) { @@ -313,6 +313,11 @@ class DBObjectSearch extends DBSearch return true; } + /** + * Move conditions from $oFilter to $this + * @param \DBSearch $oFilter + * @param $aTranslation + */ protected function TransferConditionExpression($oFilter, $aTranslation) { // Prevent collisions in the parameter names by renaming them if needed @@ -335,6 +340,7 @@ class DBObjectSearch extends DBSearch $oTranslated = $oFilter->GetCriteria()->Translate($aTranslation, false, false /* leave unresolved fields */); $this->AddConditionExpression($oTranslated); $this->m_aParams = array_merge($this->m_aParams, $oFilter->m_aParams); + $oFilter->ResetCondition(); } public function RenameParam($sOldName, $sNewName) @@ -522,6 +528,8 @@ class DBObjectSearch extends DBSearch } /** + * Helper method for IN / NOT IN conditions : values won't be parsed in the expression tree, that will save some time ! + * * @param string $sFilterCode attribute code to use * @param array $aValues * @param bool $bPositiveMatch if true will add a IN filter, else a NOT IN @@ -632,7 +640,10 @@ class DBObjectSearch extends DBSearch $oNewCond = new BinaryExpression($oTextFields, 'LIKE', $oFlexNeedle); $this->AddConditionExpression($oNewCond); - $this->m_aParams[$sQueryParam] = $sNeedle; + //replace in order to search the character "_" ("_" in mysql is like "%" for only one character). + $sFullText = str_replace('_', '\_', $sNeedle); + + $this->m_aParams[$sQueryParam] = $sFullText; } protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation, $bTranslateMainAlias = true) @@ -848,10 +859,14 @@ class DBObjectSearch extends DBSearch } /** - * @param DBObjectSearch $oFilter - * @param $sExtKeyAttCode + * Add a link to another filter, using an extkey already present in current filter + * + * @param DBObjectSearch $oFilter filter to join to + * @param string $sExtKeyAttCode extkey present in current filter, that allows to points to $oFilter * @param int $iOperatorCode - * @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed + * @param array $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed. + * Doesn't change existing alias, use {@link \DBObjectSearch::RenameAlias()} for that. + * * @throws CoreException * @throws CoreWarning */ @@ -1036,7 +1051,7 @@ class DBObjectSearch extends DBSearch public function Filter($sClassAlias, DBSearch $oFilter) { // If the conditions are the correct ones for Intersect - if (($this->GetFirstJoinedClass() == $oFilter->GetFirstJoinedClass())) + if (MetaModel::IsParentClass($oFilter->GetFirstJoinedClass(),$this->GetFirstJoinedClass())) { return $this->Intersect($oFilter); } @@ -1068,7 +1083,6 @@ class DBObjectSearch extends DBSearch { if (($oSearch->GetFirstJoinedClassAlias() == $sClassAlias)) { - $oSearch->ResetCondition(); $oSearch = $oSearch->IntersectSubClass($oFilter, $aRootClasses); return $oSearch->GetCriteria(); } @@ -1314,7 +1328,7 @@ class DBObjectSearch extends DBSearch // Make the list of acceptable arguments... could be factorized with run_query, into oSearch->GetQueryParams($bExclude magic params) $aNakedMagicArguments = array(); - foreach (MetaModel::PrepareQueryArguments(array()) as $sArgName => $value) + foreach (MetaModel::PrepareQueryArguments(array(),array(), $this->GetExpectedArguments()) as $sArgName => $value) { $iPos = strpos($sArgName, '->object()'); if ($iPos === false) @@ -1377,7 +1391,7 @@ class DBObjectSearch extends DBSearch { $aParams = array_merge($aContextParams, $this->m_aParams); } - $aParams = MetaModel::PrepareQueryArguments($aParams); + $aParams = MetaModel::PrepareQueryArguments($aParams,array(), $this->GetExpectedArguments()); } else { @@ -1570,7 +1584,7 @@ class DBObjectSearch extends DBSearch $aRet = array('selects' => array(), 'joins' => array(), 'where' => array()); $aParams = array_merge($this->m_aParams); - $aParams = MetaModel::PrepareQueryArguments($aParams); + $aParams = MetaModel::PrepareQueryArguments($aParams, array(), $this->GetExpectedArguments()); foreach ($this->m_aSelectedClasses as $sAlias => $sClass) { @@ -1777,7 +1791,7 @@ class DBObjectSearch extends DBSearch { $oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($this); $oSQLQuery = $oSQLObjectQueryBuilder->MakeSQLObjectDeleteQuery(); - $aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams()); + $aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams(), $this->GetExpectedArguments()); $sRet = $oSQLQuery->RenderDelete($aScalarArgs); return $sRet; } @@ -1793,7 +1807,7 @@ class DBObjectSearch extends DBSearch { $oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($this); $oSQLQuery = $oSQLObjectQueryBuilder->MakeSQLObjectUpdateQuery($aValues); - $aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams()); + $aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams(), $this->GetExpectedArguments()); $sRet = $oSQLQuery->RenderUpdate($aScalarArgs); return $sRet; } @@ -1812,7 +1826,7 @@ class DBObjectSearch extends DBSearch { $oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($this); $oSQLQuery = $oSQLObjectQueryBuilder->MakeSQLObjectUpdateQuery($aValues); - $aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams()); + $aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams(), $this->GetExpectedArguments()); $sRet = $oSQLQuery->RenderInsert($aScalarArgs); return $sRet; } @@ -2087,4 +2101,9 @@ class DBObjectSearch extends DBSearch } return $oExpression; } + + public function ListParameters() + { + return $this->GetCriteria()->ListParameters(); + } } diff --git a/core/dbobjectset.class.php b/core/dbobjectset.class.php index fec4798c7..7bd2e3484 100644 --- a/core/dbobjectset.class.php +++ b/core/dbobjectset.class.php @@ -424,7 +424,7 @@ class DBObjectSet implements iDBObjectSetIterator * * @api * - * @param bool $bWithId + * @param bool $bWithId if true array key will be set to object id * * @return DBObject[] * @@ -1312,34 +1312,6 @@ class DBObjectSet implements iDBObjectSetIterator return $oNewSet; } - /** - * Will be deprecated soon - use MetaModel::GetRelatedObjectsDown/Up instead to take redundancy into account - * - * @throws \Exception - */ - public function GetRelatedObjects($sRelCode, $iMaxDepth = 99) - { - $aRelatedObjs = array(); - - $aVisited = array(); // optimization for consecutive calls of MetaModel::GetRelatedObjects - $this->Seek(0); - while ($oObject = $this->Fetch()) - { - $aMore = $oObject->GetRelatedObjects($sRelCode, $iMaxDepth, $aVisited); - foreach ($aMore as $sClass => $aRelated) - { - foreach ($aRelated as $iObj => $oObj) - { - if (!isset($aRelatedObjs[$sClass][$iObj])) - { - $aRelatedObjs[$sClass][$iObj] = $oObj; - } - } - } - } - return $aRelatedObjs; - } - /** * Compute the "RelatedObjects" (forward or "down" direction) for the set * for the specified relation @@ -1496,7 +1468,7 @@ class DBObjectSet implements iDBObjectSetIterator public function ListConstantFields() { // The complete list of arguments will include magic arguments (e.g. current_user->attcode) - $aScalarArgs = MetaModel::PrepareQueryArguments($this->m_oFilter->GetInternalParams(), $this->m_aArgs); + $aScalarArgs = MetaModel::PrepareQueryArguments($this->m_oFilter->GetInternalParams(), $this->m_aArgs, $this->m_oFilter->ListParameters()); $aConst = $this->m_oFilter->ListConstantFields(); foreach($aConst as $sClassAlias => $aVals) @@ -1515,7 +1487,7 @@ class DBObjectSet implements iDBObjectSetIterator public function ApplyParameters() { - $aAllArgs = MetaModel::PrepareQueryArguments($this->m_oFilter->GetInternalParams(), $this->m_aArgs); + $aAllArgs = MetaModel::PrepareQueryArguments($this->m_oFilter->GetInternalParams(), $this->m_aArgs, $this->m_oFilter->GetExpectedArguments()); $this->m_oFilter->ApplyParameters($aAllArgs); } } diff --git a/core/dbsearch.class.php b/core/dbsearch.class.php index 0fe45b240..c4e9d6a12 100644 --- a/core/dbsearch.class.php +++ b/core/dbsearch.class.php @@ -507,6 +507,7 @@ abstract class DBSearch } else { + /** @var \DBObjectSearch $oFilter */ if ($iDirection === static::JOIN_POINTING_TO) { $oSourceFilter->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap); @@ -979,7 +980,7 @@ abstract class DBSearch $aAttToLoad = array(); $oSQLQuery = $oQueryFilter->GetSQLQuery(array(), $aArgs, $aAttToLoad, null, 0, 0, false, $aGroupByExpr, $aSelectExpr); - $aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams()); + $aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams(), $this->GetExpectedArguments()); try { $bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries; @@ -997,6 +998,10 @@ abstract class DBSearch return $sRes; } + function GetExpectedArguments() + { + return $this->GetCriteria()->ListParameters(); + } /** * Generate a SQL query from the current search @@ -1076,7 +1081,7 @@ abstract class DBSearch else { // The complete list of arguments will include magic arguments (e.g. current_user->attcode) - $aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams()); + $aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams(), $this->GetExpectedArguments()); } try { @@ -1174,8 +1179,7 @@ abstract class DBSearch if (is_object($oVisibleObjects)) { $oVisibleObjects->AllowAllData(); - $oSearch = $this->Filter($sClassAlias, $oVisibleObjects); - /** @var DBSearch $oSearch */ + $oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects); $oSearch->SetDataFiltered(); } } @@ -1226,6 +1230,8 @@ abstract class DBSearch */ public abstract function GetCriteria(); + public abstract function ListParameters(); + /** * Shortcut to add efficient IN condition * diff --git a/core/dbunionsearch.class.php b/core/dbunionsearch.class.php index ff5abe83e..3d659bbf8 100644 --- a/core/dbunionsearch.class.php +++ b/core/dbunionsearch.class.php @@ -667,6 +667,16 @@ class DBUnionSearch extends DBSearch return $oSQLQuery; } + function GetExpectedArguments() + { + $aVariableCriteria = array(); + foreach ($this->aSearches as $oSearch) + { + $aVariableCriteria = array_merge($aVariableCriteria, $oSearch->GetExpectedArguments()); + } + + return $aVariableCriteria; + } /** * @return \Expression */ @@ -718,4 +728,14 @@ class DBUnionSearch extends DBSearch $oSearch->AddConditionExpression($oInCondition); } } + + public function ListParameters() + { + $aParameters = array(); + foreach ($this->aSearches as $oSearch) + { + $aParameters = array_merge($aParameters, $oSearch->ListParameters()); + } + return $aParameters; + } } diff --git a/core/event.class.inc.php b/core/event.class.inc.php index 8e5cfd814..ea052c131 100644 --- a/core/event.class.inc.php +++ b/core/event.class.inc.php @@ -215,7 +215,7 @@ class EventIssue extends Event MetaModel::Init_AddAttribute(new AttributePropertySet("data", array("allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'issue', 'impact', 'page', 'arguments_post', 'arguments_get', 'callstack', 'data')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'issue', 'impact', 'page', 'arguments_post', 'arguments_get', 'callstack', 'data')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'issue', 'impact')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form diff --git a/core/excelbulkexport.class.inc.php b/core/excelbulkexport.class.inc.php index f19de3f0a..6717adfbb 100644 --- a/core/excelbulkexport.class.inc.php +++ b/core/excelbulkexport.class.inc.php @@ -216,7 +216,14 @@ EOF } else if (array_key_exists('formatted_text', $this->aStatusInfo) && $this->aStatusInfo['formatted_text']) { - $sRet = $oAttDef->GetEditValue($value, $oObj); + if ($oAttDef instanceof AttributeText && $oAttDef->GetFormat()=='html') + { + $sRet = str_replace(">", ">", $value); + } + else + { + $sRet = $oAttDef->GetEditValue($value, $oObj); + } } else { diff --git a/core/expressioncache.class.inc.php b/core/expressioncache.class.inc.php index e81a61bd3..ca96db358 100644 --- a/core/expressioncache.class.inc.php +++ b/core/expressioncache.class.inc.php @@ -118,7 +118,7 @@ EOF; { $sKey = static::GetKey($sClass, $sAttCode); $oExpr = DBObjectSearch::GetPolymorphicExpression($sClass, $sAttCode); - return "'".$sKey."' => '".serialize($oExpr)."',\n"; + return var_export($sKey, true)." => ".var_export(serialize($oExpr), true).",\n"; // Beware, string values can contain quotes, backslashes, etc! } /** diff --git a/core/htmlsanitizer.class.inc.php b/core/htmlsanitizer.class.inc.php index b2a7aa4c7..dbda6e59a 100644 --- a/core/htmlsanitizer.class.inc.php +++ b/core/htmlsanitizer.class.inc.php @@ -24,14 +24,14 @@ abstract class HTMLSanitizer { // Do nothing.. } - + /** * Sanitizes the given HTML document * @param string $sHTML * @return string */ abstract public function DoSanitize($sHTML); - + /** * Sanitize an HTML string with the configured sanitizer, falling back to HTMLDOMSanitizer in case of Exception or invalid configuration * @param string $sHTML @@ -50,7 +50,7 @@ abstract class HTMLSanitizer IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.'); $sSanitizerClass = 'HTMLDOMSanitizer'; } - + try { $oSanitizer = new $sSanitizerClass(); @@ -70,7 +70,7 @@ abstract class HTMLSanitizer { IssueLog::Error('Failed to sanitize an HTML string with "HTMLDOMSanitizer". The following exception occured: '.$e->getMessage()); IssueLog::Error('The HTML will NOT be sanitized.'); - $sCleanHTML = $sHTML; + $sCleanHTML = $sHTML; } } return $sCleanHTML; @@ -97,7 +97,7 @@ class HTMLNullSanitizer extends HTMLSanitizer { return $sHTML; } - + } /** @@ -109,7 +109,7 @@ class HTMLNullSanitizer extends HTMLSanitizer class HTMLPurifierSanitizer extends HTMLSanitizer { protected static $oPurifier = null; - + public function __construct() { if (self::$oPurifier == null) @@ -120,7 +120,7 @@ class HTMLPurifierSanitizer extends HTMLSanitizer throw new Exception("Missing library '$sLibPath', cannot use HTMLPurifierSanitizer."); } require_once($sLibPath); - + $oPurifierConfig = HTMLPurifier_Config::createDefault(); $oPurifierConfig->set('Core.Encoding', 'UTF-8'); // defaults to 'UTF-8' $oPurifierConfig->set('HTML.Doctype', 'XHTML 1.0 Strict'); // defaults to 'XHTML 1.0 Transitional' @@ -142,11 +142,11 @@ class HTMLPurifierSanitizer extends HTMLSanitizer self::$oPurifier = new HTMLPurifier($oPurifierConfig); } } - + public function DoSanitize($sHTML) { $sCleanHtml = self::$oPurifier->purify($sHTML); - return $sCleanHtml; + return $sCleanHtml; } } */ @@ -278,13 +278,13 @@ class HTMLDOMSanitizer extends HTMLSanitizer $sHTML = preg_replace('~\xc2\xa0~', ' ', $sHTML); @$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified - + $this->CleanNode($this->oDoc); - + $oXPath = new DOMXPath($this->oDoc); $sXPath = "//body"; $oNodesList = $oXPath->query($sXPath); - + if ($oNodesList->length == 0) { // No body, save the whole document @@ -297,10 +297,10 @@ class HTMLDOMSanitizer extends HTMLSanitizer // remove the body tag itself $sCleanHtml = str_replace( array('<body>', '</body>'), '', $sCleanHtml); } - + return $sCleanHtml; } - + protected function CleanNode(DOMNode $oElement) { $aAttrToRemove = array(); @@ -341,7 +341,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer $oElement->removeAttribute($sName); } } - + if ($oElement->hasChildNodes()) { $aChildElementsToRemove = array(); @@ -390,7 +390,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer } return implode(';', $aAllowedStyles); } - + protected function IsValidAttributeContent($sAttributeName, $sValue) { if (array_key_exists($sAttributeName, self::$aAttrsWhiteList)) diff --git a/core/inlineimage.class.inc.php b/core/inlineimage.class.inc.php index 7b6297410..05b75ddc8 100644 --- a/core/inlineimage.class.inc.php +++ b/core/inlineimage.class.inc.php @@ -176,26 +176,32 @@ class InlineImage extends DBObject $sOQL = 'SELECT InlineImage WHERE temp_id = :temp_id'; $oSearch = DBObjectSearch::FromOQL($sOQL); $oSet = new DBObjectSet($oSearch, array(), array('temp_id' => $sTempId)); + $aInlineImagesId = array(); while($oInlineImage = $oSet->Fetch()) { + $aInlineImagesId[] = $oInlineImage->GetKey(); $oInlineImage->SetItem($oObject); $oInlineImage->Set('temp_id', ''); $oInlineImage->DBUpdate(); } + IssueLog::Trace('FinalizeInlineImages (see $aInlineImagesId for the id list)', 'InlineImage', array( + '$sObjectClass' => get_class($oObject), + '$sTransactionId' => $iTransactionId, + '$sTempId' => $sTempId, + '$aInlineImagesId' => $aInlineImagesId, + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + )); } -// For tracing issues with Inline Images... but beware not all updates are interactive, so this trace happens when creating objects non-interactively (REST, Synchro...) -// else -// { -// IssueLog::Error('InlineImage: Error during FinalizeInlineImages(), no transaction ID for object '.get_class($oObject).'#'.$oObject->GetKey().'.'); -// -// IssueLog::Error('|- Call stack:'); -// $oException = new Exception(); -// $sStackTrace = $oException->getTraceAsString(); -// IssueLog::Error($sStackTrace); -// -// IssueLog::Error('|- POST vars:'); -// IssueLog::Error(print_r($_POST, true)); -// } + else + { + IssueLog::Trace('FinalizeInlineImages "error" $iTransactionId is null', 'InlineImage', array( + '$sObjectClass' => get_class($oObject), + '$sTransactionId' => $iTransactionId, + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + )); + } } /** @@ -208,10 +214,18 @@ class InlineImage extends DBObject $sOQL = 'SELECT InlineImage WHERE temp_id = :temp_id'; $oSearch = DBObjectSearch::FromOQL($sOQL); $oSet = new DBObjectSet($oSearch, array(), array('temp_id' => $sTempId)); + $aInlineImagesId = array(); while($oInlineImage = $oSet->Fetch()) { + $aInlineImagesId[] = $oInlineImage->GetKey(); $oInlineImage->DBDelete(); } + IssueLog::Trace('OnFormCancel', 'InlineImage', array( + '$sTempId' => $sTempId, + '$aInlineImagesId' => $aInlineImagesId, + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + )); } /** @@ -548,6 +562,61 @@ EOF JS ; } + + protected function AfterInsert() + { + IssueLog::Trace(__METHOD__, 'InlineImage', array( + 'id' => $this->GetKey(), + 'expire' => $this->Get('expire'), + 'temp_id' => $this->Get('temp_id'), + 'item_class' => $this->Get('item_class'), + 'item_id' => $this->Get('item_id'), + 'item_org_id' => $this->Get('item_org_id'), + 'secret' => $this->Get('secret'), + 'user' => $sUser = UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); + + parent::AfterInsert(); + } + + protected function AfterUpdate() + { + IssueLog::Trace(__METHOD__, 'InlineImage', array( + 'id' => $this->GetKey(), + 'expire' => $this->Get('expire'), + 'temp_id' => $this->Get('temp_id'), + 'item_class' => $this->Get('item_class'), + 'item_id' => $this->Get('item_id'), + 'item_org_id' => $this->Get('item_org_id'), + 'secret' => $this->Get('secret'), + 'user' => $sUser = UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); + + parent::AfterUpdate(); + } + + protected function AfterDelete() + { + IssueLog::Trace(__METHOD__, 'InlineImage', array( + 'id' => $this->GetKey(), + 'expire' => $this->Get('expire'), + 'temp_id' => $this->Get('temp_id'), + 'item_class' => $this->Get('item_class'), + 'item_id' => $this->Get('item_id'), + 'item_org_id' => $this->Get('item_org_id'), + 'secret' => $this->Get('secret'), + 'user' => $sUser = UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); + + parent::AfterDelete(); + } + } diff --git a/core/legacy/dbobjectsearchlegacy.class.php b/core/legacy/dbobjectsearchlegacy.class.php index 24e67db2c..90ff4d331 100644 --- a/core/legacy/dbobjectsearchlegacy.class.php +++ b/core/legacy/dbobjectsearchlegacy.class.php @@ -2602,5 +2602,9 @@ class DBObjectSearch extends DBSearch return $oExpression; } + public function ListParameters() + { + return $this->GetCriteria()->ListParameters(); + } } diff --git a/core/log.class.inc.php b/core/log.class.inc.php index 60ef36cfb..dfb0fa3e6 100644 --- a/core/log.class.inc.php +++ b/core/log.class.inc.php @@ -308,7 +308,7 @@ class DailyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder public function GetCronProcessNextOccurrence(DateTime $oNow) { $oOccurrence = clone $oNow; - $oOccurrence->modify('tomorrow'); + $oOccurrence->modify('tomorrow midnight'); return $oOccurrence; } @@ -359,8 +359,7 @@ class WeeklyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder public function GetCronProcessNextOccurrence(DateTime $oNow) { $oOccurrence = clone $oNow; - $oOccurrence->modify('Monday next week'); - $oOccurrence->setTime(0, 0, 0); + $oOccurrence->modify('Monday next week midnight'); return $oOccurrence; } @@ -411,8 +410,7 @@ class MonthlyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder public function GetCronProcessNextOccurrence(DateTime $oNow) { $oOccurrence = clone $oNow; - $oOccurrence->modify('first day of next month'); - $oOccurrence->setTime(0, 0, 0); + $oOccurrence->modify('first day of next month midnight'); return $oOccurrence; } @@ -544,6 +542,12 @@ abstract class LogAPI const LEVEL_OK = 'Ok'; const LEVEL_DEBUG = 'Debug'; const LEVEL_TRACE = 'Trace'; + /** + * @var string default log level, can be overrided + * @see GetMinLogLevel + * @since 2.7.1 N°2977 + */ + const LEVEL_DEFAULT = self::LEVEL_OK; protected static $aLevelsPriority = array( self::LEVEL_ERROR => 400, @@ -641,21 +645,22 @@ abstract class LogAPI /** * @param $sChannel * - * @return mixed|null + * @return string one of the LEVEL_* const value + * @uses \LogAPI::LEVEL_DEFAULT */ private static function GetMinLogLevel($sChannel) { $oConfig = (static::$m_oMockMetaModelConfig !== null) ? static::$m_oMockMetaModelConfig : \MetaModel::GetConfig(); if (!$oConfig instanceof Config) { - return self::LEVEL_OK; + return static::LEVEL_DEFAULT; } $sLogLevelMin = $oConfig->Get('log_level_min'); if (empty($sLogLevelMin)) { - return self::LEVEL_OK; + return static::LEVEL_DEFAULT; } if (!is_array($sLogLevelMin)) @@ -673,7 +678,7 @@ abstract class LogAPI return $sLogLevelMin[$sChannel]; } - return self::LEVEL_OK; + return static::LEVEL_DEFAULT; } } @@ -681,6 +686,12 @@ abstract class LogAPI class SetupLog extends LogAPI { const CHANNEL_DEFAULT = 'SetupLog'; + /** + * @inheritDoc + * + * As this object is used during setup, without any conf file available, customizing the level can be done by changing this constant ! + */ + const LEVEL_DEFAULT = self::LEVEL_INFO; protected static $m_oFileLog = null; } @@ -699,6 +710,59 @@ class ToolsLog extends LogAPI protected static $m_oFileLog = null; } +/** + * @see \CMDBSource::LogDeadLock() + * @since 2.7.1 + */ +class DeadLockLog extends LogAPI +{ + const CHANNEL_WAIT_TIMEOUT = 'Deadlock-WaitTimeout'; + const CHANNEL_DEADLOCK_FOUND = 'Deadlock-Found'; + const CHANNEL_DEFAULT = self::CHANNEL_WAIT_TIMEOUT; + + /** @var \FileLog we want our own instance ! */ + protected static $m_oFileLog = null; + + public static function Enable($sTargetFile = null) + { + if (empty($sTargetFile)) + { + $sTargetFile = APPROOT.'log/deadlocks.log'; + } + parent::Enable($sTargetFile); + } + + private static function GetChannelFromMysqlErrorNo($iMysqlErrorNo) + { + switch ($iMysqlErrorNo) + { + case 1205: + return self::CHANNEL_WAIT_TIMEOUT; + break; + case 1213: + return self::CHANNEL_DEADLOCK_FOUND; + break; + default: + return self::CHANNEL_DEFAULT; + break; + } + } + + /** + * @param int $iMySQLErrNo will be converted to channel using {@link GetChannelFromMysqlErrorNo} + * @param string $sMessage + * @param null $iMysqlErroNo + * @param array $aContext + * + * @throws \Exception + */ + public static function Log($iMySQLErrNo, $sMessage, $iMysqlErroNo = null, $aContext = array()) + { + $sChannel = self::GetChannelFromMysqlErrorNo($iMysqlErroNo); + parent::Log($iMySQLErrNo, $sMessage, $sChannel, $aContext); + } +} + class LogFileRotationProcess implements iScheduledProcess { diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 3ea6ea84d..0c8cdbd98 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -944,11 +944,6 @@ abstract class MetaModel { self::_check_subclass($sClass); $oAtt = self::GetAttributeDef($sClass, $sAttCode); - // Temporary implementation: later, we might be able to compute - // the dependencies, based on the attributes definition - // (allowed values and default values) - - // Even non-writable attributes (like ExternalFields) can now have Prerequisites return $oAtt->GetPrerequisiteAttributes(); } @@ -1853,7 +1848,7 @@ abstract class MetaModel * @param string $sClass * @param string $sListCode * - * @return array + * @return array list of attribute codes */ public static function GetZListItems($sClass, $sListCode) { @@ -1873,6 +1868,82 @@ abstract class MetaModel return self::GetZListItems($sParentClass, $sListCode); } + /** + * @param string $sRemoteClass + * + * @return \AttributeDefinition[] list of attdefs to display by default for the remote class + * + * @since 2.8.0 N°2334 + */ + public static function GetZListAttDefsFilteredForIndirectRemoteClass($sRemoteClass) + { + $aAttCodesToPrint = []; + + foreach (MetaModel::GetZListItems($sRemoteClass, 'list') as $sFieldCode) + { + //TODO: check the state of the attribute: hidden or visible ? + if ($sFieldCode == 'finalclass') + { + continue; + } + + $oRemoteAttDef = MetaModel::GetAttributeDef($sRemoteClass, $sFieldCode); + $aAttCodesToPrint[] = $oRemoteAttDef; + } + + return $aAttCodesToPrint; + } + + /** + * @param string $sClass left class + * @param string $sAttCode AttributeLinkedSetIndirect attcode + * + * @return \AttributeDefinition[] list of attdefs to display by default for lnk class + * + * @throws \CoreException + * @since 2.8.0 N°2334 + */ + public static function GetZListAttDefsFilteredForIndirectLinkClass($sClass, $sAttCode) + { + $aAttCodesToPrint = []; + + $oLinkedSetAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + $sLinkedClass = $oLinkedSetAttDef->GetLinkedClass(); + $sExtKeyToRemote = $oLinkedSetAttDef->GetExtKeyToRemote(); + $sExtKeyToMe = $oLinkedSetAttDef->GetExtKeyToMe(); + + $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); + $sDefaultState = MetaModel::GetDefaultState($sClass); + + foreach (MetaModel::FlattenZList(MetaModel::GetZListItems($sLinkedClass, 'list')) as $sLnkAttCode) + { + $oLnkAttDef = MetaModel::GetAttributeDef($sLinkedClass, $sLnkAttCode); + if ($sStateAttCode == $sLnkAttCode) + { + // State attribute is always hidden from the UI + continue; + } + if (($sLnkAttCode == $sExtKeyToMe) + || ($sLnkAttCode == $sExtKeyToRemote) + || ($sLnkAttCode == 'finalclass')) + { + continue; + } + if (!($oLnkAttDef->IsWritable())) + { + continue; + } + + $iFlags = MetaModel::GetAttributeFlags($sLinkedClass, $sDefaultState, $sLnkAttCode); + if (!($iFlags & OPT_ATT_HIDDEN) && !($iFlags & OPT_ATT_READONLY)) + { + $aAttCodesToPrint[] = $oLnkAttDef; + } + } + + return $aAttCodesToPrint; + } + /** * @param string $sClass * @param string $sListCode @@ -2047,7 +2118,6 @@ abstract class MetaModel */ protected static function ComputeRelationQueries($sRelCode) { - $bHasLegacy = false; $aQueries = array(); foreach(self::GetClasses() as $sClass) { @@ -2133,158 +2203,8 @@ abstract class MetaModel } } } - - // Read legacy definitions - // The up/down queries have to be reconcilied, which can only be done later when all the classes have been browsed - // - // The keys used to store a query (up or down) into the array are built differently between the modern and legacy made data: - // Modern way: aQueries[sClass]['up'|'down'][sArrowId], where sArrowId is made of the source class + neighbour id (XML def) - // Legacy way: aQueries[sClass]['up'|'down'][sRemoteClass] - // The modern way does allow for several arrows between two classes - // The legacy way aims at simplifying the transformation (reconciliation between up and down) - if ($sRelCode == 'impacts') - { - $sRevertCode = 'depends on'; - - $aLegacy = call_user_func_array(array($sClass, 'GetRelationQueries'), array($sRelCode)); - foreach($aLegacy as $sId => $aLegacyEntry) - { - $bHasLegacy = true; - - $oFilter = DBObjectSearch::FromOQL($aLegacyEntry['sQuery']); - $sRemoteClass = $oFilter->GetClass(); - - // Determine wether the query is inherited from a parent or not - $bInherited = false; - foreach(self::EnumParentClasses($sClass) as $sParent) - { - if (!isset($aQueries[$sParent]['down'][$sRemoteClass])) - { - continue; - } - if ($aLegacyEntry['sQuery'] == $aQueries[$sParent]['down'][$sRemoteClass]['sQueryDown']) - { - $bInherited = true; - $aQueries[$sClass]['down'][$sRemoteClass] = $aQueries[$sParent]['down'][$sRemoteClass]; - break; - } - } - - if (!$bInherited) - { - $aQueries[$sClass]['down'][$sRemoteClass] = array( - '_legacy_' => true, - 'sDefinedInClass' => $sClass, - 'sFromClass' => $sClass, - 'sToClass' => $sRemoteClass, - 'sDirection' => 'down', - 'sQueryDown' => $aLegacyEntry['sQuery'], - 'sQueryUp' => null, - 'sNeighbour' => $sRemoteClass // Normalize the neighbour id - ); - } - } - - $aLegacy = call_user_func_array(array($sClass, 'GetRelationQueries'), array($sRevertCode)); - foreach($aLegacy as $sId => $aLegacyEntry) - { - $bHasLegacy = true; - - $oFilter = DBObjectSearch::FromOQL($aLegacyEntry['sQuery']); - $sRemoteClass = $oFilter->GetClass(); - - // Determine wether the query is inherited from a parent or not - $bInherited = false; - foreach(self::EnumParentClasses($sClass) as $sParent) - { - if (!isset($aQueries[$sParent]['up'][$sRemoteClass])) - { - continue; - } - if ($aLegacyEntry['sQuery'] == $aQueries[$sParent]['up'][$sRemoteClass]['sQueryUp']) - { - $bInherited = true; - $aQueries[$sClass]['up'][$sRemoteClass] = $aQueries[$sParent]['up'][$sRemoteClass]; - break; - } - } - - if (!$bInherited) - { - $aQueries[$sClass]['up'][$sRemoteClass] = array( - '_legacy_' => true, - 'sDefinedInClass' => $sRemoteClass, - 'sFromClass' => $sRemoteClass, - 'sToClass' => $sClass, - 'sDirection' => 'both', - 'sQueryDown' => null, - 'sQueryUp' => $aLegacyEntry['sQuery'], - 'sNeighbour' => $sClass// Normalize the neighbour id - ); - } - } - } - //else - //{ - // Cannot take the legacy system into account... simply ignore it - //} } // foreach class - // Perform the up/down reconciliation for the legacy definitions - if ($bHasLegacy) - { - foreach(self::GetClasses() as $sClass) - { - // Foreach "up" legacy query, update its "down" counterpart - if (isset($aQueries[$sClass]['up'])) - { - foreach($aQueries[$sClass]['up'] as $sNeighbourId => $aNeighbourData) - { - if (!array_key_exists('_legacy_', $aNeighbourData)) - { - continue; - } - if (!$aNeighbourData['_legacy_']) - { - continue; - } // Skip modern definitions - - $sLocalClass = $aNeighbourData['sToClass']; - foreach(self::EnumChildClasses($aNeighbourData['sFromClass'], ENUM_CHILD_CLASSES_ALL) as $sRemoteClass) - { - if (isset($aQueries[$sRemoteClass]['down'][$sLocalClass])) - { - $aQueries[$sRemoteClass]['down'][$sLocalClass]['sQueryUp'] = $aNeighbourData['sQueryUp']; - $aQueries[$sRemoteClass]['down'][$sLocalClass]['sDirection'] = 'both'; - } - // Be silent in order to transparently support legacy data models where the counterpart query does not always exist - //else - //{ - // throw new Exception("Legacy definition of the relation '$sRelCode/$sRevertCode', defined on $sLocalClass (relation: $sRevertCode, inherited to $sClass), missing the counterpart query on class $sRemoteClass ($sRelCode)"); - //} - } - } - } - // Foreach "down" legacy query, update its "up" counterpart (if any) - foreach($aQueries[$sClass]['down'] as $sNeighbourId => $aNeighbourData) - { - if (!$aNeighbourData['_legacy_']) - { - continue; - } // Skip modern definitions - - $sLocalClass = $aNeighbourData['sFromClass']; - foreach(self::EnumChildClasses($aNeighbourData['sToClass'], ENUM_CHILD_CLASSES_ALL) as $sRemoteClass) - { - if (isset($aQueries[$sRemoteClass]['up'][$sLocalClass])) - { - $aQueries[$sRemoteClass]['up'][$sLocalClass]['sQueryDown'] = $aNeighbourData['sQueryDown']; - } - } - } - } - } - return $aQueries; } @@ -3318,18 +3238,6 @@ abstract class MetaModel // In fact it is an ABSTRACT function, but this is not compatible with the fact that it is STATIC (error in E_STRICT interpretation) } - /** - * To be overloaded by biz model declarations - * - * @param string $sRelCode - * - * @return array - */ - public static function GetRelationQueries($sRelCode) - { - // In fact it is an ABSTRACT function, but this is not compatible with the fact that it is STATIC (error in E_STRICT interpretation) - return array(); - } /** * @param array $aParams @@ -3496,7 +3404,7 @@ abstract class MetaModel } if (array_key_exists($sAttCode, self::$m_aAttribDefs[$sTargetClass])) { - throw new Exception("Declaration of $sTargetClass: attempting to redeclare the inherited attribute '$sAttCode', originaly declared in ".self::$m_aAttribOrigins[$sTargetClass][$sAttCode]); + throw new Exception("Declaration of $sTargetClass: attempting to redeclare the inherited attribute '$sAttCode', originally declared in ".self::$m_aAttribOrigins[$sTargetClass][$sAttCode]); } // Set the "host class" as soon as possible, since HierarchicalKeys use it for their 'target class' as well @@ -4127,44 +4035,52 @@ abstract class MetaModel * * @param array $aArgs Context arguments (some can be persistent objects) * @param array $aMoreArgs Other query parameters + * @param array $aExpectedArgs variables present in the query + * * @return array */ - public static function PrepareQueryArguments($aArgs, $aMoreArgs = array()) + public static function PrepareQueryArguments($aArgs, $aMoreArgs = array(), $aExpectedArgs = null) { $aScalarArgs = array(); - foreach(array_merge($aArgs, $aMoreArgs) as $sArgName => $value) + if (is_null($aExpectedArgs) || count($aExpectedArgs) > 0 || count($aMoreArgs)>0) { - if (self::IsValidObject($value)) + foreach (array_merge($aArgs, $aMoreArgs) as $sArgName => $value) { - if (strpos($sArgName, '->object()') === false) + if (self::IsValidObject($value)) { - // Normalize object arguments - $aScalarArgs[$sArgName.'->object()'] = $value; + if (strpos($sArgName, '->object()') === false) + { + // Normalize object arguments + $aScalarArgs[$sArgName.'->object()'] = $value; + } + else + { + // Leave as is + $aScalarArgs[$sArgName] = $value; + } } else { - // Leave as is - $aScalarArgs[$sArgName] = $value; - } - } - else - { - if (is_scalar($value)) - { - $aScalarArgs[$sArgName] = (string)$value; - } - elseif (is_null($value)) - { - $aScalarArgs[$sArgName] = null; - } - elseif (is_array($value)) - { - $aScalarArgs[$sArgName] = $value; + if (is_scalar($value)) + { + $aScalarArgs[$sArgName] = (string)$value; + } + elseif (is_null($value)) + { + $aScalarArgs[$sArgName] = null; + } + elseif (is_array($value)) + { + $aScalarArgs[$sArgName] = $value; + } } } + return static::AddMagicPlaceholders($aScalarArgs, $aExpectedArgs); + } + else + { + return array(); } - - return static::AddMagicPlaceholders($aScalarArgs); } /** @@ -4172,21 +4088,68 @@ abstract class MetaModel * * @return array of placeholder (or name->object()) => value (or object) */ - public static function AddMagicPlaceholders($aPlaceholders) + public static function AddMagicPlaceholders($aPlaceholders, $aExpectedArgs = null) { // Add standard magic arguments // - $aPlaceholders['current_contact_id'] = UserRights::GetContactId(); // legacy - - $oUser = UserRights::GetUserObject(); - if (!is_null($oUser)) + if (is_null($aExpectedArgs)) { - $aPlaceholders['current_user->object()'] = $oUser; + $aPlaceholders['current_contact_id'] = UserRights::GetContactId(); // legacy - $oContact = UserRights::GetContactObject(); - if (!is_null($oContact)) + $oUser = UserRights::GetUserObject(); + if (!is_null($oUser)) { - $aPlaceholders['current_contact->object()'] = $oContact; + $aPlaceholders['current_user->object()'] = $oUser; + + $oContact = UserRights::GetContactObject(); + if (!is_null($oContact)) + { + $aPlaceholders['current_contact->object()'] = $oContact; + } + } + } + else + { + $aCurrentUser = array(); + $aCurrentContact = array(); + foreach ($aExpectedArgs as $expression) + { + $aName = explode('->', $expression->GetName()); + if ($aName[0] == 'current_contact_id') + { + $aPlaceholders['current_contact_id'] = UserRights::GetContactId(); + } + if ($aName[0] == 'current_user') + { + array_push($aCurrentUser, $aName[1]); + } + if ($aName[0] == 'current_contact') + { + array_push($aCurrentContact, $aName[1]); + } + } + if (count($aCurrentUser) > 0) + { + $oSearch = DBObjectSearch::FromOQL("SELECT User WHERE id = :id"); + $oSet = new DBObjectSet($oSearch, array(), array('id' => UserRights::GetUserId())); + $oSet->OptimizeColumnLoad($aCurrentUser); + $oUser = $oSet->fetch(); + $aPlaceholders['current_user->object()'] = $oUser; + foreach ($aCurrentUser as $sField) + { + $aPlaceholders['current_user->'.$sField] = $oUser->Get($sField); + } + } + if (count($aCurrentContact) > 0) + { + $oSearch = DBObjectSearch::FromOQL("SELECT Contact WHERE id = :id"); + $oSet = new DBObjectSet($oSearch, array(), array('id' => UserRights::GetContactId())); + $oSet->OptimizeColumnLoad($aCurrentContact); + $oUser = $oSet->fetch(); + foreach ($aCurrentContact as $sField) + { + $aPlaceholders['current_contact->'.$sField] = $oUser->Get($sField); + } } } @@ -4879,7 +4842,7 @@ abstract class MetaModel // foreach(self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { - // Skip this attribute if not originaly defined in this class + // Skip this attribute if not originally defined in this class if (self::$m_aAttribOrigins[$sClass][$sAttCode] != $sClass) { continue; @@ -5253,7 +5216,7 @@ abstract class MetaModel $sClassRes .= self::MakeDictEntry("Class:$sClass+", self::GetClassDescription_Obsolete($sClass), '', $bNotInDico); foreach(self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { - // Skip this attribute if not originaly defined in this class + // Skip this attribute if not originally defined in this class if (self::$m_aAttribOrigins[$sClass][$sAttCode] != $sClass) { continue; @@ -5417,7 +5380,7 @@ abstract class MetaModel { if (!$oAttDef->CopyOnAllTables()) { - // Skip this attribute if not originaly defined in this class + // Skip this attribute if not originally defined in this class if (self::$m_aAttribOrigins[$sClass][$sAttCode] != $sClass) { continue; @@ -5692,6 +5655,15 @@ abstract class MetaModel { $sTableItems = implode(', ', $aCreateTableItems[$sTable]); $aCondensedQueries[] = "CREATE TABLE `$sTable` ($sTableItems) $sTableOptions"; + // Add request right after the CREATE TABLE + if (isset($aPostTableAlteration[$sTable])) + { + foreach ($aPostTableAlteration[$sTable] as $sQuery) + { + $aCondensedQueries[] = $sQuery; + } + unset($aPostTableAlteration[$sTable]); + } } foreach ($aAlterTableMetaData as $sTableAlterQuery) { @@ -5708,9 +5680,19 @@ abstract class MetaModel { $aCondensedQueries[] = $sQuery; } + unset($aPostTableAlteration[$sTable]); } } + // Add alterations not yet managed + foreach ($aPostTableAlteration as $aQueries) + { + foreach ($aQueries as $sQuery) + { + $aCondensedQueries[] = $sQuery; + } + } + return array($aErrors, $aSugFix, $aCondensedQueries); } @@ -5735,32 +5717,9 @@ abstract class MetaModel $sView = self::DBGetView($sClass); if (CMDBSource::IsTable($sView)) { - // Check that the view is complete - // - // Note: checking the list of attributes is not enough because the columns can be stable while the SELECT is not stable - // Example: new way to compute the friendly name - // The correct comparison algorithm is to compare the queries, - // by using "SHOW CREATE VIEW" (MySQL 5.0.1 required) or to look into INFORMATION_SCHEMA/views - // both requiring some privileges - // Decision: to simplify, let's consider the views as being wrong anytime - // Rework the view - // - $oFilter = new DBObjectSearch($sClass, ''); - $oFilter->AllowAllData(); - $sSQL = $oFilter->MakeSelectQuery(); - $aErrors[$sClass]['*'][] = "Redeclare view '$sView' (systematic - to support an eventual change in the friendly name computation)"; - $aSugFix[$sClass]['*'][] = "ALTER VIEW `$sView` AS $sSQL"; - } - else - { - // Create the view - // - $oFilter = new DBObjectSearch($sClass, ''); - $oFilter->AllowAllData(); - $sSQL = $oFilter->MakeSelectQuery(); - $aErrors[$sClass]['*'][] = "Missing view for class: $sClass"; - $aSugFix[$sClass]['*'][] = "DROP VIEW IF EXISTS `$sView`"; - $aSugFix[$sClass]['*'][] = "CREATE VIEW `$sView` AS $sSQL"; + // Remove deprecated views + $aErrors[$sClass]['*'][] = "Remove view '$sView' (deprecated, consider installing combodo-views if needed)"; + $aSugFix[$sClass]['*'][] = "DROP VIEW `$sView`"; } } return array($aErrors, $aSugFix); @@ -6316,6 +6275,7 @@ abstract class MetaModel self::$m_bLogWebService = self::$m_oConfig->GetLogWebService(); ToolsLog::Enable(APPROOT.'log/tools.log'); + DeadLockLog::Enable(); } else { @@ -7164,99 +7124,11 @@ abstract class MetaModel return $aResult; } - /** - * @deprecated 2.5.0 It is not recommended to use this function: call {@link MetaModel::GetLinkClasses} instead ! - * The only difference with EnumLinkingClasses is the output format - * - * @see MetaModel::GetLinkClasses - * @return string[] classes having at least two external keys (thus too many classes as compared to GetLinkClasses) - * - */ - public static function EnumLinksClasses() - { - // Returns a flat array of classes having at least two external keys - $aResult = array(); - foreach(self::$m_aAttribDefs as $sSomeClass => $aClassAttributes) - { - $iExtKeyCount = 0; - foreach($aClassAttributes as $sAttCode => $oAttDef) - { - if (self::$m_aAttribOrigins[$sSomeClass][$sAttCode] != $sSomeClass) - { - continue; - } - if ($oAttDef->IsExternalKey()) - { - $iExtKeyCount++; - } - } - if ($iExtKeyCount >= 2) - { - $aResult[] = $sSomeClass; - } - } - return $aResult; - } - - /** - * @deprecated 2.5.0 It is not recommended to use this function: call {@link MetaModel::GetLinkClasses} instead ! - * The only difference with EnumLinksClasses is the output format - * - * @see MetaModel::GetLinkClasses - * - *@param string $sClass - * - * @return string[] classes having at least two external keys (thus too many classes as compared to GetLinkClasses) - * @throws \CoreException - * - */ - public static function EnumLinkingClasses($sClass = "") - { - // N-N links, array of sLinkClass => (array of sAttCode=>sClass) - $aResult = array(); - foreach(self::EnumLinksClasses() as $sSomeClass) - { - $aTargets = array(); - $bFoundClass = false; - foreach(self::ListAttributeDefs($sSomeClass) as $sAttCode => $oAttDef) - { - if (self::$m_aAttribOrigins[$sSomeClass][$sAttCode] != $sSomeClass) - { - continue; - } - if ($oAttDef->IsExternalKey()) - { - $sRemoteClass = $oAttDef->GetTargetClass(); - if (empty($sClass)) - { - $aTargets[$sAttCode] = $sRemoteClass; - } - elseif ($sClass == $sRemoteClass) - { - $bFoundClass = true; - } - else - { - $aTargets[$sAttCode] = $sRemoteClass; - } - } - } - if (empty($sClass) || $bFoundClass) - { - $aResult[$sSomeClass] = $aTargets; - } - } - return $aResult; - } - /** * 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 * - * This function has two siblings that will be soon deprecated: - * {@link MetaModel::EnumLinkingClasses} and {@link MetaModel::EnumLinkClasses} - * * @return array (target class => (external key code => target class)) * @throws \CoreException */ diff --git a/core/oql/expression.class.inc.php b/core/oql/expression.class.inc.php index 90335f9ed..30e97553a 100644 --- a/core/oql/expression.class.inc.php +++ b/core/oql/expression.class.inc.php @@ -21,6 +21,9 @@ class MissingQueryArgument extends CoreException { } +class NotYetEvaluatedExpression extends CoreException +{ +} /** * @method Check($oModelReflection, array $aAliases, $sSourceQuery) @@ -109,6 +112,47 @@ abstract class Expression */ abstract public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false); + /** + * Collect parameters, i.e. :parameter + * + * @param null $sParentFilter + * + * @return array + */ + public function GetParameters($sParentFilter = null) + { + $aParameters = array(); + $unused = $this->RenderExpression(false, $aParameters, true); + + if (!is_null($sParentFilter)) $sParentFilter .= '->'; + + $aRet = array(); + foreach($aParameters as $sParameter => $unused) + { + if (is_null($sParentFilter)) + { + $aRet[] = $sParameter; + } + else + { + if (substr($sParameter, 0, strlen($sParentFilter)) == $sParentFilter) + { + $aRet[] = substr($sParameter, strlen($sParentFilter)); + } + } + } + return $aRet; + } + + /** + * Evaluate the value of the expression + * + * @param array $aArgs + * + * @throws \Exception if terms cannot be evaluated as scalars + */ + abstract public function Evaluate(array $aArgs); + /** * Recursively renders the expression as a structure (array) suitable for a JSON export * @@ -158,6 +202,9 @@ abstract class Expression // recursively builds an array of [classAlias][fieldName] => value abstract public function ListConstantFields(); + // recursively builds an array of parameters to give to current request + abstract public function ListParameters(); + public function RequiresField($sClass, $sFieldName) { // #@# todo - optimize : this is called quite often when building a single query ! @@ -316,6 +363,16 @@ class SQLExpression extends Expression return $this->m_sSQL; } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + throw new Exception('a nested query cannot be evaluated'); + } + // recursive rendering public function toJSON(&$aArgs = null, $bRetrofitParams = false) { @@ -354,6 +411,11 @@ class SQLExpression extends Expression return array(); } + public function ListParameters() + { + return array(); + } + public function RenameParam($sOldName, $sNewName) { // Do nothing, since there is nothing to rename @@ -458,6 +520,149 @@ class BinaryExpression extends Expression return "($sLeft $sOperator $sRight)"; } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @return mixed + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + $mLeft = $this->GetLeftExpr()->Evaluate($aArgs); + $mRight = $this->GetRightExpr()->Evaluate($aArgs); + + $sOperator = $this->GetOperator(); + $sType = null; + switch($sOperator) + { + case '+': + case '-': + case '*': + case '/': + $sType = 'maths'; + break; + case '=': + case '!=': + case '<>': + $sType = 'comp'; + break; + case '>': + case '>=': + case '<': + case '<=': + $sType = 'numcomp'; + break; + case 'OR': + case 'AND': + $sType = 'logical'; + break; + case 'LIKE': + $sType = 'like'; + break; + default: + throw new Exception("Operator '$sOperator' not yet supported"); + } + switch ($sType){ + case 'logical': + $bLeft = static::CastToBool($mLeft); + $bRight = static::CastToBool($mRight); + switch ($sOperator) + { + case 'OR': + $result = (int)($bLeft || $bRight); + break; + case 'AND': + $result = (int)($bLeft && $bRight); + break; + default: + throw new Exception("Logic: unknown operator '$sOperator'"); + } + break; + + case 'maths': + $iLeft = (int) $mLeft; + $iRight = (int) $mRight; + switch ($sOperator) + { + case '+' : $result = $iLeft + $iRight; break; + case '-' : $result = $iLeft - $iRight; break; + case '*' : $result = $iLeft * $iRight; break; + case '/' : $result = $iLeft / $iRight; break; + default: + throw new Exception("Logic: unknown operator '$sOperator'"); + } + break; + case 'comp': + $left = $mLeft; + $right = $mRight; + switch ($sOperator) + { + case '=' : $result = ($left == $right); break; + case '!=' : $result = ($left != $right); break; + case '<>' : $result = ($left != $right); break; + default: + throw new Exception("Logic: unknown operator '$sOperator'"); + } + break; + case 'numcomp': + $iLeft = static::ComparableValue($mLeft); + $iRight = static::ComparableValue($mRight); + switch ($sOperator) + { + case '=' : $result = ($iLeft == $iRight); break; + case '>' : $result = ($iLeft > $iRight); break; + case '<' : $result = ($iLeft < $iRight); break; + case '>=' : $result = ($iLeft >= $iRight); break; + case '<=' : $result = ($iLeft <= $iRight); break; + case '!=' : $result = ($iLeft != $iRight); break; + case '<>' : $result = ($iLeft != $iRight); break; + default: + throw new Exception("Logic: unknown operator '$sOperator'"); + } + break; + case 'like': + $sEscaped = preg_quote($mRight, '/'); + $sEscaped = str_replace(array('%', '_', '\\\\.*', '\\\\.'), array('.*', '.', '%', '_'), $sEscaped); + $result = (int) preg_match("/$sEscaped/i", $mLeft); + break; + } + return $result; + } + + static protected function CastToBool($mValue) + { + if (is_string($mValue)) + { + if (is_numeric($mValue)) + { + return abs($mValue) > 0; + } + return false; + } + return (bool)$mValue; + } + static protected function ComparableValue($mixed) + { + if (is_string($mixed)) + { + $oDate = new \DateTime($mixed); + if (($oDate->format('Y-m-d') == $mixed) || ($oDate->format('Y-m-d H:i:s') == $mixed)) + { + $iRet = $oDate->format('U'); + } + else + { + $iRet = (int) $mixed; + } + } + else + { + $iRet = $mixed; + } + + return $iRet; + } + /** * {@inheritDoc} * @throws \MissingQueryArgument @@ -593,6 +798,13 @@ class BinaryExpression extends Expression return $aResult; } + public function ListParameters() + { + $aLeft = $this->GetLeftExpr()->ListParameters(); + $aRight = $this->GetRightExpr()->ListParameters(); + return array_merge($aLeft, $aRight); + } + public function RenameParam($sOldName, $sNewName) { $this->GetLeftExpr()->RenameParam($sOldName, $sNewName); @@ -850,6 +1062,16 @@ class MatchExpression extends BinaryExpression return $sRet; } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + throw new Exception('evaluation of MATCHES not implemented yet'); + } + public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true) { /** @var \FieldExpression $oLeft */ @@ -888,6 +1110,16 @@ class UnaryExpression extends Expression return CMDBSource::Quote($this->m_value); } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + return $this->m_value; + } + /** * {@inheritDoc} * @throws \MissingQueryArgument @@ -934,6 +1166,11 @@ class UnaryExpression extends Expression return array(); } + public function ListParameters() + { + return array(); + } + public function RenameParam($sOldName, $sNewName) { // Do nothing @@ -1029,6 +1266,16 @@ class ScalarExpression extends UnaryExpression return $sRet; } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + return $this->m_value; + } + /** * {@inheritDoc} * @see Expression::ToJSON() @@ -1337,6 +1584,21 @@ class FieldExpression extends UnaryExpression return "`{$this->m_sParent}`.`{$this->m_sName}`"; } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + $sKey = empty($this->m_sParent) ? $this->m_sName : "{$this->m_sParent}.{$this->m_sName}"; + if (!array_key_exists($sKey, $aArgs)) + { + throw new Exception("Missing field '$sKey' from context"); + } + return $aArgs[$sKey]; + } + /** * {@inheritDoc} * @see Expression::ToJSON() @@ -1392,7 +1654,8 @@ class FieldExpression extends UnaryExpression public function ListRequiredFields() { - return array($this->m_sParent.'.'.$this->m_sName); + $sField = empty($this->m_sParent) ? $this->m_sName : "{$this->m_sParent}.{$this->m_sName}"; + return array($sField); } public function CollectUsedParents(&$aTable) @@ -1773,6 +2036,16 @@ class VariableExpression extends UnaryExpression } } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + throw new Exception('not implemented yet'); + } + /** * {@inheritDoc} * @see Expression::ToJSON() @@ -1848,6 +2121,12 @@ class VariableExpression extends UnaryExpression } return $oRet; } + + public function ListParameters() + { + return array($this); + } + } // Temporary, until we implement functions and expression casting! @@ -1907,6 +2186,16 @@ class ListExpression extends Expression return '('.implode(', ', $aRes).')'; } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + throw new Exception('list expression not yet supported'); + } + /** * {@inheritDoc} * @see Expression::ToJSON() @@ -2001,6 +2290,16 @@ class ListExpression extends Expression return $aRes; } + public function ListParameters() + { + $aRes = array(); + foreach ($this->m_aExpressions as $oExpr) + { + $aRes = array_merge($aRes, $oExpr->ListParameters()); + } + return $aRes; + } + public function RenameParam($sOldName, $sNewName) { foreach ($this->m_aExpressions as $key => $oExpr) @@ -2104,6 +2403,16 @@ class NestedQueryExpression extends Expression } } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + throw new Exception('a nested query cannot be evaluated'); + } + public function Browse(Closure $callback) { $callback($this); @@ -2142,6 +2451,11 @@ class NestedQueryExpression extends Expression return $this->m_oNestedQuery->ListConstantFields(); } + public function ListParameters() + { + return $this->m_oNestedQuery->ListParameters(); + } + public function RenameParam($sOldName, $sNewName) { $this->m_oNestedQuery->RenameParam($sOldName, $sNewName); @@ -2199,6 +2513,252 @@ class FunctionExpression extends Expression return $this->m_sVerb.'('.implode(', ', $aRes).')'; } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + switch($this->m_sVerb) + { + case 'CONCAT': + $sRet = ''; + foreach ($this->m_aArgs as $iPos => $oExpr) + { + $item = $oExpr->Evaluate($aArgs); + if (is_null($item)) return null; + $sRet .= $item; + } + return $sRet; + + case 'CONCAT_WS': + if (count($this->m_aArgs) < 3) + { + throw new \Exception("Function {$this->m_sVerb} requires at least 3 arguments"); + } + $sSeparator = $this->m_aArgs[0]->Evaluate($aArgs); + foreach ($this->m_aArgs as $iPos => $oExpr) + { + if ($iPos == 0) continue; + $item = $oExpr->Evaluate($aArgs); + if (is_null($item)) return null; + $aStrings[] = $item; + } + $sRet = implode($sSeparator, $aStrings); + return $sRet; + + case 'SUBSTR': + if (count($this->m_aArgs) < 2) + { + throw new \Exception("Function {$this->m_sVerb} requires at least 2 arguments"); + } + $sString = $this->m_aArgs[0]->Evaluate($aArgs); + $iRawPos = $this->m_aArgs[1]->Evaluate($aArgs); + $iPos = $iRawPos > 0 ? + $iRawPos - 1// 0-based in PHP (1-based in SQL) + : $iRawPos; // Negative + if (count($this->m_aArgs) == 2) + { + // Up to the end of the string + $sRet = substr($sString, $iPos); + } + else + { + // Length specified + $iLen = $this->m_aArgs[2]->Evaluate($aArgs); + $sRet = substr($sString, $iPos, $iLen); + } + return $sRet; + + case 'TRIM': + if (count($this->m_aArgs) != 1) + { + throw new \Exception("Function {$this->m_sVerb} requires 1 argument"); + } + $sRet = trim($this->m_aArgs[0]->Evaluate($aArgs)); + return $sRet; + + case 'INET_ATON': + if (count($this->m_aArgs) != 1) + { + throw new \Exception("Function {$this->m_sVerb} requires 1 argument"); + } + $sRet = ip2long($this->m_aArgs[0]->Evaluate($aArgs)); + return $sRet; + + case 'INET_NTOA': + if (count($this->m_aArgs) != 1) + { + throw new \Exception("Function {$this->m_sVerb} requires 1 argument"); + } + $sRet = long2ip($this->m_aArgs[0]->Evaluate($aArgs)); + return $sRet; + + case 'ISNULL': + if (count($this->m_aArgs) != 1) + { + throw new \Exception("Function {$this->m_sVerb} requires 1 argument"); + } + $sRet = is_null($this->m_aArgs[0]->Evaluate($aArgs)); + return $sRet; + + case 'COALESCE': + if (count($this->m_aArgs) < 1) + { + throw new \Exception("Function {$this->m_sVerb} requires at least 1 argument"); + } + $ret = null; + foreach($this->m_aArgs as $iPos => $oExpr) + { + $ret = $oExpr->Evaluate($aArgs); + if (!is_null($ret)) break; + } + return $ret; + + case 'IF': + if (count($this->m_aArgs) != 3) + { + throw new \Exception("Function {$this->m_sVerb} requires 3 arguments"); + } + $bCond = $this->m_aArgs[0]->Evaluate($aArgs); + if ($bCond) + { + $ret = $this->m_aArgs[1]->Evaluate($aArgs); + } + else + { + $ret = $this->m_aArgs[2]->Evaluate($aArgs); + } + return $ret; + + case 'ELT': + if (count($this->m_aArgs) < 2) + { + throw new \Exception("Function {$this->m_sVerb} requires at least 2 arguments"); + } + // First argument is the 1-based position + $iPosition = (int) $this->m_aArgs[0]->Evaluate($aArgs); + if (($iPosition == 0) || ($iPosition >= count($this->m_aArgs))) + { + // Out of range + $ret = null; + } + else + { + $ret = $this->m_aArgs[$iPosition]->Evaluate($aArgs); + } + return $ret; + + case 'DATE': + if (count($this->m_aArgs) != 1) + { + throw new \Exception("Function {$this->m_sVerb} requires 1 argument"); + } + $sRet = date('Y-m-d', strtotime($this->m_aArgs[0]->Evaluate($aArgs))); + return $sRet; + + case 'YEAR': + if (count($this->m_aArgs) != 1) + { + throw new \Exception("Function {$this->m_sVerb} requires 1 argument"); + } + $iRet = (int) date('Y', strtotime($this->m_aArgs[0]->Evaluate($aArgs))); + return $iRet; + + case 'MONTH': + if (count($this->m_aArgs) != 1) + { + throw new \Exception("Function {$this->m_sVerb} requires 1 argument"); + } + $iRet = (int) date('m', strtotime($this->m_aArgs[0]->Evaluate($aArgs))); + return $iRet; + + case 'DAY': + if (count($this->m_aArgs) != 1) + { + throw new \Exception("Function {$this->m_sVerb} requires 1 argument"); + } + $iRet = (int) date('d', strtotime($this->m_aArgs[0]->Evaluate($aArgs))); + return $iRet; + + case 'DATE_FORMAT': + if (count($this->m_aArgs) != 2) + { + throw new \Exception("Function {$this->m_sVerb} requires 2 arguments"); + } + $oDate = new DateTime($this->m_aArgs[0]->Evaluate($aArgs)); + $sFormat = $this->m_aArgs[1]->Evaluate($aArgs); + $sFormat = str_replace( + array('%y', '%x', '%w', '%W', '%v', '%T', '%S', '%r', '%p', '%M', '%l', '%k', '%I', '%h', '%b', '%a', '%D', '%c', '%e', '%Y', '%d', '%m', '%H', '%i', '%s'), + array('y', 'o', 'w', 'l', 'W', 'H:i:s', 's', 'h:i:s A', 'A', 'F', 'g', 'H', 'h', 'h','M', 'D', 'jS', 'n', 'j', 'Y', 'd', 'm', 'H', 'i', 's'), + $sFormat); + if (preg_match('/%j/', $sFormat)) + { + $sFormat = str_replace('%j', date_format($oDate, 'z') + 1, $sFormat); + } + if (preg_match('/%[fUuVX]/', $sFormat)) + { + throw new NotYetEvaluatedExpression("Expression ".$this->RenderExpression().' cannot be evaluated (known limitation)'); + } + $sRet = date_format($oDate, $sFormat); + return $sRet; + + case 'TO_DAYS': + if (count($this->m_aArgs) != 1) + { + throw new \Exception("Function {$this->m_sVerb} requires 1 argument"); + } + $oDate = new DateTime($this->m_aArgs[0]->Evaluate($aArgs)); + $oZero = new DateTime('1582-01-01'); + $iRet = (int) $oDate->diff($oZero)->format('%a') + 577815; + return $iRet; + + case 'FROM_DAYS': + if (count($this->m_aArgs) != 1) + { + throw new \Exception("Function {$this->m_sVerb} requires 1 argument"); + } + $iSince1582 = $this->m_aArgs[0]->Evaluate($aArgs) - 577814; + $oDate = new DateTime("1582-01-01 +$iSince1582 days"); + $sRet = $oDate->format('Y-m-d'); + return $sRet; + + case 'NOW': + $sRet = date('Y-m-d H:i:s'); + return $sRet; + + case 'CURRENT_DATE': + $sRet = date('Y-m-d'); + return $sRet; + + case 'DATE_ADD': + if (count($this->m_aArgs) != 2) + { + throw new \Exception("Function {$this->m_sVerb} requires 2 arguments"); + } + $sStartDate = $this->m_aArgs[0]->Evaluate($aArgs); + $sInterval = $this->m_aArgs[1]->Evaluate($aArgs); + $oDate = new DateTime("$sStartDate +$sInterval"); + $sRet = $oDate->format('Y-m-d H:i:s'); + return $sRet; + + case 'DATE_SUB': + if (count($this->m_aArgs) != 2) + { + throw new \Exception("Function {$this->m_sVerb} requires 2 arguments"); + } + $sStartDate = $this->m_aArgs[0]->Evaluate($aArgs); + $sInterval = $this->m_aArgs[1]->Evaluate($aArgs); + $oDate = new DateTime("$sStartDate -$sInterval"); + $sRet = $oDate->format('Y-m-d H:i:s'); + return $sRet; + + default: + throw new Exception("Function {$this->m_sVerb} cannot be evaluated -unhandled yet"); + } + } + /** * {@inheritDoc} * @see Expression::ToJSON() @@ -2289,6 +2849,17 @@ class FunctionExpression extends Expression return $aRes; } + public function ListParameters() + { + + $aRes = array(); + foreach ($this->m_aArgs as $oExpr) + { + $aRes = array_merge($aRes, $oExpr->ListParameters()); + } + return $aRes; + } + public function RenameParam($sOldName, $sNewName) { foreach ($this->m_aArgs as $key => $oExpr) @@ -2514,6 +3085,17 @@ class IntervalExpression extends Expression return 'INTERVAL '.$this->m_oValue->RenderExpression($bForSQL, $aArgs, $bRetrofitParams).' '.$this->m_sUnit; } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + $iValue = $this->m_oValue->Evaluate($aArgs); + return "$iValue {$this->m_sUnit}"; + } + /** * {@inheritDoc} * @see Expression::ToJSON() @@ -2570,6 +3152,11 @@ class IntervalExpression extends Expression return array(); } + public function ListParameters() + { + return $this->m_oValue->ListParameters(); + } + public function RenameParam($sOldName, $sNewName) { $this->m_oValue->RenameParam($sOldName, $sNewName); @@ -2627,6 +3214,21 @@ class CharConcatExpression extends Expression return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)"; } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + $sRet = ''; + foreach ($this->m_aExpressions as $oExpr) + { + $sRet .= $oExpr->Evaluate($aArgs); + } + return $sRet; + } + /** * {@inheritDoc} * @see Expression::ToJSON() @@ -2716,6 +3318,16 @@ class CharConcatExpression extends Expression return $aRes; } + public function ListParameters() + { + $aRes = array(); + foreach ($this->m_aExpressions as $oExpr) + { + $aRes = array_merge($aRes, $oExpr->ListParameters()); + } + return $aRes; + } + public function RenameParam($sOldName, $sNewName) { foreach ($this->m_aExpressions as $key => $oExpr) @@ -2758,6 +3370,21 @@ class CharConcatWSExpression extends CharConcatExpression return "CAST(CONCAT_WS($sSep, ".implode(', ', $aRes).") AS CHAR)"; } + /** + * Evaluate the value of the expression + * @param array $aArgs + * @throws \Exception if terms cannot be evaluated as scalars +*/ + public function Evaluate(array $aArgs) + { + $aRes = array(); + foreach ($this->m_aExpressions as $oExpr) + { + $aRes .= $oExpr->Evaluate($aArgs); + } + return implode($this->m_separator, $aRes); + } + public function Browse(Closure $callback) { $callback($this); diff --git a/core/ormlinkset.class.inc.php b/core/ormlinkset.class.inc.php index 0c030b161..afd0dec02 100644 --- a/core/ormlinkset.class.inc.php +++ b/core/ormlinkset.class.inc.php @@ -30,6 +30,9 @@ require_once('dbobjectiterator.php'); class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator { + public const LINK_ALIAS = 'Link'; + public const REMOTE_ALIAS = 'Remote'; + protected $sHostClass; // subclass of DBObject protected $sAttCode; // xxxxxx_list protected $sClass; // class of the links @@ -145,7 +148,8 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator /** * @param DBObject $oObject * @param string $sClassAlias - * @deprecated Since iTop 2.4, use ormLinkset->AddItem() instead. + * + * @deprecated Since iTop 2.4, use {@link \ormLinkSet::AddItem()} instead. */ public function AddObject(DBObject $oObject, $sClassAlias = '') { @@ -785,11 +789,13 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator /** * @param bool $bShowObsolete * - * @return \DBObjectSet + * @return \DBObjectSet indirect relations will get `SELECT L,R ...` (l = lnk class, R = remote) * @throws \CoreException * @throws \CoreWarning * @throws \MySQLException * @throws \Exception + * + * @since 2.8.0 N°2334 returns both lnk and remote classes for indirect relations */ public function ToDBObjectSet($bShowObsolete = true) { @@ -801,7 +807,11 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator $sExtKeyToRemote = $oAttDef->GetExtKeyToRemote(); /** @var \AttributeExternalKey $oLinkingAttDef */ $oLinkingAttDef = MetaModel::GetAttributeDef($this->sClass, $sExtKeyToRemote); + + // N°2334 add pointed class (SELECT L,R) to have all fields (lnk + remote) in display + // the pointed class is always present in the search, as generated by \AttributeLinkedSet::GetDefaultValue $sTargetClass = $oLinkingAttDef->GetTargetClass(); + $oRemoteClassSearch = new DBObjectSearch($sTargetClass); if (!$bShowObsolete && MetaModel::IsObsoletable($sTargetClass)) { $oNotObsolete = new BinaryExpression( @@ -809,10 +819,12 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator '=', new ScalarExpression(0) ); - $oNotObsoleteRemote = new DBObjectSearch($sTargetClass); - $oNotObsoleteRemote->AddConditionExpression($oNotObsolete); - $oLinkSearch->AddCondition_PointingTo($oNotObsoleteRemote, $sExtKeyToRemote); + $oRemoteClassSearch->AddConditionExpression($oNotObsolete); } + $oLinkSearch->AddCondition_PointingTo($oRemoteClassSearch, $sExtKeyToRemote); + $oLinkSearch->RenameAlias($oLinkSearch->GetClassAlias(), self::LINK_ALIAS); + $oLinkSearch->RenameAlias($sTargetClass, self::REMOTE_ALIAS); + $oLinkSearch->SetSelectedClasses([self::LINK_ALIAS, self::REMOTE_ALIAS]); } $oLinkSet = new DBObjectSet($oLinkSearch); $oLinkSet->SetShowObsoleteData($bShowObsolete); diff --git a/core/valuesetdef.class.inc.php b/core/valuesetdef.class.inc.php index 1aa742c0b..0299f5c1e 100644 --- a/core/valuesetdef.class.inc.php +++ b/core/valuesetdef.class.inc.php @@ -141,7 +141,10 @@ class ValueSetObjects extends ValueSetDefinition $this->m_oExtraCondition = $oFilter; $this->m_bIsLoaded = false; } - + public function SetOrderBy(array $aOrderBy) + { + $this->m_aOrderBy = $aOrderBy; + } public function ToObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null) { if ($this->m_bAllowAllData) @@ -225,6 +228,7 @@ class ValueSetObjects extends ValueSetDefinition else { $oFilter = DBObjectSearch::FromOQL($this->m_sFilterExpr); + $oFilter->SetShowObsoleteData(utils::ShowObsoleteData()); } if (!$oFilter) return false; if (!is_null($this->m_oExtraCondition)) @@ -242,7 +246,7 @@ class ValueSetObjects extends ValueSetDefinition $oExpression = DBObjectSearch::GetPolymorphicExpression($oFilter->GetClass(), 'friendlyname'); $aFields = $oExpression->ListRequiredFields(); $sClass = $oFilter->GetClass(); - foreach($aFields as $sField) + /*foreach($aFields as $sField) { $aFieldItems = explode('.', $sField); if ($aFieldItems[0] != $sClass) @@ -250,7 +254,7 @@ class ValueSetObjects extends ValueSetDefinition $sOperation = 'contains'; break; } - } + }*/ switch ($sOperation) { diff --git a/css/css-variables.scss b/css/css-variables.scss index b0321266c..b017a9cf2 100644 --- a/css/css-variables.scss +++ b/css/css-variables.scss @@ -15,6 +15,10 @@ * * You should have received a copy of the GNU Affero General Public License */ + + +// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0 +$version: "v2.8.0"; $approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css // Base colors @@ -115,6 +119,9 @@ $primary-text-color: #333333 !default; $secondary-text-color: $grey-color !default; $error-text-color: $white !default; $highlight-text-color: #363636 !default; +$button-content-background-color: $gray-extra-light !default; +$button-header-background-color: $gray-extra-light !default; +$main-menu-background-color: $gray-extra-light !default; $hover-background-color: #fde17c !default; $border-highlight-color: $brand-primary-dark !default; $highlight-item-color: $white !default; diff --git a/css/font-awesome/css/font-awesome.min.css b/css/font-awesome/css/font-awesome.min.css deleted file mode 100644 index 2a00d8187..000000000 --- a/css/font-awesome/css/font-awesome.min.css +++ /dev/null @@ -1,7 +0,0 @@ -/** - This was the file containing the rules of Font Awesome v4. Kept for scripts that included it directly. - @deprecated 2.7.0 N°2269, use /css/font-awesome/css/all.min.css instead (Font Awesome v5) - */ - -@import "all.min.css"; -@import "v4-shims.min.css"; diff --git a/css/font-awesome/css/v4-shims.min.css b/css/font-awesome/css/v4-shims.min.css deleted file mode 100644 index e056e3186..000000000 --- a/css/font-awesome/css/v4-shims.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Font Awesome Free 5.12.0 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - */ -.fa.fa-glass:before{content:"\f000"}.fa.fa-meetup{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-star-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-o:before{content:"\f005"}.fa.fa-close:before,.fa.fa-remove:before{content:"\f00d"}.fa.fa-gear:before{content:"\f013"}.fa.fa-trash-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-trash-o:before{content:"\f2ed"}.fa.fa-file-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-o:before{content:"\f15b"}.fa.fa-clock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-clock-o:before{content:"\f017"}.fa.fa-arrow-circle-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-down:before{content:"\f358"}.fa.fa-arrow-circle-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-up:before{content:"\f35b"}.fa.fa-play-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-play-circle-o:before{content:"\f144"}.fa.fa-repeat:before,.fa.fa-rotate-right:before{content:"\f01e"}.fa.fa-refresh:before{content:"\f021"}.fa.fa-list-alt{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-dedent:before{content:"\f03b"}.fa.fa-video-camera:before{content:"\f03d"}.fa.fa-picture-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-picture-o:before{content:"\f03e"}.fa.fa-photo{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-photo:before{content:"\f03e"}.fa.fa-image{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-image:before{content:"\f03e"}.fa.fa-pencil:before{content:"\f303"}.fa.fa-map-marker:before{content:"\f3c5"}.fa.fa-pencil-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-pencil-square-o:before{content:"\f044"}.fa.fa-share-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-share-square-o:before{content:"\f14d"}.fa.fa-check-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-check-square-o:before{content:"\f14a"}.fa.fa-arrows:before{content:"\f0b2"}.fa.fa-times-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-circle-o:before{content:"\f057"}.fa.fa-check-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-check-circle-o:before{content:"\f058"}.fa.fa-mail-forward:before{content:"\f064"}.fa.fa-expand:before{content:"\f424"}.fa.fa-compress:before{content:"\f422"}.fa.fa-eye,.fa.fa-eye-slash{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-warning:before{content:"\f071"}.fa.fa-calendar:before{content:"\f073"}.fa.fa-arrows-v:before{content:"\f338"}.fa.fa-arrows-h:before{content:"\f337"}.fa.fa-bar-chart{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bar-chart:before{content:"\f080"}.fa.fa-bar-chart-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bar-chart-o:before{content:"\f080"}.fa.fa-facebook-square,.fa.fa-twitter-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-gears:before{content:"\f085"}.fa.fa-thumbs-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-thumbs-o-up:before{content:"\f164"}.fa.fa-thumbs-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-thumbs-o-down:before{content:"\f165"}.fa.fa-heart-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-heart-o:before{content:"\f004"}.fa.fa-sign-out:before{content:"\f2f5"}.fa.fa-linkedin-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-linkedin-square:before{content:"\f08c"}.fa.fa-thumb-tack:before{content:"\f08d"}.fa.fa-external-link:before{content:"\f35d"}.fa.fa-sign-in:before{content:"\f2f6"}.fa.fa-github-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-lemon-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-lemon-o:before{content:"\f094"}.fa.fa-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-square-o:before{content:"\f0c8"}.fa.fa-bookmark-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bookmark-o:before{content:"\f02e"}.fa.fa-facebook,.fa.fa-twitter{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook:before{content:"\f39e"}.fa.fa-facebook-f{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook-f:before{content:"\f39e"}.fa.fa-github{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-credit-card{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-feed:before{content:"\f09e"}.fa.fa-hdd-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hdd-o:before{content:"\f0a0"}.fa.fa-hand-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-right:before{content:"\f0a4"}.fa.fa-hand-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-left:before{content:"\f0a5"}.fa.fa-hand-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-up:before{content:"\f0a6"}.fa.fa-hand-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-down:before{content:"\f0a7"}.fa.fa-arrows-alt:before{content:"\f31e"}.fa.fa-group:before{content:"\f0c0"}.fa.fa-chain:before{content:"\f0c1"}.fa.fa-scissors:before{content:"\f0c4"}.fa.fa-files-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-files-o:before{content:"\f0c5"}.fa.fa-floppy-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-floppy-o:before{content:"\f0c7"}.fa.fa-navicon:before,.fa.fa-reorder:before{content:"\f0c9"}.fa.fa-google-plus,.fa.fa-google-plus-square,.fa.fa-pinterest,.fa.fa-pinterest-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus:before{content:"\f0d5"}.fa.fa-money{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-money:before{content:"\f3d1"}.fa.fa-unsorted:before{content:"\f0dc"}.fa.fa-sort-desc:before{content:"\f0dd"}.fa.fa-sort-asc:before{content:"\f0de"}.fa.fa-linkedin{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-linkedin:before{content:"\f0e1"}.fa.fa-rotate-left:before{content:"\f0e2"}.fa.fa-legal:before{content:"\f0e3"}.fa.fa-dashboard:before,.fa.fa-tachometer:before{content:"\f3fd"}.fa.fa-comment-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-comment-o:before{content:"\f075"}.fa.fa-comments-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-comments-o:before{content:"\f086"}.fa.fa-flash:before{content:"\f0e7"}.fa.fa-clipboard,.fa.fa-paste{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-paste:before{content:"\f328"}.fa.fa-lightbulb-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-lightbulb-o:before{content:"\f0eb"}.fa.fa-exchange:before{content:"\f362"}.fa.fa-cloud-download:before{content:"\f381"}.fa.fa-cloud-upload:before{content:"\f382"}.fa.fa-bell-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bell-o:before{content:"\f0f3"}.fa.fa-cutlery:before{content:"\f2e7"}.fa.fa-file-text-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-text-o:before{content:"\f15c"}.fa.fa-building-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-building-o:before{content:"\f1ad"}.fa.fa-hospital-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hospital-o:before{content:"\f0f8"}.fa.fa-tablet:before{content:"\f3fa"}.fa.fa-mobile-phone:before,.fa.fa-mobile:before{content:"\f3cd"}.fa.fa-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-circle-o:before{content:"\f111"}.fa.fa-mail-reply:before{content:"\f3e5"}.fa.fa-github-alt{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-folder-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-folder-o:before{content:"\f07b"}.fa.fa-folder-open-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-folder-open-o:before{content:"\f07c"}.fa.fa-smile-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-smile-o:before{content:"\f118"}.fa.fa-frown-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-frown-o:before{content:"\f119"}.fa.fa-meh-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-meh-o:before{content:"\f11a"}.fa.fa-keyboard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-keyboard-o:before{content:"\f11c"}.fa.fa-flag-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-flag-o:before{content:"\f024"}.fa.fa-mail-reply-all:before{content:"\f122"}.fa.fa-star-half-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-o:before{content:"\f089"}.fa.fa-star-half-empty{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-empty:before{content:"\f089"}.fa.fa-star-half-full{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-full:before{content:"\f089"}.fa.fa-code-fork:before{content:"\f126"}.fa.fa-chain-broken:before{content:"\f127"}.fa.fa-shield:before{content:"\f3ed"}.fa.fa-calendar-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-o:before{content:"\f133"}.fa.fa-css3,.fa.fa-html5,.fa.fa-maxcdn{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ticket:before{content:"\f3ff"}.fa.fa-minus-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-minus-square-o:before{content:"\f146"}.fa.fa-level-up:before{content:"\f3bf"}.fa.fa-level-down:before{content:"\f3be"}.fa.fa-pencil-square:before{content:"\f14b"}.fa.fa-external-link-square:before{content:"\f360"}.fa.fa-compass{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-down:before{content:"\f150"}.fa.fa-toggle-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-down:before{content:"\f150"}.fa.fa-caret-square-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-up:before{content:"\f151"}.fa.fa-toggle-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-up:before{content:"\f151"}.fa.fa-caret-square-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-right:before{content:"\f152"}.fa.fa-toggle-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-right:before{content:"\f152"}.fa.fa-eur:before,.fa.fa-euro:before{content:"\f153"}.fa.fa-gbp:before{content:"\f154"}.fa.fa-dollar:before,.fa.fa-usd:before{content:"\f155"}.fa.fa-inr:before,.fa.fa-rupee:before{content:"\f156"}.fa.fa-cny:before,.fa.fa-jpy:before,.fa.fa-rmb:before,.fa.fa-yen:before{content:"\f157"}.fa.fa-rouble:before,.fa.fa-rub:before,.fa.fa-ruble:before{content:"\f158"}.fa.fa-krw:before,.fa.fa-won:before{content:"\f159"}.fa.fa-bitcoin,.fa.fa-btc{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bitcoin:before{content:"\f15a"}.fa.fa-file-text:before{content:"\f15c"}.fa.fa-sort-alpha-asc:before{content:"\f15d"}.fa.fa-sort-alpha-desc:before{content:"\f881"}.fa.fa-sort-amount-asc:before{content:"\f160"}.fa.fa-sort-amount-desc:before{content:"\f884"}.fa.fa-sort-numeric-asc:before{content:"\f162"}.fa.fa-sort-numeric-desc:before{content:"\f886"}.fa.fa-xing,.fa.fa-xing-square,.fa.fa-youtube,.fa.fa-youtube-play,.fa.fa-youtube-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-youtube-play:before{content:"\f167"}.fa.fa-adn,.fa.fa-bitbucket,.fa.fa-bitbucket-square,.fa.fa-dropbox,.fa.fa-flickr,.fa.fa-instagram,.fa.fa-stack-overflow{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bitbucket-square:before{content:"\f171"}.fa.fa-tumblr,.fa.fa-tumblr-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-long-arrow-down:before{content:"\f309"}.fa.fa-long-arrow-up:before{content:"\f30c"}.fa.fa-long-arrow-left:before{content:"\f30a"}.fa.fa-long-arrow-right:before{content:"\f30b"}.fa.fa-android,.fa.fa-apple,.fa.fa-dribbble,.fa.fa-foursquare,.fa.fa-gittip,.fa.fa-gratipay,.fa.fa-linux,.fa.fa-skype,.fa.fa-trello,.fa.fa-windows{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-gittip:before{content:"\f184"}.fa.fa-sun-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-sun-o:before{content:"\f185"}.fa.fa-moon-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-moon-o:before{content:"\f186"}.fa.fa-pagelines,.fa.fa-renren,.fa.fa-stack-exchange,.fa.fa-vk,.fa.fa-weibo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-arrow-circle-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-right:before{content:"\f35a"}.fa.fa-arrow-circle-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-left:before{content:"\f359"}.fa.fa-caret-square-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-left:before{content:"\f191"}.fa.fa-toggle-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-left:before{content:"\f191"}.fa.fa-dot-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-dot-circle-o:before{content:"\f192"}.fa.fa-vimeo-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-try:before,.fa.fa-turkish-lira:before{content:"\f195"}.fa.fa-plus-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-plus-square-o:before{content:"\f0fe"}.fa.fa-openid,.fa.fa-slack,.fa.fa-wordpress{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bank:before,.fa.fa-institution:before{content:"\f19c"}.fa.fa-mortar-board:before{content:"\f19d"}.fa.fa-delicious,.fa.fa-digg,.fa.fa-drupal,.fa.fa-google,.fa.fa-joomla,.fa.fa-pied-piper-alt,.fa.fa-pied-piper-pp,.fa.fa-reddit,.fa.fa-reddit-square,.fa.fa-stumbleupon,.fa.fa-stumbleupon-circle,.fa.fa-yahoo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-spoon:before{content:"\f2e5"}.fa.fa-behance,.fa.fa-behance-square,.fa.fa-steam,.fa.fa-steam-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-automobile:before{content:"\f1b9"}.fa.fa-envelope-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-envelope-o:before{content:"\f0e0"}.fa.fa-deviantart,.fa.fa-soundcloud,.fa.fa-spotify{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-file-pdf-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-pdf-o:before{content:"\f1c1"}.fa.fa-file-word-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-word-o:before{content:"\f1c2"}.fa.fa-file-excel-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-excel-o:before{content:"\f1c3"}.fa.fa-file-powerpoint-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-powerpoint-o:before{content:"\f1c4"}.fa.fa-file-image-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-image-o:before{content:"\f1c5"}.fa.fa-file-photo-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-photo-o:before{content:"\f1c5"}.fa.fa-file-picture-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-picture-o:before{content:"\f1c5"}.fa.fa-file-archive-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-archive-o:before{content:"\f1c6"}.fa.fa-file-zip-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-zip-o:before{content:"\f1c6"}.fa.fa-file-audio-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-audio-o:before{content:"\f1c7"}.fa.fa-file-sound-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-sound-o:before{content:"\f1c7"}.fa.fa-file-video-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-video-o:before{content:"\f1c8"}.fa.fa-file-movie-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-movie-o:before{content:"\f1c8"}.fa.fa-file-code-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-code-o:before{content:"\f1c9"}.fa.fa-codepen,.fa.fa-jsfiddle,.fa.fa-vine{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-life-bouy,.fa.fa-life-ring{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-bouy:before{content:"\f1cd"}.fa.fa-life-buoy{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-buoy:before{content:"\f1cd"}.fa.fa-life-saver{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-saver:before{content:"\f1cd"}.fa.fa-support{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-support:before{content:"\f1cd"}.fa.fa-circle-o-notch:before{content:"\f1ce"}.fa.fa-ra,.fa.fa-rebel{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ra:before{content:"\f1d0"}.fa.fa-resistance{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-resistance:before{content:"\f1d0"}.fa.fa-empire,.fa.fa-ge{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ge:before{content:"\f1d1"}.fa.fa-git,.fa.fa-git-square,.fa.fa-hacker-news,.fa.fa-y-combinator-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-y-combinator-square:before{content:"\f1d4"}.fa.fa-yc-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-yc-square:before{content:"\f1d4"}.fa.fa-qq,.fa.fa-tencent-weibo,.fa.fa-wechat,.fa.fa-weixin{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-wechat:before{content:"\f1d7"}.fa.fa-send:before{content:"\f1d8"}.fa.fa-paper-plane-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-paper-plane-o:before{content:"\f1d8"}.fa.fa-send-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-send-o:before{content:"\f1d8"}.fa.fa-circle-thin{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-circle-thin:before{content:"\f111"}.fa.fa-header:before{content:"\f1dc"}.fa.fa-sliders:before{content:"\f1de"}.fa.fa-futbol-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-futbol-o:before{content:"\f1e3"}.fa.fa-soccer-ball-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-soccer-ball-o:before{content:"\f1e3"}.fa.fa-slideshare,.fa.fa-twitch,.fa.fa-yelp{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-newspaper-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-newspaper-o:before{content:"\f1ea"}.fa.fa-cc-amex,.fa.fa-cc-discover,.fa.fa-cc-mastercard,.fa.fa-cc-paypal,.fa.fa-cc-stripe,.fa.fa-cc-visa,.fa.fa-google-wallet,.fa.fa-paypal{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bell-slash-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bell-slash-o:before{content:"\f1f6"}.fa.fa-trash:before{content:"\f2ed"}.fa.fa-copyright{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-eyedropper:before{content:"\f1fb"}.fa.fa-area-chart:before{content:"\f1fe"}.fa.fa-pie-chart:before{content:"\f200"}.fa.fa-line-chart:before{content:"\f201"}.fa.fa-angellist,.fa.fa-ioxhost,.fa.fa-lastfm,.fa.fa-lastfm-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-cc{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-cc:before{content:"\f20a"}.fa.fa-ils:before,.fa.fa-shekel:before,.fa.fa-sheqel:before{content:"\f20b"}.fa.fa-meanpath{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-meanpath:before{content:"\f2b4"}.fa.fa-buysellads,.fa.fa-connectdevelop,.fa.fa-dashcube,.fa.fa-forumbee,.fa.fa-leanpub,.fa.fa-sellsy,.fa.fa-shirtsinbulk,.fa.fa-simplybuilt,.fa.fa-skyatlas{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-diamond{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-diamond:before{content:"\f3a5"}.fa.fa-intersex:before{content:"\f224"}.fa.fa-facebook-official{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook-official:before{content:"\f09a"}.fa.fa-pinterest-p,.fa.fa-whatsapp{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-hotel:before{content:"\f236"}.fa.fa-medium,.fa.fa-viacoin,.fa.fa-y-combinator,.fa.fa-yc{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-yc:before{content:"\f23b"}.fa.fa-expeditedssl,.fa.fa-opencart,.fa.fa-optin-monster{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-battery-4:before,.fa.fa-battery:before{content:"\f240"}.fa.fa-battery-3:before{content:"\f241"}.fa.fa-battery-2:before{content:"\f242"}.fa.fa-battery-1:before{content:"\f243"}.fa.fa-battery-0:before{content:"\f244"}.fa.fa-object-group,.fa.fa-object-ungroup,.fa.fa-sticky-note-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-sticky-note-o:before{content:"\f249"}.fa.fa-cc-diners-club,.fa.fa-cc-jcb{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-clone,.fa.fa-hourglass-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hourglass-o:before{content:"\f254"}.fa.fa-hourglass-1:before{content:"\f251"}.fa.fa-hourglass-2:before{content:"\f252"}.fa.fa-hourglass-3:before{content:"\f253"}.fa.fa-hand-rock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-rock-o:before{content:"\f255"}.fa.fa-hand-grab-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-grab-o:before{content:"\f255"}.fa.fa-hand-paper-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-paper-o:before{content:"\f256"}.fa.fa-hand-stop-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-stop-o:before{content:"\f256"}.fa.fa-hand-scissors-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-scissors-o:before{content:"\f257"}.fa.fa-hand-lizard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-lizard-o:before{content:"\f258"}.fa.fa-hand-spock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-spock-o:before{content:"\f259"}.fa.fa-hand-pointer-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-pointer-o:before{content:"\f25a"}.fa.fa-hand-peace-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-peace-o:before{content:"\f25b"}.fa.fa-registered{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-chrome,.fa.fa-creative-commons,.fa.fa-firefox,.fa.fa-get-pocket,.fa.fa-gg,.fa.fa-gg-circle,.fa.fa-internet-explorer,.fa.fa-odnoklassniki,.fa.fa-odnoklassniki-square,.fa.fa-opera,.fa.fa-safari,.fa.fa-tripadvisor,.fa.fa-wikipedia-w{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-television:before{content:"\f26c"}.fa.fa-500px,.fa.fa-amazon,.fa.fa-contao{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-calendar-plus-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-plus-o:before{content:"\f271"}.fa.fa-calendar-minus-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-minus-o:before{content:"\f272"}.fa.fa-calendar-times-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-times-o:before{content:"\f273"}.fa.fa-calendar-check-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-check-o:before{content:"\f274"}.fa.fa-map-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-map-o:before{content:"\f279"}.fa.fa-commenting:before{content:"\f4ad"}.fa.fa-commenting-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-commenting-o:before{content:"\f4ad"}.fa.fa-houzz,.fa.fa-vimeo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-vimeo:before{content:"\f27d"}.fa.fa-black-tie,.fa.fa-edge,.fa.fa-fonticons,.fa.fa-reddit-alien{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-credit-card-alt:before{content:"\f09d"}.fa.fa-codiepie,.fa.fa-fort-awesome,.fa.fa-mixcloud,.fa.fa-modx,.fa.fa-product-hunt,.fa.fa-scribd,.fa.fa-usb{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-pause-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-pause-circle-o:before{content:"\f28b"}.fa.fa-stop-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-stop-circle-o:before{content:"\f28d"}.fa.fa-bluetooth,.fa.fa-bluetooth-b,.fa.fa-envira,.fa.fa-gitlab,.fa.fa-wheelchair-alt,.fa.fa-wpbeginner,.fa.fa-wpforms{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-wheelchair-alt:before{content:"\f368"}.fa.fa-question-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-question-circle-o:before{content:"\f059"}.fa.fa-volume-control-phone:before{content:"\f2a0"}.fa.fa-asl-interpreting:before{content:"\f2a3"}.fa.fa-deafness:before,.fa.fa-hard-of-hearing:before{content:"\f2a4"}.fa.fa-glide,.fa.fa-glide-g{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-signing:before{content:"\f2a7"}.fa.fa-first-order,.fa.fa-google-plus-official,.fa.fa-pied-piper,.fa.fa-snapchat,.fa.fa-snapchat-ghost,.fa.fa-snapchat-square,.fa.fa-themeisle,.fa.fa-viadeo,.fa.fa-viadeo-square,.fa.fa-yoast{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus-official:before{content:"\f2b3"}.fa.fa-google-plus-circle{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus-circle:before{content:"\f2b3"}.fa.fa-fa,.fa.fa-font-awesome{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-fa:before{content:"\f2b4"}.fa.fa-handshake-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-handshake-o:before{content:"\f2b5"}.fa.fa-envelope-open-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-envelope-open-o:before{content:"\f2b6"}.fa.fa-linode{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-address-book-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-address-book-o:before{content:"\f2b9"}.fa.fa-vcard:before{content:"\f2bb"}.fa.fa-address-card-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-address-card-o:before{content:"\f2bb"}.fa.fa-vcard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-vcard-o:before{content:"\f2bb"}.fa.fa-user-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-user-circle-o:before{content:"\f2bd"}.fa.fa-user-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-user-o:before{content:"\f007"}.fa.fa-id-badge{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-drivers-license:before{content:"\f2c2"}.fa.fa-id-card-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-id-card-o:before{content:"\f2c2"}.fa.fa-drivers-license-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-drivers-license-o:before{content:"\f2c2"}.fa.fa-free-code-camp,.fa.fa-quora,.fa.fa-telegram{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-thermometer-4:before,.fa.fa-thermometer:before{content:"\f2c7"}.fa.fa-thermometer-3:before{content:"\f2c8"}.fa.fa-thermometer-2:before{content:"\f2c9"}.fa.fa-thermometer-1:before{content:"\f2ca"}.fa.fa-thermometer-0:before{content:"\f2cb"}.fa.fa-bathtub:before,.fa.fa-s15:before{content:"\f2cd"}.fa.fa-window-maximize,.fa.fa-window-restore{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-rectangle:before{content:"\f410"}.fa.fa-window-close-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-window-close-o:before{content:"\f410"}.fa.fa-times-rectangle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-rectangle-o:before{content:"\f410"}.fa.fa-bandcamp,.fa.fa-eercast,.fa.fa-etsy,.fa.fa-grav,.fa.fa-imdb,.fa.fa-ravelry{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-eercast:before{content:"\f2da"}.fa.fa-snowflake-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-snowflake-o:before{content:"\f2dc"}.fa.fa-superpowers,.fa.fa-wpexplorer{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-cab:before{content:"\f1ba"} \ No newline at end of file diff --git a/css/light-grey.scss b/css/light-grey.scss index bf5446f6a..2bb5025b1 100644 --- a/css/light-grey.scss +++ b/css/light-grey.scss @@ -2690,7 +2690,7 @@ td.prop_icon { font-size: 16px; text-decoration: none; } -.dashlet-content .display_block { +.main_header h1 { text-align:left; } .dashlet-unknown, .dashlet-proxy { @@ -3903,4 +3903,64 @@ input:checked + .slider:before { } .ui-dialog .ui-dialog-content .treecontrol a { font-size: small; -} \ No newline at end of file +} +/*for autocomplete*/ +.ui-autocomplete { + padding: 0px; + background-color: white; + overflow: hidden; + z-index: 99999; + /*for scrollbar*/ + max-height: 180px; + overflow-y: auto; + /* prevent horizontal scrollbar */ + overflow-x: hidden; + + ul { + width: 100%; + list-style-position: outside; + list-style: none; + padding: 0; + margin: 0; + } + + li.ui-menu-item { + margin: 0px; + padding: 2px 5px; + white-space: nowrap; + padding-right: 20px; /* Space for the scrollbar */ + cursor: default; + display: block; + /* + if width will be 100% horizontal scrollbar will apear + when scroll mode will be used + */ + /*width: 100%;*/ + font: menu; + font-size: 12px; + /* + it is very important, if line-height not setted or setted + in relative units scroll will be broken in firefox + */ + line-height: 16px; + overflow: hidden; + + &:nth-child(odd) { + background-color: #eee; + } + + &.ui-state-focus { + background-color: #0A246A; + border-color: #0A246A; + color: white; + } + } +} +.field_autocomplete +{ + background: #fff url($approot-relative + "images/ac-background.gif?v=" + $version) no-repeat right; + border: 1px solid black; + &:focus{ + border: 2px solid black; + } +} diff --git a/css/ui-lightness/jqueryui.scss b/css/ui-lightness/jqueryui.scss index 873081358..37ebb2ec5 100644 --- a/css/ui-lightness/jqueryui.scss +++ b/css/ui-lightness/jqueryui.scss @@ -1024,7 +1024,7 @@ body { } .ui-state-default { border: 1px solid #cccccc; - background: #f1f1f1; + background: $button-content-background-color; font-weight: bold; color: $secondary-text-color; } @@ -1092,7 +1092,7 @@ body { } .ui-state-default { border: 1px solid #cccccc; - background: #f1f1f1; + background: $button-header-background-color; font-weight: bold; color: $secondary-text-color; } @@ -1152,7 +1152,7 @@ body { } .ui-state-default { border: 1px solid #cccccc; - background: #f1f1f1; + background: $main-menu-background-color; font-weight: bold; color: $secondary-text-color; a { diff --git a/data/web.config b/data/web.config index 599a5f260..58c9c3ac3 100644 --- a/data/web.config +++ b/data/web.config @@ -1,8 +1,13 @@ -<?xml version="1.0" encoding="utf-8" ?> +<?xml version="1.0" encoding="utf-8"?> <configuration> - <system.web> - <authorization> - <deny users="*" /> <!-- Denies all users --> - </authorization> - </system.web> + <system.webServer> + <security> + <requestFiltering> + <fileExtensions applyToWebDAV="false" allowUnlisted="false"></fileExtensions> + </requestFiltering> + <authorization> + <deny users="*" /> <!-- Denies all users --> + </authorization> + </security> + </system.webServer> </configuration> \ No newline at end of file diff --git a/datamodels/2.x/authent-cas/module.authent-cas.php b/datamodels/2.x/authent-cas/module.authent-cas.php index b0cf2aa1d..39c70f50a 100644 --- a/datamodels/2.x/authent-cas/module.authent-cas.php +++ b/datamodels/2.x/authent-cas/module.authent-cas.php @@ -5,7 +5,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'authent-cas/2.7.0', + 'authent-cas/2.8.0', array( // Identification // diff --git a/datamodels/2.x/authent-cas/src/CASLoginExtension.php b/datamodels/2.x/authent-cas/src/CASLoginExtension.php index cefc54b56..8489d06d6 100644 --- a/datamodels/2.x/authent-cas/src/CASLoginExtension.php +++ b/datamodels/2.x/authent-cas/src/CASLoginExtension.php @@ -19,7 +19,6 @@ use LoginTwigContext; use LoginWebPage; use MetaModel; use phpCAS; -use URP_UserProfile; use User; use UserExternal; use utils; @@ -58,18 +57,15 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte } else { - if ($_SESSION['login_mode'] == 'cas') + if (!isset($_SESSION['login_will_redirect'])) { - if (!isset($_SESSION['login_will_redirect'])) - { - $_SESSION['login_will_redirect'] = true; - } - else - { - unset($_SESSION['login_will_redirect']); - $iErrorCode = LoginWebPage::EXIT_CODE_MISSINGLOGIN; - return LoginWebPage::LOGIN_FSM_ERROR; - } + $_SESSION['login_will_redirect'] = true; + } + else + { + unset($_SESSION['login_will_redirect']); + $iErrorCode = LoginWebPage::EXIT_CODE_MISSINGLOGIN; + return LoginWebPage::LOGIN_FSM_ERROR; } $_SESSION['login_mode'] = 'cas'; phpCAS::forceAuthentication(); // Redirect to CAS and exit @@ -156,7 +152,7 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte { phpCAS::setDebug(APPROOT.'log/cas.log'); } - + // Initialize phpCAS $sCASVersion = Config::Get('cas_version'); $sCASHost = Config::Get('cas_host'); @@ -500,15 +496,8 @@ class CASUserProvisioning } // Now synchronize the profiles - $oProfilesSet = DBObjectSet::FromScratch('URP_UserProfile'); - foreach($aProfiles as $iProfileId) - { - $oLink = new URP_UserProfile(); - $oLink->Set('profileid', $iProfileId); - $oLink->Set('reason', 'CAS/LDAP Synchro'); - $oProfilesSet->AddObject($oLink); - } - $oUser->Set('profile_list', $oProfilesSet); + LoginWebPage::SynchroniseProfiles($oUser, $aProfiles, 'CAS/LDAP Synchro'); + phpCAS::log("Info: the user '".$oUser->GetName()."' (id=".$oUser->GetKey().") now has the following profiles: '".implode("', '", $aProfiles)."'."); if ($oUser->IsModified()) { diff --git a/datamodels/2.x/authent-external/module.authent-external.php b/datamodels/2.x/authent-external/module.authent-external.php index 3b64a078c..e0a0cd6c5 100755 --- a/datamodels/2.x/authent-external/module.authent-external.php +++ b/datamodels/2.x/authent-external/module.authent-external.php @@ -27,7 +27,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'authent-external/2.7.0', + 'authent-external/2.8.0', array( // Identification // diff --git a/datamodels/2.x/authent-ldap/cs.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/cs.dict.authent-ldap.php index 410cc9a5a..4639de1fc 100755 --- a/datamodels/2.x/authent-ldap/cs.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/cs.dict.authent-ldap.php @@ -38,6 +38,4 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'Class:UserLDAP' => 'LDAP uživatel', 'Class:UserLDAP+' => 'Uživatel ověřen přes LDAP', - 'Class:UserLDAP/Attribute:password' => 'Heslo', - 'Class:UserLDAP/Attribute:password+' => '', )); diff --git a/datamodels/2.x/authent-ldap/da.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/da.dict.authent-ldap.php index 9dcaab198..ab67ba012 100644 --- a/datamodels/2.x/authent-ldap/da.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/da.dict.authent-ldap.php @@ -23,6 +23,4 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'Class:UserLDAP' => 'LDAP-Bruger', 'Class:UserLDAP+' => 'Bruger der godkendes via LDAP', - 'Class:UserLDAP/Attribute:password' => 'Password', - 'Class:UserLDAP/Attribute:password+' => 'Brugerens password', )); diff --git a/datamodels/2.x/authent-ldap/de.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/de.dict.authent-ldap.php index c8a05f534..8ab279727 100755 --- a/datamodels/2.x/authent-ldap/de.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/de.dict.authent-ldap.php @@ -25,6 +25,4 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:UserLDAP' => 'LDAP-Benutzer', 'Class:UserLDAP+' => 'Benutzer, der über LDAP authentifiziert wird', - 'Class:UserLDAP/Attribute:password' => 'Passwort', - 'Class:UserLDAP/Attribute:password+' => 'Benutzerpasswort', )); diff --git a/datamodels/2.x/authent-ldap/en.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/en.dict.authent-ldap.php index 703cb64bf..6a9c66d06 100755 --- a/datamodels/2.x/authent-ldap/en.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/en.dict.authent-ldap.php @@ -38,6 +38,4 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:UserLDAP' => 'LDAP user', 'Class:UserLDAP+' => 'User authentified by LDAP', - 'Class:UserLDAP/Attribute:password' => 'Password', - 'Class:UserLDAP/Attribute:password+' => 'user authentication string', )); diff --git a/datamodels/2.x/authent-ldap/es_cr.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/es_cr.dict.authent-ldap.php index 605ffee80..6a7ded4ab 100644 --- a/datamodels/2.x/authent-ldap/es_cr.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/es_cr.dict.authent-ldap.php @@ -37,6 +37,4 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'Class:UserLDAP' => 'Usuario LDAP', 'Class:UserLDAP+' => 'Usuario Autenticado vía LDAP', - 'Class:UserLDAP/Attribute:password' => 'Contraseña', - 'Class:UserLDAP/Attribute:password+' => 'Contraseña', )); diff --git a/datamodels/2.x/authent-ldap/fr.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/fr.dict.authent-ldap.php index 0998b171a..2ade23ca4 100755 --- a/datamodels/2.x/authent-ldap/fr.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/fr.dict.authent-ldap.php @@ -22,6 +22,4 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Class:UserLDAP' => 'Utilisateur LDAP', 'Class:UserLDAP+' => 'Utilisateur authentifié par un serveur LDAP', - 'Class:UserLDAP/Attribute:password' => 'Mot de passe LDAP', - 'Class:UserLDAP/Attribute:password+' => '', )); diff --git a/datamodels/2.x/authent-ldap/hu.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/hu.dict.authent-ldap.php index 9f1a2f1e6..8c4dff067 100755 --- a/datamodels/2.x/authent-ldap/hu.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/hu.dict.authent-ldap.php @@ -22,6 +22,4 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'Class:UserLDAP' => 'LDAP felhasználó', 'Class:UserLDAP+' => '', - 'Class:UserLDAP/Attribute:password' => 'Jelszó', - 'Class:UserLDAP/Attribute:password+' => '', )); diff --git a/datamodels/2.x/authent-ldap/it.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/it.dict.authent-ldap.php index efd440d52..b3359b90f 100755 --- a/datamodels/2.x/authent-ldap/it.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/it.dict.authent-ldap.php @@ -36,6 +36,4 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'Class:UserLDAP' => 'Utente LDAP', 'Class:UserLDAP+' => 'Utente autenticato da LDAP', - 'Class:UserLDAP/Attribute:password' => 'Password', - 'Class:UserLDAP/Attribute:password+' => 'user authentication string', )); diff --git a/datamodels/2.x/authent-ldap/ja.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/ja.dict.authent-ldap.php index 3dd34329a..17423e2d8 100755 --- a/datamodels/2.x/authent-ldap/ja.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/ja.dict.authent-ldap.php @@ -23,6 +23,4 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'Class:UserLDAP' => 'LDAP ユーザー', 'Class:UserLDAP+' => 'LDAP認証ユーザー', - 'Class:UserLDAP/Attribute:password' => 'パスワード', - 'Class:UserLDAP/Attribute:password+' => '認証文字列', )); diff --git a/datamodels/2.x/authent-ldap/module.authent-ldap.php b/datamodels/2.x/authent-ldap/module.authent-ldap.php index 33bcf5303..4a3d5cdc4 100755 --- a/datamodels/2.x/authent-ldap/module.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/module.authent-ldap.php @@ -9,7 +9,7 @@ if (function_exists('ldap_connect')) SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'authent-ldap/2.7.0', + 'authent-ldap/2.8.0', array( // Identification // diff --git a/datamodels/2.x/authent-ldap/nl.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/nl.dict.authent-ldap.php index 1aee3d9e9..9daf55579 100644 --- a/datamodels/2.x/authent-ldap/nl.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/nl.dict.authent-ldap.php @@ -37,6 +37,4 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'Class:UserLDAP' => 'LDAP-gebruiker', 'Class:UserLDAP+' => 'Gebruiker die aanmeldt via LDAP', - 'Class:UserLDAP/Attribute:password' => 'Wachtwoord', - 'Class:UserLDAP/Attribute:password+' => 'Wachtwoord waarmee de gebruiker zich identificeert', )); diff --git a/datamodels/2.x/authent-ldap/pt_br.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/pt_br.dict.authent-ldap.php index 326a6ed98..192c1f078 100755 --- a/datamodels/2.x/authent-ldap/pt_br.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/pt_br.dict.authent-ldap.php @@ -22,6 +22,4 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'Class:UserLDAP' => 'Usuário externo via LDAP', 'Class:UserLDAP+' => '', - 'Class:UserLDAP/Attribute:password' => 'Senha', - 'Class:UserLDAP/Attribute:password+' => '', )); diff --git a/datamodels/2.x/authent-ldap/ru.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/ru.dict.authent-ldap.php index 584ade81d..b6b4a0276 100755 --- a/datamodels/2.x/authent-ldap/ru.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/ru.dict.authent-ldap.php @@ -14,6 +14,4 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Class:UserLDAP' => 'Пользователь LDAP', 'Class:UserLDAP+' => 'Пользователь, аутентифицируемый через LDAP', - 'Class:UserLDAP/Attribute:password' => 'Пароль', - 'Class:UserLDAP/Attribute:password+' => 'Строка аутентификации пользователя', )); diff --git a/datamodels/2.x/authent-ldap/sk.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/sk.dict.authent-ldap.php index ae0a8e6a1..aae7956e1 100644 --- a/datamodels/2.x/authent-ldap/sk.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/sk.dict.authent-ldap.php @@ -35,6 +35,4 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'Class:UserLDAP' => 'LDAP užívateľ', 'Class:UserLDAP+' => '', - 'Class:UserLDAP/Attribute:password' => 'Heslo', - 'Class:UserLDAP/Attribute:password+' => '', )); diff --git a/datamodels/2.x/authent-ldap/tr.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/tr.dict.authent-ldap.php index edde0993f..9f59149c1 100755 --- a/datamodels/2.x/authent-ldap/tr.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/tr.dict.authent-ldap.php @@ -37,6 +37,4 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'Class:UserLDAP' => 'LDAP kullanıcısı', 'Class:UserLDAP+' => 'Yetki kontrolü LDAP tarafından yapılan', - 'Class:UserLDAP/Attribute:password' => 'Şifre', - 'Class:UserLDAP/Attribute:password+' => 'şifre', )); diff --git a/datamodels/2.x/authent-ldap/zh_cn.dict.authent-ldap.php b/datamodels/2.x/authent-ldap/zh_cn.dict.authent-ldap.php index cb2214d7c..01907c2d4 100755 --- a/datamodels/2.x/authent-ldap/zh_cn.dict.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/zh_cn.dict.authent-ldap.php @@ -36,6 +36,4 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'Class:UserLDAP' => 'LDAP 用户', 'Class:UserLDAP+' => '用户身份由LDAP 认证', - 'Class:UserLDAP/Attribute:password' => '密码', - 'Class:UserLDAP/Attribute:password+' => '用于验证用户身份的字符串', )); diff --git a/datamodels/2.x/authent-local/module.authent-local.php b/datamodels/2.x/authent-local/module.authent-local.php index 09a2d9b62..1dd520fbf 100755 --- a/datamodels/2.x/authent-local/module.authent-local.php +++ b/datamodels/2.x/authent-local/module.authent-local.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'authent-local/2.7.0', + 'authent-local/2.8.0', array( // Identification // diff --git a/datamodels/2.x/authent-local/ru.dict.authent-local.php b/datamodels/2.x/authent-local/ru.dict.authent-local.php index d06536ec9..1a90b242b 100755 --- a/datamodels/2.x/authent-local/ru.dict.authent-local.php +++ b/datamodels/2.x/authent-local/ru.dict.authent-local.php @@ -30,5 +30,5 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Пароль должен содержать не менее 8 символов и включать прописные, строчные, числовые и специальные символы.', - 'UserLocal:password:expiration' => 'The fields below require an extension~~' + 'UserLocal:password:expiration' => 'Поля требуют наличия доп. расширения' )); diff --git a/datamodels/2.x/combodo-db-tools/cs.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/cs.dict.combodo-db-tools.php index f6719c0b6..46219432a 100644 --- a/datamodels/2.x/combodo-db-tools/cs.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/cs.dict.combodo-db-tools.php @@ -51,6 +51,8 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/da.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/da.dict.combodo-db-tools.php index 9adde2565..290e1c442 100644 --- a/datamodels/2.x/combodo-db-tools/da.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/da.dict.combodo-db-tools.php @@ -51,6 +51,8 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php b/datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php index d59ee7e7d..29772994e 100644 --- a/datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php +++ b/datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php @@ -57,6 +57,7 @@ class DatabaseAnalyzer if (!empty($sFixItRequest)) { $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = array($sFixItRequest); + $aErrorsAndFixes[$sClass][$sErrorDesc]['cleanup'] = array($sFixItRequest); } } else @@ -376,8 +377,18 @@ SQL; { $aFixIt = array(); $aFixIt[] = "-- Remove inconsistant entries:"; - $sIds = implode(', ', array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values'])); - $aFixIt[] = "DELETE `$sTable` FROM `$sTable` WHERE `$sTable`.`$sExtKeyField` IN ($sIds)"; + $iOffset = 0; + $iStep = 100; + do + { + $aIds = array_slice(array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']), $iOffset, $iStep); + $sIds = implode(', ', $aIds); + $sDelete = "DELETE `$sTable` FROM `$sTable` WHERE `$sTable`.`$sExtKeyField` IN ($sIds)"; + $aFixIt[] = $sDelete; + $aErrorsAndFixes[$sClass][$sErrorDesc]['cleanup'][] = $sDelete; + $iOffset += $iStep; + } + while (count($aIds) == $iStep); $aFixIt[] = ""; $aFixIt[] = "-- Or fix inconsistant values: Replace XXX with the appropriate value"; foreach (array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']) as $sKey) @@ -483,13 +494,15 @@ SQL; */ private function CheckUsers(&$aErrorsAndFixes) { - $sUserTable = MetaModel::DBGetTable('User'); + $sClass = 'User'; + $sUserTable = MetaModel::DBGetTable($sClass); $sLinkTable = MetaModel::DBGetTable('URP_UserProfile'); $sSelect = "SELECT DISTINCT u.id AS id, u.`login` AS value"; $sFilter = "FROM `$sUserTable` AS u LEFT JOIN `$sLinkTable` AS l ON l.userid = u.id WHERE l.id IS NULL"; $sSelWrongRecs = "$sSelect $sFilter"; $sFixit = "-- Remove the corresponding user(s)"; - $this->ExecQuery($sSelWrongRecs, $sFixit, Dict::S('DBAnalyzer-Integrity-UsersWithoutProfile'), 'User', $aErrorsAndFixes); + $sErrorDesc = Dict::S('DBAnalyzer-Integrity-UsersWithoutProfile'); + $this->ExecQuery($sSelWrongRecs, $sFixit, $sErrorDesc, $sClass, $aErrorsAndFixes); } diff --git a/datamodels/2.x/combodo-db-tools/dbtools.php b/datamodels/2.x/combodo-db-tools/dbtools.php index 72eaad23e..9a80d2f55 100644 --- a/datamodels/2.x/combodo-db-tools/dbtools.php +++ b/datamodels/2.x/combodo-db-tools/dbtools.php @@ -201,8 +201,10 @@ function DisplayInconsistenciesReport($aResults) { $sDBToolsFolder = str_replace("\\", '/', APPROOT.'log/'); $sReportFile = 'dbtools-report-'.date('Y-m-d-H-i-s'); + $sCleanupFile = 'dbtools-cleanup-'.date('Y-m-d-H-i-s'); $fReport = fopen($sDBToolsFolder.$sReportFile.'.txt', 'w'); + $fCleanUp = fopen($sDBToolsFolder.$sCleanupFile.'.txt', 'w'); fwrite($fReport, 'Database Maintenance tools: '.date('Y-m-d H:i:s')."\r\n"); foreach($aResults as $sClass => $aErrorList) { @@ -228,6 +230,15 @@ function DisplayInconsistenciesReport($aResults) fwrite($fReport, "\r\n"); } + if (isset($aError['cleanup'])) + { + $aQueries = $aError['cleanup']; + foreach($aQueries as $sQuery) + { + fwrite($fCleanUp, "$sQuery;\r\n"); + } + } + $sQueryResult = ''; $aIdList = array(); foreach($aError['res'] as $aRes) @@ -249,12 +260,15 @@ function DisplayInconsistenciesReport($aResults) } } fclose($fReport); + fclose($fCleanUp); $oArchive = new ZipArchive(); $oArchive->open($sDBToolsFolder.$sReportFile.'.zip', ZipArchive::CREATE); $oArchive->addFile($sDBToolsFolder.$sReportFile.'.txt', $sReportFile.'.txt'); + $oArchive->addFile($sDBToolsFolder.$sCleanupFile.'.txt', $sCleanupFile.'.txt'); $oArchive->close(); unlink($sDBToolsFolder.$sReportFile.'.txt'); + unlink($sDBToolsFolder.$sCleanupFile.'.txt'); $sReportFile = $sDBToolsFolder.$sReportFile.'.zip'; diff --git a/datamodels/2.x/combodo-db-tools/de.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/de.dict.combodo-db-tools.php index 0381b05a3..08ccfcdc6 100644 --- a/datamodels/2.x/combodo-db-tools/de.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/de.dict.combodo-db-tools.php @@ -22,7 +22,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( // Dictionary entries go here 'Menu:DBToolsMenu' => 'DB Tools', 'DBTools:Class' => 'Klasse', - 'DBTools:Title' => 'Datenbank-Pflege-Tools', + 'DBTools:Title' => 'Datenbank-Pflege-Tools~~', 'DBTools:ErrorsFound' => 'Fehler gefunden', 'DBTools:Error' => 'Fehler', 'DBTools:Count' => 'Anzahl', @@ -48,6 +48,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Ungültiger Wert für %1$s (Spalte: `%2$s.%3$s`)', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Manche Benutzerkonten haben keinerlei zugewiesenes Profi', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch-Count-Fehler in `%1$s`, %2$d Einträge geholt (fetched) / %3$d gezählt', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php index faeb87d33..84add3c25 100644 --- a/datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php @@ -26,7 +26,7 @@ Dict::Add('EN US', 'English', 'English', array( // Dictionary entries go here 'Menu:DBToolsMenu' => 'Database integrity', 'DBTools:Class' => 'Class', - 'DBTools:Title' => 'Database Maintenance Tools', + 'DBTools:Title' => 'Database integrity check', 'DBTools:ErrorsFound' => 'Errors Found', 'DBTools:Error' => 'Error', 'DBTools:Count' => 'Count', @@ -52,7 +52,7 @@ Dict::Add('EN US', 'English', 'English', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted', - 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value than `%3$s`.`%1$s`', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`', 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class', )); diff --git a/datamodels/2.x/combodo-db-tools/es_cr.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/es_cr.dict.combodo-db-tools.php index 002c819c1..46be62724 100644 --- a/datamodels/2.x/combodo-db-tools/es_cr.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/es_cr.dict.combodo-db-tools.php @@ -23,65 +23,67 @@ // Database inconsistencies Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( // Dictionary entries go here - 'Menu:DBToolsMenu' => 'DB Tools~~', - 'DBTools:Class' => 'Class~~', - 'DBTools:Title' => 'Database Maintenance Tools~~', - 'DBTools:ErrorsFound' => 'Errors Found~~', - 'DBTools:Error' => 'Error~~', - 'DBTools:Count' => 'Count~~', - 'DBTools:SQLquery' => 'SQL query~~', - 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', - 'DBTools:SQLresult' => 'SQL result~~', - 'DBTools:NoError' => 'The database is OK~~', - 'DBTools:HideIds' => 'Error List~~', - 'DBTools:ShowIds' => 'Detailed view~~', - 'DBTools:ShowReport' => 'Report~~', - 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'Menu:DBToolsMenu' => 'Herramientas de bases de datos', + 'DBTools:Class' => 'Clase', + 'DBTools:Title' => 'Herramientas de mantenimiento de base de datos~~', + 'DBTools:ErrorsFound' => 'Errores encontrados', + 'DBTools:Error' => 'Error', + 'DBTools:Count' => 'Cantidad', + 'DBTools:SQLquery' => 'Consulta SQL', + 'DBTools:FixitSQLquery' => 'Consulta SQL para solucioner el problema (sugerencia)', + 'DBTools:SQLresult' => 'Resultado SQL', + 'DBTools:NoError' => 'La base de datos está correcta', + 'DBTools:HideIds' => 'Lista de errores', + 'DBTools:ShowIds' => 'Vista detallada', + 'DBTools:ShowReport' => 'Reporte', + 'DBTools:IntegrityCheck' => 'Verificación de integridad', 'DBTools:FetchCheck' => 'Fetch Check (long)~~', - 'DBTools:Analyze' => 'Analyze~~', - 'DBTools:Details' => 'Show Details~~', - 'DBTools:ShowAll' => 'Show All Errors~~', + 'DBTools:Analyze' => 'Analizar', + 'DBTools:Details' => 'Mostrar detalles', + 'DBTools:ShowAll' => 'Mostrar todos los errores', - 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + 'DBTools:Inconsistencies' => 'Inconsistencias de base de datos', - 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', - 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', - 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', - 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', - 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Integrity-OrphanRecord' => 'Registro huérfano en `%1$s`, debería tener su contraparte en la tabla `%2$s`', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Llave externa inválida %1$s (columna: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Llave externa perdida %1$s (columna: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-InvalidValue' => 'Valor inválido para %1$s (columna: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Algunas cuentas de usuario no tienen perfil asignado', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( - 'DBTools:DatabaseInfo' => 'Database Information~~', - 'DBTools:Base' => 'Base~~', - 'DBTools:Size' => 'Size~~', + 'DBTools:DatabaseInfo' => 'Información de base de datos', + 'DBTools:Base' => 'Base', + 'DBTools:Size' => 'Tamaño', )); // Lost attachments Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( - 'DBTools:LostAttachments' => 'Lost attachments~~', - 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + 'DBTools:LostAttachments' => 'Adjuntos perdidos', + 'DBTools:LostAttachments:Disclaimer' => 'Aquí usted puede buscar adjuntos perdidos o desplazados. Esta NO es una herramienta de recuperación de datos, no obtiene datos borrados.', - 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', - 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', - 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', - 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + 'DBTools:LostAttachments:Button:Analyze' => 'Analizar', + 'DBTools:LostAttachments:Button:Restore' => 'Restaurar', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'Esta acción no se puede deshacer, por favor confirme que quiere restaurar los archivos seleccionados.', + 'DBTools:LostAttachments:Button:Busy' => 'Por favor espere...', - 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + 'DBTools:LostAttachments:Step:Analyze' => 'Primero, buscaremos adjuntos perdidos/desplazados analizando la base de datos.', - 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', - 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', - 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', - 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', - 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', - 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analizar resultados:', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Genial! Todo parece estar en el lugar correcto.', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Algunos adjuntos (%1$d) parecen estar desplazados. Mire la siguiente lista y verifique los que quiera mover.', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Nombre de archivo', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Ubicación actual', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Mover a...', - 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', - 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + 'DBTools:LostAttachments:Step:RestoreResults' => 'Resultados de restauración:', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d adjuntos fueron restaurados.', - 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', - 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Almacenado como imagen en línea', + 'DBTools:LostAttachments:History' => 'Adjunto "%1$s" restaurado con herramientas de base de datos' )); diff --git a/datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php index bc683642c..5f3c31859 100644 --- a/datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php @@ -21,7 +21,7 @@ Dict::Add('FR FR', 'French', 'Français', array( // Dictionary entries go here 'Menu:DBToolsMenu' => 'Intégrité base de données', 'DBTools:Class' => 'Classe', - 'DBTools:Title' => 'Outils maintenance base de données', + 'DBTools:Title' => 'Contrôle de l\'intégrité de la base de données', 'DBTools:ErrorsFound' => 'Erreurs trouvées', 'DBTools:Error' => 'Erreur', 'DBTools:Count' => 'Nombre', diff --git a/datamodels/2.x/combodo-db-tools/hu.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/hu.dict.combodo-db-tools.php index fb1dac12d..a056654a9 100644 --- a/datamodels/2.x/combodo-db-tools/hu.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/hu.dict.combodo-db-tools.php @@ -51,6 +51,8 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/it.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/it.dict.combodo-db-tools.php index 7d5ea3f5f..2b2fa4bef 100644 --- a/datamodels/2.x/combodo-db-tools/it.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/it.dict.combodo-db-tools.php @@ -51,6 +51,8 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/ja.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/ja.dict.combodo-db-tools.php index d6dfe83ef..31ac177c9 100644 --- a/datamodels/2.x/combodo-db-tools/ja.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/ja.dict.combodo-db-tools.php @@ -51,6 +51,8 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php index b00c2ef5c..c95e85d9c 100644 --- a/datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php @@ -24,7 +24,7 @@ /** @noinspection PhpUnhandledExceptionInspection */ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'combodo-db-tools/2.7.0', + 'combodo-db-tools/2.8.0', array( // Identification // diff --git a/datamodels/2.x/combodo-db-tools/nl.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/nl.dict.combodo-db-tools.php index 23d98235a..6dda4a00d 100644 --- a/datamodels/2.x/combodo-db-tools/nl.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/nl.dict.combodo-db-tools.php @@ -27,7 +27,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( // Dictionary entries go here 'Menu:DBToolsMenu' => 'Databasetools', 'DBTools:Class' => 'Klasse', - 'DBTools:Title' => 'Onderhoudstools voor de database', + 'DBTools:Title' => 'Onderhoudstools voor de database~~', 'DBTools:ErrorsFound' => 'Fouten gevonden', 'DBTools:Error' => 'Fout', 'DBTools:Count' => 'Aantal', @@ -53,6 +53,8 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Ongeldige waarde voor %1$s (kolom: "%2$s.%3$s")', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Sommige gebruikersaccounts hebben geen profiel', 'DBAnalyzer-Fetch-Count-Error' => 'Opvraag-fout in "%1$s", %2$d records opgevraagd / %3$d geteld', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/pt_br.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/pt_br.dict.combodo-db-tools.php index 3e0a35f22..34037ea72 100644 --- a/datamodels/2.x/combodo-db-tools/pt_br.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/pt_br.dict.combodo-db-tools.php @@ -51,6 +51,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/ru.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/ru.dict.combodo-db-tools.php index aad422575..0ad4ac74f 100644 --- a/datamodels/2.x/combodo-db-tools/ru.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/ru.dict.combodo-db-tools.php @@ -12,7 +12,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( // Dictionary entries go here 'Menu:DBToolsMenu' => 'Инструменты БД', 'DBTools:Class' => 'Класс', - 'DBTools:Title' => 'Инструменты обслуживания базы данных', + 'DBTools:Title' => 'Инструменты обслуживания базы данных~~', 'DBTools:ErrorsFound' => 'Найденные ошибки', 'DBTools:Error' => 'Ошибка', 'DBTools:Count' => 'Количество', @@ -38,6 +38,8 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Недопустимое значение для %1$s (столбец: `%2$s.%3$s`)', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Некоторые учетные записи пользователей не имеют профилей', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/sk.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/sk.dict.combodo-db-tools.php index 1bb39b752..52f3b6fae 100644 --- a/datamodels/2.x/combodo-db-tools/sk.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/sk.dict.combodo-db-tools.php @@ -51,6 +51,8 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/tr.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/tr.dict.combodo-db-tools.php index 56d2b10f6..0f202a577 100644 --- a/datamodels/2.x/combodo-db-tools/tr.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/tr.dict.combodo-db-tools.php @@ -51,6 +51,8 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/zh_cn.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/zh_cn.dict.combodo-db-tools.php index 1d9eb7c05..6eecf149a 100644 --- a/datamodels/2.x/combodo-db-tools/zh_cn.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/zh_cn.dict.combodo-db-tools.php @@ -25,7 +25,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( // Dictionary entries go here 'Menu:DBToolsMenu' => 'DB 工具', 'DBTools:Class' => 'Class~~', - 'DBTools:Title' => '数据库维护工具', + 'DBTools:Title' => '数据库维护工具~~', 'DBTools:ErrorsFound' => '发现错误', 'DBTools:Error' => '错误', 'DBTools:Count' => '个数', @@ -51,6 +51,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'DBAnalyzer-Integrity-InvalidValue' => '无效的值 %1$s (列: `%2$s.%3$s`)~~', 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', + 'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value than `%3$s`.`%1$s`~~', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~', )); // Database Info diff --git a/datamodels/2.x/itop-attachments/ajax.itop-attachment.php b/datamodels/2.x/itop-attachments/ajax.itop-attachment.php index cdcace1cb..b593cb043 100644 --- a/datamodels/2.x/itop-attachments/ajax.itop-attachment.php +++ b/datamodels/2.x/itop-attachments/ajax.itop-attachment.php @@ -32,7 +32,7 @@ require_once(APPROOT.'/application/ajaxwebpage.class.inc.php'); */ function RenderAttachments(ajax_page $oPage, $iTransactionId) { - $sClass = utils::ReadParam('objclass', ''); + $sClass = utils::ReadParam('objclass', '', false, 'class'); $sId = utils::ReadParam('objkey', ''); $oObject = MetaModel::GetObject($sClass, $sId, false); $bEditMode = utils::ReadParam('edit_mode', 0); @@ -46,7 +46,7 @@ function RenderAttachments(ajax_page $oPage, $iTransactionId) : AttachmentPlugIn::IsReadonlyState($oObject, $oObject->GetState(), AttachmentPlugIn::ENUM_GUI_BACKOFFICE); if ($bEditMode && !$bIsReadOnlyState) { - $oAttachmentsRenderer->RenderEditAttachmentsList($aAttachmentsDeleted); + $oAttachmentsRenderer->AddAttachmentsListContent(true, $aAttachmentsDeleted); } else { diff --git a/datamodels/2.x/itop-attachments/cs.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/cs.dict.itop-attachments.php index 8535c53c0..6158905aa 100755 --- a/datamodels/2.x/itop-attachments/cs.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/cs.dict.itop-attachments.php @@ -38,6 +38,9 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'Attachments:NoAttachment' => 'Žádná příloha. ', 'Attachments:PreviewNotAvailable' => 'Pro tento typ přílohy není náhled k dispozici.', 'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Display as icons~~', 'Attachments:Render:Table' => 'Display as list~~', )); diff --git a/datamodels/2.x/itop-attachments/da.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/da.dict.itop-attachments.php index d6fbe5833..f2728daa5 100644 --- a/datamodels/2.x/itop-attachments/da.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/da.dict.itop-attachments.php @@ -35,6 +35,9 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'Attachments:NoAttachment' => 'Intet vedhæftet. ', 'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.~~', 'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Display as icons~~', 'Attachments:Render:Table' => 'Display as list~~', )); diff --git a/datamodels/2.x/itop-attachments/de.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/de.dict.itop-attachments.php index fb11a475e..6ef6891ed 100644 --- a/datamodels/2.x/itop-attachments/de.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/de.dict.itop-attachments.php @@ -37,6 +37,9 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'Attachments:NoAttachment' => 'Kein Attachment. ', 'Attachments:PreviewNotAvailable' => 'Vorschau für diesen Attachment-Typ nicht verfügbar.', 'Attachments:Error:FileTooLarge' => 'Die Datei ist zu groß für den Upload: %1$s', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Als Icons anzeigen', 'Attachments:Render:Table' => 'Als Liste anzeigen', )); diff --git a/datamodels/2.x/itop-attachments/en.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/en.dict.itop-attachments.php index ba40a951e..77ceccbf2 100755 --- a/datamodels/2.x/itop-attachments/en.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/en.dict.itop-attachments.php @@ -32,7 +32,9 @@ Dict::Add('EN US', 'English', 'English', array( 'Attachments:NoAttachment' => 'No attachment. ', 'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.', 'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s', - 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty. Check that your server disk is not full.', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.', 'Attachments:Render:Icons' => 'Display as icons', 'Attachments:Render:Table' => 'Display as list', )); diff --git a/datamodels/2.x/itop-attachments/es_cr.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/es_cr.dict.itop-attachments.php index 8855cff8d..35246a0d4 100755 --- a/datamodels/2.x/itop-attachments/es_cr.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/es_cr.dict.itop-attachments.php @@ -37,6 +37,9 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'Attachments:NoAttachment' => 'No hay Anexo. ', 'Attachments:PreviewNotAvailable' => 'Vista preliminar no disponible para este tipo de Anexo.', 'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Display as icons~~', 'Attachments:Render:Table' => 'Display as list~~', )); diff --git a/datamodels/2.x/itop-attachments/fr.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/fr.dict.itop-attachments.php index 6359f2a72..db9a50d7d 100755 --- a/datamodels/2.x/itop-attachments/fr.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/fr.dict.itop-attachments.php @@ -31,7 +31,9 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Attachments:NoAttachment' => 'Aucune pièce jointe.', 'Attachments:PreviewNotAvailable' => 'Pas d\'aperçu pour ce type de pièce jointe.', 'Attachments:Error:FileTooLarge' => 'Le fichier est trop gros pour être chargé. %1$s', - 'Attachments:Error:UploadedFileEmpty' => 'Le fichier téléchargé est vide. Vérifiez qu\'il reste de la place disque disponible sur le serveur', + 'Attachments:Error:UploadedFileEmpty' => 'Le fichier téléchargé est vide et ne peut pas être attaché. +Soit vous avez attaché un fichier vide, +Soit demandez à votre administrateur système s\'il reste de la place disque disponible sur le serveur', 'Attachments:Render:Icons' => 'Affichage en icônes', 'Attachments:Render:Table' => 'Affichage en liste', )); diff --git a/datamodels/2.x/itop-attachments/hu.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/hu.dict.itop-attachments.php index 106e8e126..c4acd9c0a 100644 --- a/datamodels/2.x/itop-attachments/hu.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/hu.dict.itop-attachments.php @@ -35,6 +35,9 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'Attachments:NoAttachment' => 'No attachment. ~~', 'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.~~', 'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Display as icons~~', 'Attachments:Render:Table' => 'Display as list~~', )); diff --git a/datamodels/2.x/itop-attachments/it.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/it.dict.itop-attachments.php index b6e0c350e..d3de6b368 100644 --- a/datamodels/2.x/itop-attachments/it.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/it.dict.itop-attachments.php @@ -35,6 +35,9 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'Attachments:NoAttachment' => 'No attachment. ~~', 'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.~~', 'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Display as icons~~', 'Attachments:Render:Table' => 'Display as list~~', )); diff --git a/datamodels/2.x/itop-attachments/ja.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/ja.dict.itop-attachments.php index 68cf0fd1a..7cddac5e4 100644 --- a/datamodels/2.x/itop-attachments/ja.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/ja.dict.itop-attachments.php @@ -34,6 +34,9 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'Attachments:NoAttachment' => '添付はありません。', 'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.~~', 'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Display as icons~~', 'Attachments:Render:Table' => 'Display as list~~', )); diff --git a/datamodels/2.x/itop-attachments/module.itop-attachments.php b/datamodels/2.x/itop-attachments/module.itop-attachments.php index abbfdeb42..19593f0b7 100644 --- a/datamodels/2.x/itop-attachments/module.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/module.itop-attachments.php @@ -19,7 +19,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-attachments/2.7.0', + 'itop-attachments/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-attachments/nl.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/nl.dict.itop-attachments.php index 78be5fe67..cdce20e54 100644 --- a/datamodels/2.x/itop-attachments/nl.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/nl.dict.itop-attachments.php @@ -40,6 +40,9 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'Attachments:NoAttachment' => 'Geen bijlage. ', 'Attachments:PreviewNotAvailable' => 'Er is geen voorbeeld beschikbaar voor dit type bijlage.', 'Attachments:Error:FileTooLarge' => 'Het bestand is te groot om geüpload te worden: %1$s', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Toon als pictogram', 'Attachments:Render:Table' => 'Toon als lijst', )); diff --git a/datamodels/2.x/itop-attachments/pt_br.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/pt_br.dict.itop-attachments.php index 3118abcb8..afeccab1c 100644 --- a/datamodels/2.x/itop-attachments/pt_br.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/pt_br.dict.itop-attachments.php @@ -36,6 +36,9 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'Attachments:NoAttachment' => 'Nenhum anexo. ', 'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.~~', 'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Display as icons~~', 'Attachments:Render:Table' => 'Display as list~~', )); diff --git a/datamodels/2.x/itop-attachments/renderers.itop-attachments.php b/datamodels/2.x/itop-attachments/renderers.itop-attachments.php index b91f59670..16c848fbd 100644 --- a/datamodels/2.x/itop-attachments/renderers.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/renderers.itop-attachments.php @@ -69,6 +69,14 @@ abstract class AbstractAttachmentsRenderer */ const MAX_SIZE_FOR_PREVIEW = 500000; + /** + * Attachments list container HTML id, that must be generated in {@link RenderEditAttachmentsList} + * + * @since 2.7.0-2 N°2968 ajax buttons (on especially the #attachment_plugin hidden input) should not be refreshed + * so we are refreshing only the content of this container + */ + const ATTACHMENTS_LIST_CONTAINER_ID = 'AttachmentsListContainer'; + /** @var \WebPage */ protected $oPage; /** @@ -129,13 +137,38 @@ abstract class AbstractAttachmentsRenderer } /** + * Can be overriden to change display order, but must generate an HTML container of ID {@link ATTACHMENTS_LIST_CONTAINER_ID} for JS refresh. + * * @param int[] $aAttachmentsDeleted Attachments id that should be deleted after form submission * - * @return string + * @return void will print using {@link oPage} */ - abstract public function RenderEditAttachmentsList($aAttachmentsDeleted = array()); + public function RenderEditAttachmentsList($aAttachmentsDeleted = array()) + { + $this->AddUploadButton(); - abstract public function RenderViewAttachmentsList(); + $this->oPage->add('<div id="'.self::ATTACHMENTS_LIST_CONTAINER_ID.'">'); + $this->AddAttachmentsListContent(true, $aAttachmentsDeleted); + $this->oPage->add('</div>'); + } + + /** + * Generates the attachments list content + * + * @param bool $bWithDeleteButton + * @param array $aAttachmentsDeleted + * + * @throws \ArchivedObjectException + * @throws \CoreException + * @throws \CoreUnexpectedValue + * @throws \MySQLException + */ + abstract public function AddAttachmentsListContent($bWithDeleteButton, $aAttachmentsDeleted = array()); + + public function RenderViewAttachmentsList() + { + $this->AddAttachmentsListContent(false, array()); + } protected function AddUploadButton() { @@ -154,9 +187,9 @@ abstract class AbstractAttachmentsRenderer $this->oPage->add_ready_script( <<<JS - function RefreshAttachmentsDisplay() + function RefreshAttachmentsDisplay(dataUpload) { - var sContentNode = '#AttachmentsContent', + var sContentNode = '#AttachmentsContent>div#AttachmentsListContainer', aAttachmentsDeletedHiddenInputs = $('table.attachmentsList>tbody>tr[id^="display_attachment_"]>td input[name="removed_attachments[]"]'), aAttachmentsDeletedIds = aAttachmentsDeletedHiddenInputs.map(function() { return $(this).val() }).toArray(); $(sContentNode).block(); @@ -172,6 +205,8 @@ abstract class AbstractAttachmentsRenderer function(data) { $(sContentNode).html(data); $(sContentNode).unblock(); + + $('#attachment_plugin').trigger('add_attachment', [dataUpload.result.att_id, dataUpload.result.msg, false]); } ) } @@ -181,20 +216,18 @@ abstract class AbstractAttachmentsRenderer formData: { operation: 'add', temp_id: '$this->sTransactionId', obj_class: '$sClass' }, dataType: 'json', pasteZone: null, // Don't accept files via Chrome's copy/paste - done: function (e, data) { + done: function(e, data) { if(typeof(data.result.error) != 'undefined') { if(data.result.error !== '') { alert(data.result.error); - } - else - { - RefreshAttachmentsDisplay(); + return; } } + RefreshAttachmentsDisplay(data); }, - send: function(e, data){ + send: function(e, data){ // Don't send attachment if size is greater than PHP post_max_size, otherwise it will break the request and all its parameters (\$_REQUEST, \$_POST, ...) // Note: We loop on the files as the data structures is an array but in this case, we only upload 1 file at a time. var iTotalSizeInBytes = 0; @@ -337,16 +370,7 @@ JS; */ class TableDetailsAttachmentsRenderer extends AbstractAttachmentsRenderer { - /** - * @param bool $bWithDeleteButton - * @param array $aAttachmentsDeleted - * - * @throws \ArchivedObjectException - * @throws \CoreException - * @throws \CoreUnexpectedValue - * @throws \MySQLException - */ - private function AddAttachmentsTable($bWithDeleteButton, $aAttachmentsDeleted = array()) + public function AddAttachmentsListContent($bWithDeleteButton, $aAttachmentsDeleted = array()) { if ($this->GetAttachmentsCount() === 0) { @@ -534,22 +558,4 @@ JS HTML ); } - - /** - * @inheritDoc - */ - public function RenderEditAttachmentsList($aAttachmentsDeleted = array()) - { - $this->AddUploadButton(); - - $this->AddAttachmentsTable(true, $aAttachmentsDeleted); - } - - /** - * @inheritDoc - */ - public function RenderViewAttachmentsList() - { - $this->AddAttachmentsTable(false); - } } diff --git a/datamodels/2.x/itop-attachments/ru.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/ru.dict.itop-attachments.php index 3d9e74e25..330d2f8df 100755 --- a/datamodels/2.x/itop-attachments/ru.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/ru.dict.itop-attachments.php @@ -23,6 +23,9 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Attachments:NoAttachment' => 'Нет вложений.', 'Attachments:PreviewNotAvailable' => 'Предварительный просмотр не доступен для этого типа вложений.', 'Attachments:Error:FileTooLarge' => 'Файл слишком велик для загрузки. %1$s', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Display as icons~~', 'Attachments:Render:Table' => 'Display as list~~', )); @@ -50,22 +53,22 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( Dict::Add('RU RU', 'Russian', 'Русский', array( - 'Attachments:File:Thumbnail' => 'Icon~~', - 'Attachments:File:Name' => 'File name~~', - 'Attachments:File:Date' => 'Upload date~~', - 'Attachments:File:Uploader' => 'Uploaded by~~', - 'Attachments:File:Size' => 'Size~~', - 'Attachments:File:MimeType' => 'Type~~', + 'Attachments:File:Thumbnail' => 'Предпросмотр', + 'Attachments:File:Name' => 'Имя файла', + 'Attachments:File:Date' => 'Дата', + 'Attachments:File:Uploader' => 'Пользователь', + 'Attachments:File:Size' => 'Размер', + 'Attachments:File:MimeType' => 'Тип', )); // // Class: Attachment // Dict::Add('RU RU', 'Russian', 'Русский', array( - 'Class:Attachment/Attribute:creation_date' => 'Creation date~~', - 'Class:Attachment/Attribute:creation_date+' => '~~', - 'Class:Attachment/Attribute:user_id' => 'User id~~', - 'Class:Attachment/Attribute:user_id+' => '~~', - 'Class:Attachment/Attribute:contact_id' => 'Contact id~~', - 'Class:Attachment/Attribute:contact_id+' => '~~', + 'Class:Attachment/Attribute:creation_date' => 'Дата создания', + 'Class:Attachment/Attribute:creation_date+' => '', + 'Class:Attachment/Attribute:user_id' => 'Пользователь', + 'Class:Attachment/Attribute:user_id+' => '', + 'Class:Attachment/Attribute:contact_id' => 'Контакт', + 'Class:Attachment/Attribute:contact_id+' => '', )); diff --git a/datamodels/2.x/itop-attachments/sk.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/sk.dict.itop-attachments.php index d226685d3..9a0bd6504 100644 --- a/datamodels/2.x/itop-attachments/sk.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/sk.dict.itop-attachments.php @@ -35,6 +35,9 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'Attachments:NoAttachment' => 'Bez prílohy. ', 'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.~~', 'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Display as icons~~', 'Attachments:Render:Table' => 'Display as list~~', )); diff --git a/datamodels/2.x/itop-attachments/tr.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/tr.dict.itop-attachments.php index c6f62a915..639f4cbb1 100644 --- a/datamodels/2.x/itop-attachments/tr.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/tr.dict.itop-attachments.php @@ -35,6 +35,9 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'Attachments:NoAttachment' => 'No attachment. ~~', 'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.~~', 'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => 'Display as icons~~', 'Attachments:Render:Table' => 'Display as list~~', )); diff --git a/datamodels/2.x/itop-attachments/zh_cn.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/zh_cn.dict.itop-attachments.php index ac05938d8..ae6db1849 100644 --- a/datamodels/2.x/itop-attachments/zh_cn.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/zh_cn.dict.itop-attachments.php @@ -31,6 +31,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'Attachments:NoAttachment' => '没有附件. ', 'Attachments:PreviewNotAvailable' => '该附件类型不支持预览.', 'Attachments:Error:FileTooLarge' => '上传的文件过大. %1$s~~', + 'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached. +Either you have pushed an empty file, +or ask your iTop administrator if the iTop server disk is full.~~', 'Attachments:Render:Icons' => '显示为图标', 'Attachments:Render:Table' => '显示为列表', )); diff --git a/datamodels/2.x/itop-backup/ajax.backup.php b/datamodels/2.x/itop-backup/ajax.backup.php index 2d026d20c..7196242c3 100644 --- a/datamodels/2.x/itop-backup/ajax.backup.php +++ b/datamodels/2.x/itop-backup/ajax.backup.php @@ -1,6 +1,6 @@ <?php /** - * Copyright (C) 2013-2019 Combodo SARL + * Copyright (C) 2010-2020 Combodo SARL * * This file is part of iTop. * @@ -25,77 +25,140 @@ require_once(APPROOT.'/application/ajaxwebpage.class.inc.php'); require_once(APPROOT.'core/mutex.class.inc.php'); + +/** + * @param WebPage $oPage + * @param string $sHtmlErrorMessage the whole HTML error, cinluding div/p/... + * @param int|string $exitCode + * + * @uses \die() https://www.php.net/manual/fr/function.die.php + * + * @since 2.6.5 2.7.1 N°2989 + */ +function DisplayErrorAndDie($oPage, $sHtmlErrorMessage, $exitCode = null) +{ + $oPage->add($sHtmlErrorMessage); + $oPage->output(); + + die($exitCode); +} + + + + + + +$sOperation = utils::ReadParam('operation', ''); + +$oPage = new ajax_page(''); +$oPage->no_cache(); +$oPage->SetContentType('text/html'); + + + +/** + * Check security + */ +switch ($sOperation) +{ + /** + * Can't use normal check methods (DoLogin for ex) as the datamodel can't be loaded here + * So we're only using a token generated in the restore_token operation + */ + case 'restore_exec': + IssueLog::Enable(APPROOT.'log/error.log'); + if (utils::GetConfig()->Get('demo_mode')) + { + DisplayErrorAndDie($oPage, '<div data-error-stimulus="Error">Sorry, '.ITOP_APPLICATION_SHORT.' is in <b>demonstration mode</b>: the feature is disabled.</div>'); + } + + $sToken = utils::ReadParam('token', '', false, 'raw_data'); + $sBasePath = APPROOT.'/data/'; + $sTokenFile = $sBasePath.'restore.'.$sToken.'.tok'; + $tokenRealPath = utils::RealPath($sTokenFile, $sBasePath); + if (($tokenRealPath === false) || (!is_file($tokenRealPath))) + { + IssueLog::Error("ajax.backup.php operation=$sOperation ERROR = inexisting token $sToken"); + $sEscapedToken = utils::HtmlEntities($sToken); + DisplayErrorAndDie($oPage, "<p>Error: missing token file: '$sEscapedToken'</p>"); + } + + break; + + default: + require_once(APPROOT.'/application/startup.inc.php'); + require_once(APPROOT.'/application/loginwebpage.class.inc.php'); + + LoginWebPage::DoLogin(); + + $sTransactionId = utils::ReadParam('transaction_id', '', true, 'transaction_id'); + // the consumer page is not reloaded after download, we need to keep the transaction_id + $bRemoveTransactionId = ($sOperation !== 'download'); + if (!utils::IsTransactionValid($sTransactionId, $bRemoveTransactionId)) + { + $sEscapedOperation = utils::HtmlEntities($sOperation); + DisplayErrorAndDie($oPage, "<div data-error-stimulus=\"Error\">Error: invalid Transaction ID. The operation '$sEscapedOperation' was NOT performed!</div>"); + } + + ApplicationMenu::CheckMenuIdEnabled('BackupStatus'); + + if (utils::GetConfig()->Get('demo_mode')) + { + DisplayErrorAndDie($oPage, '<div data-error-stimulus="Error">Sorry, '.ITOP_APPLICATION_SHORT.' is in <b>demonstration mode</b>: the feature is disabled.</div>'); + } + break; +} + + +/** + * Backup from an interactive session + */ try { - $sOperation = utils::ReadParam('operation', ''); - switch ($sOperation) { case 'backup': - require_once(APPROOT.'/application/startup.inc.php'); - require_once(APPROOT.'/application/loginwebpage.class.inc.php'); - LoginWebPage::DoLogin(); // Check user rights and prompt if needed - ApplicationMenu::CheckMenuIdEnabled('BackupStatus'); - $oPage = new ajax_page(""); - $oPage->no_cache(); - $oPage->SetContentType('text/html'); + try + { + set_time_limit(0); + $oBB = new BackupExec(APPROOT.'data/backups/manual/', 0 /*iRetentionCount*/); + $sRes = $oBB->Process(time() + 36000); // 10 hours to complete should be sufficient! + } + catch (Exception $e) + { + $oPage->p('Error: '.$e->getMessage()); + IssueLog::Error($sOperation.' - '.$e->getMessage()); + } - if (utils::GetConfig()->Get('demo_mode')) - { - $oPage->add("<div data-error-stimulus=\"Error\">Sorry, iTop is in <b>demonstration mode</b>: the feature is disabled.</div>"); - } - else - { - try - { - set_time_limit(0); - $oBB = new BackupExec(APPROOT.'data/backups/manual/', 0 /*iRetentionCount*/); - $sRes = $oBB->Process(time() + 36000); // 10 hours to complete should be sufficient! - } - catch (Exception $e) - { - $oPage->p('Error: '.$e->getMessage()); - IssueLog::Error($sOperation.' - '.$e->getMessage()); - } - } $oPage->output(); break; /* - * Fix a token : + * Fix a specific token : * We can't load the MetaModel because in DBRestore, after restore is done we're launching a compile ! - * So as \LoginWebPage::DoLogin needs a loaded DataModel, we can't use it + * So as LoginWebPage::DoLogin needs a loaded DataModel, we can't use it + * Also, we can't use \utils::IsTransactionValid as it uses \MetaModel::GetConfig * As a result we're setting a token file to make sure the restore is called by an authenticated user with the correct rights ! */ case 'restore_get_token': - require_once(APPROOT.'/application/startup.inc.php'); - require_once(APPROOT.'/application/loginwebpage.class.inc.php'); - LoginWebPage::DoLogin(); // Check user rights and prompt if needed - ApplicationMenu::CheckMenuIdEnabled('BackupStatus'); - - $oPage = new ajax_page(""); - $oPage->no_cache(); - $oPage->SetContentType('text/html'); - $sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data'); $oRestoreMutex = new iTopMutex('restore.'.$sEnvironment); - if (!$oRestoreMutex->IsLocked()) + if ($oRestoreMutex->IsLocked()) { - $sFile = utils::ReadParam('file', '', false, 'raw_data'); - $sToken = str_replace(' ', '', (string)microtime()); - $sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok'; - file_put_contents($sTokenFile, $sFile); + DisplayErrorAndDie($oPage, '<p>'.Dict::S('bkp-restore-running').'</p>'); + } + + $sFile = utils::ReadParam('file', '', false, 'raw_data'); + $sToken = str_replace(' ', '', (string)microtime()).$sTransactionId; + $sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok'; + file_put_contents($sTokenFile, $sFile); + + $oPage->add_ready_script( + <<<JS +$("#restore_token").val('$sToken'); +JS + ); - $oPage->add_ready_script( - <<<EOF - $("#restore_token").val('$sToken'); -EOF - ); - } - else - { - $oPage->p(Dict::S('bkp-restore-running')); - } $oPage->output(); break; @@ -109,72 +172,47 @@ EOF require_once(APPROOT.'/setup/backup.class.inc.php'); require_once(dirname(__FILE__).'/dbrestore.class.inc.php'); - IssueLog::Enable(APPROOT.'log/error.log'); - - $oPage = new ajax_page(""); - $oPage->no_cache(); - $oPage->SetContentType('text/html'); - - if (utils::GetConfig()->Get('demo_mode')) + $sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data'); + $oRestoreMutex = new iTopMutex('restore.'.$sEnvironment); + IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'"); + $oRestoreMutex->Lock(); + IssueLog::Info('Backup Restore - LOCK acquired, executing...'); + try { - $oPage->add("<div data-error-stimulus=\"Error\">Sorry, iTop is in <b>demonstration mode</b>: the feature is disabled.</div>"); + set_time_limit(0); + + // Get the file and destroy the token (single usage) + $sFile = file_get_contents($tokenRealPath); + + // Loading config file : we don't have the MetaModel but we have the current env ! + $sConfigFilePath = utils::GetConfigFilePath($sEnvironment); + $oItopConfig = new Config($sConfigFilePath, true); + $sMySQLBinDir = $oItopConfig->GetModuleSetting('itop-backup', 'mysql_bindir', ''); + + $oDBRS = new DBRestore($oItopConfig); + $oDBRS->SetMySQLBinDir($sMySQLBinDir); + + $sBackupDir = APPROOT.'data/backups/'; + $sBackupFile = $sBackupDir.$sFile; + $sRes = $oDBRS->RestoreFromCompressedBackup($sBackupFile, $sEnvironment); + + IssueLog::Info('Backup Restore - Done, releasing the LOCK'); } - else + catch (Exception $e) { - $sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data'); - $oRestoreMutex = new iTopMutex('restore.'.$sEnvironment); - IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'"); - $oRestoreMutex->Lock(); - IssueLog::Info('Backup Restore - LOCK acquired, executing...'); - try - { - set_time_limit(0); - - // Get the file and destroy the token (single usage) - $sToken = utils::ReadParam('token', '', false, 'raw_data'); - $sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok'; - if (!is_file($sTokenFile)) - { - throw new Exception("Error: missing token file: '$sTokenFile'"); - } - $sFile = file_get_contents($sTokenFile); - unlink($sTokenFile); - - // Loading config file : we don't have the MetaModel but we have the current env ! - $sConfigFilePath = utils::GetConfigFilePath($sEnvironment); - $oItopConfig = new Config($sConfigFilePath, true); - $sMySQLBinDir = $oItopConfig->GetModuleSetting('itop-backup', 'mysql_bindir', ''); - - $oDBRS = new DBRestore($oItopConfig); - $oDBRS->SetMySQLBinDir($sMySQLBinDir); - - $sBackupDir = APPROOT.'data/backups/'; - $sBackupFile = $sBackupDir.$sFile; - $sRes = $oDBRS->RestoreFromCompressedBackup($sBackupFile, $sEnvironment); - - IssueLog::Info('Backup Restore - Done, releasing the LOCK'); - $oRestoreMutex->Unlock(); - } - catch (Exception $e) - { - $oRestoreMutex->Unlock(); - $oPage->p('Error: '.$e->getMessage()); - IssueLog::Error($sOperation.' - '.$e->getMessage()); - } + $oPage->p('Error: '.$e->getMessage()); + IssueLog::Error($sOperation.' - '.$e->getMessage()); } + finally + { + unlink($tokenRealPath); + $oRestoreMutex->Unlock(); + } + $oPage->output(); break; case 'download': - require_once(APPROOT.'/application/startup.inc.php'); - require_once(APPROOT.'/application/loginwebpage.class.inc.php'); - LoginWebPage::DoLogin(); // Check user rights and prompt if needed - ApplicationMenu::CheckMenuIdEnabled('BackupStatus'); - - if (utils::GetConfig()->Get('demo_mode')) - { - throw new Exception('iTop is in demonstration mode: the feature is disabled'); - } $sFile = utils::ReadParam('file', '', false, 'raw_data'); $oBackup = new DBBackupScheduled(); $sBackupDir = APPROOT.'data/backups/'; diff --git a/datamodels/2.x/itop-backup/backup.php b/datamodels/2.x/itop-backup/backup.php index 116f17564..4b93379ef 100644 --- a/datamodels/2.x/itop-backup/backup.php +++ b/datamodels/2.x/itop-backup/backup.php @@ -134,6 +134,8 @@ set_time_limit(0); if (utils::IsModeCLI()) { $oP = new CLIPage("iTop - Database Backup"); + + SetupUtils::CheckPhpAndExtensionsForCli($oP); } else { diff --git a/datamodels/2.x/itop-backup/check-backup.php b/datamodels/2.x/itop-backup/check-backup.php index 4f8a5cfed..4f17b32e1 100644 --- a/datamodels/2.x/itop-backup/check-backup.php +++ b/datamodels/2.x/itop-backup/check-backup.php @@ -250,6 +250,8 @@ catch(Exception $e) if (utils::IsModeCLI()) { + SetupUtils::CheckPhpAndExtensionsForCli(new CLIPage('Check backup utility')); + echo date('Y-m-d H:i:s')." - running check-backup utility\n"; try { diff --git a/datamodels/2.x/itop-backup/dbrestore.class.inc.php b/datamodels/2.x/itop-backup/dbrestore.class.inc.php index a849a4793..d274047fa 100644 --- a/datamodels/2.x/itop-backup/dbrestore.class.inc.php +++ b/datamodels/2.x/itop-backup/dbrestore.class.inc.php @@ -69,10 +69,11 @@ class DBRestore extends DBBackup { $sPortOption = '--port='.$this->iDBPort.' '; } + $sTlsOptions = self::GetMysqlCliTlsOptions($this->oConfig); $sDataFileEscaped = self::EscapeShellArg($sDataFile); - $sCommand = "$sMySQLExe --default-character-set=".DEFAULT_CHARACTER_SET." --host=$sHost $sPortOption --user=$sUser --password=$sPwd $sDBName <$sDataFileEscaped 2>&1"; - $sCommandDisplay = "$sMySQLExe --default-character-set=".DEFAULT_CHARACTER_SET." --host=$sHost $sPortOption --user=xxxx --password=xxxx $sDBName <$sDataFileEscaped 2>&1"; + $sCommand = "$sMySQLExe --default-character-set=".DEFAULT_CHARACTER_SET." --host=$sHost $sPortOption --user=$sUser --password=$sPwd $sTlsOptions $sDBName <$sDataFileEscaped 2>&1"; + $sCommandDisplay = "$sMySQLExe --default-character-set=".DEFAULT_CHARACTER_SET." --host=$sHost $sPortOption --user=xxxx --password=xxxx $sTlsOptions $sDBName <$sDataFileEscaped 2>&1"; // Now run the command for real $this->LogInfo("Executing command: $sCommandDisplay"); diff --git a/datamodels/2.x/itop-backup/module.itop-backup.php b/datamodels/2.x/itop-backup/module.itop-backup.php index 95a8a0e69..ca0c58182 100644 --- a/datamodels/2.x/itop-backup/module.itop-backup.php +++ b/datamodels/2.x/itop-backup/module.itop-backup.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-backup/2.7.0', + 'itop-backup/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-backup/status.php b/datamodels/2.x/itop-backup/status.php index b6ce74f76..feeeb9b1b 100644 --- a/datamodels/2.x/itop-backup/status.php +++ b/datamodels/2.x/itop-backup/status.php @@ -1,6 +1,6 @@ <?php /** - * Copyright (C) 2013-2019 Combodo SARL + * Copyright (C) 2010-2020 Combodo SARL * * This file is part of iTop. * @@ -33,13 +33,9 @@ require_once(APPROOT.'application/loginwebpage.class.inc.php'); LoginWebPage::DoLogin(); // Check user rights and prompt if needed ApplicationMenu::CheckMenuIdEnabled('BackupStatus'); -//$sOperation = utils::ReadParam('operation', 'menu'); -//$oAppContext = new ApplicationContext(); - - - try { + $sTransactionId = utils::GetNewTransactionId(); $oP = new iTopWebPage(Dict::S('bkp-status-title')); $oP->set_base(utils::GetAbsoluteUrlAppRoot().'pages/'); @@ -186,7 +182,13 @@ try } else { - $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', array('operation' => 'download', 'file' => $sFilePath)); + $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', + array( + 'operation' => 'download', + 'file' => $sFilePath, + 'transaction_id' => $sTransactionId, + ) + ); $sName = "<a href=\"$sAjax\">".$sFileName.'</a>'; } $sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile)); @@ -234,7 +236,13 @@ try } else { - $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', array('operation' => 'download', 'file' => $sFilePath)); + $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', + array( + 'operation' => 'download', + 'file' => $sFilePath, + 'transaction_id' => $sTransactionId, + ) + ); $sName = "<a href=\"$sAjax\">".$sFileName.'</a>'; } $sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile)); @@ -298,9 +306,9 @@ try $sDBSubName = addslashes(MetaModel::GetConfig()->Get('db_subname')); $sEnvironment = addslashes(utils::GetCurrentEnvironment()); - + $oP->add_script( -<<<EOF +<<<JS function LaunchBackupNow() { $('#backup_success').hide(); @@ -312,6 +320,7 @@ function LaunchBackupNow() var oParams = {}; oParams.operation = 'backup'; + oParams.transaction_id = "$sTransactionId"; $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){ if (data.search(/error|exceptio|notice|warning/i) != -1) { @@ -337,7 +346,8 @@ function LaunchRestoreNow(sBackupFile, sConfirmationMessage) var oParams = {}; oParams.operation = 'restore_get_token'; - oParams.file = sBackupFile; + oParams.file = sBackupFile; + oParams.transaction_id = "$sTransactionId"; $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){ // Get the value of restore_token @@ -347,6 +357,7 @@ function LaunchRestoreNow(sBackupFile, sConfirmationMessage) oParams.operation = 'restore_exec'; oParams.token = $("#restore_token").val(); // token to check auth + rights without loading MetaModel oParams.environment = '$sEnvironment'; // needed to load the config + oParams.transaction_id = "$sTransactionId"; if (oParams.token.length > 0) { $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){ @@ -373,7 +384,7 @@ function LaunchRestoreNow(sBackupFile, sConfirmationMessage) }); } } -EOF +JS ); if (MetaModel::GetConfig()->Get('demo_mode')) diff --git a/datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php b/datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php index 965b786ae..e3c7adba1 100644 --- a/datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php +++ b/datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-bridge-virtualization-storage/2.7.0', + 'itop-bridge-virtualization-storage/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php b/datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php index 5b75ee686..e7f47f384 100755 --- a/datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php +++ b/datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-change-mgmt-itil/2.7.0', + 'itop-change-mgmt-itil/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php b/datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php index 93383ba26..a095feebc 100755 --- a/datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php +++ b/datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-change-mgmt/2.7.0', + 'itop-change-mgmt/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml b/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml index bdea5402f..58719ee78 100755 --- a/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml +++ b/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml @@ -610,9 +610,6 @@ </field> <field id="manager_id" xsi:type="AttributeExternalKey"> <filter><![CDATA[SELECT Person]]></filter> - <dependencies> - <attribute id="org_id"/> - </dependencies> <sql>manager_id</sql> <target_class>Person</target_class> <is_null_allowed>true</is_null_allowed> @@ -1684,16 +1681,6 @@ </list> </presentation> <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> <method id="DisplayBareRelations"> <static>false</static> <access>public</access> @@ -2170,18 +2157,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> <class id="DatacenterDevice" _delta="define"> <parent>ConnectableCI</parent> @@ -2732,18 +2708,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> @@ -3060,18 +3025,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> <class id="ApplicationSolution" _delta="define"> <parent>FunctionalCI</parent> @@ -3210,18 +3164,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> @@ -3341,18 +3284,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> <class id="SoftwareInstance" _delta="define"> <parent>FunctionalCI</parent> @@ -3533,18 +3465,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> <class id="Middleware" _delta="define"> <parent>SoftwareInstance</parent> @@ -3669,18 +3590,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> @@ -3814,18 +3724,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> @@ -3959,18 +3858,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> @@ -4329,18 +4217,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> <class id="DatabaseSchema" _delta="define"> <parent>FunctionalCI</parent> @@ -4462,18 +4339,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> <class id="WebApplication" _delta="define"> <parent>FunctionalCI</parent> @@ -4604,18 +4470,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> <class id="Software" _delta="define"> <parent>cmdbAbstractObject</parent> diff --git a/datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php b/datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php index 6bd32b4ff..6ae525f15 100755 --- a/datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php +++ b/datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-config-mgmt/2.7.0', + 'itop-config-mgmt/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-config-mgmt/ru.dict.itop-config-mgmt.php b/datamodels/2.x/itop-config-mgmt/ru.dict.itop-config-mgmt.php index 9d183d1c3..62d1ea3bc 100755 --- a/datamodels/2.x/itop-config-mgmt/ru.dict.itop-config-mgmt.php +++ b/datamodels/2.x/itop-config-mgmt/ru.dict.itop-config-mgmt.php @@ -1882,9 +1882,9 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Server:moreinfo' => 'Спецификация', 'Server:otherinfo' => 'Дополнительно', 'Server:power' => 'Электропитание', - 'Person:info' => 'Основное', - 'UserLocal:info' => 'General information~~', - 'Person:personal_info' => 'Персональная информация', + 'Person:info' => 'Основная информация', + 'UserLocal:info' => 'Основная информация', + 'Person:personal_info' => 'Личная информация', 'Person:notifiy' => 'Уведомления', 'Class:Subnet/Tab:IPUsage' => 'Использование IP-адресов', 'Class:Subnet/Tab:IPUsage-explain' => 'Интерфейсы с IP-адресом в диапазоне: <em>%1$s</em> - <em>%2$s</em>', diff --git a/datamodels/2.x/itop-config-mgmt/zh_cn.dict.itop-config-mgmt.php b/datamodels/2.x/itop-config-mgmt/zh_cn.dict.itop-config-mgmt.php index c52a28d36..546ed1ef9 100755 --- a/datamodels/2.x/itop-config-mgmt/zh_cn.dict.itop-config-mgmt.php +++ b/datamodels/2.x/itop-config-mgmt/zh_cn.dict.itop-config-mgmt.php @@ -1247,11 +1247,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( // Dict::Add('ZH CN', 'Chinese', '简体中文', array( - 'Class:Typology' => '拓扑', + 'Class:Typology' => '类型', 'Class:Typology+' => '', 'Class:Typology/Attribute:name' => '名称', 'Class:Typology/Attribute:name+' => '', - 'Class:Typology/Attribute:finalclass' => '拓扑子类别', + 'Class:Typology/Attribute:finalclass' => '类型', 'Class:Typology/Attribute:finalclass+' => 'Name of the final class', )); @@ -1874,14 +1874,14 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'Menu:Group+' => '配置组', 'Menu:ConfigManagement:Shortcuts' => '快捷方式', 'Menu:ConfigManagement:AllContacts' => '所有联系人: %1$d', - 'Menu:Typology' => '配置管理', - 'Menu:Typology+' => '配置管理', + 'Menu:Typology' => '类型配置', + 'Menu:Typology+' => '类型配置', 'Menu:OSVersion' => 'OS 版本', 'Menu:OSVersion+' => '', 'Menu:Software' => '软件清单', 'Menu:Software+' => '软件清单', 'UI_WelcomeMenu_AllConfigItems' => '摘要', - 'Menu:ConfigManagement:Typology' => '配置管理', + 'Menu:ConfigManagement:Typology' => '类型配置', )); diff --git a/datamodels/2.x/itop-config/module.itop-config.php b/datamodels/2.x/itop-config/module.itop-config.php index 0f070bbcc..cddfe4c3f 100644 --- a/datamodels/2.x/itop-config/module.itop-config.php +++ b/datamodels/2.x/itop-config/module.itop-config.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-config/2.7.0', + 'itop-config/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-core-update/cs.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/cs.dict.itop-core-update.php index db1cef907..9ecf759f1 100644 --- a/datamodels/2.x/itop-core-update/cs.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/cs.dict.itop-core-update.php @@ -73,7 +73,8 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Checking filesystem failed (File not exist %1$s)~~', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Checking filesystem failed~~', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated~~', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~', diff --git a/datamodels/2.x/itop-core-update/da.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/da.dict.itop-core-update.php index f5609070d..60f55a372 100644 --- a/datamodels/2.x/itop-core-update/da.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/da.dict.itop-core-update.php @@ -73,7 +73,8 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Checking filesystem failed (File not exist %1$s)~~', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Checking filesystem failed~~', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated~~', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~', diff --git a/datamodels/2.x/itop-core-update/de.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/de.dict.itop-core-update.php index 914afa4b5..6c8072cc9 100644 --- a/datamodels/2.x/itop-core-update/de.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/de.dict.itop-core-update.php @@ -73,7 +73,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Dateisystemprüfung fehlgeschlagen (Datei nicht vorhanden %1$s)', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Dateisystemprüfung fehlgeschlagen', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Anwendungsupgrade kann durchgeführt werden', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Anwendungsupgrade nicht möglich: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Anwendungsupgrade nicht möglich: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Bereit zum Upgrade', diff --git a/datamodels/2.x/itop-core-update/en.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/en.dict.itop-core-update.php index 5d3fff5cd..72288f7cf 100644 --- a/datamodels/2.x/itop-core-update/en.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/en.dict.itop-core-update.php @@ -73,7 +73,8 @@ Dict::Add('EN US', 'English', 'English', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Checking files failed (File not exist %1$s)', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Checking files failed', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start', diff --git a/datamodels/2.x/itop-core-update/es_cr.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/es_cr.dict.itop-core-update.php index 2d56da7cd..89d45d273 100644 --- a/datamodels/2.x/itop-core-update/es_cr.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/es_cr.dict.itop-core-update.php @@ -21,97 +21,98 @@ * along with iTop. If not, see <http://www.gnu.org/licenses/> */ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( - 'iTopUpdate:UI:PageTitle' => 'Application Upgrade~~', + 'iTopUpdate:UI:PageTitle' => 'Actualización de aplicación', 'itop-core-update:UI:SelectUpdateFile' => 'Application Upgrade~~', 'itop-core-update:UI:ConfirmUpdate' => 'Application Upgrade~~', 'itop-core-update:UI:UpdateCoreFiles' => 'Application Upgrade~~', - 'iTopUpdate:UI:MaintenanceModeActive' => 'The application is currently under maintenance, no user can access the application. You have to run a setup or restore the application archive to return in normal mode.~~', + 'iTopUpdate:UI:MaintenanceModeActive' => 'La aplicación está actualmente en mantenimiento, ningún usuario puede acceder. UStede debe ejecutar la instalación o restaturar la aplicación para regresar al modo normal.', 'itop-core-update:UI:UpdateDone' => 'Application Upgrade~~', 'itop-core-update/Operation:SelectUpdateFile/Title' => 'Application Upgrade~~', - 'itop-core-update/Operation:ConfirmUpdate/Title' => 'Confirm Application Upgrade~~', - 'itop-core-update/Operation:UpdateCoreFiles/Title' => 'Application Upgrading~~', - 'itop-core-update/Operation:UpdateDone/Title' => 'Application Upgrade Done~~', + 'itop-core-update/Operation:ConfirmUpdate/Title' => 'Confirmar actualización de la aplicación', + 'itop-core-update/Operation:UpdateCoreFiles/Title' => 'Actualizando aplicación', + 'itop-core-update/Operation:UpdateDone/Title' => 'Actualización de aplicación terminada', - 'iTopUpdate:UI:SelectUpdateFile' => 'Select an upgrade file to upload~~', - 'iTopUpdate:UI:CheckUpdate' => 'Verify upgrade file~~', - 'iTopUpdate:UI:ConfirmInstallFile' => 'You are about to install %1$s~~', - 'iTopUpdate:UI:DoUpdate' => 'Upgrade~~', - 'iTopUpdate:UI:CurrentVersion' => 'Current installed version~~', - 'iTopUpdate:UI:NewVersion' => 'Newly installed version~~', - 'iTopUpdate:UI:Back' => 'Back~~', - 'iTopUpdate:UI:Cancel' => 'Cancel~~', - 'iTopUpdate:UI:Continue' => 'Continue~~', - 'iTopUpdate:UI:RunSetup' => 'Run Setup~~', - 'iTopUpdate:UI:WithDBBackup' => 'Database backup~~', - 'iTopUpdate:UI:WithFilesBackup' => 'Application files backup~~', - 'iTopUpdate:UI:WithoutBackup' => 'No backup is planned~~', - 'iTopUpdate:UI:Backup' => 'Backup generated before update~~', - 'iTopUpdate:UI:DoFilesArchive' => 'Archive application files~~', - 'iTopUpdate:UI:UploadArchive' => 'Select a package to upload~~', - 'iTopUpdate:UI:ServerFile' => 'Path of a package already on the server~~', - 'iTopUpdate:UI:WarningReadOnlyDuringUpdate' => 'During the upgrade, the application will be read-only.~~', + 'iTopUpdate:UI:SelectUpdateFile' => 'Seleccione un archivo de actualización para subir', + 'iTopUpdate:UI:CheckUpdate' => 'Verificar archivo de actualización', + 'iTopUpdate:UI:ConfirmInstallFile' => 'Usted va a instalar %1$s', + 'iTopUpdate:UI:DoUpdate' => 'Actualizar', + 'iTopUpdate:UI:CurrentVersion' => 'Versión instalada actualmente', + 'iTopUpdate:UI:NewVersion' => 'Nueva versión instalada', + 'iTopUpdate:UI:Back' => 'Volver', + 'iTopUpdate:UI:Cancel' => 'Cancelar', + 'iTopUpdate:UI:Continue' => 'Continuar', + 'iTopUpdate:UI:RunSetup' => 'Ejecutar instalación', + 'iTopUpdate:UI:WithDBBackup' => 'Respaldo de base de datos', + 'iTopUpdate:UI:WithFilesBackup' => 'Respaldo de archivos de aplicación', + 'iTopUpdate:UI:WithoutBackup' => 'No hay respaldos planificados', + 'iTopUpdate:UI:Backup' => 'Respaldo generado antes de actualizar', + 'iTopUpdate:UI:DoFilesArchive' => 'Respaldar archivos de aplicación', + 'iTopUpdate:UI:UploadArchive' => 'Selecione un paquete para subir', + 'iTopUpdate:UI:ServerFile' => 'Ruta del paquete disponible en el servidor', + 'iTopUpdate:UI:WarningReadOnlyDuringUpdate' => 'Durante la actualización, la aplicación estará en modo sólo lectura.', - 'iTopUpdate:UI:Status' => 'Status~~', - 'iTopUpdate:UI:Action' => 'Update~~', - 'iTopUpdate:UI:History' => 'Versions History~~', - 'iTopUpdate:UI:Progress' => 'Progress of the upgrade~~', + 'iTopUpdate:UI:Status' => 'Estado', + 'iTopUpdate:UI:Action' => 'Actualización', + 'iTopUpdate:UI:History' => 'Historial de versiones', + 'iTopUpdate:UI:Progress' => 'Progreso de actualización', - 'iTopUpdate:UI:DoBackup:Label' => 'Backup files and database~~', - 'iTopUpdate:UI:DoBackup:Warning' => 'Backup is not recommended due to limited available disk space~~', + 'iTopUpdate:UI:DoBackup:Label' => 'Respaldo de archivos y base de datos', + 'iTopUpdate:UI:DoBackup:Warning' => 'El respaldo no está recomendado por el limitado espacio en el dispositivo', - 'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space~~', - 'iTopUpdate:UI:ItopDiskSpace' => 'iTop disk space~~', - 'iTopUpdate:UI:DBDiskSpace' => 'Database disk space~~', - 'iTopUpdate:UI:FileUploadMaxSize' => 'File upload max size~~', + 'iTopUpdate:UI:DiskFreeSpace' => 'Espaciolibre en el dispositivo', + 'iTopUpdate:UI:ItopDiskSpace' => 'Espacio en diso de iTop', + 'iTopUpdate:UI:DBDiskSpace' => 'Espacio en diso de base de datos', + 'iTopUpdate:UI:FileUploadMaxSize' => 'Máximo tamaño de subida de archivos', - 'iTopUpdate:UI:PostMaxSize' => 'PHP ini value post_max_size: %1$s~~', - 'iTopUpdate:UI:UploadMaxFileSize' => 'PHP ini value upload_max_filesize: %1$s~~', + 'iTopUpdate:UI:PostMaxSize' => 'Valor post_max_size en PHP ini: %1$s~~', + 'iTopUpdate:UI:UploadMaxFileSize' => 'Valor upload_max_filesize en PHP ini: %1$s~~', - 'iTopUpdate:UI:CanCoreUpdate:Loading' => 'Checking filesystem~~', - 'iTopUpdate:UI:CanCoreUpdate:Error' => 'Checking filesystem failed (%1$s)~~', - 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Checking filesystem failed (File not exist %1$s)~~', - 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Checking filesystem failed~~', - 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated~~', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:Loading' => 'Revisando sistema de archivos', + 'iTopUpdate:UI:CanCoreUpdate:Error' => 'La revisión del sistema de archivos falló (%1$s)', + 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'La revisión del sistema de archivos falló (Archivo no existe %1$s)~~', + 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'La revisión del sistema de archivos falló', + 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'La aplicación puede ser actualizada', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'La aplicación no puede ser actualizada: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages - 'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~', - 'iTopUpdate:UI:SetupMessage:EnterMaintenance' => 'Entering maintenance mode~~', - 'iTopUpdate:UI:SetupMessage:Backup' => 'Database backup~~', - 'iTopUpdate:UI:SetupMessage:FilesArchive' => 'Archive application files~~', - 'iTopUpdate:UI:SetupMessage:CopyFiles' => 'Copy new version files~~', - 'iTopUpdate:UI:SetupMessage:CheckCompile' => 'Check application upgrade~~', - 'iTopUpdate:UI:SetupMessage:Compile' => 'Upgrade application and database~~', - 'iTopUpdate:UI:SetupMessage:UpdateDatabase' => 'Upgrade database~~', - 'iTopUpdate:UI:SetupMessage:ExitMaintenance' => 'Exiting maintenance mode~~', - 'iTopUpdate:UI:SetupMessage:UpdateDone' => 'Upgrade completed~~', + 'iTopUpdate:UI:SetupMessage:Ready' => 'Listo para empezar', + 'iTopUpdate:UI:SetupMessage:EnterMaintenance' => 'Entrando en modo mantenimiento', + 'iTopUpdate:UI:SetupMessage:Backup' => 'Respaldo de base de datos', + 'iTopUpdate:UI:SetupMessage:FilesArchive' => 'Respaldar archivos de aplicación', + 'iTopUpdate:UI:SetupMessage:CopyFiles' => 'Copiar archivos de nueva version', + 'iTopUpdate:UI:SetupMessage:CheckCompile' => 'Revisar actualización de aplicación', + 'iTopUpdate:UI:SetupMessage:Compile' => 'Actualizar aplicación y base de datos', + 'iTopUpdate:UI:SetupMessage:UpdateDatabase' => 'Actualizar base de datos', + 'iTopUpdate:UI:SetupMessage:ExitMaintenance' => 'Saliendo del modo mantenimiento', + 'iTopUpdate:UI:SetupMessage:UpdateDone' => 'Actualización completada', // Errors - 'iTopUpdate:Error:MissingFunction' => 'Impossible to start upgrade, missing function~~', - 'iTopUpdate:Error:MissingFile' => 'Missing file: %1$s~~', - 'iTopUpdate:Error:CorruptedFile' => 'File %1$s is corrupted~~', - 'iTopUpdate:Error:BadFileFormat' => 'Upgrade file is not a zip file~~', - 'iTopUpdate:Error:BadFileContent' => 'Upgrade file is not an application archive~~', - 'iTopUpdate:Error:BadItopProduct' => 'Upgrade file is not compatible with your application~~', - 'iTopUpdate:Error:Copy' => 'Error, cannot copy \'%1$s\' to \'%2$s\'~~', - 'iTopUpdate:Error:FileNotFound' => 'File not found~~', - 'iTopUpdate:Error:NoFile' => 'No file provided~~', - 'iTopUpdate:Error:InvalidToken' => 'Invalid token~~', - 'iTopUpdate:Error:UpdateFailed' => 'Upgrade failed ~~', - 'iTopUpdate:Error:FileUploadMaxSizeTooSmall' => 'The upload max size seems too small for update. Please change the PHP configuration.~~', + 'iTopUpdate:Error:MissingFunction' => 'Imposible comenzar actualización, función no disponible', + 'iTopUpdate:Error:MissingFile' => 'Archivo no encontrado: %1$s~~', + 'iTopUpdate:Error:CorruptedFile' => 'El archivo %1$s está corrupto', + 'iTopUpdate:Error:BadFileFormat' => 'El archivo de actualización no es un archivo zip', + 'iTopUpdate:Error:BadFileContent' => 'El archivo de actualización no es correcto', + 'iTopUpdate:Error:BadItopProduct' => 'El archivo de actualización no es compatible con su producto', + 'iTopUpdate:Error:Copy' => 'Error, no puedo copiar \'%1$s\' a \'%2$s\'~~', + 'iTopUpdate:Error:FileNotFound' => 'Archivo no encontrado', + 'iTopUpdate:Error:NoFile' => 'Archivo no seleccionado', + 'iTopUpdate:Error:InvalidToken' => 'Token inválido', + 'iTopUpdate:Error:UpdateFailed' => 'La actualización ha fallado', + 'iTopUpdate:Error:FileUploadMaxSizeTooSmall' => 'El archivo de actualización parece demasiado pequeño. Por favor cambie la configuración PHP.', - 'iTopUpdate:UI:RestoreArchive' => 'You can restore your application from the archive \'%1$s\'~~', - 'iTopUpdate:UI:RestoreBackup' => 'You can restore the database from \'%1$s\'~~', - 'iTopUpdate:UI:UpdateDone' => 'Upgrade successful~~', - 'Menu:iTopUpdate' => 'Application Upgrade~~', - 'Menu:iTopUpdate+' => 'Application Upgrade~~', + 'iTopUpdate:UI:RestoreArchive' => 'Usted puede restaurar su aplicación desde el archivo \'%1$s\'', + 'iTopUpdate:UI:RestoreBackup' => 'Usted puede restaurar la base de datos desde \'%1$s\'', + 'iTopUpdate:UI:UpdateDone' => 'Actualización exitosa', + 'Menu:iTopUpdate' => 'Actualización de aplicación', + 'Menu:iTopUpdate+' => 'Actualización de aplicación', // Missing itop entries - 'Class:ModuleInstallation/Attribute:installed' => 'Installed on~~', - 'Class:ModuleInstallation/Attribute:name' => 'Name~~', - 'Class:ModuleInstallation/Attribute:version' => 'Version~~', - 'Class:ModuleInstallation/Attribute:comment' => 'Comment~~', + 'Class:ModuleInstallation/Attribute:installed' => 'Instalado en', + 'Class:ModuleInstallation/Attribute:name' => 'Nombre', + 'Class:ModuleInstallation/Attribute:version' => 'Versión', + 'Class:ModuleInstallation/Attribute:comment' => 'Commentario', )); diff --git a/datamodels/2.x/itop-core-update/fr.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/fr.dict.itop-core-update.php index bb1f926c9..8dae6b928 100644 --- a/datamodels/2.x/itop-core-update/fr.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/fr.dict.itop-core-update.php @@ -74,6 +74,7 @@ Dict::Add('FR FR', 'French', 'Français', array( 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Échec de la vérification des fichiers', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'L\'application peut être mise à jour', 'iTopUpdate:UI:CanCoreUpdate:No' => 'L\'application ne peut pas être mise à jour : %1$s', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Attention : la mise à jour de l\'application peut échouer : %1$s', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Prêt pour l\\installation', diff --git a/datamodels/2.x/itop-core-update/hu.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/hu.dict.itop-core-update.php index b81aced19..a9237bc05 100644 --- a/datamodels/2.x/itop-core-update/hu.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/hu.dict.itop-core-update.php @@ -73,7 +73,8 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Checking filesystem failed (File not exist %1$s)~~', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Checking filesystem failed~~', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated~~', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~', diff --git a/datamodels/2.x/itop-core-update/it.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/it.dict.itop-core-update.php index e3aa5d0de..e7eb78ff1 100644 --- a/datamodels/2.x/itop-core-update/it.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/it.dict.itop-core-update.php @@ -73,7 +73,8 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Checking filesystem failed (File not exist %1$s)~~', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Checking filesystem failed~~', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated~~', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~', diff --git a/datamodels/2.x/itop-core-update/ja.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/ja.dict.itop-core-update.php index a82b9cef5..67bd2b8ef 100644 --- a/datamodels/2.x/itop-core-update/ja.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/ja.dict.itop-core-update.php @@ -73,7 +73,8 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Checking filesystem failed (File not exist %1$s)~~', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Checking filesystem failed~~', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated~~', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~', diff --git a/datamodels/2.x/itop-core-update/module.itop-core-update.php b/datamodels/2.x/itop-core-update/module.itop-core-update.php index 571750885..e3dd60013 100644 --- a/datamodels/2.x/itop-core-update/module.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/module.itop-core-update.php @@ -24,7 +24,7 @@ /** @noinspection PhpUnhandledExceptionInspection */ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-core-update/2.7.0', + 'itop-core-update/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-core-update/nl.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/nl.dict.itop-core-update.php index 054a69f4c..5ddff37db 100644 --- a/datamodels/2.x/itop-core-update/nl.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/nl.dict.itop-core-update.php @@ -75,7 +75,8 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Controle van het bestandssysteem mislukt (Bestand bestaat niet: %1$s)', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Controle van het bestandssysteem is mislukt', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Updaten van toepassing is mogelijk', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Updaten van de toepassing is niet mogelijk: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Updaten van de toepassing is niet mogelijk: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Klaar om verder te gaan', diff --git a/datamodels/2.x/itop-core-update/pt_br.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/pt_br.dict.itop-core-update.php index 61f8f7a3e..b7cc44c40 100644 --- a/datamodels/2.x/itop-core-update/pt_br.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/pt_br.dict.itop-core-update.php @@ -73,7 +73,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Checking filesystem failed (File not exist %1$s)~~', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Checking filesystem failed~~', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated~~', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~', diff --git a/datamodels/2.x/itop-core-update/ru.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/ru.dict.itop-core-update.php index c183fc1bd..c8b6a4f3e 100644 --- a/datamodels/2.x/itop-core-update/ru.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/ru.dict.itop-core-update.php @@ -16,21 +16,21 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'iTopUpdate:UI:MaintenanceModeActive' => 'В настоящее время приложение находится в режиме технического обслуживания, пользователи не могут получить доступ к приложению. Вы должны запустить программу установки или восстановить архив приложения, чтобы вернуться к нормальному режиму.', 'itop-core-update:UI:UpdateDone' => 'Обновление завершено', - 'itop-core-update/Operation:SelectUpdateFile/Title' => 'Application Upgrade~~', - 'itop-core-update/Operation:ConfirmUpdate/Title' => 'Confirm Application Upgrade~~', - 'itop-core-update/Operation:UpdateCoreFiles/Title' => 'Application Upgrading~~', - 'itop-core-update/Operation:UpdateDone/Title' => 'Application Upgrade Done~~', + 'itop-core-update/Operation:SelectUpdateFile/Title' => 'Обновление приложения', + 'itop-core-update/Operation:ConfirmUpdate/Title' => 'Подтверждение обновления приложения', + 'itop-core-update/Operation:UpdateCoreFiles/Title' => 'Обновление приложения', + 'itop-core-update/Operation:UpdateDone/Title' => 'Обновление приложения завершено', 'iTopUpdate:UI:SelectUpdateFile' => 'Выбор файла обновления', 'iTopUpdate:UI:CheckUpdate' => 'Проверить файл обновления', 'iTopUpdate:UI:ConfirmInstallFile' => 'Вы собираетесь установить %1$s', 'iTopUpdate:UI:DoUpdate' => 'Начать обновление', 'iTopUpdate:UI:CurrentVersion' => 'Текущая версия', - 'iTopUpdate:UI:NewVersion' => 'Newly installed version~~', + 'iTopUpdate:UI:NewVersion' => 'Новая версия', 'iTopUpdate:UI:Back' => 'Назад', 'iTopUpdate:UI:Cancel' => 'Отменть', 'iTopUpdate:UI:Continue' => 'Продолжить', - 'iTopUpdate:UI:RunSetup' => 'Run Setup~~', + 'iTopUpdate:UI:RunSetup' => 'Запустить установку', 'iTopUpdate:UI:WithDBBackup' => 'Резервная копия базы данных', 'iTopUpdate:UI:WithFilesBackup' => 'Архив файлов приложения', 'iTopUpdate:UI:WithoutBackup' => 'Без резервного копирования', @@ -38,7 +38,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'iTopUpdate:UI:DoFilesArchive' => 'Создать архив файлов приложения', 'iTopUpdate:UI:UploadArchive' => 'Выбор пакета для загрузки', 'iTopUpdate:UI:ServerFile' => 'Путь к пакету на сервере', - 'iTopUpdate:UI:WarningReadOnlyDuringUpdate' => 'During the upgrade, the application will be read-only.~~', + 'iTopUpdate:UI:WarningReadOnlyDuringUpdate' => 'Во время обновления приложение будет доступно только для чтения.', 'iTopUpdate:UI:Status' => 'Статус', 'iTopUpdate:UI:Action' => 'Обновление', @@ -53,15 +53,16 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'iTopUpdate:UI:DBDiskSpace' => 'Размер базы данных', 'iTopUpdate:UI:FileUploadMaxSize' => 'Максимальный размер загружаемого файла', - 'iTopUpdate:UI:PostMaxSize' => 'Значение PHP ini post_max_size: %1$s~~', - 'iTopUpdate:UI:UploadMaxFileSize' => 'Значение PHP ini upload_max_filesize: %1$s~~', + 'iTopUpdate:UI:PostMaxSize' => 'Значение PHP ini post_max_size: %1$s', + 'iTopUpdate:UI:UploadMaxFileSize' => 'Значение PHP ini upload_max_filesize: %1$s', 'iTopUpdate:UI:CanCoreUpdate:Loading' => 'Проверка файловой системы', 'iTopUpdate:UI:CanCoreUpdate:Error' => 'Ошибка проверки файловой системы (%1$s)', 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Ошибка проверки файловой системы (файл не существует %1$s)', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Ошибка проверки файловой системы', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Приложение может быть обновлено', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Приложение не может быть обновлено: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Приложение не может быть обновлено: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Всё готово к началу', @@ -69,9 +70,9 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'iTopUpdate:UI:SetupMessage:Backup' => 'Резервное копирование базы данных', 'iTopUpdate:UI:SetupMessage:FilesArchive' => 'Архивирование файлов приложения', 'iTopUpdate:UI:SetupMessage:CopyFiles' => 'Копирование файлов обновления', - 'iTopUpdate:UI:SetupMessage:CheckCompile' => 'Check application upgrade~~', - 'iTopUpdate:UI:SetupMessage:Compile' => 'Обновление приложения и базы данных', - 'iTopUpdate:UI:SetupMessage:UpdateDatabase' => 'Upgrade database~~', + 'iTopUpdate:UI:SetupMessage:CheckCompile' => 'Проверка обновления', + 'iTopUpdate:UI:SetupMessage:Compile' => 'Обновление приложения', + 'iTopUpdate:UI:SetupMessage:UpdateDatabase' => 'Обновление базы данных', 'iTopUpdate:UI:SetupMessage:ExitMaintenance' => 'Выход из режима технического обслуживания', 'iTopUpdate:UI:SetupMessage:UpdateDone' => 'Обновление завершено', diff --git a/datamodels/2.x/itop-core-update/sk.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/sk.dict.itop-core-update.php index 55101d8f2..be40becf1 100644 --- a/datamodels/2.x/itop-core-update/sk.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/sk.dict.itop-core-update.php @@ -73,7 +73,8 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Checking filesystem failed (File not exist %1$s)~~', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Checking filesystem failed~~', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated~~', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~', diff --git a/datamodels/2.x/itop-core-update/src/Controller/AjaxController.php b/datamodels/2.x/itop-core-update/src/Controller/AjaxController.php index 736947708..dc33efa7f 100644 --- a/datamodels/2.x/itop-core-update/src/Controller/AjaxController.php +++ b/datamodels/2.x/itop-core-update/src/Controller/AjaxController.php @@ -28,7 +28,8 @@ class AjaxController extends Controller try { - $bCanUpdateCore = FilesInformation::CanUpdateCore($sMessage); + $sCanUpdateCore = FilesInformation::CanUpdateCore($sMessage); + $bCanUpdateCore = ($sCanUpdateCore == 'Yes'); $aParams['bStatus'] = $bCanUpdateCore; if ($bCanUpdateCore) { @@ -36,7 +37,7 @@ class AjaxController extends Controller } else { - $aParams['sMessage'] = Dict::Format('iTopUpdate:UI:CanCoreUpdate:No', $sMessage); + $aParams['sMessage'] = Dict::Format("iTopUpdate:UI:CanCoreUpdate:{$sCanUpdateCore}", $sMessage); } } catch (FileNotExistException $e) { diff --git a/datamodels/2.x/itop-core-update/tr.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/tr.dict.itop-core-update.php index 7d8a5878d..345b706ff 100644 --- a/datamodels/2.x/itop-core-update/tr.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/tr.dict.itop-core-update.php @@ -73,7 +73,8 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Checking filesystem failed (File not exist %1$s)~~', 'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Checking filesystem failed~~', 'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated~~', - 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start~~', diff --git a/datamodels/2.x/itop-core-update/zh_cn.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/zh_cn.dict.itop-core-update.php index 3a5ef6400..b89043804 100644 --- a/datamodels/2.x/itop-core-update/zh_cn.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/zh_cn.dict.itop-core-update.php @@ -73,7 +73,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => '文件系统检查失败 ( %1$s 文件不存在)', 'iTopUpdate:UI:CanCoreUpdate:Failed' => '文件系统检查失败', 'iTopUpdate:UI:CanCoreUpdate:Yes' => '应用无法升级', - 'iTopUpdate:UI:CanCoreUpdate:No' => '应用无法升级: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:No' => '应用无法升级: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => '准备开始', diff --git a/datamodels/2.x/itop-datacenter-mgmt/datamodel.itop-datacenter-mgmt.xml b/datamodels/2.x/itop-datacenter-mgmt/datamodel.itop-datacenter-mgmt.xml index da2cd7849..77836cdf3 100755 --- a/datamodels/2.x/itop-datacenter-mgmt/datamodel.itop-datacenter-mgmt.xml +++ b/datamodels/2.x/itop-datacenter-mgmt/datamodel.itop-datacenter-mgmt.xml @@ -232,18 +232,7 @@ <count_max>0</count_max> </field> </fields> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <presentation> <details> <items> @@ -411,18 +400,7 @@ </reconciliation> </properties> <fields/> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <presentation> <details> <items> @@ -911,18 +889,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> </classes> <menus> diff --git a/datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php b/datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php index 44b4ff158..7998563a7 100755 --- a/datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php +++ b/datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php @@ -18,7 +18,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-datacenter-mgmt/2.7.0', + 'itop-datacenter-mgmt/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php b/datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php index af97065c8..97f31ed1d 100644 --- a/datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php +++ b/datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php @@ -25,7 +25,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-endusers-devices/2.7.0', + 'itop-endusers-devices/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-files-information/module.itop-files-information.php b/datamodels/2.x/itop-files-information/module.itop-files-information.php index 72884f223..15080edfd 100644 --- a/datamodels/2.x/itop-files-information/module.itop-files-information.php +++ b/datamodels/2.x/itop-files-information/module.itop-files-information.php @@ -24,7 +24,7 @@ /** @noinspection PhpUnhandledExceptionInspection */ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-files-information/2.7.0', + 'itop-files-information/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-files-information/src/Service/FilesInformation.php b/datamodels/2.x/itop-files-information/src/Service/FilesInformation.php index 251ea6e7d..db105ba8a 100644 --- a/datamodels/2.x/itop-files-information/src/Service/FilesInformation.php +++ b/datamodels/2.x/itop-files-information/src/Service/FilesInformation.php @@ -21,33 +21,71 @@ class FilesInformation * * @param string $sMessage * - * @return bool true if core update is possible + * @return string 'Yes', 'No', 'Warning' * @throws \Combodo\iTop\FilesInformation\Service\FileNotExistException + * @throws \Exception */ public static function CanUpdateCore(&$sMessage) { self::Init(); // Check than iTop can write everywhere - if (!self::CanWriteRecursive('', $sMessage)) + $aFilesInfo = FilesIntegrity::GetInstalledFiles(APPROOT.'manifest.xml'); + if ($aFilesInfo === false) + { + $sMessage = Dict::Format('FilesInformation:Error:MissingFile', 'manifest.xml'); + return 'No'; + } + // generate files and folders list + $aInstalledFiles = array(); + foreach (array_keys($aFilesInfo) as $sFile) + { + $sLocalDirPath = utils::LocalPath(APPROOT.dirname($sFile)); + if ($sLocalDirPath !== false) + { + if (!isset($aInstalledFiles[$sLocalDirPath])) + { + $aInstalledFiles[$sLocalDirPath] = true; + } + $aInstalledFiles[$sFile] = true; + } + } + if (!self::CanWriteRecursive('', $sMessage, $aInstalledFiles)) { - return false; + return 'No'; } - return true; + try + { + FilesIntegrity::CheckInstallationIntegrity(); + } + catch (FileIntegrityException $e) + { + $sMessage = $e->getMessage(); + return 'Warning'; + } + + return 'Yes'; } /** * @param string $sRootPath * @param string $sMessage + * @param array $aInstalledFiles * * @return bool * @throws \Combodo\iTop\FilesInformation\Service\FileNotExistException */ - private static function CanWriteRecursive($sRootPath = '', &$sMessage = null) + private static function CanWriteRecursive($sRootPath = '', &$sMessage = null, $aInstalledFiles = array()) { $aDirStats = FilesInformationUtils::Scan($sRootPath, false); foreach ($aDirStats as $sFileName => $aFileStats) { + // For name normalization + $sLocalPath = utils::LocalPath(APPROOT.$sRootPath.DIRECTORY_SEPARATOR.$sFileName); + if (($sLocalPath === false) || !isset($aInstalledFiles[$sLocalPath])) + { + continue; + } if (!self::CanWriteToFile($aFileStats)) { $sMessage = Dict::Format('FilesInformation:Error:CantWriteToFile', $sRootPath.DIRECTORY_SEPARATOR.$sFileName); @@ -55,7 +93,7 @@ class FilesInformation } if (($sFileName != '.') && ($aFileStats['type'] == 'dir')) { - if (!self::CanWriteRecursive($sRootPath.DIRECTORY_SEPARATOR.$sFileName, $sMessage)) + if (!self::CanWriteRecursive($sRootPath.DIRECTORY_SEPARATOR.$sFileName, $sMessage, $aInstalledFiles)) { return false; } diff --git a/datamodels/2.x/itop-files-information/src/Service/FilesIntegrity.php b/datamodels/2.x/itop-files-information/src/Service/FilesIntegrity.php index 25753b3b7..66c8163cf 100644 --- a/datamodels/2.x/itop-files-information/src/Service/FilesIntegrity.php +++ b/datamodels/2.x/itop-files-information/src/Service/FilesIntegrity.php @@ -25,7 +25,7 @@ class FilesIntegrity * @return array|false list of file info (path, size, md5) * @throws \Exception */ - private static function GetInstalledFiles($sManifest) + public static function GetInstalledFiles($sManifest) { $aFiles = array(); @@ -54,14 +54,19 @@ class FilesIntegrity if ($oFileNode->hasChildNodes()) { $aFileInfo = array(); + $sFilePath = uniqid(); // just in case no path... foreach ($oFileNode->childNodes as $oFileInfo) { if ($oFileInfo instanceof DOMElement) { $aFileInfo[$oFileInfo->tagName] = $oFileInfo->textContent; + if ($oFileInfo->tagName == 'path') + { + $sFilePath = $oFileInfo->textContent; + } } } - $aFiles[] = $aFileInfo; + $aFiles[$sFilePath] = $aFileInfo; } } } @@ -98,7 +103,6 @@ class FilesIntegrity $sChecksum = md5($sContent); if (($iSize != $aFileInfo['size']) || ($sChecksum != $aFileInfo['md5'])) { - throw new FileIntegrityException(Dict::Format('FilesInformation:Error:CorruptedFile', basename($sFile))); } } diff --git a/datamodels/2.x/itop-full-itil/module.itop-full-itil.php b/datamodels/2.x/itop-full-itil/module.itop-full-itil.php index 5a4ed0da6..bd7e5a7d2 100644 --- a/datamodels/2.x/itop-full-itil/module.itop-full-itil.php +++ b/datamodels/2.x/itop-full-itil/module.itop-full-itil.php @@ -6,7 +6,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-full-itil/2.7.0', + 'itop-full-itil/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-hub-connector/land.php b/datamodels/2.x/itop-hub-connector/land.php index e3f209084..031ee6852 100644 --- a/datamodels/2.x/itop-hub-connector/land.php +++ b/datamodels/2.x/itop-hub-connector/land.php @@ -241,7 +241,6 @@ function DoInstall(WebPage $oPage) $oPage->add_linked_stylesheet('../css/font-awesome/css/all.min.css'); - $oPage->add_linked_stylesheet('../css/font-awesome/css/v4-shims.min.css'); $oPage->add('<div id="hub_installation_widget"></div>'); @@ -326,7 +325,7 @@ try catch(Exception $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); $oP->output(); diff --git a/datamodels/2.x/itop-hub-connector/launch.php b/datamodels/2.x/itop-hub-connector/launch.php index 5e1f352d8..5e042c734 100644 --- a/datamodels/2.x/itop-hub-connector/launch.php +++ b/datamodels/2.x/itop-hub-connector/launch.php @@ -414,7 +414,7 @@ EOF catch (CoreException $e) { require_once (APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getHtmlDesc())); $oP->output(); @@ -443,7 +443,7 @@ catch (CoreException $e) catch (Exception $e) { require_once (APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); $oP->output(); diff --git a/datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php b/datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php index e25a4804a..6edc0d6d9 100644 --- a/datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php +++ b/datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php @@ -5,7 +5,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-hub-connector/2.7.0', + 'itop-hub-connector/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php b/datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php index ed4cb3c3c..8fe0ff2c7 100755 --- a/datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php +++ b/datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-incident-mgmt-itil/2.7.0', + 'itop-incident-mgmt-itil/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php b/datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php index 10a1c8d31..1c8217a0f 100755 --- a/datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php +++ b/datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-knownerror-mgmt/2.7.0', + 'itop-knownerror-mgmt/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-portal-base/module.itop-portal-base.php b/datamodels/2.x/itop-portal-base/module.itop-portal-base.php index 9588546f9..35c6a6238 100644 --- a/datamodels/2.x/itop-portal-base/module.itop-portal-base.php +++ b/datamodels/2.x/itop-portal-base/module.itop-portal-base.php @@ -20,7 +20,7 @@ /** @noinspection PhpUnhandledExceptionInspection */ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-portal-base/2.7.0', array( + 'itop-portal-base/2.8.0', array( // Identification 'label' => 'Portal Development Library', 'category' => 'Portal', diff --git a/datamodels/2.x/itop-portal-base/portal/composer.json b/datamodels/2.x/itop-portal-base/portal/composer.json index 0042dcb7f..92a2d0e13 100644 --- a/datamodels/2.x/itop-portal-base/portal/composer.json +++ b/datamodels/2.x/itop-portal-base/portal/composer.json @@ -1,8 +1,5 @@ { "license": "AGPLv3", - "config": { - "classmap-authoritative": true - }, "autoload": { "psr-4": { "Combodo\\iTop\\Portal\\": "src/" diff --git a/datamodels/2.x/itop-portal-base/portal/public/css/portal.css b/datamodels/2.x/itop-portal-base/portal/public/css/portal.css index 563a27f29..d43e17703 100644 --- a/datamodels/2.x/itop-portal-base/portal/public/css/portal.css +++ b/datamodels/2.x/itop-portal-base/portal/public/css/portal.css @@ -1014,6 +1014,9 @@ table .group-actions { /****************/ /* Filter brick */ /****************/ +.tile.tile-filter-brick .tile_decoration .icon { + color: #da7014; +} .tile.tile-filter-brick .tile_filterbox .form-group:first-child { width: 100%; } diff --git a/datamodels/2.x/itop-portal-base/portal/public/css/portal.scss b/datamodels/2.x/itop-portal-base/portal/public/css/portal.scss index f06bf7d38..18b89ed9e 100644 --- a/datamodels/2.x/itop-portal-base/portal/public/css/portal.scss +++ b/datamodels/2.x/itop-portal-base/portal/public/css/portal.scss @@ -1077,6 +1077,10 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{ /****************/ /* Filter brick */ /****************/ +.tile.tile-filter-brick .tile_decoration .icon { + color: $link-color; +} + .tile.tile-filter-brick .tile_filterbox .form-group:first-child{ width: 100%; } diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php index 702bb3611..ab8921327 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php @@ -35,6 +35,7 @@ use Combodo\iTop\Portal\Helper\ApplicationHelper; use DBObject; use DBObjectSet; use DBSearch; +use DBUnionSearch; use Dict; use Exception; use FieldExpression; @@ -343,11 +344,13 @@ class ManageBrickController extends BrickController // Otherwise we create the tabs from the SQL expressions else { + $aConditionQueryGrouping = array(); foreach ($aGroupingTabs['groups'] as $aGroup) { - $oConditionQuery = $oQuery->Intersect(DBSearch::FromOQL($aGroup['condition'])); + $oDBSearch = DBSearch::FromOQL($aGroup['condition']); + $oConditionQuery = $oQuery->Intersect($oDBSearch); // - Restricting query to scope - + array_push($aConditionQueryGrouping,$oDBSearch); $bHasScope = $oScopeValidator->AddScopeToQuery($oConditionQuery, $oConditionQuery->GetClass()); if ($bHasScope) { @@ -368,7 +371,26 @@ class ManageBrickController extends BrickController 'condition' => $oConditionQuery, 'count' => $iGroupCount, ); - $iCount += $iGroupCount; + } + try + { + $oConditionQuery = $oQuery->Intersect(new DBUnionSearch($aConditionQueryGrouping)); + $bHasScope = $oScopeValidator->AddScopeToQuery($oConditionQuery, $oConditionQuery->GetClass()); + if ($bHasScope) + { + // - Building ObjectSet + $oConditionSet = new DBObjectSet($oConditionQuery); + $iCount = $oConditionSet->Count(); + } + else + { + $oConditionSet = null; + $iCount = 0; + } + } + catch (Exception $e){ + $oConditionSet = null; + $iCount = -1; } } } diff --git a/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php b/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php index c014500eb..835099838 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php @@ -341,6 +341,19 @@ class ObjectFormManager extends FormManager } // Building form from its properties + // - Consistency checks for stimulus form + if (isset($this->aFormProperties['stimulus_code'])) + { + $aTransitions = MetaModel::EnumTransitions($sObjectClass, $this->oObject->GetState()); + if (!isset($aTransitions[$this->aFormProperties['stimulus_code']])) + { + $aStimuli = Metamodel::EnumStimuli($sObjectClass); + $sStimulusLabel = $aStimuli[$this->aFormProperties['stimulus_code']]->GetLabel(); + + $sExceptionMessage = Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulusLabel, $this->oObject->GetName(), $this->oObject->GetStateLabel()); + throw new Exception($sExceptionMessage); + } + } // - The fields switch ($this->aFormProperties['type']) { diff --git a/datamodels/2.x/itop-portal-base/portal/src/Helper/ApplicationHelper.php b/datamodels/2.x/itop-portal-base/portal/src/Helper/ApplicationHelper.php index 79d3fcb4c..8076fe4ba 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Helper/ApplicationHelper.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/ApplicationHelper.php @@ -28,10 +28,6 @@ use Dict; use Exception; use IssueLog; use MetaModel; -use Silex\Application; -use Symfony\Component\Debug\ErrorHandler; -use Symfony\Component\Debug\ExceptionHandler; -use Symfony\Component\HttpKernel\Exception\HttpException; /** * Contains static methods to help loading / registering classes of the application. @@ -134,13 +130,14 @@ class ApplicationHelper * If not found, tries to find one from the closest parent class. * Else returns a default form based on zlist 'details' * - * @param array $aForms + * @param array $aForms * @param string $sClass Object class to find a form for - * @param string $sMode Form mode to find (view|edit|create) + * @param string $sMode Form mode to find (view|edit|create) * * @return array * * @throws \CoreException + * @throws \Exception */ public static function GetLoadedFormFromClass($aForms, $sClass, $sMode) { @@ -244,10 +241,11 @@ class ApplicationHelper * Generate the form data for the $sClass. * Form will look like the "Properties" tab of a $sClass object in the console. * - * @param string $sClass - * @param bool $bAddLinksets + * @param string $sClass + * @param bool $bAddLinksets * * @return array + * @throws \Exception */ protected static function GenerateDefaultFormForClass($sClass, $bAddLinksets = false) { @@ -275,6 +273,13 @@ class ApplicationHelper // - Retrieve zlist details $aDetailsList = MetaModel::GetZListItems($sClass, 'details'); $aDetailsStruct = cmdbAbstractObject::ProcessZlist($aDetailsList, array(), 'UI:PropertiesTab', 'col1', ''); + if(!isset($aDetailsStruct['UI:PropertiesTab'])) + { + // For the iTop administrator + IssueLog::Error('Could not generate default form for "'.$sClass.'" class. Is the "details" zlist empty?'); + // For the end-user + throw new Exception('Could not generate form, check the error log for more information.'); + } $aPropertiesStruct = $aDetailsStruct['UI:PropertiesTab']; // Count cols (not linksets) diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/filter/tile.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/filter/tile.html.twig index 52d2f6bf3..3009aa2bc 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/filter/tile.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/filter/tile.html.twig @@ -9,7 +9,7 @@ <div class="col-xs-12 col-sm-{{ brick.GetWidth }}"> {% block pTileWrapper %} - <a href="#" class="tile tile-filter-brick" id="brick-{{ brick.GetId }}" data-brick-id="{{ brick.GetId }}"> + <div class="tile tile-filter-brick" id="brick-{{ brick.GetId }}" data-brick-id="{{ brick.GetId }}"> <div class="tile_decoration"> <span class="icon {{ brick.GetDecorationClassHome }}"></span> @@ -32,17 +32,6 @@ </form> </div> </div> - </a> + </div> {% endblock %} -</div> - -<script type="text/javascript"> - $(document).ready(function(){ - // Preventing click on tile to scroll up - $('#brick-{{ brick.GetId }}').on('click', function(oEvent){ - if(oEvent.target.type !== 'submit'){ - oEvent.preventDefault(); - } - }); - }); -</script> \ No newline at end of file +</div> \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout.html.twig index 5b39f7ef1..cb36cc05d 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout.html.twig @@ -1,7 +1,7 @@ {# itop-portal-base/portal/templates/bricks/manage/layout.html.twig #} {% extends 'itop-portal-base/portal/templates/bricks/layout.html.twig' %} -{% block pMainHeaderTitle %}{{ oBrick.GetTitle()|dict_s }} ({{ iCount }}) {% endblock %} +{% block pMainHeaderTitle %}{{ oBrick.GetTitle()|dict_s }} {% if iCount >= 0 %} ({{ iCount }}){% endif %} {% endblock %} {% block pMainHeaderActions %} {% if oBrick.GetAvailablesDisplayModes|length > 1 %} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig index 1e6b89245..1c866602f 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig @@ -10,7 +10,7 @@ id="brick-{{ oBrick.GetId }}" data-brick-id="{{ oBrick.GetId }}"> <div> - <div class="tile_title"><span class="icon fa fa-{{ oBrick.GetTileMode }}"></span> {{ oBrick.GetTitle()|dict_s }} + <div class="tile_title"><span class="icon fas fa-{{ oBrick.GetTileMode }}"></span> {{ oBrick.GetTitle()|dict_s }} ({{ iCount }})</span> </div> {% include 'itop-portal-base/portal/templates/bricks/manage/mode-' ~ oBrick.GetTileMode ~ '.html.twig' with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %} </div> diff --git a/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig index e8395a953..92229fbf2 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig @@ -43,7 +43,6 @@ <link href="{{ app['combodo.absolute_url'] ~ 'css/font-combodo/font-combodo.css'|add_itop_version }}" rel="stylesheet"> {# - Font awesome #} <link href="{{ app['combodo.absolute_url'] ~ 'css/font-awesome/css/all.min.css'|add_itop_version }}" rel="stylesheet"> - <link href="{{ app['combodo.absolute_url'] ~ 'css/font-awesome/css/v4-shims.min.css'|add_itop_version }}" rel="stylesheet"> {# - Misc libs #} <link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/typeahead/css/typeaheadjs.bootstrap.css'|add_itop_version }}" rel="stylesheet"> <link href="{{ app['combodo.absolute_url'] ~ 'css/selectize.default.css'|add_itop_version }}" rel="stylesheet"> diff --git a/datamodels/2.x/itop-portal-base/portal/templates/modal/mode_loader.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/modal/mode_loader.html.twig index 75c4afd6e..2061b19a5 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/modal/mode_loader.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/modal/mode_loader.html.twig @@ -8,7 +8,14 @@ {% if redirection is defined and redirection.url is defined %} <script type="text/javascript"> $(document).ready( function(){ - window.location = '{{ redirection.url|raw }}'; + if ('{{ redirection.url|raw }}' =='') + { + window.location.href = window.location.href; + } + else + { + window.location = '{{ redirection.url|raw }}'; + } }); </script> {% endif %} diff --git a/datamodels/2.x/itop-portal-base/ru.dict.itop-portal-base.php b/datamodels/2.x/itop-portal-base/ru.dict.itop-portal-base.php index b77a1b6f7..09fc4c448 100755 --- a/datamodels/2.x/itop-portal-base/ru.dict.itop-portal-base.php +++ b/datamodels/2.x/itop-portal-base/ru.dict.itop-portal-base.php @@ -134,7 +134,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Brick:Portal:Object:Form:View:Title' => '%1$s : %2$s', 'Brick:Portal:Object:Form:Stimulus:Title' => 'Пожалуйста, укажите следующую информацию:', 'Brick:Portal:Object:Form:Message:Saved' => 'Сохранено', - 'Brick:Portal:Object:Form:Message:ObjectSaved' => '%1$s cохранено~~', + 'Brick:Portal:Object:Form:Message:ObjectSaved' => '%1$s сохранено', 'Brick:Portal:Object:Search:Regular:Title' => 'Выбрать %1$s (%2$s)', 'Brick:Portal:Object:Search:Hierarchy:Title' => 'Выбрать %1$s (%2$s)', 'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s', diff --git a/datamodels/2.x/itop-portal/module.itop-portal.php b/datamodels/2.x/itop-portal/module.itop-portal.php index e4a8dbef5..7d43de806 100644 --- a/datamodels/2.x/itop-portal/module.itop-portal.php +++ b/datamodels/2.x/itop-portal/module.itop-portal.php @@ -20,7 +20,7 @@ /** @noinspection PhpUnhandledExceptionInspection */ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-portal/2.7.0', array( + 'itop-portal/2.8.0', array( // Identification 'label' => 'Enhanced Customer Portal', 'category' => 'Portal', diff --git a/datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php b/datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php index e583c8f00..132d0dbef 100755 --- a/datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php +++ b/datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-problem-mgmt/2.7.0', + 'itop-problem-mgmt/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php b/datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php index 680d05238..31497e2d9 100755 --- a/datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php +++ b/datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php @@ -19,7 +19,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-profiles-itil/2.7.0', + 'itop-profiles-itil/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-request-mgmt-itil/cs.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/cs.dict.itop-request-mgmt-itil.php index ce3621075..26c4b0c75 100755 --- a/datamodels/2.x/itop-request-mgmt-itil/cs.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/cs.dict.itop-request-mgmt-itil.php @@ -163,8 +163,6 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Datum posledního pozastavení', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Kumulovaná doba pozastavení', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt-itil/da.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/da.dict.itop-request-mgmt-itil.php index 8fb46eb44..555d1aa95 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/da.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/da.dict.itop-request-mgmt-itil.php @@ -160,8 +160,6 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Sidste udsættelsesdato', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Akkumuleret nedetid', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO (Time To Own)', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR (Time To Resolve)', diff --git a/datamodels/2.x/itop-request-mgmt-itil/de.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/de.dict.itop-request-mgmt-itil.php index 76cb83669..8d586f11b 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/de.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/de.dict.itop-request-mgmt-itil.php @@ -162,8 +162,6 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Letztes Auszeit-Datum', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Kumulierte Auszeit', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO (Time To Own)', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR (Time To Resolve)', diff --git a/datamodels/2.x/itop-request-mgmt-itil/en.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/en.dict.itop-request-mgmt-itil.php index 6e2a927eb..f9b964b9c 100755 --- a/datamodels/2.x/itop-request-mgmt-itil/en.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/en.dict.itop-request-mgmt-itil.php @@ -161,8 +161,6 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Last pending date', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'cumulatedpending', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt-itil/es_cr.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/es_cr.dict.itop-request-mgmt-itil.php index 71091e36f..15ebd9c20 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/es_cr.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/es_cr.dict.itop-request-mgmt-itil.php @@ -162,8 +162,6 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'Class:UserRequest/Attribute:resolution_date+' => 'Fecha de Solución', 'Class:UserRequest/Attribute:last_pending_date' => 'Última Fecha de Espera', 'Class:UserRequest/Attribute:last_pending_date+' => 'Última Fecha de Espera', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Espera Acumulada', - 'Class:UserRequest/Attribute:cumulatedpending+' => 'Espera Acumulada', 'Class:UserRequest/Attribute:tto' => 'TDA - Tiempo de Asignación', 'Class:UserRequest/Attribute:tto+' => 'Tiempo de Asignación', 'Class:UserRequest/Attribute:ttr' => 'TDS - Tiempo de Solución', diff --git a/datamodels/2.x/itop-request-mgmt-itil/fr.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/fr.dict.itop-request-mgmt-itil.php index f9100274b..a411765df 100755 --- a/datamodels/2.x/itop-request-mgmt-itil/fr.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/fr.dict.itop-request-mgmt-itil.php @@ -162,8 +162,6 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Dernière date de suspension', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Temps cumulé de suspension', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt-itil/hu.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/hu.dict.itop-request-mgmt-itil.php index 506a2b956..e13537e90 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/hu.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/hu.dict.itop-request-mgmt-itil.php @@ -160,8 +160,6 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'Class:UserRequest/Attribute:resolution_date+' => '~~', 'Class:UserRequest/Attribute:last_pending_date' => 'Last pending date~~', 'Class:UserRequest/Attribute:last_pending_date+' => '~~', - 'Class:UserRequest/Attribute:cumulatedpending' => 'cumulatedpending~~', - 'Class:UserRequest/Attribute:cumulatedpending+' => '~~', 'Class:UserRequest/Attribute:tto' => 'TTO~~', 'Class:UserRequest/Attribute:tto+' => '~~', 'Class:UserRequest/Attribute:ttr' => 'TTR~~', diff --git a/datamodels/2.x/itop-request-mgmt-itil/it.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/it.dict.itop-request-mgmt-itil.php index 235d7441a..99e744d8a 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/it.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/it.dict.itop-request-mgmt-itil.php @@ -160,8 +160,6 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'Class:UserRequest/Attribute:resolution_date+' => '~~', 'Class:UserRequest/Attribute:last_pending_date' => 'Ultima data di messa in attesa', 'Class:UserRequest/Attribute:last_pending_date+' => '~~', - 'Class:UserRequest/Attribute:cumulatedpending' => 'cumulatedpending~~', - 'Class:UserRequest/Attribute:cumulatedpending+' => '~~', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '~~', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt-itil/ja.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/ja.dict.itop-request-mgmt-itil.php index abc95271d..d1905cb39 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/ja.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/ja.dict.itop-request-mgmt-itil.php @@ -159,8 +159,6 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => '最後の保留日', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => '合計保留', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php index 83dad511d..38c83194e 100755 --- a/datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-request-mgmt-itil/2.7.0', + 'itop-request-mgmt-itil/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-request-mgmt-itil/nl.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/nl.dict.itop-request-mgmt-itil.php index 19e0ea968..3fad185e7 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/nl.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/nl.dict.itop-request-mgmt-itil.php @@ -164,8 +164,6 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Laatste in afwachting op', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Opgetelde wachttijd', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt-itil/pt_br.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/pt_br.dict.itop-request-mgmt-itil.php index 36fb44206..f595fc3cc 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/pt_br.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/pt_br.dict.itop-request-mgmt-itil.php @@ -161,8 +161,6 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Última data pendente', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Pendências acumuladas', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt-itil/ru.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/ru.dict.itop-request-mgmt-itil.php index f2d8ccfd1..2199456e4 100755 --- a/datamodels/2.x/itop-request-mgmt-itil/ru.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/ru.dict.itop-request-mgmt-itil.php @@ -148,8 +148,6 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Дата последнего ожидания', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Накопленное ожидание', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt-itil/sk.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/sk.dict.itop-request-mgmt-itil.php index 53b2869c2..5fb5f9ffe 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/sk.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/sk.dict.itop-request-mgmt-itil.php @@ -160,8 +160,6 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Posledný dátum pozdržania', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Kumulatívne pozdržané', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt-itil/tr.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/tr.dict.itop-request-mgmt-itil.php index 901224686..1e9a2107f 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/tr.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/tr.dict.itop-request-mgmt-itil.php @@ -160,8 +160,6 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'Class:UserRequest/Attribute:resolution_date+' => '~~', 'Class:UserRequest/Attribute:last_pending_date' => 'Last pending date~~', 'Class:UserRequest/Attribute:last_pending_date+' => '~~', - 'Class:UserRequest/Attribute:cumulatedpending' => 'cumulatedpending~~', - 'Class:UserRequest/Attribute:cumulatedpending+' => '~~', 'Class:UserRequest/Attribute:tto' => 'TTO~~', 'Class:UserRequest/Attribute:tto+' => '~~', 'Class:UserRequest/Attribute:ttr' => 'TTR~~', diff --git a/datamodels/2.x/itop-request-mgmt-itil/zh_cn.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/zh_cn.dict.itop-request-mgmt-itil.php index f3db0766b..e244861a6 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/zh_cn.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/zh_cn.dict.itop-request-mgmt-itil.php @@ -160,8 +160,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => '最后更新', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => '累计待定', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => '响应时间', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => '解决时间', diff --git a/datamodels/2.x/itop-request-mgmt/cs.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/cs.dict.itop-request-mgmt.php index 6256272d9..b9f9d4d95 100755 --- a/datamodels/2.x/itop-request-mgmt/cs.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/cs.dict.itop-request-mgmt.php @@ -169,8 +169,6 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Datum posledního pozastavení', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Kumulovaná doba pozastavení', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt/da.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/da.dict.itop-request-mgmt.php index c3fdcb590..76a41bc30 100644 --- a/datamodels/2.x/itop-request-mgmt/da.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/da.dict.itop-request-mgmt.php @@ -166,8 +166,6 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Sidste udsættelses dato', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Akkumuleret nedetid', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO (Time To Own)', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR (Time To Resolve)', diff --git a/datamodels/2.x/itop-request-mgmt/de.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/de.dict.itop-request-mgmt.php index 94b22f38b..831d9d33a 100644 --- a/datamodels/2.x/itop-request-mgmt/de.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/de.dict.itop-request-mgmt.php @@ -168,8 +168,6 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Letztes Auszeit-Datum', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Kumulierte Auszeit', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO (Time To Own)', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR (Time To Resolve)', diff --git a/datamodels/2.x/itop-request-mgmt/en.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/en.dict.itop-request-mgmt.php index e74b57c23..9536f2d17 100755 --- a/datamodels/2.x/itop-request-mgmt/en.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/en.dict.itop-request-mgmt.php @@ -167,8 +167,6 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Last pending date', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'cumulatedpending', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt/es_cr.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/es_cr.dict.itop-request-mgmt.php index ec9e74af1..f0f94f716 100644 --- a/datamodels/2.x/itop-request-mgmt/es_cr.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/es_cr.dict.itop-request-mgmt.php @@ -168,8 +168,6 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'Class:UserRequest/Attribute:resolution_date+' => 'Fecha de Solución', 'Class:UserRequest/Attribute:last_pending_date' => 'Última Fecha de Espera', 'Class:UserRequest/Attribute:last_pending_date+' => 'Última Fecha de Espera', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Espera Acumulada', - 'Class:UserRequest/Attribute:cumulatedpending+' => 'Espera Acumulada', 'Class:UserRequest/Attribute:tto' => 'TDA - Tiempo de Asignación', 'Class:UserRequest/Attribute:tto+' => 'Tiempo de Asignación', 'Class:UserRequest/Attribute:ttr' => 'TDS - Tiempo de Solución', diff --git a/datamodels/2.x/itop-request-mgmt/fr.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/fr.dict.itop-request-mgmt.php index eb922ecc9..7a3a7ff05 100755 --- a/datamodels/2.x/itop-request-mgmt/fr.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/fr.dict.itop-request-mgmt.php @@ -168,8 +168,6 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Dernière date de suspension', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Temps cumulé de suspension', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt/hu.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/hu.dict.itop-request-mgmt.php index 378955385..1cd84c4a4 100644 --- a/datamodels/2.x/itop-request-mgmt/hu.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/hu.dict.itop-request-mgmt.php @@ -166,8 +166,6 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'Class:UserRequest/Attribute:resolution_date+' => '~~', 'Class:UserRequest/Attribute:last_pending_date' => 'Last pending date~~', 'Class:UserRequest/Attribute:last_pending_date+' => '~~', - 'Class:UserRequest/Attribute:cumulatedpending' => 'cumulatedpending~~', - 'Class:UserRequest/Attribute:cumulatedpending+' => '~~', 'Class:UserRequest/Attribute:tto' => 'TTO~~', 'Class:UserRequest/Attribute:tto+' => '~~', 'Class:UserRequest/Attribute:ttr' => 'TTR~~', diff --git a/datamodels/2.x/itop-request-mgmt/it.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/it.dict.itop-request-mgmt.php index 6b3e2936b..13abcbe7e 100644 --- a/datamodels/2.x/itop-request-mgmt/it.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/it.dict.itop-request-mgmt.php @@ -166,8 +166,6 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'Class:UserRequest/Attribute:resolution_date+' => '~~', 'Class:UserRequest/Attribute:last_pending_date' => 'Ultima data di messa in attesa', 'Class:UserRequest/Attribute:last_pending_date+' => '~~', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Messa in attesa cumulativa', - 'Class:UserRequest/Attribute:cumulatedpending+' => '~~', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '~~', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt/ja.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/ja.dict.itop-request-mgmt.php index 88509bf95..7f58a8099 100644 --- a/datamodels/2.x/itop-request-mgmt/ja.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/ja.dict.itop-request-mgmt.php @@ -165,8 +165,6 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => '最終保留日', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => '累積保留', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php index ab1f64583..74550ccb6 100755 --- a/datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-request-mgmt/2.7.0', + 'itop-request-mgmt/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-request-mgmt/nl.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/nl.dict.itop-request-mgmt.php index 39266fd66..c61fed01a 100644 --- a/datamodels/2.x/itop-request-mgmt/nl.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/nl.dict.itop-request-mgmt.php @@ -172,8 +172,6 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Laatst in afwachting op', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Opgetelde wachttijd', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt/pt_br.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/pt_br.dict.itop-request-mgmt.php index a968a04af..52e2fae12 100644 --- a/datamodels/2.x/itop-request-mgmt/pt_br.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/pt_br.dict.itop-request-mgmt.php @@ -167,8 +167,6 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Última data pendente', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Pendências acumuladas', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt/ru.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/ru.dict.itop-request-mgmt.php index c092291a8..61b4209d5 100755 --- a/datamodels/2.x/itop-request-mgmt/ru.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/ru.dict.itop-request-mgmt.php @@ -154,8 +154,6 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Дата последнего ожидания', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Накопленное ожидание', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt/sk.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/sk.dict.itop-request-mgmt.php index d353a53d1..1139db4ca 100644 --- a/datamodels/2.x/itop-request-mgmt/sk.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/sk.dict.itop-request-mgmt.php @@ -166,8 +166,6 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Posledný dátum pozdržania', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Kumulatívne pozdržané', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', diff --git a/datamodels/2.x/itop-request-mgmt/tr.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/tr.dict.itop-request-mgmt.php index 44f100cca..76dee340f 100644 --- a/datamodels/2.x/itop-request-mgmt/tr.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/tr.dict.itop-request-mgmt.php @@ -166,8 +166,6 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'Class:UserRequest/Attribute:resolution_date+' => '~~', 'Class:UserRequest/Attribute:last_pending_date' => 'Last pending date~~', 'Class:UserRequest/Attribute:last_pending_date+' => '~~', - 'Class:UserRequest/Attribute:cumulatedpending' => 'cumulatedpending~~', - 'Class:UserRequest/Attribute:cumulatedpending+' => '~~', 'Class:UserRequest/Attribute:tto' => 'TTO~~', 'Class:UserRequest/Attribute:tto+' => '~~', 'Class:UserRequest/Attribute:ttr' => 'TTR~~', diff --git a/datamodels/2.x/itop-request-mgmt/zh_cn.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/zh_cn.dict.itop-request-mgmt.php index 52a0ce12c..8d64a5e77 100644 --- a/datamodels/2.x/itop-request-mgmt/zh_cn.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/zh_cn.dict.itop-request-mgmt.php @@ -166,8 +166,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => '最近待定日期', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => '累计待定', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => '响应时间', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => '解决时间', diff --git a/datamodels/2.x/itop-service-mgmt-provider/cs.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/cs.dict.itop-service-mgmt-provider.php index 98d6dba8a..5786cac52 100755 --- a/datamodels/2.x/itop-service-mgmt-provider/cs.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/cs.dict.itop-service-mgmt-provider.php @@ -370,6 +370,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'Class:SLA/Attribute:slts_list+' => 'Všechny cíle úrovně služeb pro tuto dohodu o úrovni služeb', 'Class:SLA/Attribute:customercontracts_list' => 'Smlouvy se zákazníky', 'Class:SLA/Attribute:customercontracts_list+' => 'Všechny smlouvy se zákazníky využívající tuto dohodu o úrovni služeb', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/da.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/da.dict.itop-service-mgmt-provider.php index ae3747b72..3848955e4 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/da.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/da.dict.itop-service-mgmt-provider.php @@ -358,6 +358,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'Class:SLA/Attribute:slts_list+' => '', 'Class:SLA/Attribute:customercontracts_list' => 'Kundekontrakt', 'Class:SLA/Attribute:customercontracts_list+' => '', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/datamodel.itop-service-mgmt-provider.xml b/datamodels/2.x/itop-service-mgmt-provider/datamodel.itop-service-mgmt-provider.xml index b91938e34..e623d2689 100755 --- a/datamodels/2.x/itop-service-mgmt-provider/datamodel.itop-service-mgmt-provider.xml +++ b/datamodels/2.x/itop-service-mgmt-provider/datamodel.itop-service-mgmt-provider.xml @@ -1627,7 +1627,39 @@ public function PrefillSearchForm(&$aContextParam) <duplicates>true</duplicates> </field> </fields> - <methods/> + <methods> + <method id="DoCheckToWrite"> + <static>false</static> + <access>public</access> + <code><![CDATA[ + public function DoCheckToWrite() + { + parent::DoCheckToWrite(); + + $aCustomerContracts = $this->Get("customercontracts_list"); + foreach ($aCustomerContracts as $sAttCode => $oCustomerContracts) + { + // Recurse inside the subdirectories + $sOql = "SELECT lnkCustomerContractToService AS ccs WHERE ccs.customercontract_id=:customercontract_id AND ccs.service_id=:service_id"; + $aQueryParams['customercontract_id'] = $oCustomerContracts->Get("customercontract_id"); + $aQueryParams['service_id'] = $oCustomerContracts->Get("service_id"); + if ($this->Get("id") != null) + { + $sOql = $sOql." AND ccs.sla_id!=:sla_id"; + $aQueryParams['sla_id'] = $this->Get("id"); + } + $oQuery = DBSearch::FromOQL($sOql, $aQueryParams); + $oResultSql = new DBObjectSet($oQuery); + $oResultSql->OptimizeColumnLoad(['ccs.customercontract_name','ccs.service_name']); + if ($aCurrentRow = $oResultSql->Fetch()) + { + $this->m_aCheckIssues[] = Dict::Format('Class:SLA/Error:UniqueLnkCustomerContractToService',$aCurrentRow->Get('customercontract_name'),$aCurrentRow->Get('service_name')); + } + } + } + ]]></code> + </method> + </methods> <presentation> <details> <items> diff --git a/datamodels/2.x/itop-service-mgmt-provider/de.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/de.dict.itop-service-mgmt-provider.php index ff424fa92..1b44c1d43 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/de.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/de.dict.itop-service-mgmt-provider.php @@ -360,6 +360,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:SLA/Attribute:slts_list+' => '', 'Class:SLA/Attribute:customercontracts_list' => 'Kunden-Verträge', 'Class:SLA/Attribute:customercontracts_list+' => '', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/en.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/en.dict.itop-service-mgmt-provider.php index 1df7491f6..1f8f5ac9a 100755 --- a/datamodels/2.x/itop-service-mgmt-provider/en.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/en.dict.itop-service-mgmt-provider.php @@ -370,6 +370,7 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:SLA/Attribute:slts_list+' => 'All the service level targets for this SLA', 'Class:SLA/Attribute:customercontracts_list' => 'Customer contracts', 'Class:SLA/Attribute:customercontracts_list+' => 'All the customer contracts using this SLA', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/es_cr.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/es_cr.dict.itop-service-mgmt-provider.php index 2554e622f..ee2dead72 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/es_cr.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/es_cr.dict.itop-service-mgmt-provider.php @@ -369,6 +369,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'Class:SLA/Attribute:slts_list+' => 'Objetivos de Nivel de Servicio', 'Class:SLA/Attribute:customercontracts_list' => 'Acuerdos con Clientes', 'Class:SLA/Attribute:customercontracts_list+' => 'Acuerdos con Clientes', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/fr.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/fr.dict.itop-service-mgmt-provider.php index d6dd37589..c26b9174d 100755 --- a/datamodels/2.x/itop-service-mgmt-provider/fr.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/fr.dict.itop-service-mgmt-provider.php @@ -360,6 +360,7 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Class:SLA/Attribute:slts_list+' => '', 'Class:SLA/Attribute:customercontracts_list' => 'Contrats clients', 'Class:SLA/Attribute:customercontracts_list+' => '', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Impossible de sauvegarder le lien avec le contrat client %1$s et le service %2$s : un SLA existe déjà.', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/hu.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/hu.dict.itop-service-mgmt-provider.php index 047e793c8..1e6d17999 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/hu.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/hu.dict.itop-service-mgmt-provider.php @@ -367,6 +367,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'Class:SLA/Attribute:slts_list+' => 'All the service level targets for this SLA~~', 'Class:SLA/Attribute:customercontracts_list' => 'Customer contracts~~', 'Class:SLA/Attribute:customercontracts_list+' => 'All the customer contracts using this SLA~~', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/it.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/it.dict.itop-service-mgmt-provider.php index 5d99ac107..4a788755a 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/it.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/it.dict.itop-service-mgmt-provider.php @@ -367,6 +367,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'Class:SLA/Attribute:slts_list+' => 'All the service level targets for this SLA~~', 'Class:SLA/Attribute:customercontracts_list' => 'Customer contracts~~', 'Class:SLA/Attribute:customercontracts_list+' => 'All the customer contracts using this SLA~~', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/ja.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/ja.dict.itop-service-mgmt-provider.php index d53a20d5c..e374106bd 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/ja.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/ja.dict.itop-service-mgmt-provider.php @@ -357,6 +357,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'Class:SLA/Attribute:slts_list+' => '', 'Class:SLA/Attribute:customercontracts_list' => '顧客契約', 'Class:SLA/Attribute:customercontracts_list+' => '', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php index 7b4c8588d..9df9fe668 100755 --- a/datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-service-mgmt-provider/2.7.0', + 'itop-service-mgmt-provider/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-service-mgmt-provider/nl.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/nl.dict.itop-service-mgmt-provider.php index f1f633160..8fcedfc86 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/nl.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/nl.dict.itop-service-mgmt-provider.php @@ -372,6 +372,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'Class:SLA/Attribute:slts_list+' => 'Alle servicelevel-doelstellingen voor deze SLA', 'Class:SLA/Attribute:customercontracts_list' => 'Klantencontracten', 'Class:SLA/Attribute:customercontracts_list+' => 'Alle klantencontracten die gebruik maken van deze SLA', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/pt_br.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/pt_br.dict.itop-service-mgmt-provider.php index 3550fceb5..a00b591a4 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/pt_br.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/pt_br.dict.itop-service-mgmt-provider.php @@ -368,6 +368,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'Class:SLA/Attribute:slts_list+' => 'Todos os SLTs para essa SLA', 'Class:SLA/Attribute:customercontracts_list' => 'Contratos clientes', 'Class:SLA/Attribute:customercontracts_list+' => 'Todos os contratos de clientes utilizando essa SLA', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/ru.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/ru.dict.itop-service-mgmt-provider.php index 1d7d8ca2d..f87cac961 100755 --- a/datamodels/2.x/itop-service-mgmt-provider/ru.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/ru.dict.itop-service-mgmt-provider.php @@ -346,6 +346,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Class:SLA/Attribute:slts_list+' => 'Целевой показатель уровня услуги (Service Level Target)', 'Class:SLA/Attribute:customercontracts_list' => 'Договоры с заказчиками', 'Class:SLA/Attribute:customercontracts_list+' => 'Договоры с заказчиками, в которых используется SLA', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/sk.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/sk.dict.itop-service-mgmt-provider.php index 8fb35f4db..80e3283ed 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/sk.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/sk.dict.itop-service-mgmt-provider.php @@ -367,6 +367,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'Class:SLA/Attribute:slts_list+' => '', 'Class:SLA/Attribute:customercontracts_list' => 'Zákaznícke zmluvy', 'Class:SLA/Attribute:customercontracts_list+' => '', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/tr.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/tr.dict.itop-service-mgmt-provider.php index a519e3713..b1249b1cf 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/tr.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/tr.dict.itop-service-mgmt-provider.php @@ -367,6 +367,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'Class:SLA/Attribute:slts_list+' => 'All the service level targets for this SLA~~', 'Class:SLA/Attribute:customercontracts_list' => 'Customer contracts~~', 'Class:SLA/Attribute:customercontracts_list+' => 'All the customer contracts using this SLA~~', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt-provider/zh_cn.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/zh_cn.dict.itop-service-mgmt-provider.php index 804d411e0..1db05a1bf 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/zh_cn.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/zh_cn.dict.itop-service-mgmt-provider.php @@ -367,6 +367,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'Class:SLA/Attribute:slts_list+' => '该SLA 包含的所有服务等级目标', 'Class:SLA/Attribute:customercontracts_list' => '客户合同', 'Class:SLA/Attribute:customercontracts_list+' => '使用该SLA 的所有客户合同', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/cs.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/cs.dict.itop-service-mgmt.php index 0c3f2fcf9..687513852 100755 --- a/datamodels/2.x/itop-service-mgmt/cs.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/cs.dict.itop-service-mgmt.php @@ -360,6 +360,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'Class:SLA/Attribute:slts_list+' => 'Všechny cíle úrovně služeb pro tuto dohodu o úrovni služeb', 'Class:SLA/Attribute:customercontracts_list' => 'Smlouvy se zákazníky', 'Class:SLA/Attribute:customercontracts_list+' => 'Všechny smlouvy se zákazníky využívající tuto dohodu o úrovni služeb', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/da.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/da.dict.itop-service-mgmt.php index d8ab156c2..38e9142ae 100644 --- a/datamodels/2.x/itop-service-mgmt/da.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/da.dict.itop-service-mgmt.php @@ -348,6 +348,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'Class:SLA/Attribute:slts_list+' => 'Service Level Threshholds:', 'Class:SLA/Attribute:customercontracts_list' => 'Kunde kontrakter', 'Class:SLA/Attribute:customercontracts_list+' => '', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/datamodel.itop-service-mgmt.xml b/datamodels/2.x/itop-service-mgmt/datamodel.itop-service-mgmt.xml index 2bc52b711..20aa4680c 100755 --- a/datamodels/2.x/itop-service-mgmt/datamodel.itop-service-mgmt.xml +++ b/datamodels/2.x/itop-service-mgmt/datamodel.itop-service-mgmt.xml @@ -1621,7 +1621,39 @@ public function PrefillSearchForm(&$aContextParam) <duplicates>true</duplicates> </field> </fields> - <methods/> + <methods> + <method id="DoCheckToWrite"> + <static>false</static> + <access>public</access> + <code><![CDATA[ + public function DoCheckToWrite() + { + parent::DoCheckToWrite(); + + $aCustomerContracts = $this->Get("customercontracts_list"); + foreach ($aCustomerContracts as $sAttCode => $oCustomerContracts) + { + // Recurse inside the subdirectories + $sOql = "SELECT lnkCustomerContractToService AS ccs WHERE ccs.customercontract_id=:customercontract_id AND ccs.service_id=:service_id"; + $aQueryParams['customercontract_id'] = $oCustomerContracts->Get("customercontract_id"); + $aQueryParams['service_id'] = $oCustomerContracts->Get("service_id"); + if ($this->Get("id") != null) + { + $sOql = $sOql." AND ccs.sla_id!=:sla_id"; + $aQueryParams['sla_id'] = $this->Get("id"); + } + $oQuery = DBSearch::FromOQL($sOql, $aQueryParams); + $oResultSql = new DBObjectSet($oQuery); + $oResultSql->OptimizeColumnLoad(['ccs.customercontract_name','ccs.service_name']); + if ($aCurrentRow = $oResultSql->Fetch()) + { + $this->m_aCheckIssues[] = Dict::Format('Class:SLA/Error:UniqueLnkCustomerContractToService',$aCurrentRow->Get('customercontract_name'),$aCurrentRow->Get('service_name')); + } + } + } + ]]></code> + </method> + </methods> <presentation> <details> <items> diff --git a/datamodels/2.x/itop-service-mgmt/de.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/de.dict.itop-service-mgmt.php index 9cff6e75f..a1af52d22 100755 --- a/datamodels/2.x/itop-service-mgmt/de.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/de.dict.itop-service-mgmt.php @@ -350,6 +350,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:SLA/Attribute:slts_list+' => 'Service Level Targets:', 'Class:SLA/Attribute:customercontracts_list' => 'Kunden-Verträge', 'Class:SLA/Attribute:customercontracts_list+' => '', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/en.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/en.dict.itop-service-mgmt.php index f63362e80..478eb9357 100755 --- a/datamodels/2.x/itop-service-mgmt/en.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/en.dict.itop-service-mgmt.php @@ -360,6 +360,7 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:SLA/Attribute:slts_list+' => 'All the service level targets for this SLA', 'Class:SLA/Attribute:customercontracts_list' => 'Customer contracts', 'Class:SLA/Attribute:customercontracts_list+' => 'All the customer contracts using this SLA', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists', )); // diff --git a/datamodels/2.x/itop-service-mgmt/es_cr.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/es_cr.dict.itop-service-mgmt.php index cfff7f8a1..af6a1f187 100755 --- a/datamodels/2.x/itop-service-mgmt/es_cr.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/es_cr.dict.itop-service-mgmt.php @@ -359,6 +359,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'Class:SLA/Attribute:slts_list+' => 'Objetivos de Nivel de Servicio', 'Class:SLA/Attribute:customercontracts_list' => 'Acuerdos con Clientes', 'Class:SLA/Attribute:customercontracts_list+' => 'Acuerdos con Clientes', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/fr.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/fr.dict.itop-service-mgmt.php index aff3f1944..99bd14b07 100755 --- a/datamodels/2.x/itop-service-mgmt/fr.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/fr.dict.itop-service-mgmt.php @@ -350,6 +350,7 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Class:SLA/Attribute:slts_list+' => '', 'Class:SLA/Attribute:customercontracts_list' => 'Contrats clients', 'Class:SLA/Attribute:customercontracts_list+' => '', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Impossible de sauvegarder le lien avec le contrat client %1$s et le service %2$s : un SLA existe déjà.', )); // diff --git a/datamodels/2.x/itop-service-mgmt/hu.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/hu.dict.itop-service-mgmt.php index 747f67477..104f885c6 100755 --- a/datamodels/2.x/itop-service-mgmt/hu.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/hu.dict.itop-service-mgmt.php @@ -347,6 +347,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'Class:SLA/Attribute:slts_list+' => 'All the service level targets for this SLA~~', 'Class:SLA/Attribute:customercontracts_list' => 'Customer contracts~~', 'Class:SLA/Attribute:customercontracts_list+' => 'All the customer contracts using this SLA~~', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/it.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/it.dict.itop-service-mgmt.php index 1edd94a09..a05f3f34d 100755 --- a/datamodels/2.x/itop-service-mgmt/it.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/it.dict.itop-service-mgmt.php @@ -358,6 +358,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'Class:SLA/Attribute:slts_list+' => 'All the service level targets for this SLA~~', 'Class:SLA/Attribute:customercontracts_list' => 'Customer contracts~~', 'Class:SLA/Attribute:customercontracts_list+' => 'All the customer contracts using this SLA~~', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/ja.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/ja.dict.itop-service-mgmt.php index 6d0cb21c9..cc3f652dc 100755 --- a/datamodels/2.x/itop-service-mgmt/ja.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/ja.dict.itop-service-mgmt.php @@ -347,6 +347,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'Class:SLA/Attribute:slts_list+' => '', 'Class:SLA/Attribute:customercontracts_list' => '顧客連絡先', 'Class:SLA/Attribute:customercontracts_list+' => '', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php index 3fac59cc0..4ca0f7e42 100755 --- a/datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-service-mgmt/2.7.0', + 'itop-service-mgmt/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-service-mgmt/nl.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/nl.dict.itop-service-mgmt.php index c0ae8fd99..fc7db7ee8 100644 --- a/datamodels/2.x/itop-service-mgmt/nl.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/nl.dict.itop-service-mgmt.php @@ -362,6 +362,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'Class:SLA/Attribute:slts_list+' => 'Alle servicelevel-doelstellingen voor deze SLA', 'Class:SLA/Attribute:customercontracts_list' => 'Klantencontracten', 'Class:SLA/Attribute:customercontracts_list+' => 'Alle klantencontracten die gebruik maken van deze SLA', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/pt_br.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/pt_br.dict.itop-service-mgmt.php index 99cc68a4d..852062ed5 100755 --- a/datamodels/2.x/itop-service-mgmt/pt_br.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/pt_br.dict.itop-service-mgmt.php @@ -358,6 +358,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'Class:SLA/Attribute:slts_list+' => 'Todos os SLTs para essa SLA', 'Class:SLA/Attribute:customercontracts_list' => 'Contratos clientes', 'Class:SLA/Attribute:customercontracts_list+' => 'Todos os contratos de clientes utilizando essa SLA', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/ru.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/ru.dict.itop-service-mgmt.php index 94b96ae87..bdb29ee52 100755 --- a/datamodels/2.x/itop-service-mgmt/ru.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/ru.dict.itop-service-mgmt.php @@ -336,6 +336,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Class:SLA/Attribute:slts_list+' => 'Целевые показатели уровня услуги (Service Level Target)', 'Class:SLA/Attribute:customercontracts_list' => 'Договоры с заказчиками', 'Class:SLA/Attribute:customercontracts_list+' => 'Договоры с заказчиками, в которых используется SLA', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/sk.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/sk.dict.itop-service-mgmt.php index f1d019f57..668d43566 100644 --- a/datamodels/2.x/itop-service-mgmt/sk.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/sk.dict.itop-service-mgmt.php @@ -357,6 +357,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'Class:SLA/Attribute:slts_list+' => '', 'Class:SLA/Attribute:customercontracts_list' => 'Zákaznícke zmluvy', 'Class:SLA/Attribute:customercontracts_list+' => '', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/tr.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/tr.dict.itop-service-mgmt.php index a9955073b..a9f5ae313 100755 --- a/datamodels/2.x/itop-service-mgmt/tr.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/tr.dict.itop-service-mgmt.php @@ -359,6 +359,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'Class:SLA/Attribute:slts_list+' => 'All the service level targets for this SLA~~', 'Class:SLA/Attribute:customercontracts_list' => 'Customer contracts~~', 'Class:SLA/Attribute:customercontracts_list+' => 'All the customer contracts using this SLA~~', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-service-mgmt/zh_cn.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/zh_cn.dict.itop-service-mgmt.php index c58fb5217..b0562e714 100755 --- a/datamodels/2.x/itop-service-mgmt/zh_cn.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/zh_cn.dict.itop-service-mgmt.php @@ -357,6 +357,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'Class:SLA/Attribute:slts_list+' => '该SLA 包含的所有服务等级目标', 'Class:SLA/Attribute:customercontracts_list' => '客户合同', 'Class:SLA/Attribute:customercontracts_list+' => '使用这个SLA 的所有客户合同', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists~~', )); // diff --git a/datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php b/datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php index 35bda14e0..c1f0829c9 100755 --- a/datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php +++ b/datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php @@ -18,7 +18,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-sla-computation/2.7.0', + 'itop-sla-computation/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-storage-mgmt/datamodel.itop-storage-mgmt.xml b/datamodels/2.x/itop-storage-mgmt/datamodel.itop-storage-mgmt.xml index 3ea014dae..b7f24aaaf 100644 --- a/datamodels/2.x/itop-storage-mgmt/datamodel.itop-storage-mgmt.xml +++ b/datamodels/2.x/itop-storage-mgmt/datamodel.itop-storage-mgmt.xml @@ -240,18 +240,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> @@ -498,18 +487,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> @@ -756,18 +734,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> @@ -1014,18 +981,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> @@ -1127,18 +1083,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> <class id="NASFileSystem" _delta="define"> <parent>cmdbAbstractObject</parent> @@ -1245,18 +1190,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> <class id="FiberChannelInterface" _delta="define"> <parent>NetworkInterface</parent> @@ -1511,18 +1445,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> diff --git a/datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php b/datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php index 5487af8d0..a49cc7135 100644 --- a/datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php +++ b/datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php @@ -25,7 +25,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-storage-mgmt/2.7.0', + 'itop-storage-mgmt/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml index 0fd72e1d5..98cbc3ab6 100755 --- a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml +++ b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml @@ -806,217 +806,6 @@ </classes> <meta> <classes> - <class id="cmdbAbstractObject" _delta="define"> - <methods> - <method id="Set"> - <arguments> - <argument id="1"> - <type>attcode</type> - <mandatory>true</mandatory> - <type_restrictions> - <operation>deny</operation> - <types> - <type id="AttributeStopWatch"/> - <type id="AttributeSubItem"/> - <type id="AttributeExternalField"/> - <type id="AttributeLinkedSetIndirect"/> - <type id="AttributeLinkedSet"/> - <type id="AttributeImage"/> - <type id="AttributeBlob"/> - </types> - </type_restrictions> - </argument> - <argument id="2"> - <type>string</type> - <mandatory>true</mandatory> - </argument> - </arguments> - </method> - <method id="SetIfNull"> - <arguments> - <argument id="1"> - <type>attcode</type> - <mandatory>true</mandatory> - <type_restrictions> - <operation>deny</operation> - <types> - <type id="AttributeStopWatch"/> - <type id="AttributeSubItem"/> - <type id="AttributeExternalField"/> - <type id="AttributeLinkedSetIndirect"/> - <type id="AttributeLinkedSet"/> - <type id="AttributeImage"/> - <type id="AttributeBlob"/> - </types> - </type_restrictions> - </argument> - <argument id="2"> - <type>string</type> - <mandatory>true</mandatory> - </argument> - </arguments> - </method> - <method id="SetCurrentDate"> - <arguments> - <argument id="1"> - <type>attcode</type> - <mandatory>true</mandatory> - <type_restrictions> - <operation>allow</operation> - <types> - <type id="AttributeDate"/> - <type id="AttributeDateTime"/> - <type id="AttributeString"/> - </types> - </type_restrictions> - </argument> - </arguments> - </method> - <method id="SetCurrentUser"> - <arguments> - <argument id="1"> - <type>attcode</type> - <mandatory>true</mandatory> - <type_restrictions> - <operation>allow</operation> - <types> - <type id="AttributeExternalKey"/> - <type id="AttributeInteger"/> - <type id="AttributeString"/> - </types> - </type_restrictions> - </argument> - </arguments> - </method> - <method id="SetCurrentPerson"> - <arguments> - <argument id="1"> - <type>attcode</type> - <mandatory>true</mandatory> - <type_restrictions> - <operation>allow</operation> - <types> - <type id="AttributeExternalKey"/> - <type id="AttributeInteger"/> - <type id="AttributeString"/> - </types> - </type_restrictions> - </argument> - </arguments> - </method> - <method id="SetElapsedTime"> - <arguments> - <argument id="1"> - <type>attcode</type> - <mandatory>true</mandatory> - <type_restrictions> - <operation>allow</operation> - <types> - <type id="AttributeDuration"/> - </types> - </type_restrictions> - </argument> - <argument id="2"> - <type>attcode</type> - <mandatory>true</mandatory> - <type_restrictions> - <operation>allow</operation> - <types> - <type id="AttributeDate"/> - <type id="AttributeDateTime"/> - </types> - </type_restrictions> - </argument> - <argument id="3"> - <type>string</type> - <mandatory>false</mandatory> - </argument> - </arguments> - </method> - <method id="Reset"> - <arguments> - <argument id="1"> - <type>attcode</type> - <mandatory>true</mandatory> - <type_restrictions> - <operation>deny</operation> - <types> - <type id="AttributeStopWatch"/> - <type id="AttributeSubItem"/> - <type id="AttributeExternalField"/> - </types> - </type_restrictions> - </argument> - </arguments> - </method> - <method id="ResetStopWatch"> - <arguments> - <argument id="1"> - <type>attcode</type> - <mandatory>true</mandatory> - <type_restrictions> - <operation>allow</operation> - <types> - <type id="AttributeStopWatch"/> - </types> - </type_restrictions> - </argument> - </arguments> - </method> - <method id="Copy"> - <arguments> - <argument id="1"> - <type>attcode</type> - <mandatory>true</mandatory> - <type_restrictions> - <operation>deny</operation> - <types> - <type id="AttributeStopWatch"/> - <type id="AttributeSubItem"/> - <type id="AttributeExternalField"/> - </types> - </type_restrictions> - </argument> - <argument id="2"> - <type>attcode</type> - <mandatory>true</mandatory> - </argument> - </arguments> - </method> - <method id="ApplyStimulus"> - <arguments> - <argument id="1"> - <type>string</type> - <mandatory>true</mandatory> - </argument> - </arguments> - </method> - <method id="PrefillCreationForm"> - <arguments> - <argument id="1"> - <type>reference</type> - <mandatory>true</mandatory> - </argument> - </arguments> - </method> - <method id="PrefillTransitionForm"> - <arguments> - <argument id="1"> - <type>reference</type> - <mandatory>true</mandatory> - </argument> - </arguments> - </method> - <method id="PrefillSearchForm"> - <arguments> - <argument id="1"> - <type>reference</type> - <mandatory>true</mandatory> - </argument> - </arguments> - </method> - </methods> - </class> <class id="ResponseTicketTTO" _delta="define"> <interfaces> <interface id="iMetricComputer"/> diff --git a/datamodels/2.x/itop-tickets/module.itop-tickets.php b/datamodels/2.x/itop-tickets/module.itop-tickets.php index 4be3a3d22..c6fe4c42f 100755 --- a/datamodels/2.x/itop-tickets/module.itop-tickets.php +++ b/datamodels/2.x/itop-tickets/module.itop-tickets.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, - 'itop-tickets/2.7.0', + 'itop-tickets/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml b/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml index b2ab32f2c..30a137c50 100644 --- a/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml +++ b/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml @@ -235,18 +235,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> @@ -394,18 +383,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> <relations> <relation id="impacts"> <neighbours> @@ -540,18 +518,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> <class id="VirtualMachine" _delta="define"> <parent>VirtualDevice</parent> @@ -783,18 +750,7 @@ </items> </list> </presentation> - <methods> - <method id="GetRelationQueries"> - <comment>/** - * Placeholder for backward compatibility (iTop <= 2.1.0) - * in case an extension attempts to redefine this function... - */</comment> - <static>true</static> - <access>public</access> - <type>Overload-DBObject</type> - <code><![CDATA[ public static function GetRelationQueries($sRelCode){return parent::GetRelationQueries($sRelCode);} ]]></code> - </method> - </methods> + <methods/> </class> <class id="LogicalInterface" _delta="define"> <parent>IPInterface</parent> diff --git a/datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php b/datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php index 9aa9ec868..061a5c423 100644 --- a/datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php +++ b/datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php @@ -16,7 +16,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-virtualization-mgmt/2.7.0', + 'itop-virtualization-mgmt/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-welcome-itil/cs.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/cs.dict.itop-welcome-itil.php index 8329ccdf8..67b9532db 100755 --- a/datamodels/2.x/itop-welcome-itil/cs.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/cs.dict.itop-welcome-itil.php @@ -23,77 +23,4 @@ * @copyright Copyright (C) 2010-2014 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ -Dict::Add('CS CZ', 'Czech', 'Čeština', array( - 'Menu:DataSources' => 'Zdroje dat pro synchronizaci', - 'Menu:DataSources+' => 'Všechny zdroje dat pro synchronizaci', - 'Menu:WelcomeMenu' => 'Vítejte', - 'Menu:WelcomeMenu+' => 'Vítejte v iTop', - 'Menu:WelcomeMenuPage' => 'Vítejte', - 'Menu:WelcomeMenuPage+' => 'Vítejte v iTop', - 'Menu:AdminTools' => 'Administrace', - 'Menu:AdminTools+' => 'Nástroje pro administraci', - 'Menu:AdminTools?' => 'Nástroje přístupné pouze uživatelům, kteří mají potřbná oprávnění', - 'Menu:DataModelMenu' => 'Datový model', - 'Menu:DataModelMenu+' => 'Přehled datového modelu', - 'Menu:ExportMenu' => 'Exportovat', - 'Menu:ExportMenu+' => 'Exportovat výsledky jakéhokoli dotazu do HTML, CSV nebo XML', - 'Menu:NotificationsMenu' => 'Upozornění', - 'Menu:NotificationsMenu+' => 'Konfigurace upozornění', - 'Menu:AuditCategories' => 'Kategorie auditu', - 'Menu:AuditCategories+' => 'Kategorie auditu', - 'Menu:Notifications:Title' => 'Kategorie auditu', - 'Menu:RunQueriesMenu' => 'Provést dotaz', - 'Menu:RunQueriesMenu+' => 'Provést dotaz', - 'Menu:QueryMenu' => 'Knihovna dotazů', - 'Menu:QueryMenu+' => 'Knihovna dotazů', - 'Menu:UniversalSearchMenu' => 'Univerzální hledání', - 'Menu:UniversalSearchMenu+' => 'Hledejte cokoli...', - 'Menu:UserManagementMenu' => 'Správa uživatelů', - 'Menu:UserManagementMenu+' => 'Správa uživatelů', - 'Menu:ProfilesMenu' => 'Profily (Role)', - 'Menu:ProfilesMenu+' => 'Profily (Role)', - 'Menu:ProfilesMenu:Title' => 'Profily (Role)', - 'Menu:UserAccountsMenu' => 'Uživatelské účty', - 'Menu:UserAccountsMenu+' => 'Uživatelské účty', - 'Menu:UserAccountsMenu:Title' => 'Uživatelské účty', - 'Menu:MyShortcuts' => 'Mé odkazy', - 'Menu:UserManagement' => 'User Management~~', - 'Menu:Queries' => 'Queries~~', - 'Menu:ConfigurationTools' => 'Configuration~~', -)); -// -// Class: AbstractResource -// - -Dict::Add('CS CZ', 'Czech', 'Čeština', array( - 'Class:AbstractResource' => 'Abstract Resource~~', - 'Class:AbstractResource+' => '~~', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('CS CZ', 'Czech', 'Čeština', array( - 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', - 'Class:ResourceAdminMenu+' => '~~', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('CS CZ', 'Czech', 'Čeština', array( - 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', - 'Class:ResourceRunQueriesMenu+' => '~~', -)); - -// -// Class: Action -// - -Dict::Add('CS CZ', 'Czech', 'Čeština', array( - 'Class:ResourceSystemMenu' => 'Resource System Menu~~', - 'Class:ResourceSystemMenu+' => '~~', -)); diff --git a/datamodels/2.x/itop-welcome-itil/da.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/da.dict.itop-welcome-itil.php index 2bd9e201e..9fbec3a84 100644 --- a/datamodels/2.x/itop-welcome-itil/da.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/da.dict.itop-welcome-itil.php @@ -20,77 +20,3 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see <http://www.gnu.org/licenses/> */ -Dict::Add('DA DA', 'Danish', 'Dansk', array( - 'Menu:DataSources' => 'Synkroniserings Data Kilder', - 'Menu:DataSources+' => 'All Synchronization Data Sources~~', - 'Menu:WelcomeMenu' => 'Velkomen', - 'Menu:WelcomeMenu+' => 'Velkommen til iTop', - 'Menu:WelcomeMenuPage' => 'Velkomen', - 'Menu:WelcomeMenuPage+' => 'Velkommen til iTop', - 'Menu:AdminTools' => 'Admin værktøjer', - 'Menu:AdminTools+' => 'Administration tools~~', - 'Menu:AdminTools?' => 'Værktøjer kun tilgængelige for brugere med administrator profil', - 'Menu:DataModelMenu' => 'Data Model~~', - 'Menu:DataModelMenu+' => 'Overview of the Data Model~~', - 'Menu:ExportMenu' => 'Export~~', - 'Menu:ExportMenu+' => 'Export the results of any query in HTML, CSV or XML~~', - 'Menu:NotificationsMenu' => 'Notifikationer', - 'Menu:NotificationsMenu+' => 'Configuration of the Notifications~~', - 'Menu:AuditCategories' => 'Audit Kategorier', - 'Menu:AuditCategories+' => 'Audit Categories~~', - 'Menu:Notifications:Title' => 'Audit Kategorier', - 'Menu:RunQueriesMenu' => 'Kør forespørgsler', - 'Menu:RunQueriesMenu+' => 'Run any query~~', - 'Menu:QueryMenu' => 'Query parlør', - 'Menu:QueryMenu+' => 'Query phrasebook~~', - 'Menu:UniversalSearchMenu' => 'Universal Søgning', - 'Menu:UniversalSearchMenu+' => 'Search for anything...~~', - 'Menu:UserManagementMenu' => 'Bruger styring', - 'Menu:UserManagementMenu+' => 'User management~~', - 'Menu:ProfilesMenu' => 'Profiler', - 'Menu:ProfilesMenu+' => 'Profiles~~', - 'Menu:ProfilesMenu:Title' => 'Profiler', - 'Menu:UserAccountsMenu' => 'Bruger konti', - 'Menu:UserAccountsMenu+' => 'User Accounts~~', - 'Menu:UserAccountsMenu:Title' => 'Bruger konti', - 'Menu:MyShortcuts' => 'Mine Genveje', - 'Menu:UserManagement' => 'User Management~~', - 'Menu:Queries' => 'Queries~~', - 'Menu:ConfigurationTools' => 'Configuration~~', -)); - -// -// Class: AbstractResource -// - -Dict::Add('DA DA', 'Danish', 'Dansk', array( - 'Class:AbstractResource' => 'Abstract Resource~~', - 'Class:AbstractResource+' => '~~', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('DA DA', 'Danish', 'Dansk', array( - 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', - 'Class:ResourceAdminMenu+' => '~~', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('DA DA', 'Danish', 'Dansk', array( - 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', - 'Class:ResourceRunQueriesMenu+' => '~~', -)); - -// -// Class: Action -// - -Dict::Add('DA DA', 'Danish', 'Dansk', array( - 'Class:ResourceSystemMenu' => 'Resource System Menu~~', - 'Class:ResourceSystemMenu+' => '~~', -)); diff --git a/datamodels/2.x/itop-welcome-itil/datamodel.itop-welcome-itil.xml b/datamodels/2.x/itop-welcome-itil/datamodel.itop-welcome-itil.xml index 70534c9ad..85e69f6e6 100644 --- a/datamodels/2.x/itop-welcome-itil/datamodel.itop-welcome-itil.xml +++ b/datamodels/2.x/itop-welcome-itil/datamodel.itop-welcome-itil.xml @@ -1,51 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.7"> - <classes> - <class id="AbstractResource" _delta="define"> - <parent>cmdbAbstractObject</parent> - <properties> - <comment>/* Resource access control abstraction. Can be herited by abstract resource access control classes. Generaly controlled using UR_ACTION_MODIFY access right. */</comment> - <abstract>true</abstract> - </properties> - <presentation/> - <methods/> - </class> - <class id="ResourceAdminMenu" _delta="define"> - <parent>AbstractResource</parent> - <properties> - <comment>/* AdminTools menu access control. */</comment> - <abstract>true</abstract> - <category>grant_by_profile</category> - </properties> - <presentation/> - <methods/> - </class> - <class id="ResourceRunQueriesMenu" _delta="define"> - <parent>AbstractResource</parent> - <properties> - <comment>/* RunQueriesMenu menu access control. */</comment> - <abstract>true</abstract> - <category>grant_by_profile</category> - </properties> - <presentation/> - <methods/> - </class> - <class id="ResourceSystemMenu" _delta="define"> - <parent>AbstractResource</parent> - <properties> - <comment>/* System menu access control. */</comment> - <abstract>true</abstract> - <category>grant_by_profile</category> - </properties> - <presentation/> - <methods/> - </class> - </classes> <menus> - <menu id="WelcomeMenu" xsi:type="MenuGroup" _delta="define"> - <rank>10</rank> - </menu> - <menu id="WelcomeMenuPage" xsi:type="DashboardMenuNode" _delta="define"> + <menu id="WelcomeMenuPage" xsi:type="DashboardMenuNode" _delta="redefine"> <rank>10</rank> <parent>WelcomeMenu</parent> <definition> @@ -93,98 +49,5 @@ </cells> </definition> </menu> - <menu id="MyShortcuts" xsi:type="ShortcutContainerMenuNode" _delta="define"> - <rank>20</rank> - <parent>WelcomeMenu</parent> - </menu> - <menu id="UserManagement" xsi:type="TemplateMenuNode" _delta="define"> - <rank>10</rank> - <parent>AdminTools</parent> - <template_file/> - </menu> - <menu id="UserAccountsMenu" xsi:type="OQLMenuNode" _delta="define"> - <rank>11</rank> - <parent>UserManagement</parent> - <oql><![CDATA[SELECT User]]></oql> - <do_search>1</do_search> - <search_form_open>1</search_form_open> - <enable_class>User</enable_class> - <enable_action>UR_ACTION_MODIFY</enable_action> - </menu> - <menu id="ProfilesMenu" xsi:type="OQLMenuNode" _delta="define"> - <rank>12</rank> - <parent>UserManagement</parent> - <oql><![CDATA[SELECT URP_Profiles]]></oql> - <do_search>1</do_search> - <enable_class>URP_Profiles</enable_class> - <enable_action>UR_ACTION_MODIFY</enable_action> - </menu> - <menu id="AuditCategories" xsi:type="OQLMenuNode" _delta="define"> - <rank>20</rank> - <parent>AdminTools</parent> - <oql><![CDATA[SELECT AuditCategory]]></oql> - <do_search>1</do_search> - <enable_class>AuditCategory</enable_class> - <enable_action>UR_ACTION_MODIFY</enable_action> - </menu> - <menu id="Queries" xsi:type="TemplateMenuNode" _delta="define"> - <rank>30</rank> - <parent>AdminTools</parent> - <template_file/> - </menu> - <menu id="RunQueriesMenu" xsi:type="WebPageMenuNode" _delta="define"> - <rank>31</rank> - <parent>Queries</parent> - <url>$pages/run_query.php</url> - <enable_class>ResourceRunQueriesMenu</enable_class> - <enable_action>UR_ACTION_MODIFY</enable_action> - </menu> - <menu id="QueryMenu" xsi:type="OQLMenuNode" _delta="define"> - <rank>32</rank> - <parent>Queries</parent> - <oql><![CDATA[SELECT Query]]></oql> - <do_search>1</do_search> - <enable_class>Query</enable_class> - <enable_action>UR_ACTION_MODIFY</enable_action> - </menu> - <menu id="ExportMenu" xsi:type="WebPageMenuNode" _delta="define"> - <rank>33</rank> - <parent>Queries</parent> - <url>$webservices/export-v2.php?interactive=1</url> - <enable_class>ResourceAdminMenu</enable_class> - <enable_action>UR_ACTION_MODIFY</enable_action> - </menu> - <menu id="DataModelMenu" xsi:type="WebPageMenuNode" _delta="define"> - <rank>40</rank> - <parent>AdminTools</parent> - <url>$pages/schema.php</url> - <enable_class>ResourceRunQueriesMenu</enable_class> - <enable_action>UR_ACTION_MODIFY</enable_action> - </menu> - <menu id="UniversalSearchMenu" xsi:type="WebPageMenuNode" _delta="define"> - <rank>35</rank> - <parent>Queries</parent> - <url>$pages/UniversalSearch.php</url> - <enable_class>ResourceAdminMenu</enable_class> - <enable_action>UR_ACTION_MODIFY</enable_action> - </menu> - <menu id="ConfigurationTools" xsi:type="MenuGroup" _delta="define_if_not_exists"> - <rank>90</rank> - </menu> - <menu id="DataSources" xsi:type="OQLMenuNode" _delta="define"> - <rank>20</rank> - <parent>ConfigurationTools</parent> - <oql><![CDATA[SELECT SynchroDataSource]]></oql> - <do_search>1</do_search> - <enable_class>SynchroDataSource</enable_class> - <enable_action>UR_ACTION_MODIFY</enable_action> - </menu> - <menu id="NotificationsMenu" xsi:type="WebPageMenuNode" _delta="define"> - <rank>40</rank> - <parent>ConfigurationTools</parent> - <url>$pages/notifications.php</url> - <enable_class>Trigger</enable_class> - <enable_action>UR_ACTION_MODIFY</enable_action> - </menu> </menus> </itop_design> diff --git a/datamodels/2.x/itop-welcome-itil/de.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/de.dict.itop-welcome-itil.php index eb93f7fff..1dcebc847 100644 --- a/datamodels/2.x/itop-welcome-itil/de.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/de.dict.itop-welcome-itil.php @@ -22,77 +22,3 @@ * @licence http://opensource.org/licenses/AGPL-3.0 * */ -Dict::Add('DE DE', 'German', 'Deutsch', array( - 'Menu:DataSources' => 'Datenquellen für die Synchronisation', - 'Menu:DataSources+' => 'Alle Datenquellen für die Synchronisation', - 'Menu:WelcomeMenu' => 'Willkommen', - 'Menu:WelcomeMenu+' => 'Willkommen bei iTop', - 'Menu:WelcomeMenuPage' => 'Willkommen', - 'Menu:WelcomeMenuPage+' => 'Willkommen bei iTop', - 'Menu:AdminTools' => 'Admin-Tools', - 'Menu:AdminTools+' => 'Administrationswerkzeuge', - 'Menu:AdminTools?' => 'Werkzeuge, die nur für Benutzer mit Adminstratorprofil zugänglich sind', - 'Menu:DataModelMenu' => 'Datenmodell', - 'Menu:DataModelMenu+' => 'Übersicht des Datenmodells', - 'Menu:ExportMenu' => 'Export', - 'Menu:ExportMenu+' => 'Export einer beliebigen Abfrage in HTML, CSV oder XML', - 'Menu:NotificationsMenu' => 'Benachrichtigungen', - 'Menu:NotificationsMenu+' => 'Einstellungen der Benachrichtigungen', - 'Menu:AuditCategories' => 'Audit-Kategorien', - 'Menu:AuditCategories+' => 'Audit-Kategorien', - 'Menu:Notifications:Title' => 'Audit-Kategorien', - 'Menu:RunQueriesMenu' => 'Abfrage ausführen', - 'Menu:RunQueriesMenu+' => 'Eine beliebige Abfrage ausführen', - 'Menu:QueryMenu' => 'Query-Bibliothek', - 'Menu:QueryMenu+' => '', - 'Menu:UniversalSearchMenu' => 'Universelle Suche', - 'Menu:UniversalSearchMenu+' => 'Suchen Sie nach beliebigen Inhalt...', - 'Menu:UserManagementMenu' => 'Benutzerverwaltung', - 'Menu:UserManagementMenu+' => 'Benutzerverwaltung', - 'Menu:ProfilesMenu' => 'Profile', - 'Menu:ProfilesMenu+' => 'Profile', - 'Menu:ProfilesMenu:Title' => 'Profile', - 'Menu:UserAccountsMenu' => 'Benutzerkonten', - 'Menu:UserAccountsMenu+' => 'Benutzerkonten', - 'Menu:UserAccountsMenu:Title' => 'Benutzerkonten', - 'Menu:MyShortcuts' => 'Meine Shortcuts', - 'Menu:UserManagement' => 'Benutzerverwaltung', - 'Menu:Queries' => 'OQL Abfragen', - 'Menu:ConfigurationTools' => 'Konfiguration', -)); - -// -// Class: AbstractResource -// - -Dict::Add('DE DE', 'German', 'Deutsch', array( - 'Class:AbstractResource' => 'Abstrakte Ressource', - 'Class:AbstractResource+' => '', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('DE DE', 'German', 'Deutsch', array( - 'Class:ResourceAdminMenu' => 'Ressource "Admin Menü"', - 'Class:ResourceAdminMenu+' => '', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('DE DE', 'German', 'Deutsch', array( - 'Class:ResourceRunQueriesMenu' => 'Ressource "Abfrage Ausführen Menü"', - 'Class:ResourceRunQueriesMenu+' => '', -)); - -// -// Class: Action -// - -Dict::Add('DE DE', 'German', 'Deutsch', array( - 'Class:ResourceSystemMenu' => 'Ressource "System Menü"', - 'Class:ResourceSystemMenu+' => '', -)); diff --git a/datamodels/2.x/itop-welcome-itil/en.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/en.dict.itop-welcome-itil.php index 22bf6f57f..fda164c39 100644 --- a/datamodels/2.x/itop-welcome-itil/en.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/en.dict.itop-welcome-itil.php @@ -21,77 +21,3 @@ * along with iTop. If not, see <http://www.gnu.org/licenses/> */ -Dict::Add('EN US', 'English', 'English', array( - 'Menu:DataSources' => 'Synchronization Data Sources', - 'Menu:DataSources+' => 'All Synchronization Data Sources', - 'Menu:WelcomeMenu' => 'Welcome', - 'Menu:WelcomeMenu+' => 'Welcome to iTop', - 'Menu:WelcomeMenuPage' => 'Welcome', - 'Menu:WelcomeMenuPage+' => 'Welcome to iTop', - 'Menu:AdminTools' => 'Administration', - 'Menu:AdminTools+' => 'Administration tools', - 'Menu:AdminTools?' => 'Tools accessible only to users having the administrator profile', - 'Menu:DataModelMenu' => 'Data Model', - 'Menu:DataModelMenu+' => 'Overview of the Data Model', - 'Menu:ExportMenu' => 'Export', - 'Menu:ExportMenu+' => 'Export the results of any query in HTML, CSV or XML', - 'Menu:NotificationsMenu' => 'Notifications', - 'Menu:NotificationsMenu+' => 'Configuration of the Notifications', - 'Menu:AuditCategories' => 'Audit Categories', - 'Menu:AuditCategories+' => 'Audit Categories', - 'Menu:Notifications:Title' => 'Audit Categories', - 'Menu:RunQueriesMenu' => 'Run Queries', - 'Menu:RunQueriesMenu+' => 'Run any query', - 'Menu:QueryMenu' => 'Query phrasebook', - 'Menu:QueryMenu+' => 'Query phrasebook', - 'Menu:UniversalSearchMenu' => 'Universal Search', - 'Menu:UniversalSearchMenu+' => 'Search for anything...', - 'Menu:UserManagementMenu' => 'User Management', - 'Menu:UserManagementMenu+' => 'User management', - 'Menu:ProfilesMenu' => 'Profiles', - 'Menu:ProfilesMenu+' => 'Profiles', - 'Menu:ProfilesMenu:Title' => 'Profiles', - 'Menu:UserAccountsMenu' => 'User Accounts', - 'Menu:UserAccountsMenu+' => 'User Accounts', - 'Menu:UserAccountsMenu:Title' => 'User Accounts', - 'Menu:MyShortcuts' => 'My Shortcuts', - 'Menu:UserManagement' => 'User Management', - 'Menu:Queries' => 'Queries', - 'Menu:ConfigurationTools' => 'Configuration', -)); - -// -// Class: AbstractResource -// - -Dict::Add('EN US', 'English', 'English', array( - 'Class:AbstractResource' => 'Abstract Resource', - 'Class:AbstractResource+' => '', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('EN US', 'English', 'English', array( - 'Class:ResourceAdminMenu' => 'Resource Admin Menu', - 'Class:ResourceAdminMenu+' => '', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('EN US', 'English', 'English', array( - 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu', - 'Class:ResourceRunQueriesMenu+' => '', -)); - -// -// Class: Action -// - -Dict::Add('EN US', 'English', 'English', array( - 'Class:ResourceSystemMenu' => 'Resource System Menu', - 'Class:ResourceSystemMenu+' => '', -)); diff --git a/datamodels/2.x/itop-welcome-itil/es_cr.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/es_cr.dict.itop-welcome-itil.php index 9dc30e30e..9885c8675 100644 --- a/datamodels/2.x/itop-welcome-itil/es_cr.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/es_cr.dict.itop-welcome-itil.php @@ -19,77 +19,3 @@ * @copyright Copyright (C) 2010-2014 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ -Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( - 'Menu:DataSources' => 'Fuentes de Datos Sincronizables', - 'Menu:DataSources+' => 'Fuentes de Datos Sincronizables', - 'Menu:WelcomeMenu' => 'Bienvenido', - 'Menu:WelcomeMenu+' => 'Bienvenido a iTop', - 'Menu:WelcomeMenuPage' => 'Bienvenido', - 'Menu:WelcomeMenuPage+' => 'Bienvenido a iTop', - 'Menu:AdminTools' => 'Herramientas Administrativas', - 'Menu:AdminTools+' => 'Herramientas Administrativas', - 'Menu:AdminTools?' => 'Herramientas accesibles sólo a usuarios con Perfil de administrador', - 'Menu:DataModelMenu' => 'Modelo de Datos', - 'Menu:DataModelMenu+' => 'Resumen del Modelo de Datos', - 'Menu:ExportMenu' => 'Exportar', - 'Menu:ExportMenu+' => 'Exportar los Resultados de Cualquier Consulta en HTML, CSV o XML', - 'Menu:NotificationsMenu' => 'Notificaciones', - 'Menu:NotificationsMenu+' => 'Configuración de las Notificaciones', - 'Menu:AuditCategories' => 'Auditar Categorías', - 'Menu:AuditCategories+' => 'Auditar Categorías', - 'Menu:Notifications:Title' => 'Auditar Categorías', - 'Menu:RunQueriesMenu' => 'Ejecutar Consultas', - 'Menu:RunQueriesMenu+' => 'Ejecutar Cualquier Consulta', - 'Menu:QueryMenu' => 'Libreta de Consultas', - 'Menu:QueryMenu+' => 'Libreta de Consultas', - 'Menu:UniversalSearchMenu' => 'Búsqueda Universal', - 'Menu:UniversalSearchMenu+' => 'Buscar cualquier cosa', - 'Menu:UserManagementMenu' => 'Administración de Usuarios', - 'Menu:UserManagementMenu+' => 'Administración de Usuarios', - 'Menu:ProfilesMenu' => 'Perfiles', - 'Menu:ProfilesMenu+' => 'Perfiles', - 'Menu:ProfilesMenu:Title' => 'Perfiles', - 'Menu:UserAccountsMenu' => 'Cuentas de Usuario', - 'Menu:UserAccountsMenu+' => 'Cuentas de Usuario', - 'Menu:UserAccountsMenu:Title' => 'Cuentas de Usuario', - 'Menu:MyShortcuts' => 'Mis Accesos Rápidos', - 'Menu:UserManagement' => 'Gestión de usuarios', - 'Menu:Queries' => 'Consultas', - 'Menu:ConfigurationTools' => 'configuración', -)); - -// -// Class: AbstractResource -// - -Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( - 'Class:AbstractResource' => 'Abstract Resource~~', - 'Class:AbstractResource+' => '~~', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( - 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', - 'Class:ResourceAdminMenu+' => '~~', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( - 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', - 'Class:ResourceRunQueriesMenu+' => '~~', -)); - -// -// Class: Action -// - -Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( - 'Class:ResourceSystemMenu' => 'Resource System Menu~~', - 'Class:ResourceSystemMenu+' => '~~', -)); diff --git a/datamodels/2.x/itop-welcome-itil/fr.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/fr.dict.itop-welcome-itil.php index bae4383bd..9885c8675 100644 --- a/datamodels/2.x/itop-welcome-itil/fr.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/fr.dict.itop-welcome-itil.php @@ -19,77 +19,3 @@ * @copyright Copyright (C) 2010-2014 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ -Dict::Add('FR FR', 'French', 'Français', array( - 'Menu:DataSources' => 'Synchronisation', - 'Menu:DataSources+' => '', - 'Menu:WelcomeMenu' => 'Bienvenue', - 'Menu:WelcomeMenu+' => 'Bienvenue dans iTop', - 'Menu:WelcomeMenuPage' => 'Bienvenue', - 'Menu:WelcomeMenuPage+' => 'Bienvenue dans iTop', - 'Menu:AdminTools' => 'Administration', - 'Menu:AdminTools+' => 'Outils d\'administration', - 'Menu:AdminTools?' => 'Ces outils sont accessibles uniquement aux utilisateurs possédant le profil Administrateur.', - 'Menu:DataModelMenu' => 'Modèle de Données', - 'Menu:DataModelMenu+' => 'Résumé du Modèle de Données', - 'Menu:ExportMenu' => 'Export', - 'Menu:ExportMenu+' => 'Export des résultats d\'une requête en HTML, CSV ou XML', - 'Menu:NotificationsMenu' => 'Notifications', - 'Menu:NotificationsMenu+' => 'Configuration des Notifications', - 'Menu:AuditCategories' => 'Catégories d\'audit', - 'Menu:AuditCategories+' => 'Catégories d\'audit', - 'Menu:Notifications:Title' => 'Catégories d\'audit', - 'Menu:RunQueriesMenu' => 'Requêtes OQL', - 'Menu:RunQueriesMenu+' => 'Executer une requête OQL', - 'Menu:QueryMenu' => 'Livre des requêtes', - 'Menu:QueryMenu+' => 'Livre des requêtes', - 'Menu:UniversalSearchMenu' => 'Recherche Universelle', - 'Menu:UniversalSearchMenu+' => 'Rechercher n\'importe quel objet...', - 'Menu:UserManagementMenu' => 'Gestion des Utilisateurs', - 'Menu:UserManagementMenu+' => 'Gestion des Utilisateurs', - 'Menu:ProfilesMenu' => 'Profils', - 'Menu:ProfilesMenu+' => 'Profils', - 'Menu:ProfilesMenu:Title' => 'Profils', - 'Menu:UserAccountsMenu' => 'Comptes Utilisateurs', - 'Menu:UserAccountsMenu+' => 'Comptes Utilisateurs', - 'Menu:UserAccountsMenu:Title' => 'Comptes Utilisateurs', - 'Menu:MyShortcuts' => 'Mes raccourcis', - 'Menu:UserManagement' => 'Utilisateurs', - 'Menu:Queries' => 'Requêtes', - 'Menu:ConfigurationTools' => 'Configuration', -)); - -// -// Class: AbstractResource -// - -Dict::Add('FR FR', 'French', 'Français', array( - 'Class:AbstractResource' => 'Ressource', - 'Class:AbstractResource+' => '', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('FR FR', 'French', 'Français', array( - 'Class:ResourceAdminMenu' => 'Menu Administration', - 'Class:ResourceAdminMenu+' => '', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('FR FR', 'French', 'Français', array( - 'Class:ResourceRunQueriesMenu' => 'Menu Requête OQL', - 'Class:ResourceRunQueriesMenu+' => '', -)); - -// -// Class: Action -// - -Dict::Add('FR FR', 'French', 'Français', array( - 'Class:ResourceSystemMenu' => 'Menu System', - 'Class:ResourceSystemMenu+' => '', -)); diff --git a/datamodels/2.x/itop-welcome-itil/hu.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/hu.dict.itop-welcome-itil.php index 139f390ae..9885c8675 100644 --- a/datamodels/2.x/itop-welcome-itil/hu.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/hu.dict.itop-welcome-itil.php @@ -19,77 +19,3 @@ * @copyright Copyright (C) 2010-2014 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ -Dict::Add('HU HU', 'Hungarian', 'Magyar', array( - 'Menu:DataSources' => 'Szinkronizált adatforrások', - 'Menu:DataSources+' => '', - 'Menu:WelcomeMenu' => 'Üdvözlöm', - 'Menu:WelcomeMenu+' => '', - 'Menu:WelcomeMenuPage' => 'Üdvözlöm', - 'Menu:WelcomeMenuPage+' => '', - 'Menu:AdminTools' => 'Adminisztrációs eszközök', - 'Menu:AdminTools+' => '', - 'Menu:AdminTools?' => 'Eszközök csak az adminisztrátori profilhoz rendlet felhasználók számára elérhetők.', - 'Menu:DataModelMenu' => 'Adatmodell', - 'Menu:DataModelMenu+' => '', - 'Menu:ExportMenu' => 'Export', - 'Menu:ExportMenu+' => '', - 'Menu:NotificationsMenu' => 'Értesítések', - 'Menu:NotificationsMenu+' => '', - 'Menu:AuditCategories' => 'Audit kategóriák', - 'Menu:AuditCategories+' => '', - 'Menu:Notifications:Title' => 'Audit kategóriák', - 'Menu:RunQueriesMenu' => 'Lekérdezés futtatás', - 'Menu:RunQueriesMenu+' => '', - 'Menu:QueryMenu' => 'Query phrasebook~~', - 'Menu:QueryMenu+' => 'Query phrasebook~~', - 'Menu:UniversalSearchMenu' => 'Univerzális keresés', - 'Menu:UniversalSearchMenu+' => '', - 'Menu:UserManagementMenu' => 'Felhasználó menedzsment', - 'Menu:UserManagementMenu+' => '', - 'Menu:ProfilesMenu' => 'Profilok', - 'Menu:ProfilesMenu+' => '', - 'Menu:ProfilesMenu:Title' => 'Profilok', - 'Menu:UserAccountsMenu' => 'Felhasználói fiókok', - 'Menu:UserAccountsMenu+' => '', - 'Menu:UserAccountsMenu:Title' => 'Felhasználói fiókok', - 'Menu:MyShortcuts' => 'My Shortcuts~~', - 'Menu:UserManagement' => 'User Management~~', - 'Menu:Queries' => 'Queries~~', - 'Menu:ConfigurationTools' => 'Configuration~~', -)); - -// -// Class: AbstractResource -// - -Dict::Add('HU HU', 'Hungarian', 'Magyar', array( - 'Class:AbstractResource' => 'Abstract Resource~~', - 'Class:AbstractResource+' => '~~', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('HU HU', 'Hungarian', 'Magyar', array( - 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', - 'Class:ResourceAdminMenu+' => '~~', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('HU HU', 'Hungarian', 'Magyar', array( - 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', - 'Class:ResourceRunQueriesMenu+' => '~~', -)); - -// -// Class: Action -// - -Dict::Add('HU HU', 'Hungarian', 'Magyar', array( - 'Class:ResourceSystemMenu' => 'Resource System Menu~~', - 'Class:ResourceSystemMenu+' => '~~', -)); diff --git a/datamodels/2.x/itop-welcome-itil/it.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/it.dict.itop-welcome-itil.php index 6ffaa5f90..9885c8675 100644 --- a/datamodels/2.x/itop-welcome-itil/it.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/it.dict.itop-welcome-itil.php @@ -19,77 +19,3 @@ * @copyright Copyright (C) 2010-2014 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ -Dict::Add('IT IT', 'Italian', 'Italiano', array( - 'Menu:DataSources' => 'Sorgente di sincronizzazione dei dati', - 'Menu:DataSources+' => '', - 'Menu:WelcomeMenu' => 'Benveuto', - 'Menu:WelcomeMenu+' => '', - 'Menu:WelcomeMenuPage' => 'Benvenuto', - 'Menu:WelcomeMenuPage+' => '', - 'Menu:AdminTools' => 'Strumenti di amministrazione', - 'Menu:AdminTools+' => '', - 'Menu:AdminTools?' => 'Strumenti accessibile solo agli utenti con il profilo di amministratore', - 'Menu:DataModelMenu' => 'Modello Dati', - 'Menu:DataModelMenu+' => '', - 'Menu:ExportMenu' => 'Esporta', - 'Menu:ExportMenu+' => '', - 'Menu:NotificationsMenu' => 'Notifiche', - 'Menu:NotificationsMenu+' => '', - 'Menu:AuditCategories' => 'Categorie di Audit', - 'Menu:AuditCategories+' => '', - 'Menu:Notifications:Title' => 'Categorie di Audit', - 'Menu:RunQueriesMenu' => 'Esegui query', - 'Menu:RunQueriesMenu+' => '', - 'Menu:QueryMenu' => 'Rubbrica delle Query', - 'Menu:QueryMenu+' => 'Rubbrica delle Query', - 'Menu:UniversalSearchMenu' => 'Ricerca universale', - 'Menu:UniversalSearchMenu+' => '', - 'Menu:UserManagementMenu' => 'Gestione degli utenti', - 'Menu:UserManagementMenu+' => '', - 'Menu:ProfilesMenu' => 'Profili', - 'Menu:ProfilesMenu+' => '', - 'Menu:ProfilesMenu:Title' => 'Profili', - 'Menu:UserAccountsMenu' => 'Account utente', - 'Menu:UserAccountsMenu+' => '', - 'Menu:UserAccountsMenu:Title' => 'Account utente', - 'Menu:MyShortcuts' => 'Le mie scorciatoie', - 'Menu:UserManagement' => 'Gestione utenti', - 'Menu:Queries' => 'Interrogazioni', - 'Menu:ConfigurationTools' => 'configurazione', -)); - -// -// Class: AbstractResource -// - -Dict::Add('IT IT', 'Italian', 'Italiano', array( - 'Class:AbstractResource' => 'Abstract Resource~~', - 'Class:AbstractResource+' => '~~', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('IT IT', 'Italian', 'Italiano', array( - 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', - 'Class:ResourceAdminMenu+' => '~~', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('IT IT', 'Italian', 'Italiano', array( - 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', - 'Class:ResourceRunQueriesMenu+' => '~~', -)); - -// -// Class: Action -// - -Dict::Add('IT IT', 'Italian', 'Italiano', array( - 'Class:ResourceSystemMenu' => 'Resource System Menu~~', - 'Class:ResourceSystemMenu+' => '~~', -)); diff --git a/datamodels/2.x/itop-welcome-itil/ja.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/ja.dict.itop-welcome-itil.php index ef44d89a4..9885c8675 100644 --- a/datamodels/2.x/itop-welcome-itil/ja.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/ja.dict.itop-welcome-itil.php @@ -19,77 +19,3 @@ * @copyright Copyright (C) 2010-2014 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ -Dict::Add('JA JP', 'Japanese', '日本語', array( - 'Menu:DataSources' => '同期データソース', - 'Menu:DataSources+' => '全ての同期データソース', - 'Menu:WelcomeMenu' => 'ようこそ', - 'Menu:WelcomeMenu+' => 'ようこそ、iTopへ', - 'Menu:WelcomeMenuPage' => 'ようこそ', - 'Menu:WelcomeMenuPage+' => 'ようこそ、iTopへ', - 'Menu:AdminTools' => '管理ツール', - 'Menu:AdminTools+' => '管理ツール', - 'Menu:AdminTools?' => 'このツールは管理者プロフィールを持つユーザのみアクセスが可能です。', - 'Menu:DataModelMenu' => 'データモデル', - 'Menu:DataModelMenu+' => 'データモデル概要', - 'Menu:ExportMenu' => 'エクスポート', - 'Menu:ExportMenu+' => '任意のクエリ結果をHTML、CSV、XMLでエクスポートする', - 'Menu:NotificationsMenu' => '通知', - 'Menu:NotificationsMenu+' => '通知の設定', - 'Menu:AuditCategories' => '監査カテゴリ', - 'Menu:AuditCategories+' => '監査カテゴリ', - 'Menu:Notifications:Title' => '監査カテゴリ', - 'Menu:RunQueriesMenu' => 'クエリ実行', - 'Menu:RunQueriesMenu+' => '任意のクエリを実行', - 'Menu:QueryMenu' => 'クエリのフレーズブック', - 'Menu:QueryMenu+' => 'クエリのフレーズブック', - 'Menu:UniversalSearchMenu' => '全検索', - 'Menu:UniversalSearchMenu+' => '何か...検索', - 'Menu:UserManagementMenu' => 'ユーザ管理', - 'Menu:UserManagementMenu+' => 'ユーザ管理', - 'Menu:ProfilesMenu' => 'プロフィール', - 'Menu:ProfilesMenu+' => 'プロフィール', - 'Menu:ProfilesMenu:Title' => 'プロフィール', - 'Menu:UserAccountsMenu' => 'ユーザアカウント', - 'Menu:UserAccountsMenu+' => 'ユーザアカウント', - 'Menu:UserAccountsMenu:Title' => 'ユーザアカウント', - 'Menu:MyShortcuts' => '私のショートカット', - 'Menu:UserManagement' => 'User Management~~', - 'Menu:Queries' => 'Queries~~', - 'Menu:ConfigurationTools' => 'Configuration~~', -)); - -// -// Class: AbstractResource -// - -Dict::Add('JA JP', 'Japanese', '日本語', array( - 'Class:AbstractResource' => 'Abstract Resource~~', - 'Class:AbstractResource+' => '~~', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('JA JP', 'Japanese', '日本語', array( - 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', - 'Class:ResourceAdminMenu+' => '~~', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('JA JP', 'Japanese', '日本語', array( - 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', - 'Class:ResourceRunQueriesMenu+' => '~~', -)); - -// -// Class: Action -// - -Dict::Add('JA JP', 'Japanese', '日本語', array( - 'Class:ResourceSystemMenu' => 'Resource System Menu~~', - 'Class:ResourceSystemMenu+' => '~~', -)); diff --git a/datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php index cfdfaac9a..01bf82956 100755 --- a/datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php @@ -3,7 +3,7 @@ SetupWebPage::AddModule( __FILE__, // Path to the current file, all other file names are relative to the directory containing this file - 'itop-welcome-itil/2.7.0', + 'itop-welcome-itil/2.8.0', array( // Identification // diff --git a/datamodels/2.x/itop-welcome-itil/nl.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/nl.dict.itop-welcome-itil.php index 4616d401f..d64d6d242 100644 --- a/datamodels/2.x/itop-welcome-itil/nl.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/nl.dict.itop-welcome-itil.php @@ -22,77 +22,3 @@ * * @author Jeffrey Bostoen - <jbostoen.itop@outlook.com> (2018 - 2020) */ -Dict::Add('NL NL', 'Dutch', 'Nederlands', array( - 'Menu:DataSources' => 'Synchronisatie-databronnen', - 'Menu:DataSources+' => 'Alle Synchronisatie-databronnen', - 'Menu:WelcomeMenu' => 'Welkom', - 'Menu:WelcomeMenu+' => 'Welkom in iTop', - 'Menu:WelcomeMenuPage' => 'Welkom', - 'Menu:WelcomeMenuPage+' => 'Welkom in iTop', - 'Menu:AdminTools' => 'Admintools', - 'Menu:AdminTools+' => 'Beheertools', - 'Menu:AdminTools?' => 'Tools die enkel toegankelijk zijn voor gebruikers met een administratorprofiel.', - 'Menu:DataModelMenu' => 'Datamodel', - 'Menu:DataModelMenu+' => 'Overzicht van het datamodel', - 'Menu:ExportMenu' => 'Export', - 'Menu:ExportMenu+' => 'Exporteer de resultaten van query\'s als HTML, CSV of XML', - 'Menu:NotificationsMenu' => 'Meldingen', - 'Menu:NotificationsMenu+' => 'Configuratie van de meldingen', - 'Menu:AuditCategories' => 'Auditcategorieën', - 'Menu:AuditCategories+' => 'Auditcategorieën', - 'Menu:Notifications:Title' => 'Auditcategorieën', - 'Menu:RunQueriesMenu' => 'Query\'s uitvoeren', - 'Menu:RunQueriesMenu+' => 'Voer een query uit', - 'Menu:QueryMenu' => 'Favoriete query\'s', - 'Menu:QueryMenu+' => 'Favoriete query\'s', - 'Menu:UniversalSearchMenu' => 'Globale zoekopdracht', - 'Menu:UniversalSearchMenu+' => 'Zoek in alle data...', - 'Menu:UserManagementMenu' => 'Gebruikersbeheer', - 'Menu:UserManagementMenu+' => 'Gebruikersbeheer', - 'Menu:ProfilesMenu' => 'Profielen', - 'Menu:ProfilesMenu+' => 'Profielen', - 'Menu:ProfilesMenu:Title' => 'Profielen', - 'Menu:UserAccountsMenu' => 'Gebruikersaccounts', - 'Menu:UserAccountsMenu+' => 'Gebruikersaccounts', - 'Menu:UserAccountsMenu:Title' => 'Gebruikersaccounts', - 'Menu:MyShortcuts' => 'Mijn snelkoppelingen', - 'Menu:UserManagement' => 'Gebruikersbeheer', - 'Menu:Queries' => 'Query\'s', - 'Menu:ConfigurationTools' => 'Configuratie', -)); - -// -// Class: AbstractResource -// - -Dict::Add('NL NL', 'Dutch', 'Nederlands', array( - 'Class:AbstractResource' => 'Abstracte Tool', - 'Class:AbstractResource+' => '', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('NL NL', 'Dutch', 'Nederlands', array( - 'Class:ResourceAdminMenu' => 'Tool "Admin Menu"', - 'Class:ResourceAdminMenu+' => '', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('NL NL', 'Dutch', 'Nederlands', array( - 'Class:ResourceRunQueriesMenu' => 'Tool "Voer query\'s uit" Menu', - 'Class:ResourceRunQueriesMenu+' => '', -)); - -// -// Class: Action -// - -Dict::Add('NL NL', 'Dutch', 'Nederlands', array( - 'Class:ResourceSystemMenu' => 'Tool "System Menu"', - 'Class:ResourceSystemMenu+' => '', -)); diff --git a/datamodels/2.x/itop-welcome-itil/pt_br.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/pt_br.dict.itop-welcome-itil.php index 87b308a69..9885c8675 100644 --- a/datamodels/2.x/itop-welcome-itil/pt_br.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/pt_br.dict.itop-welcome-itil.php @@ -19,77 +19,3 @@ * @copyright Copyright (C) 2010-2014 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ -Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( - 'Menu:DataSources' => 'Fontes de dados de sincronização', - 'Menu:DataSources+' => 'Todas fontes de dados de sincronização', - 'Menu:WelcomeMenu' => 'Bem-vindo ao iTop', - 'Menu:WelcomeMenu+' => 'Bem-vindo ao iTop', - 'Menu:WelcomeMenuPage' => 'Bem-vindo ao iTop', - 'Menu:WelcomeMenuPage+' => 'Bem-vindo ao iTop', - 'Menu:AdminTools' => 'Ferramentas Administrativas', - 'Menu:AdminTools+' => 'Ferramentas Administrativas', - 'Menu:AdminTools?' => 'Ferramentas acessíveis apenas para usuários com o perfil do administrador', - 'Menu:DataModelMenu' => 'Modelo Dados', - 'Menu:DataModelMenu+' => 'Visão geral do Modelo Dados', - 'Menu:ExportMenu' => 'Exportar', - 'Menu:ExportMenu+' => 'Exportar o resultado de qualquer consulta em HTML, CSV ou XML', - 'Menu:NotificationsMenu' => 'Notificações', - 'Menu:NotificationsMenu+' => 'Configuração de Notificações', - 'Menu:AuditCategories' => 'Categoria Auditorias', - 'Menu:AuditCategories+' => 'Categoria Auditorias', - 'Menu:Notifications:Title' => 'Categoria Auditorias', - 'Menu:RunQueriesMenu' => 'Executar consultas', - 'Menu:RunQueriesMenu+' => 'Executar qualquer consulta', - 'Menu:QueryMenu' => 'Consulta definida', - 'Menu:QueryMenu+' => 'Consulta definida', - 'Menu:UniversalSearchMenu' => 'Pesquisa Universal', - 'Menu:UniversalSearchMenu+' => 'Pesquisar por nada...', - 'Menu:UserManagementMenu' => 'Gerenciamento Usuários', - 'Menu:UserManagementMenu+' => 'Gerenciamento Usuários', - 'Menu:ProfilesMenu' => 'Perfis', - 'Menu:ProfilesMenu+' => 'Perfis', - 'Menu:ProfilesMenu:Title' => 'Perfis', - 'Menu:UserAccountsMenu' => 'Contas usuários', - 'Menu:UserAccountsMenu+' => 'Contas usuários', - 'Menu:UserAccountsMenu:Title' => 'Contas usuários', - 'Menu:MyShortcuts' => 'Meus atalhos', - 'Menu:UserManagement' => 'User Management~~', - 'Menu:Queries' => 'Queries~~', - 'Menu:ConfigurationTools' => 'Configuration~~', -)); - -// -// Class: AbstractResource -// - -Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( - 'Class:AbstractResource' => 'Recurso abstrato', - 'Class:AbstractResource+' => '', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( - 'Class:ResourceAdminMenu' => 'Recurso Admin Menu', - 'Class:ResourceAdminMenu+' => '', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( - 'Class:ResourceRunQueriesMenu' => 'Menu de consultas de execução de recursos', - 'Class:ResourceRunQueriesMenu+' => '', -)); - -// -// Class: Action -// - -Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( - 'Class:ResourceSystemMenu' => 'Resource System Menu~~', - 'Class:ResourceSystemMenu+' => '~~', -)); diff --git a/datamodels/2.x/itop-welcome-itil/ru.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/ru.dict.itop-welcome-itil.php index 2ce6a6285..84ef741ee 100644 --- a/datamodels/2.x/itop-welcome-itil/ru.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/ru.dict.itop-welcome-itil.php @@ -8,77 +8,3 @@ * @license http://opensource.org/licenses/AGPL-3.0 * */ -Dict::Add('RU RU', 'Russian', 'Русский', array( - 'Menu:DataSources' => 'Синхронизация данных', - 'Menu:DataSources+' => 'Синхронизация данных', - 'Menu:WelcomeMenu' => 'Добро пожаловать', - 'Menu:WelcomeMenu+' => 'Добро пожаловать в iTop', - 'Menu:WelcomeMenuPage' => 'Добро пожаловать', - 'Menu:WelcomeMenuPage+' => 'Добро пожаловать в iTop', - 'Menu:AdminTools' => 'Инструменты администратора', - 'Menu:AdminTools+' => 'Инструменты администратора', - 'Menu:AdminTools?' => 'Инструменты доступны только для пользователей c правами администратора', - 'Menu:DataModelMenu' => 'Модель данных', - 'Menu:DataModelMenu+' => 'Обзор модели данных', - 'Menu:ExportMenu' => 'Экспорт', - 'Menu:ExportMenu+' => 'Экспорт результатов любого запроса в HTML, CSV или XML', - 'Menu:NotificationsMenu' => 'Уведомления', - 'Menu:NotificationsMenu+' => 'Конфигурация уведомлений', - 'Menu:AuditCategories' => 'Категории аудита', - 'Menu:AuditCategories+' => 'Категории аудита', - 'Menu:Notifications:Title' => 'Категории аудита', - 'Menu:RunQueriesMenu' => 'Выполнение запросов', - 'Menu:RunQueriesMenu+' => 'Выполнение любых запросов', - 'Menu:QueryMenu' => 'Книга запросов', - 'Menu:QueryMenu+' => 'Книга запросов', - 'Menu:UniversalSearchMenu' => 'Универсальный поиск', - 'Menu:UniversalSearchMenu+' => 'Поиск чего угодно...', - 'Menu:UserManagementMenu' => 'Управление пользователями', - 'Menu:UserManagementMenu+' => 'Управление пользователями', - 'Menu:ProfilesMenu' => 'Профили', - 'Menu:ProfilesMenu+' => 'Профили пользователей', - 'Menu:ProfilesMenu:Title' => 'Профили пользователей', - 'Menu:UserAccountsMenu' => 'Учетные записи', - 'Menu:UserAccountsMenu+' => 'Учетные записи пользователей', - 'Menu:UserAccountsMenu:Title' => 'Учетные записи пользователей', - 'Menu:MyShortcuts' => 'Избранное', - 'Menu:UserManagement' => 'Управление пользователями', - 'Menu:Queries' => 'Запросы OQL', - 'Menu:ConfigurationTools' => 'Конфигурация', -)); - -// -// Class: AbstractResource -// - -Dict::Add('RU RU', 'Russian', 'Русский', array( - 'Class:AbstractResource' => 'Ресурс', - 'Class:AbstractResource+' => '', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('RU RU', 'Russian', 'Русский', array( - 'Class:ResourceAdminMenu' => 'Меню Инструменты администратора', - 'Class:ResourceAdminMenu+' => '', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('RU RU', 'Russian', 'Русский', array( - 'Class:ResourceRunQueriesMenu' => 'Меню Выполнение запросов', - 'Class:ResourceRunQueriesMenu+' => '', -)); - -// -// Class: Action -// - -Dict::Add('RU RU', 'Russian', 'Русский', array( - 'Class:ResourceSystemMenu' => 'Меню Система', - 'Class:ResourceSystemMenu+' => '', -)); diff --git a/datamodels/2.x/itop-welcome-itil/sk.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/sk.dict.itop-welcome-itil.php index 395f7f0fb..9fbec3a84 100644 --- a/datamodels/2.x/itop-welcome-itil/sk.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/sk.dict.itop-welcome-itil.php @@ -20,77 +20,3 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see <http://www.gnu.org/licenses/> */ -Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( - 'Menu:DataSources' => 'Synchronizované zdroje dát', - 'Menu:DataSources+' => '', - 'Menu:WelcomeMenu' => 'Vitajte', - 'Menu:WelcomeMenu+' => '', - 'Menu:WelcomeMenuPage' => 'Vitajte', - 'Menu:WelcomeMenuPage+' => '', - 'Menu:AdminTools' => 'Administrátorské pomôcky', - 'Menu:AdminTools+' => '', - 'Menu:AdminTools?' => 'Pomôcky prístupné iba užívateľom majúcim administrátorský profil', - 'Menu:DataModelMenu' => 'Dátový model', - 'Menu:DataModelMenu+' => '', - 'Menu:ExportMenu' => 'Export', - 'Menu:ExportMenu+' => '', - 'Menu:NotificationsMenu' => 'Upozornenia', - 'Menu:NotificationsMenu+' => '', - 'Menu:AuditCategories' => 'Kategórie auditu', - 'Menu:AuditCategories+' => '', - 'Menu:Notifications:Title' => 'Kategórie auditu', - 'Menu:RunQueriesMenu' => 'Spustiť dopyty', - 'Menu:RunQueriesMenu+' => '', - 'Menu:QueryMenu' => 'Dopyt frázy', - 'Menu:QueryMenu+' => '', - 'Menu:UniversalSearchMenu' => 'Univerzálne vyhľadávanie', - 'Menu:UniversalSearchMenu+' => '', - 'Menu:UserManagementMenu' => 'Užívateľský manažment', - 'Menu:UserManagementMenu+' => '', - 'Menu:ProfilesMenu' => 'Profily', - 'Menu:ProfilesMenu+' => '', - 'Menu:ProfilesMenu:Title' => 'Profily', - 'Menu:UserAccountsMenu' => 'Užívateľské účty', - 'Menu:UserAccountsMenu+' => '', - 'Menu:UserAccountsMenu:Title' => 'Užívateľské účty', - 'Menu:MyShortcuts' => 'Moje skratky', - 'Menu:UserManagement' => 'User Management~~', - 'Menu:Queries' => 'Queries~~', - 'Menu:ConfigurationTools' => 'Configuration~~', -)); - -// -// Class: AbstractResource -// - -Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( - 'Class:AbstractResource' => 'Abstract Resource~~', - 'Class:AbstractResource+' => '~~', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( - 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', - 'Class:ResourceAdminMenu+' => '~~', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( - 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', - 'Class:ResourceRunQueriesMenu+' => '~~', -)); - -// -// Class: Action -// - -Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( - 'Class:ResourceSystemMenu' => 'Resource System Menu~~', - 'Class:ResourceSystemMenu+' => '~~', -)); diff --git a/datamodels/2.x/itop-welcome-itil/tr.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/tr.dict.itop-welcome-itil.php index 7bf762018..9885c8675 100644 --- a/datamodels/2.x/itop-welcome-itil/tr.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/tr.dict.itop-welcome-itil.php @@ -19,77 +19,3 @@ * @copyright Copyright (C) 2010-2014 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ -Dict::Add('TR TR', 'Turkish', 'Türkçe', array( - 'Menu:DataSources' => 'Synchronization Data Sources~~', - 'Menu:DataSources+' => 'All Synchronization Data Sources~~', - 'Menu:WelcomeMenu' => 'Hoşgeldiniz', - 'Menu:WelcomeMenu+' => 'iTop\'a Hoşgeldiniz', - 'Menu:WelcomeMenuPage' => 'Hoşgeldiniz', - 'Menu:WelcomeMenuPage+' => 'iTop\'a Hoşgeldiniz', - 'Menu:AdminTools' => 'Yönetim Araçları', - 'Menu:AdminTools+' => 'Yönetim Araçları', - 'Menu:AdminTools?' => 'Yönetici profiline izin verilen araçlar', - 'Menu:DataModelMenu' => 'Veri Modeli', - 'Menu:DataModelMenu+' => 'Veri Modeli Özeti', - 'Menu:ExportMenu' => 'Dışarı ver', - 'Menu:ExportMenu+' => 'Sorgu sonucunu HTML, CSV veya XML olarak dışarı aktar', - 'Menu:NotificationsMenu' => 'Uyarılar', - 'Menu:NotificationsMenu+' => 'Uyarıların yapılandırılması', - 'Menu:AuditCategories' => 'Denetleme Kategorileri', - 'Menu:AuditCategories+' => 'Denetleme Kategorileri', - 'Menu:Notifications:Title' => 'Denetleme Kategorileri', - 'Menu:RunQueriesMenu' => 'Sorgu çalıştır', - 'Menu:RunQueriesMenu+' => 'Sorgu çalıştır', - 'Menu:QueryMenu' => 'Query phrasebook~~', - 'Menu:QueryMenu+' => 'Query phrasebook~~', - 'Menu:UniversalSearchMenu' => 'Genel sorgu', - 'Menu:UniversalSearchMenu+' => 'Herhangi bir arama...', - 'Menu:UserManagementMenu' => 'Kullanıcı Yönetimi', - 'Menu:UserManagementMenu+' => 'Kullanıcı Yönetimi', - 'Menu:ProfilesMenu' => 'Profiller', - 'Menu:ProfilesMenu+' => 'Profiller', - 'Menu:ProfilesMenu:Title' => 'Profiller', - 'Menu:UserAccountsMenu' => 'Kullanıcı Hesapları', - 'Menu:UserAccountsMenu+' => 'Kullanıcı Hesapları', - 'Menu:UserAccountsMenu:Title' => 'Kullanıcı Hesapları', - 'Menu:MyShortcuts' => 'My Shortcuts~~', - 'Menu:UserManagement' => 'User Management~~', - 'Menu:Queries' => 'Queries~~', - 'Menu:ConfigurationTools' => 'Configuration~~', -)); - -// -// Class: AbstractResource -// - -Dict::Add('TR TR', 'Turkish', 'Türkçe', array( - 'Class:AbstractResource' => 'Abstract Resource~~', - 'Class:AbstractResource+' => '~~', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('TR TR', 'Turkish', 'Türkçe', array( - 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', - 'Class:ResourceAdminMenu+' => '~~', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('TR TR', 'Turkish', 'Türkçe', array( - 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', - 'Class:ResourceRunQueriesMenu+' => '~~', -)); - -// -// Class: Action -// - -Dict::Add('TR TR', 'Turkish', 'Türkçe', array( - 'Class:ResourceSystemMenu' => 'Resource System Menu~~', - 'Class:ResourceSystemMenu+' => '~~', -)); diff --git a/datamodels/2.x/itop-welcome-itil/zh_cn.dict.itop-welcome-itil.php b/datamodels/2.x/itop-welcome-itil/zh_cn.dict.itop-welcome-itil.php index 20e7959b7..9fbec3a84 100644 --- a/datamodels/2.x/itop-welcome-itil/zh_cn.dict.itop-welcome-itil.php +++ b/datamodels/2.x/itop-welcome-itil/zh_cn.dict.itop-welcome-itil.php @@ -20,77 +20,3 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see <http://www.gnu.org/licenses/> */ -Dict::Add('ZH CN', 'Chinese', '简体中文', array( - 'Menu:DataSources' => '同步数据源', - 'Menu:DataSources+' => '所有同步数据源', - 'Menu:WelcomeMenu' => '欢迎', - 'Menu:WelcomeMenu+' => '欢迎使用iTop', - 'Menu:WelcomeMenuPage' => '欢迎', - 'Menu:WelcomeMenuPage+' => '欢迎使用iTop', - 'Menu:AdminTools' => '管理工具', - 'Menu:AdminTools+' => '管理工具', - 'Menu:AdminTools?' => '具有管理员角色的用户才能使用的工具', - 'Menu:DataModelMenu' => '数据模型', - 'Menu:DataModelMenu+' => '数据模型概况', - 'Menu:ExportMenu' => '导出', - 'Menu:ExportMenu+' => '以HTML, CSV 或XML 格式导出任何查询的结果', - 'Menu:NotificationsMenu' => '通知', - 'Menu:NotificationsMenu+' => '配置通知', - 'Menu:AuditCategories' => '审计类别', - 'Menu:AuditCategories+' => '审计类别', - 'Menu:Notifications:Title' => '审计类别', - 'Menu:RunQueriesMenu' => '运行查询', - 'Menu:RunQueriesMenu+' => '运行任何查询', - 'Menu:QueryMenu' => '查询手册', - 'Menu:QueryMenu+' => '查询手册', - 'Menu:UniversalSearchMenu' => '全局搜索', - 'Menu:UniversalSearchMenu+' => '搜索所有...', - 'Menu:UserManagementMenu' => '用户管理', - 'Menu:UserManagementMenu+' => '用户管理', - 'Menu:ProfilesMenu' => '角色', - 'Menu:ProfilesMenu+' => '角色', - 'Menu:ProfilesMenu:Title' => '角色', - 'Menu:UserAccountsMenu' => '用户帐户', - 'Menu:UserAccountsMenu+' => '用户帐户', - 'Menu:UserAccountsMenu:Title' => '用户帐户', - 'Menu:MyShortcuts' => '我的快捷方式', - 'Menu:UserManagement' => '用户管理', - 'Menu:Queries' => '查询', - 'Menu:ConfigurationTools' => '配置', -)); - -// -// Class: AbstractResource -// - -Dict::Add('ZH CN', 'Chinese', '简体中文', array( - 'Class:AbstractResource' => '抽象资源', - 'Class:AbstractResource+' => '', -)); - -// -// Class: ResourceAdminMenu -// - -Dict::Add('ZH CN', 'Chinese', '简体中文', array( - 'Class:ResourceAdminMenu' => '资源管理菜单', - 'Class:ResourceAdminMenu+' => '', -)); - -// -// Class: ResourceRunQueriesMenu -// - -Dict::Add('ZH CN', 'Chinese', '简体中文', array( - 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', - 'Class:ResourceRunQueriesMenu+' => '', -)); - -// -// Class: Action -// - -Dict::Add('ZH CN', 'Chinese', '简体中文', array( - 'Class:ResourceSystemMenu' => 'Resource System Menu~~', - 'Class:ResourceSystemMenu+' => '', -)); diff --git a/datamodels/2.x/version.xml b/datamodels/2.x/version.xml index 57807b22e..5f5ac5376 100755 --- a/datamodels/2.x/version.xml +++ b/datamodels/2.x/version.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> <information> - <version>2.8.0-dev</version> + <version>2.8.0</version> </information> diff --git a/dictionaries/cs.dictionary.itop.core.php b/dictionaries/cs.dictionary.itop.core.php index a6cbc5787..ac78abe2c 100755 --- a/dictionaries/cs.dictionary.itop.core.php +++ b/dictionaries/cs.dictionary.itop.core.php @@ -1048,3 +1048,39 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'Class:AsyncTask/Attribute:finalclass' => 'Final class~~', 'Class:AsyncTask/Attribute:finalclass+' => '~~', )); + +// +// Class: AbstractResource +// + +Dict::Add('CS CZ', 'Czech', 'Čeština', array( + 'Class:AbstractResource' => 'Abstract Resource~~', + 'Class:AbstractResource+' => '~~', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('CS CZ', 'Czech', 'Čeština', array( + 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', + 'Class:ResourceAdminMenu+' => '~~', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('CS CZ', 'Czech', 'Čeština', array( + 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', + 'Class:ResourceRunQueriesMenu+' => '~~', +)); + +// +// Class: Action +// + +Dict::Add('CS CZ', 'Czech', 'Čeština', array( + 'Class:ResourceSystemMenu' => 'Resource System Menu~~', + 'Class:ResourceSystemMenu+' => '~~', +)); diff --git a/dictionaries/cs.dictionary.itop.ui.php b/dictionaries/cs.dictionary.itop.ui.php index 1016bf0ab..a876cb021 100755 --- a/dictionaries/cs.dictionary.itop.ui.php +++ b/dictionaries/cs.dictionary.itop.ui.php @@ -420,6 +420,9 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'UI:Button:Insert' => 'Insert~~', 'UI:Button:More' => 'More~~', 'UI:Button:Less' => 'Less~~', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => 'Hledání', 'UI:ClickToCreateNew' => 'Nový objekt (%1$s)', @@ -857,7 +860,6 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'UI:FatalErrorMessage' => 'Závažná chyba, iTop nemůže pokračovat.', 'UI:Error_Details' => 'Chyba: %1$s', - 'UI:PageTitle:ClassProjections' => 'iTop správa uživatelů - projekce tříd', 'UI:PageTitle:ProfileProjections' => 'iTop správa uživatelů - projekce profilů', 'UI:UserManagement:Class' => 'Třída', 'UI:UserManagement:Class+' => 'Třída objektů', @@ -866,8 +868,6 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'UI:UserManagement:AnyObject' => '* jakýkoli *', 'UI:UserManagement:User' => 'Uživatel', 'UI:UserManagement:User+' => 'User zapojený do projekce', - 'UI:UserManagement:Profile' => 'Profil', - 'UI:UserManagement:Profile+' => 'Profil, ve kterém je projekce specifikována', 'UI:UserManagement:Action:Read' => 'Čtení', 'UI:UserManagement:Action:Read+' => 'Čtení/zobrazování objektů', 'UI:UserManagement:Action:Modify' => 'Upravování', @@ -907,7 +907,6 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'UI:ChangeManagementMenu:Title' => 'Přehled změn', 'UI-ChangeManagementMenu-ChangesByType' => 'Změny podle typu', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Změny podle stavu', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Změny podle pracovních skupin', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Nepřidělené změny', 'UI:ConfigurationManagementMenu' => 'Správa konfigurací', @@ -1441,6 +1440,8 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'UI:Button:RemoveImage' => 'Odebrat obrázek', 'UI:UploadNotSupportedInThisMode' => 'Úprava obrázků není v tomto režimu podporována.', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Minimize / Expand~~', 'UI:Search:AutoSubmit:DisabledHint' => 'Auto submit has been disabled for this class~~', @@ -1584,3 +1585,42 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Display messages from %1$s~~', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Display up to %1$s messages in the %2$s menu.~~', )); + +Dict::Add('CS CZ', 'Czech', 'Čeština', array( + 'Menu:DataSources' => 'Zdroje dat pro synchronizaci', + 'Menu:DataSources+' => 'Všechny zdroje dat pro synchronizaci', + 'Menu:WelcomeMenu' => 'Vítejte', + 'Menu:WelcomeMenu+' => 'Vítejte v iTop', + 'Menu:WelcomeMenuPage' => 'Vítejte', + 'Menu:WelcomeMenuPage+' => 'Vítejte v iTop', + 'Menu:AdminTools' => 'Administrace', + 'Menu:AdminTools+' => 'Nástroje pro administraci', + 'Menu:AdminTools?' => 'Nástroje přístupné pouze uživatelům, kteří mají potřbná oprávnění', + 'Menu:DataModelMenu' => 'Datový model', + 'Menu:DataModelMenu+' => 'Přehled datového modelu', + 'Menu:ExportMenu' => 'Exportovat', + 'Menu:ExportMenu+' => 'Exportovat výsledky jakéhokoli dotazu do HTML, CSV nebo XML', + 'Menu:NotificationsMenu' => 'Upozornění', + 'Menu:NotificationsMenu+' => 'Konfigurace upozornění', + 'Menu:AuditCategories' => 'Kategorie auditu', + 'Menu:AuditCategories+' => 'Kategorie auditu', + 'Menu:Notifications:Title' => 'Kategorie auditu', + 'Menu:RunQueriesMenu' => 'Provést dotaz', + 'Menu:RunQueriesMenu+' => 'Provést dotaz', + 'Menu:QueryMenu' => 'Knihovna dotazů', + 'Menu:QueryMenu+' => 'Knihovna dotazů', + 'Menu:UniversalSearchMenu' => 'Univerzální hledání', + 'Menu:UniversalSearchMenu+' => 'Hledejte cokoli...', + 'Menu:UserManagementMenu' => 'Správa uživatelů', + 'Menu:UserManagementMenu+' => 'Správa uživatelů', + 'Menu:ProfilesMenu' => 'Profily (Role)', + 'Menu:ProfilesMenu+' => 'Profily (Role)', + 'Menu:ProfilesMenu:Title' => 'Profily (Role)', + 'Menu:UserAccountsMenu' => 'Uživatelské účty', + 'Menu:UserAccountsMenu+' => 'Uživatelské účty', + 'Menu:UserAccountsMenu:Title' => 'Uživatelské účty', + 'Menu:MyShortcuts' => 'Mé odkazy', + 'Menu:UserManagement' => 'User Management~~', + 'Menu:Queries' => 'Queries~~', + 'Menu:ConfigurationTools' => 'Configuration~~', +)); diff --git a/dictionaries/da.dictionary.itop.core.php b/dictionaries/da.dictionary.itop.core.php index 619451c68..b6c7e7bdc 100644 --- a/dictionaries/da.dictionary.itop.core.php +++ b/dictionaries/da.dictionary.itop.core.php @@ -1046,3 +1046,39 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'Class:AsyncTask/Attribute:finalclass' => 'Final class~~', 'Class:AsyncTask/Attribute:finalclass+' => '~~', )); + +// +// Class: AbstractResource +// + +Dict::Add('DA DA', 'Danish', 'Dansk', array( + 'Class:AbstractResource' => 'Abstract Resource~~', + 'Class:AbstractResource+' => '~~', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('DA DA', 'Danish', 'Dansk', array( + 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', + 'Class:ResourceAdminMenu+' => '~~', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('DA DA', 'Danish', 'Dansk', array( + 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', + 'Class:ResourceRunQueriesMenu+' => '~~', +)); + +// +// Class: Action +// + +Dict::Add('DA DA', 'Danish', 'Dansk', array( + 'Class:ResourceSystemMenu' => 'Resource System Menu~~', + 'Class:ResourceSystemMenu+' => '~~', +)); diff --git a/dictionaries/da.dictionary.itop.ui.php b/dictionaries/da.dictionary.itop.ui.php index c963f2bb9..a9580962e 100644 --- a/dictionaries/da.dictionary.itop.ui.php +++ b/dictionaries/da.dictionary.itop.ui.php @@ -407,6 +407,9 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'UI:Button:Insert' => 'Insert~~', 'UI:Button:More' => 'More~~', 'UI:Button:Less' => 'Less~~', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => 'Søg', 'UI:ClickToCreateNew' => 'Opret nyt objekt af typen %1$s ', @@ -844,7 +847,6 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'UI:FatalErrorMessage' => 'Fatal fejl, iTop kan ikke fortsætte.', 'UI:Error_Details' => 'Fejl: %1$s.', - 'UI:PageTitle:ClassProjections' => 'iTop bruger styring - klasse projection', 'UI:PageTitle:ProfileProjections' => 'iTop bruger styring - profil projection', 'UI:UserManagement:Class' => 'Klasse', 'UI:UserManagement:Class+' => '', @@ -853,8 +855,6 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'UI:UserManagement:AnyObject' => '* enhver *', 'UI:UserManagement:User' => 'Bruger', 'UI:UserManagement:User+' => '', - 'UI:UserManagement:Profile' => 'Profil', - 'UI:UserManagement:Profile+' => '', 'UI:UserManagement:Action:Read' => 'Læs', 'UI:UserManagement:Action:Read+' => '', 'UI:UserManagement:Action:Modify' => 'Ændring', @@ -894,7 +894,6 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'UI:ChangeManagementMenu:Title' => 'Changes Overblik', 'UI-ChangeManagementMenu-ChangesByType' => 'Changes efter type', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Changes efter status', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Changes efter workgroup', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Changes endnu ikke tildelt', 'UI:ConfigurationManagementMenu' => 'Configuration Management', @@ -1430,6 +1429,8 @@ Ved tilknytningen til en trigger, bliver hver handling tildelt et "rækkefølge" 'UI:Button:RemoveImage' => 'Remove the image~~', 'UI:UploadNotSupportedInThisMode' => 'The modification of images or files is not supported in this mode.~~', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Minimize / Expand~~', 'UI:Search:AutoSubmit:DisabledHint' => 'Auto submit has been disabled for this class~~', @@ -1573,3 +1574,42 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Display messages from %1$s~~', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Display up to %1$s messages in the %2$s menu.~~', )); + +Dict::Add('DA DA', 'Danish', 'Dansk', array( + 'Menu:DataSources' => 'Synkroniserings Data Kilder', + 'Menu:DataSources+' => 'All Synchronization Data Sources~~', + 'Menu:WelcomeMenu' => 'Velkomen', + 'Menu:WelcomeMenu+' => 'Velkommen til iTop', + 'Menu:WelcomeMenuPage' => 'Velkomen', + 'Menu:WelcomeMenuPage+' => 'Velkommen til iTop', + 'Menu:AdminTools' => 'Admin værktøjer', + 'Menu:AdminTools+' => 'Administration tools~~', + 'Menu:AdminTools?' => 'Værktøjer kun tilgængelige for brugere med administrator profil', + 'Menu:DataModelMenu' => 'Data Model~~', + 'Menu:DataModelMenu+' => 'Overview of the Data Model~~', + 'Menu:ExportMenu' => 'Export~~', + 'Menu:ExportMenu+' => 'Export the results of any query in HTML, CSV or XML~~', + 'Menu:NotificationsMenu' => 'Notifikationer', + 'Menu:NotificationsMenu+' => 'Configuration of the Notifications~~', + 'Menu:AuditCategories' => 'Audit Kategorier', + 'Menu:AuditCategories+' => 'Audit Categories~~', + 'Menu:Notifications:Title' => 'Audit Kategorier', + 'Menu:RunQueriesMenu' => 'Kør forespørgsler', + 'Menu:RunQueriesMenu+' => 'Run any query~~', + 'Menu:QueryMenu' => 'Query parlør', + 'Menu:QueryMenu+' => 'Query phrasebook~~', + 'Menu:UniversalSearchMenu' => 'Universal Søgning', + 'Menu:UniversalSearchMenu+' => 'Search for anything...~~', + 'Menu:UserManagementMenu' => 'Bruger styring', + 'Menu:UserManagementMenu+' => 'User management~~', + 'Menu:ProfilesMenu' => 'Profiler', + 'Menu:ProfilesMenu+' => 'Profiles~~', + 'Menu:ProfilesMenu:Title' => 'Profiler', + 'Menu:UserAccountsMenu' => 'Bruger konti', + 'Menu:UserAccountsMenu+' => 'User Accounts~~', + 'Menu:UserAccountsMenu:Title' => 'Bruger konti', + 'Menu:MyShortcuts' => 'Mine Genveje', + 'Menu:UserManagement' => 'User Management~~', + 'Menu:Queries' => 'Queries~~', + 'Menu:ConfigurationTools' => 'Configuration~~', +)); diff --git a/dictionaries/de.dictionary.itop.core.php b/dictionaries/de.dictionary.itop.core.php index 720df9d69..567ceb1b4 100644 --- a/dictionaries/de.dictionary.itop.core.php +++ b/dictionaries/de.dictionary.itop.core.php @@ -16,7 +16,7 @@ // 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 Stephan Rosenke <stephan.rosenke@itomig.de> + * @author Stephan Rosenke <stephan.rosenke@itomig.de>, <martin.raenker@itomig.de> * @copyright Copyright (C) 2010-2017 Combodo SARL * @licence http://opensource.org/licenses/AGPL-3.0 */ @@ -1045,3 +1045,39 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:AsyncTask/Attribute:finalclass' => 'Final Class', 'Class:AsyncTask/Attribute:finalclass+' => '', )); + +// +// Class: AbstractResource +// + +Dict::Add('DE DE', 'German', 'Deutsch', array( + 'Class:AbstractResource' => 'Abstrakte Ressource', + 'Class:AbstractResource+' => '', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('DE DE', 'German', 'Deutsch', array( + 'Class:ResourceAdminMenu' => 'Ressource "Admin Menü"', + 'Class:ResourceAdminMenu+' => '', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('DE DE', 'German', 'Deutsch', array( + 'Class:ResourceRunQueriesMenu' => 'Ressource "Abfrage Ausführen Menü"', + 'Class:ResourceRunQueriesMenu+' => '', +)); + +// +// Class: Action +// + +Dict::Add('DE DE', 'German', 'Deutsch', array( + 'Class:ResourceSystemMenu' => 'Ressource "System Menü"', + 'Class:ResourceSystemMenu+' => '', +)); diff --git a/dictionaries/de.dictionary.itop.ui.php b/dictionaries/de.dictionary.itop.ui.php index 7728a33c4..65e9ef506 100644 --- a/dictionaries/de.dictionary.itop.ui.php +++ b/dictionaries/de.dictionary.itop.ui.php @@ -16,7 +16,7 @@ // 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 Stephan Rosenke <stephan.rosenke@itomig.de> + * @author Stephan Rosenke <stephan.rosenke@itomig.de>, <martin.raenker@itomig.de> * @copyright Copyright (C) 2010-2017 Combodo SARL * @licence http://opensource.org/licenses/AGPL-3.0 */ @@ -406,6 +406,9 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'UI:Button:Insert' => 'Einfügen', 'UI:Button:More' => 'Mehr', 'UI:Button:Less' => 'Weniger', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => 'Suche', 'UI:ClickToCreateNew' => 'Klicken Sie hier, um eine neues Objekt vom Typ %1$s zu erstellen', @@ -843,7 +846,6 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'UI:FatalErrorMessage' => 'Fataler Fehler! iTop kann leider nicht fortfahren.', 'UI:Error_Details' => 'Fehler: %1$s.', - 'UI:PageTitle:ClassProjections' => 'iTop Benutzerverwaltung - Klassenabbildung', 'UI:PageTitle:ProfileProjections' => 'iTop Benutzerverwaltung - Profilabbildung', 'UI:UserManagement:Class' => 'Klasse', 'UI:UserManagement:Class+' => 'Klasse von Objekten', @@ -852,8 +854,6 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'UI:UserManagement:AnyObject' => '* beliebig *', 'UI:UserManagement:User' => 'Benutzer', 'UI:UserManagement:User+' => 'Benutzer, der in Abbildung beteiligt ist.', - 'UI:UserManagement:Profile' => 'Profil', - 'UI:UserManagement:Profile+' => 'Profil, in welchem die Abbildung spezifiziert wird.', 'UI:UserManagement:Action:Read' => 'Lesen', 'UI:UserManagement:Action:Read+' => 'Lesen/Anzeigen von Objekten', 'UI:UserManagement:Action:Modify' => 'Verändern', @@ -893,7 +893,6 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'UI:ChangeManagementMenu:Title' => 'Übersicht an Changes', 'UI-ChangeManagementMenu-ChangesByType' => 'Changes ~nach Typ', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Changes nach Status', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Changes nach Arbeitsgruppen', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Changes, die noch nicht zugeordnet wurden', 'UI:ConfigurationManagementMenu' => 'Configuration Management', @@ -1428,6 +1427,8 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm 'UI:Button:RemoveImage' => 'Bild löschen', 'UI:UploadNotSupportedInThisMode' => 'Die Modifizierung von Bildern oder Dateien wird in diesem Modus nicht unterstützt.', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Ein-/Ausklappen', 'UI:Search:AutoSubmit:DisabledHint' => 'Automatische Eingabe für diese Klasse deaktiviert', @@ -1571,3 +1572,42 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Nachrichten von %1$s anzeigen', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Zeigen Sie höchstens %1$s Beiträge im Menü (%2$s) an.', )); + +Dict::Add('DE DE', 'German', 'Deutsch', array( + 'Menu:DataSources' => 'Datenquellen für die Synchronisation', + 'Menu:DataSources+' => 'Alle Datenquellen für die Synchronisation', + 'Menu:WelcomeMenu' => 'Willkommen', + 'Menu:WelcomeMenu+' => 'Willkommen bei iTop', + 'Menu:WelcomeMenuPage' => 'Willkommen', + 'Menu:WelcomeMenuPage+' => 'Willkommen bei iTop', + 'Menu:AdminTools' => 'Admin-Tools', + 'Menu:AdminTools+' => 'Administrationswerkzeuge', + 'Menu:AdminTools?' => 'Werkzeuge, die nur für Benutzer mit Adminstratorprofil zugänglich sind', + 'Menu:DataModelMenu' => 'Datenmodell', + 'Menu:DataModelMenu+' => 'Übersicht des Datenmodells', + 'Menu:ExportMenu' => 'Export', + 'Menu:ExportMenu+' => 'Export einer beliebigen Abfrage in HTML, CSV oder XML', + 'Menu:NotificationsMenu' => 'Benachrichtigungen', + 'Menu:NotificationsMenu+' => 'Einstellungen der Benachrichtigungen', + 'Menu:AuditCategories' => 'Audit-Kategorien', + 'Menu:AuditCategories+' => 'Audit-Kategorien', + 'Menu:Notifications:Title' => 'Audit-Kategorien', + 'Menu:RunQueriesMenu' => 'Abfrage ausführen', + 'Menu:RunQueriesMenu+' => 'Eine beliebige Abfrage ausführen', + 'Menu:QueryMenu' => 'Query-Bibliothek', + 'Menu:QueryMenu+' => '', + 'Menu:UniversalSearchMenu' => 'Universelle Suche', + 'Menu:UniversalSearchMenu+' => 'Suchen Sie nach beliebigen Inhalt...', + 'Menu:UserManagementMenu' => 'Benutzerverwaltung', + 'Menu:UserManagementMenu+' => 'Benutzerverwaltung', + 'Menu:ProfilesMenu' => 'Profile', + 'Menu:ProfilesMenu+' => 'Profile', + 'Menu:ProfilesMenu:Title' => 'Profile', + 'Menu:UserAccountsMenu' => 'Benutzerkonten', + 'Menu:UserAccountsMenu+' => 'Benutzerkonten', + 'Menu:UserAccountsMenu:Title' => 'Benutzerkonten', + 'Menu:MyShortcuts' => 'Meine Shortcuts', + 'Menu:UserManagement' => 'Benutzerverwaltung', + 'Menu:Queries' => 'OQL Abfragen', + 'Menu:ConfigurationTools' => 'Konfiguration', +)); diff --git a/dictionaries/en.dictionary.itop.core.php b/dictionaries/en.dictionary.itop.core.php index 38214190e..efda67374 100644 --- a/dictionaries/en.dictionary.itop.core.php +++ b/dictionaries/en.dictionary.itop.core.php @@ -1046,3 +1046,39 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:AsyncTask/Attribute:finalclass' => 'Final class', 'Class:AsyncTask/Attribute:finalclass+' => '', )); + +// +// Class: AbstractResource +// + +Dict::Add('EN US', 'English', 'English', array( + 'Class:AbstractResource' => 'Abstract Resource', + 'Class:AbstractResource+' => '', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('EN US', 'English', 'English', array( + 'Class:ResourceAdminMenu' => 'Resource Admin Menu', + 'Class:ResourceAdminMenu+' => '', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('EN US', 'English', 'English', array( + 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu', + 'Class:ResourceRunQueriesMenu+' => '', +)); + +// +// Class: Action +// + +Dict::Add('EN US', 'English', 'English', array( + 'Class:ResourceSystemMenu' => 'Resource System Menu', + 'Class:ResourceSystemMenu+' => '', +)); diff --git a/dictionaries/en.dictionary.itop.ui.php b/dictionaries/en.dictionary.itop.ui.php index a9852d686..05746117c 100644 --- a/dictionaries/en.dictionary.itop.ui.php +++ b/dictionaries/en.dictionary.itop.ui.php @@ -460,7 +460,7 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:Error:BulkModifyNotAllowedOn_Class' => 'You are not allowed to perform a bulk update of objects of class %1$s', 'UI:Error:ObjectAlreadyCloned' => 'Error: the object has already been cloned!', 'UI:Error:ObjectAlreadyCreated' => 'Error: the object has already been created!', - 'UI:Error:Invalid_Stimulus_On_Object_In_State' => 'Error: invalid stimulus "%1$s" on object %2$s in state "%3$s".', + 'UI:Error:Invalid_Stimulus_On_Object_In_State' => '%2$s is currently in the "%3$s" state, the "%1$s" operation you requested could not be applied.', 'UI:Error:InvalidDashboardFile' => 'Error: invalid dashboard file', 'UI:Error:InvalidDashboard' => 'Error: invalid dashboard', 'UI:Error:MaintenanceMode' => 'Application is currently in maintenance', @@ -863,7 +863,6 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:FatalErrorMessage' => 'Fatal error, '.ITOP_APPLICATION_SHORT.' cannot continue.', 'UI:Error_Details' => 'Error: %1$s.', - 'UI:PageTitle:ClassProjections' => ITOP_APPLICATION_SHORT.' user management - class projections', 'UI:PageTitle:ProfileProjections' => ITOP_APPLICATION_SHORT.' user management - profile projections', 'UI:UserManagement:Class' => 'Class', 'UI:UserManagement:Class+' => 'Class of objects', @@ -872,8 +871,6 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:UserManagement:AnyObject' => '* any *', 'UI:UserManagement:User' => 'User', 'UI:UserManagement:User+' => 'User involved in the projection', - 'UI:UserManagement:Profile' => 'Profile', - 'UI:UserManagement:Profile+' => 'Profile in which the projection is specified', 'UI:UserManagement:Action:Read' => 'Read', 'UI:UserManagement:Action:Read+' => 'Read/display objects', 'UI:UserManagement:Action:Modify' => 'Modify', @@ -913,7 +910,6 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:ChangeManagementMenu:Title' => 'Changes Overview', 'UI-ChangeManagementMenu-ChangesByType' => 'Changes by type', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Changes by status', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Changes by workgroup', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Changes not yet assigned', 'UI:ConfigurationManagementMenu' => 'Configuration Management', @@ -1595,3 +1591,43 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Display messages from %1$s', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Display up to %1$s messages in the %2$s menu.', )); + + +Dict::Add('EN US', 'English', 'English', array( + 'Menu:DataSources' => 'Synchronization Data Sources', + 'Menu:DataSources+' => 'All Synchronization Data Sources', + 'Menu:WelcomeMenu' => 'Welcome', + 'Menu:WelcomeMenu+' => 'Welcome to iTop', + 'Menu:WelcomeMenuPage' => 'Welcome', + 'Menu:WelcomeMenuPage+' => 'Welcome to iTop', + 'Menu:AdminTools' => 'Administration', + 'Menu:AdminTools+' => 'Administration tools', + 'Menu:AdminTools?' => 'Tools accessible only to users having the administrator profile', + 'Menu:DataModelMenu' => 'Data Model', + 'Menu:DataModelMenu+' => 'Overview of the Data Model', + 'Menu:ExportMenu' => 'Export', + 'Menu:ExportMenu+' => 'Export the results of any query in HTML, CSV or XML', + 'Menu:NotificationsMenu' => 'Notifications', + 'Menu:NotificationsMenu+' => 'Configuration of the Notifications', + 'Menu:AuditCategories' => 'Audit Categories', + 'Menu:AuditCategories+' => 'Audit Categories', + 'Menu:Notifications:Title' => 'Audit Categories', + 'Menu:RunQueriesMenu' => 'Run Queries', + 'Menu:RunQueriesMenu+' => 'Run any query', + 'Menu:QueryMenu' => 'Query phrasebook', + 'Menu:QueryMenu+' => 'Query phrasebook', + 'Menu:UniversalSearchMenu' => 'Universal Search', + 'Menu:UniversalSearchMenu+' => 'Search for anything...', + 'Menu:UserManagementMenu' => 'User Management', + 'Menu:UserManagementMenu+' => 'User management', + 'Menu:ProfilesMenu' => 'Profiles', + 'Menu:ProfilesMenu+' => 'Profiles', + 'Menu:ProfilesMenu:Title' => 'Profiles', + 'Menu:UserAccountsMenu' => 'User Accounts', + 'Menu:UserAccountsMenu+' => 'User Accounts', + 'Menu:UserAccountsMenu:Title' => 'User Accounts', + 'Menu:MyShortcuts' => 'My Shortcuts', + 'Menu:UserManagement' => 'User Management', + 'Menu:Queries' => 'Queries', + 'Menu:ConfigurationTools' => 'Configuration', +)); diff --git a/dictionaries/es_cr.dictionary.itop.core.php b/dictionaries/es_cr.dictionary.itop.core.php index c0d645a24..bbfc2006c 100644 --- a/dictionaries/es_cr.dictionary.itop.core.php +++ b/dictionaries/es_cr.dictionary.itop.core.php @@ -1046,3 +1046,39 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'Class:AsyncTask/Attribute:finalclass' => 'Clase', 'Class:AsyncTask/Attribute:finalclass+' => 'Clase', )); + +// +// Class: AbstractResource +// + +Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( + 'Class:AbstractResource' => 'Recurso abstracto', + 'Class:AbstractResource+' => '~~', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( + 'Class:ResourceAdminMenu' => 'Recursos de Menú de Administración', + 'Class:ResourceAdminMenu+' => '~~', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( + 'Class:ResourceRunQueriesMenu' => 'Recurso Libreta de Consultas', + 'Class:ResourceRunQueriesMenu+' => '~~', +)); + +// +// Class: Action +// + +Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( + 'Class:ResourceSystemMenu' => 'Recurso Menú de Sistema', + 'Class:ResourceSystemMenu+' => '~~', +)); diff --git a/dictionaries/es_cr.dictionary.itop.ui.php b/dictionaries/es_cr.dictionary.itop.ui.php index 514ee398d..0e5699ad1 100644 --- a/dictionaries/es_cr.dictionary.itop.ui.php +++ b/dictionaries/es_cr.dictionary.itop.ui.php @@ -418,6 +418,9 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'UI:Button:Insert' => 'Insertar', 'UI:Button:More' => 'Más', 'UI:Button:Less' => 'Menos', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => 'Buscar', 'UI:ClickToCreateNew' => 'Crear %1$s', @@ -855,7 +858,6 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'UI:FatalErrorMessage' => 'Error fatal, iTop no puede continuar.', 'UI:Error_Details' => 'Error: %1$s.', - 'UI:PageTitle:ClassProjections' => 'Administración de usuarios iTop - proyecciones de clases', 'UI:PageTitle:ProfileProjections' => 'Administración de usuarios iTop - proyecciones de Perfil', 'UI:UserManagement:Class' => 'Clase', 'UI:UserManagement:Class+' => 'Clase de objetos', @@ -864,8 +866,6 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'UI:UserManagement:AnyObject' => '* cualquiera *', 'UI:UserManagement:User' => 'Usuario', 'UI:UserManagement:User+' => 'Usuario implicado en la proyección', - 'UI:UserManagement:Profile' => 'Perfil', - 'UI:UserManagement:Profile+' => 'Perfil en el cual se especifico la proyección', 'UI:UserManagement:Action:Read' => 'Leer', 'UI:UserManagement:Action:Read+' => 'Leer/Mostrar objetos', 'UI:UserManagement:Action:Modify' => 'Modificar', @@ -905,7 +905,6 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'UI:ChangeManagementMenu:Title' => 'Resumen de Cambios', 'UI-ChangeManagementMenu-ChangesByType' => 'Cambios por Tipo', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Cambios por Estatus', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Cambios por Grupo de Trabajo', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Cambios No Asignados Aún', 'UI:ConfigurationManagementMenu' => 'Administración de la Configuración', @@ -1443,6 +1442,8 @@ Cuando se asocien con un disparador, cada acción recibe un número de "orden", 'UI:Button:RemoveImage' => 'Remover imágen', 'UI:UploadNotSupportedInThisMode' => 'La modificación de imágenes o archivos no está soportado en este modo.', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Minimizar/ Expandir', 'UI:Search:AutoSubmit:DisabledHint' => 'Auto enviar ha sido deshabilitado para esta clase', @@ -1586,3 +1587,42 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Display messages from %1$s~~', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Display up to %1$s messages in the %2$s menu.~~', )); + +Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( + 'Menu:DataSources' => 'Fuentes de Datos Sincronizables', + 'Menu:DataSources+' => 'Fuentes de Datos Sincronizables', + 'Menu:WelcomeMenu' => 'Bienvenido', + 'Menu:WelcomeMenu+' => 'Bienvenido a iTop', + 'Menu:WelcomeMenuPage' => 'Bienvenido', + 'Menu:WelcomeMenuPage+' => 'Bienvenido a iTop', + 'Menu:AdminTools' => 'Herramientas Administrativas', + 'Menu:AdminTools+' => 'Herramientas Administrativas', + 'Menu:AdminTools?' => 'Herramientas accesibles sólo a usuarios con Perfil de administrador', + 'Menu:DataModelMenu' => 'Modelo de Datos', + 'Menu:DataModelMenu+' => 'Resumen del Modelo de Datos', + 'Menu:ExportMenu' => 'Exportar', + 'Menu:ExportMenu+' => 'Exportar los Resultados de Cualquier Consulta en HTML, CSV o XML', + 'Menu:NotificationsMenu' => 'Notificaciones', + 'Menu:NotificationsMenu+' => 'Configuración de las Notificaciones', + 'Menu:AuditCategories' => 'Auditar Categorías', + 'Menu:AuditCategories+' => 'Auditar Categorías', + 'Menu:Notifications:Title' => 'Auditar Categorías', + 'Menu:RunQueriesMenu' => 'Ejecutar Consultas', + 'Menu:RunQueriesMenu+' => 'Ejecutar Cualquier Consulta', + 'Menu:QueryMenu' => 'Libreta de Consultas', + 'Menu:QueryMenu+' => 'Libreta de Consultas', + 'Menu:UniversalSearchMenu' => 'Búsqueda Universal', + 'Menu:UniversalSearchMenu+' => 'Buscar cualquier cosa', + 'Menu:UserManagementMenu' => 'Administración de Usuarios', + 'Menu:UserManagementMenu+' => 'Administración de Usuarios', + 'Menu:ProfilesMenu' => 'Perfiles', + 'Menu:ProfilesMenu+' => 'Perfiles', + 'Menu:ProfilesMenu:Title' => 'Perfiles', + 'Menu:UserAccountsMenu' => 'Cuentas de Usuario', + 'Menu:UserAccountsMenu+' => 'Cuentas de Usuario', + 'Menu:UserAccountsMenu:Title' => 'Cuentas de Usuario', + 'Menu:MyShortcuts' => 'Mis Accesos Rápidos', + 'Menu:UserManagement' => 'Gestión de usuarios', + 'Menu:Queries' => 'Consultas', + 'Menu:ConfigurationTools' => 'Configuración', +)); diff --git a/dictionaries/fr.dictionary.itop.core.php b/dictionaries/fr.dictionary.itop.core.php index e4d60f306..11170e532 100644 --- a/dictionaries/fr.dictionary.itop.core.php +++ b/dictionaries/fr.dictionary.itop.core.php @@ -1054,3 +1054,39 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Core:Context=CRON+' => 'cron', 'Core:Context=GUI:Portal+' => 'GUI:Portal', )); + +// +// Class: AbstractResource +// + +Dict::Add('FR FR', 'French', 'Français', array( + 'Class:AbstractResource' => 'Ressource', + 'Class:AbstractResource+' => '', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('FR FR', 'French', 'Français', array( + 'Class:ResourceAdminMenu' => 'Menu Administration', + 'Class:ResourceAdminMenu+' => '', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('FR FR', 'French', 'Français', array( + 'Class:ResourceRunQueriesMenu' => 'Menu Requête OQL', + 'Class:ResourceRunQueriesMenu+' => '', +)); + +// +// Class: Action +// + +Dict::Add('FR FR', 'French', 'Français', array( + 'Class:ResourceSystemMenu' => 'Menu System', + 'Class:ResourceSystemMenu+' => '', +)); diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index 3fcd9000f..662c8d3f4 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -406,6 +406,8 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:Button:More' => 'Plus', 'UI:Button:Less' => 'Moins', 'UI:Button:Wait' => 'Patientez pendant la mise à jour des champs', + 'UI:Treeview:CollapseAll' => 'Tout replier', + 'UI:Treeview:ExpandAll' => 'Tout déplier', 'UI:SearchToggle' => 'Recherche', 'UI:ClickToCreateNew' => 'Créer un nouvel objet de type %1$s', @@ -441,7 +443,7 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:Error:BulkModifyNotAllowedOn_Class' => 'Vous n\'êtes pas autorisé à faire une modification massive sur les objets de type %1$s', 'UI:Error:ObjectAlreadyCloned' => 'Erreur: l\'objet a déjà été dupliqué !', 'UI:Error:ObjectAlreadyCreated' => 'Erreur: l\'objet a déjà été créé !', - 'UI:Error:Invalid_Stimulus_On_Object_In_State' => 'Erreur: le stimulus "%1$s" n\'est pas valide pour l\'objet %2$s dans l\'état "%3$s".', + 'UI:Error:Invalid_Stimulus_On_Object_In_State' => '%2$s est actuellement dans l\'état "%3$s", l\'opération "%1$s" que vous avez demandée ne peut être appliquée.', 'UI:Error:InvalidDashboardFile' => 'Erreur: Le fichier tableau de bord est invalide', 'UI:Error:InvalidDashboard' => 'Erreur: Le tableau de bord est invalide', 'UI:Error:MaintenanceMode' => 'L\'application est en maintenance', @@ -843,7 +845,6 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:FatalErrorMessage' => 'Erreur fatale, iTop ne peut pas continuer.', 'UI:Error_Details' => 'Erreur: %1$s.', - 'UI:PageTitle:ClassProjections' => 'iTop gestion des utilisateurs - projections des classes', 'UI:PageTitle:ProfileProjections' => 'iTop gestion des utilisateurs - projections des profils', 'UI:UserManagement:Class' => 'Type', 'UI:UserManagement:Class+' => 'Type des objets', @@ -852,8 +853,6 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:UserManagement:AnyObject' => '* indifférent *', 'UI:UserManagement:User' => 'Utilisateur', 'UI:UserManagement:User+' => 'L\'utilisateur', - 'UI:UserManagement:Profile' => 'Profil', - 'UI:UserManagement:Profile+' => 'Profil dans lequel la projection est définie', 'UI:UserManagement:Action:Read' => 'Lecture', 'UI:UserManagement:Action:Read+' => 'Lecture et affichage d\'un objet', 'UI:UserManagement:Action:Modify' => 'Modification', @@ -893,7 +892,6 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:ChangeManagementMenu:Title' => 'Résumé des changements', 'UI-ChangeManagementMenu-ChangesByType' => 'Changements par type', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Changements par état', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Changements par workgroup', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Changements en attente d\'assignation', 'UI:ConfigurationManagementMenu' => 'Gestion de Configuration', @@ -1573,3 +1571,42 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Afficher les messages de %1$s', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Afficher au plus %1$s messages dans le menu %2$s.', )); + +Dict::Add('FR FR', 'French', 'Français', array( + 'Menu:DataSources' => 'Synchronisation', + 'Menu:DataSources+' => '', + 'Menu:WelcomeMenu' => 'Bienvenue', + 'Menu:WelcomeMenu+' => 'Bienvenue dans iTop', + 'Menu:WelcomeMenuPage' => 'Bienvenue', + 'Menu:WelcomeMenuPage+' => 'Bienvenue dans iTop', + 'Menu:AdminTools' => 'Administration', + 'Menu:AdminTools+' => 'Outils d\'administration', + 'Menu:AdminTools?' => 'Ces outils sont accessibles uniquement aux utilisateurs possédant le profil Administrateur.', + 'Menu:DataModelMenu' => 'Modèle de Données', + 'Menu:DataModelMenu+' => 'Résumé du Modèle de Données', + 'Menu:ExportMenu' => 'Export', + 'Menu:ExportMenu+' => 'Export des résultats d\'une requête en HTML, CSV ou XML', + 'Menu:NotificationsMenu' => 'Notifications', + 'Menu:NotificationsMenu+' => 'Configuration des Notifications', + 'Menu:AuditCategories' => 'Catégories d\'audit', + 'Menu:AuditCategories+' => 'Catégories d\'audit', + 'Menu:Notifications:Title' => 'Catégories d\'audit', + 'Menu:RunQueriesMenu' => 'Requêtes OQL', + 'Menu:RunQueriesMenu+' => 'Executer une requête OQL', + 'Menu:QueryMenu' => 'Livre des requêtes', + 'Menu:QueryMenu+' => 'Livre des requêtes', + 'Menu:UniversalSearchMenu' => 'Recherche Universelle', + 'Menu:UniversalSearchMenu+' => 'Rechercher n\'importe quel objet...', + 'Menu:UserManagementMenu' => 'Gestion des Utilisateurs', + 'Menu:UserManagementMenu+' => 'Gestion des Utilisateurs', + 'Menu:ProfilesMenu' => 'Profils', + 'Menu:ProfilesMenu+' => 'Profils', + 'Menu:ProfilesMenu:Title' => 'Profils', + 'Menu:UserAccountsMenu' => 'Comptes Utilisateurs', + 'Menu:UserAccountsMenu+' => 'Comptes Utilisateurs', + 'Menu:UserAccountsMenu:Title' => 'Comptes Utilisateurs', + 'Menu:MyShortcuts' => 'Mes raccourcis', + 'Menu:UserManagement' => 'Utilisateurs', + 'Menu:Queries' => 'Requêtes', + 'Menu:ConfigurationTools' => 'Configuration', +)); diff --git a/dictionaries/hu.dictionary.itop.core.php b/dictionaries/hu.dictionary.itop.core.php index bc42eae22..38cad2b89 100755 --- a/dictionaries/hu.dictionary.itop.core.php +++ b/dictionaries/hu.dictionary.itop.core.php @@ -1044,3 +1044,39 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'Class:AsyncTask/Attribute:finalclass' => 'Final class~~', 'Class:AsyncTask/Attribute:finalclass+' => '~~', )); + +// +// Class: AbstractResource +// + +Dict::Add('HU HU', 'Hungarian', 'Magyar', array( + 'Class:AbstractResource' => 'Abstract Resource~~', + 'Class:AbstractResource+' => '~~', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('HU HU', 'Hungarian', 'Magyar', array( + 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', + 'Class:ResourceAdminMenu+' => '~~', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('HU HU', 'Hungarian', 'Magyar', array( + 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', + 'Class:ResourceRunQueriesMenu+' => '~~', +)); + +// +// Class: Action +// + +Dict::Add('HU HU', 'Hungarian', 'Magyar', array( + 'Class:ResourceSystemMenu' => 'Resource System Menu~~', + 'Class:ResourceSystemMenu+' => '~~', +)); diff --git a/dictionaries/hu.dictionary.itop.ui.php b/dictionaries/hu.dictionary.itop.ui.php index 488306153..add7c54a1 100755 --- a/dictionaries/hu.dictionary.itop.ui.php +++ b/dictionaries/hu.dictionary.itop.ui.php @@ -405,6 +405,9 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'UI:Button:Insert' => 'Insert~~', 'UI:Button:More' => 'More~~', 'UI:Button:Less' => 'Less~~', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => 'Keresés', 'UI:ClickToCreateNew' => 'Új %1$s létrehozása', @@ -842,7 +845,6 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'UI:FatalErrorMessage' => 'Fatális hiba, iTop nem tudja a működését folytatni', 'UI:Error_Details' => 'Hiba: %1$s.', - 'UI:PageTitle:ClassProjections' => 'iTop felhasználó menedzsmet - osztály nézet', 'UI:PageTitle:ProfileProjections' => 'iTop felhasználó menedzsmet - profil nézet', 'UI:UserManagement:Class' => 'Osztály', 'UI:UserManagement:Class+' => '', @@ -851,8 +853,6 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'UI:UserManagement:AnyObject' => '* any *', 'UI:UserManagement:User' => 'Felhasználó', 'UI:UserManagement:User+' => '', - 'UI:UserManagement:Profile' => 'Profil', - 'UI:UserManagement:Profile+' => '', 'UI:UserManagement:Action:Read' => 'Olvas', 'UI:UserManagement:Action:Read+' => '', 'UI:UserManagement:Action:Modify' => 'Módosít', @@ -892,7 +892,6 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'UI:ChangeManagementMenu:Title' => 'Változás áttekintése', 'UI-ChangeManagementMenu-ChangesByType' => 'Változások típusok szerint', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Változások státusz szerint', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Változások csoportok szerint', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Még nem kiosztott változások', 'UI:ConfigurationManagementMenu' => 'Konfiguráció menedzsment', @@ -1427,6 +1426,8 @@ Akció kiváltó okhoz rendelésekor kap egy sorszámot , amely meghatározza az 'UI:Button:RemoveImage' => 'Remove the image~~', 'UI:UploadNotSupportedInThisMode' => 'The modification of images or files is not supported in this mode.~~', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Minimize / Expand~~', 'UI:Search:AutoSubmit:DisabledHint' => 'Auto submit has been disabled for this class~~', @@ -1570,3 +1571,42 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Display messages from %1$s~~', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Display up to %1$s messages in the %2$s menu.~~', )); + +Dict::Add('HU HU', 'Hungarian', 'Magyar', array( + 'Menu:DataSources' => 'Szinkronizált adatforrások', + 'Menu:DataSources+' => '', + 'Menu:WelcomeMenu' => 'Üdvözlöm', + 'Menu:WelcomeMenu+' => '', + 'Menu:WelcomeMenuPage' => 'Üdvözlöm', + 'Menu:WelcomeMenuPage+' => '', + 'Menu:AdminTools' => 'Adminisztrációs eszközök', + 'Menu:AdminTools+' => '', + 'Menu:AdminTools?' => 'Eszközök csak az adminisztrátori profilhoz rendlet felhasználók számára elérhetők.', + 'Menu:DataModelMenu' => 'Adatmodell', + 'Menu:DataModelMenu+' => '', + 'Menu:ExportMenu' => 'Export', + 'Menu:ExportMenu+' => '', + 'Menu:NotificationsMenu' => 'Értesítések', + 'Menu:NotificationsMenu+' => '', + 'Menu:AuditCategories' => 'Audit kategóriák', + 'Menu:AuditCategories+' => '', + 'Menu:Notifications:Title' => 'Audit kategóriák', + 'Menu:RunQueriesMenu' => 'Lekérdezés futtatás', + 'Menu:RunQueriesMenu+' => '', + 'Menu:QueryMenu' => 'Query phrasebook~~', + 'Menu:QueryMenu+' => 'Query phrasebook~~', + 'Menu:UniversalSearchMenu' => 'Univerzális keresés', + 'Menu:UniversalSearchMenu+' => '', + 'Menu:UserManagementMenu' => 'Felhasználó menedzsment', + 'Menu:UserManagementMenu+' => '', + 'Menu:ProfilesMenu' => 'Profilok', + 'Menu:ProfilesMenu+' => '', + 'Menu:ProfilesMenu:Title' => 'Profilok', + 'Menu:UserAccountsMenu' => 'Felhasználói fiókok', + 'Menu:UserAccountsMenu+' => '', + 'Menu:UserAccountsMenu:Title' => 'Felhasználói fiókok', + 'Menu:MyShortcuts' => 'My Shortcuts~~', + 'Menu:UserManagement' => 'User Management~~', + 'Menu:Queries' => 'Queries~~', + 'Menu:ConfigurationTools' => 'Configuration~~', +)); diff --git a/dictionaries/it.dictionary.itop.core.php b/dictionaries/it.dictionary.itop.core.php index ea4d21ed0..98b877294 100644 --- a/dictionaries/it.dictionary.itop.core.php +++ b/dictionaries/it.dictionary.itop.core.php @@ -1046,3 +1046,39 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'Class:AsyncTask/Attribute:finalclass' => 'Final class~~', 'Class:AsyncTask/Attribute:finalclass+' => '~~', )); + +// +// Class: AbstractResource +// + +Dict::Add('IT IT', 'Italian', 'Italiano', array( + 'Class:AbstractResource' => 'Abstract Resource~~', + 'Class:AbstractResource+' => '~~', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('IT IT', 'Italian', 'Italiano', array( + 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', + 'Class:ResourceAdminMenu+' => '~~', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('IT IT', 'Italian', 'Italiano', array( + 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', + 'Class:ResourceRunQueriesMenu+' => '~~', +)); + +// +// Class: Action +// + +Dict::Add('IT IT', 'Italian', 'Italiano', array( + 'Class:ResourceSystemMenu' => 'Resource System Menu~~', + 'Class:ResourceSystemMenu+' => '~~', +)); diff --git a/dictionaries/it.dictionary.itop.ui.php b/dictionaries/it.dictionary.itop.ui.php index e567cd02d..f176e1378 100644 --- a/dictionaries/it.dictionary.itop.ui.php +++ b/dictionaries/it.dictionary.itop.ui.php @@ -418,6 +418,9 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'UI:Button:Insert' => 'Insert~~', 'UI:Button:More' => 'More~~', 'UI:Button:Less' => 'Less~~', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => 'Cerca', 'UI:ClickToCreateNew' => 'Crea un nuovo %1$s~~', @@ -855,7 +858,6 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'UI:FatalErrorMessage' => 'Fatal error, iTop non può continuare.', 'UI:Error_Details' => 'Errore: %1$s.', - 'UI:PageTitle:ClassProjections' => 'iTop gestione degli utenti - proiezioni classe', 'UI:PageTitle:ProfileProjections' => 'iTop gestione degli utenti - proiezioni profilo', 'UI:UserManagement:Class' => 'Classe', 'UI:UserManagement:Class+' => '', @@ -864,8 +866,6 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'UI:UserManagement:AnyObject' => '* qualsiasi *', 'UI:UserManagement:User' => 'Utente', 'UI:UserManagement:User+' => '', - 'UI:UserManagement:Profile' => 'Profilo', - 'UI:UserManagement:Profile+' => '', 'UI:UserManagement:Action:Read' => 'Leggi', 'UI:UserManagement:Action:Read+' => '', 'UI:UserManagement:Action:Modify' => 'Modifica', @@ -905,7 +905,6 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'UI:ChangeManagementMenu:Title' => 'Panoramica dei cambi', 'UI-ChangeManagementMenu-ChangesByType' => 'Cambi per tipo', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Cambi per stato', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Cambi per gruppi di lavoro', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Cambi non ancora assegnati', 'UI:ConfigurationManagementMenu' => 'Gestione Configurazione', @@ -1442,6 +1441,8 @@ Quando è associata a un trigger, ad ogni azione è assegnato un numero "ordine" 'UI:Button:RemoveImage' => 'Remove the image~~', 'UI:UploadNotSupportedInThisMode' => 'The modification of images or files is not supported in this mode.~~', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Minimize / Expand~~', 'UI:Search:AutoSubmit:DisabledHint' => 'Auto submit has been disabled for this class~~', @@ -1585,3 +1586,42 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Display messages from %1$s~~', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Display up to %1$s messages in the %2$s menu.~~', )); + +Dict::Add('IT IT', 'Italian', 'Italiano', array( + 'Menu:DataSources' => 'Sorgente di sincronizzazione dei dati', + 'Menu:DataSources+' => '', + 'Menu:WelcomeMenu' => 'Benveuto', + 'Menu:WelcomeMenu+' => '', + 'Menu:WelcomeMenuPage' => 'Benvenuto', + 'Menu:WelcomeMenuPage+' => '', + 'Menu:AdminTools' => 'Strumenti di amministrazione', + 'Menu:AdminTools+' => '', + 'Menu:AdminTools?' => 'Strumenti accessibile solo agli utenti con il profilo di amministratore', + 'Menu:DataModelMenu' => 'Modello Dati', + 'Menu:DataModelMenu+' => '', + 'Menu:ExportMenu' => 'Esporta', + 'Menu:ExportMenu+' => '', + 'Menu:NotificationsMenu' => 'Notifiche', + 'Menu:NotificationsMenu+' => '', + 'Menu:AuditCategories' => 'Categorie di Audit', + 'Menu:AuditCategories+' => '', + 'Menu:Notifications:Title' => 'Categorie di Audit', + 'Menu:RunQueriesMenu' => 'Esegui query', + 'Menu:RunQueriesMenu+' => '', + 'Menu:QueryMenu' => 'Rubbrica delle Query', + 'Menu:QueryMenu+' => 'Rubbrica delle Query', + 'Menu:UniversalSearchMenu' => 'Ricerca universale', + 'Menu:UniversalSearchMenu+' => '', + 'Menu:UserManagementMenu' => 'Gestione degli utenti', + 'Menu:UserManagementMenu+' => '', + 'Menu:ProfilesMenu' => 'Profili', + 'Menu:ProfilesMenu+' => '', + 'Menu:ProfilesMenu:Title' => 'Profili', + 'Menu:UserAccountsMenu' => 'Account utente', + 'Menu:UserAccountsMenu+' => '', + 'Menu:UserAccountsMenu:Title' => 'Account utente', + 'Menu:MyShortcuts' => 'Le mie scorciatoie', + 'Menu:UserManagement' => 'Gestione utenti', + 'Menu:Queries' => 'Interrogazioni', + 'Menu:ConfigurationTools' => 'configurazione', +)); diff --git a/dictionaries/ja.dictionary.itop.core.php b/dictionaries/ja.dictionary.itop.core.php index ee45b3014..613418d4d 100644 --- a/dictionaries/ja.dictionary.itop.core.php +++ b/dictionaries/ja.dictionary.itop.core.php @@ -1044,3 +1044,39 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'Class:AsyncTask/Attribute:finalclass' => 'Final class~~', 'Class:AsyncTask/Attribute:finalclass+' => '~~', )); + +// +// Class: AbstractResource +// + +Dict::Add('JA JP', 'Japanese', '日本語', array( + 'Class:AbstractResource' => 'Abstract Resource~~', + 'Class:AbstractResource+' => '~~', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('JA JP', 'Japanese', '日本語', array( + 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', + 'Class:ResourceAdminMenu+' => '~~', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('JA JP', 'Japanese', '日本語', array( + 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', + 'Class:ResourceRunQueriesMenu+' => '~~', +)); + +// +// Class: Action +// + +Dict::Add('JA JP', 'Japanese', '日本語', array( + 'Class:ResourceSystemMenu' => 'Resource System Menu~~', + 'Class:ResourceSystemMenu+' => '~~', +)); diff --git a/dictionaries/ja.dictionary.itop.ui.php b/dictionaries/ja.dictionary.itop.ui.php index 5c53b3dfe..39644c3ab 100644 --- a/dictionaries/ja.dictionary.itop.ui.php +++ b/dictionaries/ja.dictionary.itop.ui.php @@ -405,6 +405,9 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'UI:Button:Insert' => 'Insert~~', 'UI:Button:More' => 'More~~', 'UI:Button:Less' => 'Less~~', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => '検索(トグル↓↑)', 'UI:ClickToCreateNew' => '新規 %1$s を作成', @@ -842,7 +845,6 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'UI:FatalErrorMessage' => '致命的なエラー、ITOPを続行することはできません。', 'UI:Error_Details' => 'エラー:%1$s', - 'UI:PageTitle:ClassProjections' => 'iTop ユーザ管理 - クラスプロジェクション', 'UI:PageTitle:ProfileProjections' => 'iTop ユーザ管理 - プロフィールプロジェクション', 'UI:UserManagement:Class' => 'クラス', 'UI:UserManagement:Class+' => 'オブジェクトのクラス', @@ -851,8 +853,6 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'UI:UserManagement:AnyObject' => '* 任意 *', 'UI:UserManagement:User' => 'ユーザ', 'UI:UserManagement:User+' => 'このプロジェクションに関与しているユーザー', - 'UI:UserManagement:Profile' => 'プロフィール', - 'UI:UserManagement:Profile+' => 'プロジェクションが指定されているプロフィール', 'UI:UserManagement:Action:Read' => '読み込み', 'UI:UserManagement:Action:Read+' => 'オブジェクトの読み込み/表示', 'UI:UserManagement:Action:Modify' => '修正', @@ -892,7 +892,6 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'UI:ChangeManagementMenu:Title' => '変更管理概要', 'UI-ChangeManagementMenu-ChangesByType' => 'タイプ別変更', 'UI-ChangeManagementMenu-ChangesByStatus' => '状態別変更', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'ワークグループ別変更', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'まだ割り当てられていない変更', 'UI:ConfigurationManagementMenu' => '構成管理', @@ -1428,6 +1427,8 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'UI:Button:RemoveImage' => 'Remove the image~~', 'UI:UploadNotSupportedInThisMode' => 'The modification of images or files is not supported in this mode.~~', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Minimize / Expand~~', 'UI:Search:AutoSubmit:DisabledHint' => 'Auto submit has been disabled for this class~~', @@ -1571,3 +1572,42 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Display messages from %1$s~~', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Display up to %1$s messages in the %2$s menu.~~', )); + +Dict::Add('JA JP', 'Japanese', '日本語', array( + 'Menu:DataSources' => '同期データソース', + 'Menu:DataSources+' => '全ての同期データソース', + 'Menu:WelcomeMenu' => 'ようこそ', + 'Menu:WelcomeMenu+' => 'ようこそ、iTopへ', + 'Menu:WelcomeMenuPage' => 'ようこそ', + 'Menu:WelcomeMenuPage+' => 'ようこそ、iTopへ', + 'Menu:AdminTools' => '管理ツール', + 'Menu:AdminTools+' => '管理ツール', + 'Menu:AdminTools?' => 'このツールは管理者プロフィールを持つユーザのみアクセスが可能です。', + 'Menu:DataModelMenu' => 'データモデル', + 'Menu:DataModelMenu+' => 'データモデル概要', + 'Menu:ExportMenu' => 'エクスポート', + 'Menu:ExportMenu+' => '任意のクエリ結果をHTML、CSV、XMLでエクスポートする', + 'Menu:NotificationsMenu' => '通知', + 'Menu:NotificationsMenu+' => '通知の設定', + 'Menu:AuditCategories' => '監査カテゴリ', + 'Menu:AuditCategories+' => '監査カテゴリ', + 'Menu:Notifications:Title' => '監査カテゴリ', + 'Menu:RunQueriesMenu' => 'クエリ実行', + 'Menu:RunQueriesMenu+' => '任意のクエリを実行', + 'Menu:QueryMenu' => 'クエリのフレーズブック', + 'Menu:QueryMenu+' => 'クエリのフレーズブック', + 'Menu:UniversalSearchMenu' => '全検索', + 'Menu:UniversalSearchMenu+' => '何か...検索', + 'Menu:UserManagementMenu' => 'ユーザ管理', + 'Menu:UserManagementMenu+' => 'ユーザ管理', + 'Menu:ProfilesMenu' => 'プロフィール', + 'Menu:ProfilesMenu+' => 'プロフィール', + 'Menu:ProfilesMenu:Title' => 'プロフィール', + 'Menu:UserAccountsMenu' => 'ユーザアカウント', + 'Menu:UserAccountsMenu+' => 'ユーザアカウント', + 'Menu:UserAccountsMenu:Title' => 'ユーザアカウント', + 'Menu:MyShortcuts' => '私のショートカット', + 'Menu:UserManagement' => 'User Management~~', + 'Menu:Queries' => 'Queries~~', + 'Menu:ConfigurationTools' => 'Configuration~~', +)); diff --git a/dictionaries/nl.dictionary.itop.core.php b/dictionaries/nl.dictionary.itop.core.php index b1bacd34d..717cfc5d7 100644 --- a/dictionaries/nl.dictionary.itop.core.php +++ b/dictionaries/nl.dictionary.itop.core.php @@ -820,9 +820,9 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'Class:SynchroAttribute/Attribute:update' => 'Update', 'Class:SynchroAttribute/Attribute:reconcile' => 'Reconcile', 'Class:SynchroAttribute/Attribute:update_policy' => 'Update Policy', - 'Class:SynchroAttribute/Attribute:update_policy/Value:master_locked' => 'Gesloten', - 'Class:SynchroAttribute/Attribute:update_policy/Value:master_unlocked' => 'Open', - 'Class:SynchroAttribute/Attribute:update_policy/Value:write_if_empty' => 'Begin indien leeg', + 'Class:SynchroAttribute/Attribute:update_policy/Value:master_locked' => 'Geblokkeerd', + 'Class:SynchroAttribute/Attribute:update_policy/Value:master_unlocked' => 'Vrij', + 'Class:SynchroAttribute/Attribute:update_policy/Value:write_if_empty' => 'Vul in indien leeg', 'Class:SynchroAttribute/Attribute:finalclass' => 'Klasse', 'Class:SynchroAttExtKey' => 'Synchro Attribuut (ExtKey)', 'Class:SynchroAttExtKey/Attribute:reconciliation_attcode' => 'Reconciliation-attribuut', @@ -1052,3 +1052,39 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'Class:AsyncTask/Attribute:finalclass' => 'Uiteindelijke klasse', 'Class:AsyncTask/Attribute:finalclass+' => '', )); + +// +// Class: AbstractResource +// + +Dict::Add('NL NL', 'Dutch', 'Nederlands', array( + 'Class:AbstractResource' => 'Abstracte Tool', + 'Class:AbstractResource+' => '', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('NL NL', 'Dutch', 'Nederlands', array( + 'Class:ResourceAdminMenu' => 'Tool "Admin Menu"', + 'Class:ResourceAdminMenu+' => '', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('NL NL', 'Dutch', 'Nederlands', array( + 'Class:ResourceRunQueriesMenu' => 'Tool "Voer query\'s uit" Menu', + 'Class:ResourceRunQueriesMenu+' => '', +)); + +// +// Class: Action +// + +Dict::Add('NL NL', 'Dutch', 'Nederlands', array( + 'Class:ResourceSystemMenu' => 'Tool "System Menu"', + 'Class:ResourceSystemMenu+' => '', +)); diff --git a/dictionaries/nl.dictionary.itop.ui.php b/dictionaries/nl.dictionary.itop.ui.php index 171248845..8786cda35 100644 --- a/dictionaries/nl.dictionary.itop.ui.php +++ b/dictionaries/nl.dictionary.itop.ui.php @@ -22,7 +22,7 @@ * http://www.linprofs.com * * @author Hipska (2018) - * @author Jeffrey Bostoen - <jbostoen.itop@outlook.com> (2018 - 2019) + * @author Jeffrey Bostoen - <jbostoen.itop@outlook.com> (2018 - 2020) * * @copyright Copyright (C) 2010-2019 Combodo SARL * @licence http://opensource.org/licenses/AGPL-3.0 @@ -424,6 +424,9 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'UI:Button:Insert' => 'Invoegen', 'UI:Button:More' => 'Meer', 'UI:Button:Less' => 'Minder', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => 'Zoek', 'UI:ClickToCreateNew' => 'Maak een %1$s aan', @@ -845,7 +848,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'UI:ModificationTitle_Class_Object' => 'Aanpassen van %1$s: <span class="hilite">%2$s</span>', 'UI:ClonePageTitle_Object_Class' => 'ITOP_APPLICATION_SHORT - Kloon %1$s - %2$s aanpassing', 'UI:CloneTitle_Class_Object' => 'Klonen van %1$s: <span class="hilite">%2$s</span>', - 'UI:CreationPageTitle_Class' => 'ITOP_APPLICATION_SHORT - Nieuwe %1$s aangemaakt', + 'UI:CreationPageTitle_Class' => 'ITOP_APPLICATION_SHORT - %1$s aanmaken', 'UI:CreationTitle_Class' => '%1$s aanmaken', 'UI:SelectTheTypeOf_Class_ToCreate' => 'Selecteer het type %1$s dat moet worden aangemaakt:', 'UI:Class_Object_NotUpdated' => 'Geen verandering waargenomen, %1$s (%2$s) is <strong>niet</strong> aangepast.', @@ -861,7 +864,6 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'UI:FatalErrorMessage' => 'Fatale fout, ITOP_APPLICATION_SHORT kan niet doorgaan.', 'UI:Error_Details' => 'Fout: %1$s.', - 'UI:PageTitle:ClassProjections' => 'ITOP_APPLICATION_SHORT gebruikersbeheer - klasse-projecties', 'UI:PageTitle:ProfileProjections' => 'ITOP_APPLICATION_SHORT gebruikersbeheer - profiel-projecties', 'UI:UserManagement:Class' => 'Klasse', 'UI:UserManagement:Class+' => 'Klasse van objecten', @@ -870,8 +872,6 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'UI:UserManagement:AnyObject' => '* elk *', 'UI:UserManagement:User' => 'Gebruiker', 'UI:UserManagement:User+' => 'Gebruiker bezig met de projectie', - 'UI:UserManagement:Profile' => 'Profiel', - 'UI:UserManagement:Profile+' => 'Profiel waarin de projectie is opgegeven', 'UI:UserManagement:Action:Read' => 'Lezen', 'UI:UserManagement:Action:Read+' => 'Lezen/weergeven van objecten', 'UI:UserManagement:Action:Modify' => 'Aanpassen', @@ -911,7 +911,6 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'UI:ChangeManagementMenu:Title' => 'Overzicht changes', 'UI-ChangeManagementMenu-ChangesByType' => 'Changes aan de hand van soort', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Changes aan de hand van soort status', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Changes aan de hand van werkgroep', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Nog niet toegewezen Changes', 'UI:ConfigurationManagementMenu' => 'Configuratie Management', @@ -1450,6 +1449,8 @@ Bij die koppeling wordt aan elke actie een volgorde-nummer gegeven. Dit bepaalt 'UI:Button:RemoveImage' => 'Afbeelding verwijderen', 'UI:UploadNotSupportedInThisMode' => 'Het aanpassen van afbeeldingen of bestanden wordt niet ondersteund in deze mode.', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Inklappen / uitklappen', 'UI:Search:AutoSubmit:DisabledHint' => 'Direct zoeken werd uitgeschakeld voor deze klasse.', @@ -1593,3 +1594,42 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Bekijk berichten van %1$s', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Toon maximaal %1$s berichten in het %2$s menu.', )); + +Dict::Add('NL NL', 'Dutch', 'Nederlands', array( + 'Menu:DataSources' => 'Synchronisatie-databronnen', + 'Menu:DataSources+' => 'Alle Synchronisatie-databronnen', + 'Menu:WelcomeMenu' => 'Welkom', + 'Menu:WelcomeMenu+' => 'Welkom in iTop', + 'Menu:WelcomeMenuPage' => 'Welkom', + 'Menu:WelcomeMenuPage+' => 'Welkom in iTop', + 'Menu:AdminTools' => 'Admintools', + 'Menu:AdminTools+' => 'Beheertools', + 'Menu:AdminTools?' => 'Tools die enkel toegankelijk zijn voor gebruikers met een administratorprofiel.', + 'Menu:DataModelMenu' => 'Datamodel', + 'Menu:DataModelMenu+' => 'Overzicht van het datamodel', + 'Menu:ExportMenu' => 'Export', + 'Menu:ExportMenu+' => 'Exporteer de resultaten van query\'s als HTML, CSV of XML', + 'Menu:NotificationsMenu' => 'Meldingen', + 'Menu:NotificationsMenu+' => 'Configuratie van de meldingen', + 'Menu:AuditCategories' => 'Auditcategorieën', + 'Menu:AuditCategories+' => 'Auditcategorieën', + 'Menu:Notifications:Title' => 'Auditcategorieën', + 'Menu:RunQueriesMenu' => 'Query\'s uitvoeren', + 'Menu:RunQueriesMenu+' => 'Voer een query uit', + 'Menu:QueryMenu' => 'Favoriete query\'s', + 'Menu:QueryMenu+' => 'Favoriete query\'s', + 'Menu:UniversalSearchMenu' => 'Globale zoekopdracht', + 'Menu:UniversalSearchMenu+' => 'Zoek in alle data...', + 'Menu:UserManagementMenu' => 'Gebruikersbeheer', + 'Menu:UserManagementMenu+' => 'Gebruikersbeheer', + 'Menu:ProfilesMenu' => 'Profielen', + 'Menu:ProfilesMenu+' => 'Profielen', + 'Menu:ProfilesMenu:Title' => 'Profielen', + 'Menu:UserAccountsMenu' => 'Gebruikersaccounts', + 'Menu:UserAccountsMenu+' => 'Gebruikersaccounts', + 'Menu:UserAccountsMenu:Title' => 'Gebruikersaccounts', + 'Menu:MyShortcuts' => 'Mijn snelkoppelingen', + 'Menu:UserManagement' => 'Gebruikersbeheer', + 'Menu:Queries' => 'Query\'s', + 'Menu:ConfigurationTools' => 'Configuratie', +)); diff --git a/dictionaries/pt_br.dictionary.itop.core.php b/dictionaries/pt_br.dictionary.itop.core.php index 887873b3e..2f99aaa2d 100644 --- a/dictionaries/pt_br.dictionary.itop.core.php +++ b/dictionaries/pt_br.dictionary.itop.core.php @@ -1051,3 +1051,39 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'INTERNAL:JQuery-DatePicker:LangCode' => 'pt-BR', )); + +// +// Class: AbstractResource +// + +Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( + 'Class:AbstractResource' => 'Recurso abstrato', + 'Class:AbstractResource+' => '', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( + 'Class:ResourceAdminMenu' => 'Recurso Admin Menu', + 'Class:ResourceAdminMenu+' => '', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( + 'Class:ResourceRunQueriesMenu' => 'Menu de consultas de execução de recursos', + 'Class:ResourceRunQueriesMenu+' => '', +)); + +// +// Class: Action +// + +Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( + 'Class:ResourceSystemMenu' => 'Resource System Menu~~', + 'Class:ResourceSystemMenu+' => '~~', +)); diff --git a/dictionaries/pt_br.dictionary.itop.ui.php b/dictionaries/pt_br.dictionary.itop.ui.php index d1db6d8e2..eeed31871 100644 --- a/dictionaries/pt_br.dictionary.itop.ui.php +++ b/dictionaries/pt_br.dictionary.itop.ui.php @@ -418,6 +418,9 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'UI:Button:Insert' => 'Inserir', 'UI:Button:More' => 'Mais', 'UI:Button:Less' => 'Menos', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => 'Pesquisar', 'UI:ClickToCreateNew' => 'Criar um(a) novo(a) %1$s', @@ -855,7 +858,6 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'UI:FatalErrorMessage' => 'Erro fatal, o sistema não pode continuar.', 'UI:Error_Details' => 'Erro: %1$s.', - 'UI:PageTitle:ClassProjections' => 'Gerenciamento Usuários - projeções de classe', 'UI:PageTitle:ProfileProjections' => 'Gerenciamento Usuários - projeções de classe', 'UI:UserManagement:Class' => 'Classe', 'UI:UserManagement:Class+' => 'Classe de objetos', @@ -864,8 +866,6 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'UI:UserManagement:AnyObject' => '* qualquer *', 'UI:UserManagement:User' => 'Usuário', 'UI:UserManagement:User+' => 'Usuário(s) envolvido(s) na projeção', - 'UI:UserManagement:Profile' => 'Perfis', - 'UI:UserManagement:Profile+' => 'Perfil em que a projeção é especificado', 'UI:UserManagement:Action:Read' => 'Leitura', 'UI:UserManagement:Action:Read+' => 'Leitura/mostrar objetos', 'UI:UserManagement:Action:Modify' => 'Modificação', @@ -905,7 +905,6 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'UI:ChangeManagementMenu:Title' => 'Visão geral', 'UI-ChangeManagementMenu-ChangesByType' => 'Mudanças por tipo', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Mudanças por status', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Mudanças por grupo de trabalho', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Mudanças ainda não atribuídas', 'UI:ConfigurationManagementMenu' => 'Gerenciamento Configurações', @@ -1441,6 +1440,8 @@ When associated with a trigger, each action is given an "order" number, specifyi 'UI:Button:RemoveImage' => 'Remover a imagem', 'UI:UploadNotSupportedInThisMode' => 'A modificação de imagens ou arquivos não é suportada neste modo.', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Minimizar / Expandir', 'UI:Search:AutoSubmit:DisabledHint' => 'O envio automático foi desativado para esta classe', @@ -1584,3 +1585,42 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Mostrar mensagens de %1$s', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Exibir até %1$s mensagens no menu %2$s.', )); + +Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( + 'Menu:DataSources' => 'Fontes de dados de sincronização', + 'Menu:DataSources+' => 'Todas fontes de dados de sincronização', + 'Menu:WelcomeMenu' => 'Bem-vindo ao iTop', + 'Menu:WelcomeMenu+' => 'Bem-vindo ao iTop', + 'Menu:WelcomeMenuPage' => 'Bem-vindo ao iTop', + 'Menu:WelcomeMenuPage+' => 'Bem-vindo ao iTop', + 'Menu:AdminTools' => 'Ferramentas Administrativas', + 'Menu:AdminTools+' => 'Ferramentas Administrativas', + 'Menu:AdminTools?' => 'Ferramentas acessíveis apenas para usuários com o perfil do administrador', + 'Menu:DataModelMenu' => 'Modelo Dados', + 'Menu:DataModelMenu+' => 'Visão geral do Modelo Dados', + 'Menu:ExportMenu' => 'Exportar', + 'Menu:ExportMenu+' => 'Exportar o resultado de qualquer consulta em HTML, CSV ou XML', + 'Menu:NotificationsMenu' => 'Notificações', + 'Menu:NotificationsMenu+' => 'Configuração de Notificações', + 'Menu:AuditCategories' => 'Categoria Auditorias', + 'Menu:AuditCategories+' => 'Categoria Auditorias', + 'Menu:Notifications:Title' => 'Categoria Auditorias', + 'Menu:RunQueriesMenu' => 'Executar consultas', + 'Menu:RunQueriesMenu+' => 'Executar qualquer consulta', + 'Menu:QueryMenu' => 'Consulta definida', + 'Menu:QueryMenu+' => 'Consulta definida', + 'Menu:UniversalSearchMenu' => 'Pesquisa Universal', + 'Menu:UniversalSearchMenu+' => 'Pesquisar por nada...', + 'Menu:UserManagementMenu' => 'Gerenciamento Usuários', + 'Menu:UserManagementMenu+' => 'Gerenciamento Usuários', + 'Menu:ProfilesMenu' => 'Perfis', + 'Menu:ProfilesMenu+' => 'Perfis', + 'Menu:ProfilesMenu:Title' => 'Perfis', + 'Menu:UserAccountsMenu' => 'Contas usuários', + 'Menu:UserAccountsMenu+' => 'Contas usuários', + 'Menu:UserAccountsMenu:Title' => 'Contas usuários', + 'Menu:MyShortcuts' => 'Meus atalhos', + 'Menu:UserManagement' => 'User Management~~', + 'Menu:Queries' => 'Queries~~', + 'Menu:ConfigurationTools' => 'Configuration~~', +)); diff --git a/dictionaries/ru.dictionary.itop.core.php b/dictionaries/ru.dictionary.itop.core.php index 948161571..dc3cf29b8 100644 --- a/dictionaries/ru.dictionary.itop.core.php +++ b/dictionaries/ru.dictionary.itop.core.php @@ -191,12 +191,12 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Core:AttributeTag' => 'Тег', 'Core:AttributeTag+' => 'Тег', - 'Core:Context=REST/JSON' => 'REST~~', - 'Core:Context=Synchro' => 'Synchro~~', - 'Core:Context=Setup' => 'Setup~~', - 'Core:Context=GUI:Console' => 'Console~~', - 'Core:Context=CRON' => 'cron~~', - 'Core:Context=GUI:Portal' => 'Portal~~', + 'Core:Context=REST/JSON' => 'REST', + 'Core:Context=Synchro' => 'Synchro', + 'Core:Context=Setup' => 'Setup', + 'Core:Context=GUI:Console' => 'Console', + 'Core:Context=CRON' => 'cron', + 'Core:Context=GUI:Portal' => 'Portal', )); @@ -529,8 +529,8 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Class:Trigger/Attribute:action_list+' => 'Действия, выполняемые при срабатывании триггера', 'Class:Trigger/Attribute:finalclass' => 'Тип', 'Class:Trigger/Attribute:finalclass+' => '', - 'Class:Trigger/Attribute:context' => 'Context~~', - 'Class:Trigger/Attribute:context+' => 'Context to allow the trigger to start~~', + 'Class:Trigger/Attribute:context' => 'Контекст', + 'Class:Trigger/Attribute:context+' => 'Контекст, в котором будет срабатывать триггер', )); // @@ -1033,3 +1033,39 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Class:AsyncTask/Attribute:finalclass' => 'Final class~~', 'Class:AsyncTask/Attribute:finalclass+' => '~~', )); + +// +// Class: AbstractResource +// + +Dict::Add('RU RU', 'Russian', 'Русский', array( + 'Class:AbstractResource' => 'Ресурс', + 'Class:AbstractResource+' => '', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('RU RU', 'Russian', 'Русский', array( + 'Class:ResourceAdminMenu' => 'Меню Инструменты администратора', + 'Class:ResourceAdminMenu+' => '', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('RU RU', 'Russian', 'Русский', array( + 'Class:ResourceRunQueriesMenu' => 'Меню Выполнение запросов', + 'Class:ResourceRunQueriesMenu+' => '', +)); + +// +// Class: Action +// + +Dict::Add('RU RU', 'Russian', 'Русский', array( + 'Class:ResourceSystemMenu' => 'Меню Система', + 'Class:ResourceSystemMenu+' => '', +)); diff --git a/dictionaries/ru.dictionary.itop.ui.php b/dictionaries/ru.dictionary.itop.ui.php index 69892c9a2..c95697a79 100644 --- a/dictionaries/ru.dictionary.itop.ui.php +++ b/dictionaries/ru.dictionary.itop.ui.php @@ -397,6 +397,9 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'UI:Button:Insert' => 'Вставить', 'UI:Button:More' => 'Больше', 'UI:Button:Less' => 'Меньше', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => 'Поиск', 'UI:ClickToCreateNew' => 'Создать: %1$s', @@ -834,7 +837,6 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'UI:FatalErrorMessage' => 'Критическая ошибка, iTop не может продолжать работу.', 'UI:Error_Details' => 'Ошибка: %1$s.', - 'UI:PageTitle:ClassProjections' => 'iTop управление пользователями - проектирование классов', 'UI:PageTitle:ProfileProjections' => 'iTop управление пользователями - проектирование профилей', 'UI:UserManagement:Class' => 'Классs', 'UI:UserManagement:Class+' => 'Класс объектов', @@ -843,8 +845,6 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'UI:UserManagement:AnyObject' => '* любой *', 'UI:UserManagement:User' => 'Пользователь', 'UI:UserManagement:User+' => 'Пользователь учавствует', - 'UI:UserManagement:Profile' => 'Профиль', - 'UI:UserManagement:Profile+' => 'Профиль, указанный в проектировании', 'UI:UserManagement:Action:Read' => 'Чтение', 'UI:UserManagement:Action:Read+' => 'Чтение/отображение объектов', 'UI:UserManagement:Action:Modify' => 'Изменить', @@ -884,7 +884,6 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'UI:ChangeManagementMenu:Title' => 'Обзор изменений', 'UI-ChangeManagementMenu-ChangesByType' => 'Изменения по типу', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Изменения по статутсу', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Изменения по рабочей группе', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Не назначенные изменения', 'UI:ConfigurationManagementMenu' => 'Управление конфигурациями', @@ -1419,6 +1418,8 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'UI:Button:RemoveImage' => 'Удалить изображение', 'UI:UploadNotSupportedInThisMode' => 'Изменение изображений и файлов не поддерживается в этом режиме.', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Развернуть / Свернуть', 'UI:Search:AutoSubmit:DisabledHint' => 'Автоматический запуск поиска отключен для данного класса', @@ -1562,3 +1563,42 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Показать сообщения от %1$s', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Отобразите не более %1$s сообщений в меню %2$s.', )); + +Dict::Add('RU RU', 'Russian', 'Русский', array( + 'Menu:DataSources' => 'Синхронизация данных', + 'Menu:DataSources+' => 'Синхронизация данных', + 'Menu:WelcomeMenu' => 'Добро пожаловать', + 'Menu:WelcomeMenu+' => 'Добро пожаловать в iTop', + 'Menu:WelcomeMenuPage' => 'Добро пожаловать', + 'Menu:WelcomeMenuPage+' => 'Добро пожаловать в iTop', + 'Menu:AdminTools' => 'Инструменты администратора', + 'Menu:AdminTools+' => 'Инструменты администратора', + 'Menu:AdminTools?' => 'Инструменты доступны только для пользователей c правами администратора', + 'Menu:DataModelMenu' => 'Модель данных', + 'Menu:DataModelMenu+' => 'Обзор модели данных', + 'Menu:ExportMenu' => 'Экспорт', + 'Menu:ExportMenu+' => 'Экспорт результатов любого запроса в HTML, CSV или XML', + 'Menu:NotificationsMenu' => 'Уведомления', + 'Menu:NotificationsMenu+' => 'Конфигурация уведомлений', + 'Menu:AuditCategories' => 'Категории аудита', + 'Menu:AuditCategories+' => 'Категории аудита', + 'Menu:Notifications:Title' => 'Категории аудита', + 'Menu:RunQueriesMenu' => 'Выполнение запросов', + 'Menu:RunQueriesMenu+' => 'Выполнение любых запросов', + 'Menu:QueryMenu' => 'Книга запросов', + 'Menu:QueryMenu+' => 'Книга запросов', + 'Menu:UniversalSearchMenu' => 'Универсальный поиск', + 'Menu:UniversalSearchMenu+' => 'Поиск чего угодно...', + 'Menu:UserManagementMenu' => 'Управление пользователями', + 'Menu:UserManagementMenu+' => 'Управление пользователями', + 'Menu:ProfilesMenu' => 'Профили', + 'Menu:ProfilesMenu+' => 'Профили пользователей', + 'Menu:ProfilesMenu:Title' => 'Профили пользователей', + 'Menu:UserAccountsMenu' => 'Учетные записи', + 'Menu:UserAccountsMenu+' => 'Учетные записи пользователей', + 'Menu:UserAccountsMenu:Title' => 'Учетные записи пользователей', + 'Menu:MyShortcuts' => 'Избранное', + 'Menu:UserManagement' => 'Управление пользователями', + 'Menu:Queries' => 'Запросы OQL', + 'Menu:ConfigurationTools' => 'Конфигурация', +)); diff --git a/dictionaries/sk.dictionary.itop.core.php b/dictionaries/sk.dictionary.itop.core.php index b59571617..f28a123f8 100644 --- a/dictionaries/sk.dictionary.itop.core.php +++ b/dictionaries/sk.dictionary.itop.core.php @@ -2034,8 +2034,6 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => 'Posledný dátum pozdržania', 'Class:UserRequest/Attribute:last_pending_date+' => '', - 'Class:UserRequest/Attribute:cumulatedpending' => 'Kumulatívne pozdržané', - 'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:tto' => 'TTO', 'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:ttr' => 'TTR', @@ -2646,7 +2644,6 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'Server:moreinfo' => 'Viac informácií', 'Server:otherinfo' => 'Iné informácie', 'Person:info' => 'Všeobecné informácie', - 'Person:notifiy' => 'Upozornenie', 'Class:Subnet/Tab:IPUsage' => 'Využívanosť IP adries', 'Class:Subnet/Tab:IPUsage-explain' => 'Rozhrania majúce IP adresu v rozsahu: <em>%1$s</em> do <em>%2$s</em>', 'Class:Subnet/Tab:FreeIPs' => 'Voľné IP adresy', @@ -2925,3 +2922,39 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'Portal:LanguageChangedTo_Lang' => 'Jazyk zmenený na', 'Portal:ChooseYourFavoriteLanguage' => 'Vyberte si svoj obľúbený jazyk', )); + +// +// Class: AbstractResource +// + +Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( + 'Class:AbstractResource' => 'Abstract Resource~~', + 'Class:AbstractResource+' => '~~', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( + 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', + 'Class:ResourceAdminMenu+' => '~~', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( + 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', + 'Class:ResourceRunQueriesMenu+' => '~~', +)); + +// +// Class: Action +// + +Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( + 'Class:ResourceSystemMenu' => 'Resource System Menu~~', + 'Class:ResourceSystemMenu+' => '~~', +)); diff --git a/dictionaries/sk.dictionary.itop.ui.php b/dictionaries/sk.dictionary.itop.ui.php index fa4b5844b..d2b164b05 100644 --- a/dictionaries/sk.dictionary.itop.ui.php +++ b/dictionaries/sk.dictionary.itop.ui.php @@ -405,6 +405,9 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'UI:Button:Insert' => 'Insert~~', 'UI:Button:More' => 'More~~', 'UI:Button:Less' => 'Less~~', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => 'Vyhľadávanie', 'UI:ClickToCreateNew' => 'Vytvoriť nové %1$s', @@ -842,7 +845,6 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'UI:FatalErrorMessage' => 'Fatálna chyba, iTop nemôže pokračovať.', 'UI:Error_Details' => 'Chyba: %1$s.', - 'UI:PageTitle:ClassProjections' => 'iTop užívateľský manažment - projekcie tried', 'UI:PageTitle:ProfileProjections' => 'iTop užívateľský manažment - projekcie profilov', 'UI:UserManagement:Class' => 'Trieda', 'UI:UserManagement:Class+' => '', @@ -851,8 +853,6 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'UI:UserManagement:AnyObject' => '* Akýkoľvek *', 'UI:UserManagement:User' => 'Užívateľ', 'UI:UserManagement:User+' => '', - 'UI:UserManagement:Profile' => 'Profil', - 'UI:UserManagement:Profile+' => '', 'UI:UserManagement:Action:Read' => 'Čítať', 'UI:UserManagement:Action:Read+' => '', 'UI:UserManagement:Action:Modify' => 'Upravovať', @@ -892,7 +892,6 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'UI:ChangeManagementMenu:Title' => 'Prehľad zmien', 'UI-ChangeManagementMenu-ChangesByType' => 'Zmeny podľa typu', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Zmeny podľa stavu', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'Zmeny podľa pracovnej skupiny', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Zmeny ešte nepriradené', 'UI:ConfigurationManagementMenu' => 'Konfiguračný manažment', @@ -1430,6 +1429,8 @@ Keď sú priradené spúštačom, každej akcii je dané číslo "príkazu", šp 'UI:Button:RemoveImage' => 'Remove the image~~', 'UI:UploadNotSupportedInThisMode' => 'The modification of images or files is not supported in this mode.~~', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Minimize / Expand~~', 'UI:Search:AutoSubmit:DisabledHint' => 'Auto submit has been disabled for this class~~', @@ -1573,3 +1574,42 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Display messages from %1$s~~', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Display up to %1$s messages in the %2$s menu.~~', )); + +Dict::Add('SK SK', 'Slovak', 'Slovenčina', array( + 'Menu:DataSources' => 'Synchronizované zdroje dát', + 'Menu:DataSources+' => '', + 'Menu:WelcomeMenu' => 'Vitajte', + 'Menu:WelcomeMenu+' => '', + 'Menu:WelcomeMenuPage' => 'Vitajte', + 'Menu:WelcomeMenuPage+' => '', + 'Menu:AdminTools' => 'Administrátorské pomôcky', + 'Menu:AdminTools+' => '', + 'Menu:AdminTools?' => 'Pomôcky prístupné iba užívateľom majúcim administrátorský profil', + 'Menu:DataModelMenu' => 'Dátový model', + 'Menu:DataModelMenu+' => '', + 'Menu:ExportMenu' => 'Export', + 'Menu:ExportMenu+' => '', + 'Menu:NotificationsMenu' => 'Upozornenia', + 'Menu:NotificationsMenu+' => '', + 'Menu:AuditCategories' => 'Kategórie auditu', + 'Menu:AuditCategories+' => '', + 'Menu:Notifications:Title' => 'Kategórie auditu', + 'Menu:RunQueriesMenu' => 'Spustiť dopyty', + 'Menu:RunQueriesMenu+' => '', + 'Menu:QueryMenu' => 'Dopyt frázy', + 'Menu:QueryMenu+' => '', + 'Menu:UniversalSearchMenu' => 'Univerzálne vyhľadávanie', + 'Menu:UniversalSearchMenu+' => '', + 'Menu:UserManagementMenu' => 'Užívateľský manažment', + 'Menu:UserManagementMenu+' => '', + 'Menu:ProfilesMenu' => 'Profily', + 'Menu:ProfilesMenu+' => '', + 'Menu:ProfilesMenu:Title' => 'Profily', + 'Menu:UserAccountsMenu' => 'Užívateľské účty', + 'Menu:UserAccountsMenu+' => '', + 'Menu:UserAccountsMenu:Title' => 'Užívateľské účty', + 'Menu:MyShortcuts' => 'Moje skratky', + 'Menu:UserManagement' => 'User Management~~', + 'Menu:Queries' => 'Queries~~', + 'Menu:ConfigurationTools' => 'Configuration~~', +)); diff --git a/dictionaries/tr.dictionary.itop.core.php b/dictionaries/tr.dictionary.itop.core.php index 8ec3141fb..ac835cb6f 100644 --- a/dictionaries/tr.dictionary.itop.core.php +++ b/dictionaries/tr.dictionary.itop.core.php @@ -1054,3 +1054,39 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'Class:AsyncTask/Attribute:finalclass' => 'Final class~~', 'Class:AsyncTask/Attribute:finalclass+' => '~~', )); + +// +// Class: AbstractResource +// + +Dict::Add('TR TR', 'Turkish', 'Türkçe', array( + 'Class:AbstractResource' => 'Abstract Resource~~', + 'Class:AbstractResource+' => '~~', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('TR TR', 'Turkish', 'Türkçe', array( + 'Class:ResourceAdminMenu' => 'Resource Admin Menu~~', + 'Class:ResourceAdminMenu+' => '~~', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('TR TR', 'Turkish', 'Türkçe', array( + 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', + 'Class:ResourceRunQueriesMenu+' => '~~', +)); + +// +// Class: Action +// + +Dict::Add('TR TR', 'Turkish', 'Türkçe', array( + 'Class:ResourceSystemMenu' => 'Resource System Menu~~', + 'Class:ResourceSystemMenu+' => '~~', +)); diff --git a/dictionaries/tr.dictionary.itop.ui.php b/dictionaries/tr.dictionary.itop.ui.php index 154e7acf4..09471847e 100644 --- a/dictionaries/tr.dictionary.itop.ui.php +++ b/dictionaries/tr.dictionary.itop.ui.php @@ -419,6 +419,9 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'UI:Button:Insert' => 'Insert~~', 'UI:Button:More' => 'More~~', 'UI:Button:Less' => 'Less~~', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => 'Ara', 'UI:ClickToCreateNew' => 'Yeni %1$s yarat', @@ -856,7 +859,6 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'UI:FatalErrorMessage' => 'Kritik Hata, iTop devam edemiyor.', 'UI:Error_Details' => 'Hata: %1$s.', - 'UI:PageTitle:ClassProjections' => 'iTop Kullanıcı Yönetimi - sınıf koruması', 'UI:PageTitle:ProfileProjections' => 'iTop Kullanıcı Yönetimi - profil koruması', 'UI:UserManagement:Class' => 'Sınıf', 'UI:UserManagement:Class+' => 'Nesnin sınıfı', @@ -865,8 +867,6 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'UI:UserManagement:AnyObject' => '* herhangi *', 'UI:UserManagement:User' => 'Kullanıcı', 'UI:UserManagement:User+' => 'User involved in the projection', - 'UI:UserManagement:Profile' => 'Profil', - 'UI:UserManagement:Profile+' => 'Profile in which the projection is specified', 'UI:UserManagement:Action:Read' => 'Oku', 'UI:UserManagement:Action:Read+' => 'Nesneyi görüntüle', 'UI:UserManagement:Action:Modify' => 'Güncelle', @@ -906,7 +906,6 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'UI:ChangeManagementMenu:Title' => 'Değişiklik Özeti', 'UI-ChangeManagementMenu-ChangesByType' => 'Değişiklik tipine göre', 'UI-ChangeManagementMenu-ChangesByStatus' => 'Değişiklik durumuna göre', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => 'İş grubuna değişiklikler', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => 'Atanmamış Değişiklikler', 'UI:ConfigurationManagementMenu' => 'Konfigürasyon Yönetimi', @@ -1442,6 +1441,8 @@ Tetikleme gerçekleştiriğinde işlemler tanımlanan sıra numarası ile gerçe 'UI:Button:RemoveImage' => 'Remove the image~~', 'UI:UploadNotSupportedInThisMode' => 'The modification of images or files is not supported in this mode.~~', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => 'Minimize / Expand~~', 'UI:Search:AutoSubmit:DisabledHint' => 'Auto submit has been disabled for this class~~', @@ -1585,3 +1586,42 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => 'Display messages from %1$s~~', 'UI:Newsroom:DisplayAtMost_X_Messages' => 'Display up to %1$s messages in the %2$s menu.~~', )); + +Dict::Add('TR TR', 'Turkish', 'Türkçe', array( + 'Menu:DataSources' => 'Synchronization Data Sources~~', + 'Menu:DataSources+' => 'All Synchronization Data Sources~~', + 'Menu:WelcomeMenu' => 'Hoşgeldiniz', + 'Menu:WelcomeMenu+' => 'iTop\'a Hoşgeldiniz', + 'Menu:WelcomeMenuPage' => 'Hoşgeldiniz', + 'Menu:WelcomeMenuPage+' => 'iTop\'a Hoşgeldiniz', + 'Menu:AdminTools' => 'Yönetim Araçları', + 'Menu:AdminTools+' => 'Yönetim Araçları', + 'Menu:AdminTools?' => 'Yönetici profiline izin verilen araçlar', + 'Menu:DataModelMenu' => 'Veri Modeli', + 'Menu:DataModelMenu+' => 'Veri Modeli Özeti', + 'Menu:ExportMenu' => 'Dışarı ver', + 'Menu:ExportMenu+' => 'Sorgu sonucunu HTML, CSV veya XML olarak dışarı aktar', + 'Menu:NotificationsMenu' => 'Uyarılar', + 'Menu:NotificationsMenu+' => 'Uyarıların yapılandırılması', + 'Menu:AuditCategories' => 'Denetleme Kategorileri', + 'Menu:AuditCategories+' => 'Denetleme Kategorileri', + 'Menu:Notifications:Title' => 'Denetleme Kategorileri', + 'Menu:RunQueriesMenu' => 'Sorgu çalıştır', + 'Menu:RunQueriesMenu+' => 'Sorgu çalıştır', + 'Menu:QueryMenu' => 'Query phrasebook~~', + 'Menu:QueryMenu+' => 'Query phrasebook~~', + 'Menu:UniversalSearchMenu' => 'Genel sorgu', + 'Menu:UniversalSearchMenu+' => 'Herhangi bir arama...', + 'Menu:UserManagementMenu' => 'Kullanıcı Yönetimi', + 'Menu:UserManagementMenu+' => 'Kullanıcı Yönetimi', + 'Menu:ProfilesMenu' => 'Profiller', + 'Menu:ProfilesMenu+' => 'Profiller', + 'Menu:ProfilesMenu:Title' => 'Profiller', + 'Menu:UserAccountsMenu' => 'Kullanıcı Hesapları', + 'Menu:UserAccountsMenu+' => 'Kullanıcı Hesapları', + 'Menu:UserAccountsMenu:Title' => 'Kullanıcı Hesapları', + 'Menu:MyShortcuts' => 'My Shortcuts~~', + 'Menu:UserManagement' => 'User Management~~', + 'Menu:Queries' => 'Queries~~', + 'Menu:ConfigurationTools' => 'Configuration~~', +)); diff --git a/dictionaries/zh_cn.dictionary.itop.core.php b/dictionaries/zh_cn.dictionary.itop.core.php index 7807f117d..3dca3013e 100644 --- a/dictionaries/zh_cn.dictionary.itop.core.php +++ b/dictionaries/zh_cn.dictionary.itop.core.php @@ -1050,3 +1050,39 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'INTERNAL:JQuery-DatePicker:LangCode' => 'zh-CN', )); + +// +// Class: AbstractResource +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', array( + 'Class:AbstractResource' => '抽象资源', + 'Class:AbstractResource+' => '', +)); + +// +// Class: ResourceAdminMenu +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', array( + 'Class:ResourceAdminMenu' => '资源管理菜单', + 'Class:ResourceAdminMenu+' => '', +)); + +// +// Class: ResourceRunQueriesMenu +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', array( + 'Class:ResourceRunQueriesMenu' => 'Resource Run Queries Menu~~', + 'Class:ResourceRunQueriesMenu+' => '', +)); + +// +// Class: Action +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', array( + 'Class:ResourceSystemMenu' => 'Resource System Menu~~', + 'Class:ResourceSystemMenu+' => '', +)); diff --git a/dictionaries/zh_cn.dictionary.itop.ui.php b/dictionaries/zh_cn.dictionary.itop.ui.php index 8ace65950..52bca2ff4 100644 --- a/dictionaries/zh_cn.dictionary.itop.ui.php +++ b/dictionaries/zh_cn.dictionary.itop.ui.php @@ -418,6 +418,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'UI:Button:Insert' => '插入', 'UI:Button:More' => '更多', 'UI:Button:Less' => '更少', + 'UI:Button:Wait' => 'Please wait while updating fields~~', + 'UI:Treeview:CollapseAll' => 'Collapse All~~', + 'UI:Treeview:ExpandAll' => 'Expand All~~', 'UI:SearchToggle' => '搜索', 'UI:ClickToCreateNew' => '新建 %1$s', @@ -855,7 +858,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'UI:FatalErrorMessage' => '致命错误, iTop 无法继续.', 'UI:Error_Details' => '错误: %1$s.', - 'UI:PageTitle:ClassProjections' => 'iTop 用户管理 - 类映射', 'UI:PageTitle:ProfileProjections' => 'iTop 用户管理 - 角色映射', 'UI:UserManagement:Class' => '类', 'UI:UserManagement:Class+' => '对象的类', @@ -864,8 +866,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'UI:UserManagement:AnyObject' => '* 任何 *', 'UI:UserManagement:User' => '用户', 'UI:UserManagement:User+' => '与该映射相关的用户', - 'UI:UserManagement:Profile' => '角色', - 'UI:UserManagement:Profile+' => '映射被指定的角色', 'UI:UserManagement:Action:Read' => '读', 'UI:UserManagement:Action:Read+' => '读/显示 对象', 'UI:UserManagement:Action:Modify' => '修改', @@ -905,7 +905,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'UI:ChangeManagementMenu:Title' => '变更概况', 'UI-ChangeManagementMenu-ChangesByType' => '按类别划分的变更', 'UI-ChangeManagementMenu-ChangesByStatus' => '按状态划分的变更', - 'UI-ChangeManagementMenu-ChangesByWorkgroup' => '按执行团队划分的变更', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => '尚未分配的变更', 'UI:ConfigurationManagementMenu' => '配置管理', @@ -1441,6 +1440,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'UI:Button:RemoveImage' => '移除图片', 'UI:UploadNotSupportedInThisMode' => '本模式下不支持修改文件或图片.', + 'UI:Button:RemoveDocument' => 'Remove the document~~', + // Search form 'UI:Search:Toggle' => '折叠 / 展开', 'UI:Search:AutoSubmit:DisabledHint' => '该类别已禁用自动提交', @@ -1584,3 +1585,42 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'UI:Newsroom:DisplayMessagesFor_Provider' => '显示来自 %1$s 的消息', 'UI:Newsroom:DisplayAtMost_X_Messages' => '在 %2$s 菜单中最多显示 %1$s 条消息.', )); + +Dict::Add('ZH CN', 'Chinese', '简体中文', array( + 'Menu:DataSources' => '同步数据源', + 'Menu:DataSources+' => '所有同步数据源', + 'Menu:WelcomeMenu' => '欢迎', + 'Menu:WelcomeMenu+' => '欢迎使用iTop', + 'Menu:WelcomeMenuPage' => '欢迎', + 'Menu:WelcomeMenuPage+' => '欢迎使用iTop', + 'Menu:AdminTools' => '管理工具', + 'Menu:AdminTools+' => '管理工具', + 'Menu:AdminTools?' => '具有管理员角色的用户才能使用的工具', + 'Menu:DataModelMenu' => '数据模型', + 'Menu:DataModelMenu+' => '数据模型概况', + 'Menu:ExportMenu' => '导出', + 'Menu:ExportMenu+' => '以HTML, CSV 或XML 格式导出任何查询的结果', + 'Menu:NotificationsMenu' => '通知', + 'Menu:NotificationsMenu+' => '配置通知', + 'Menu:AuditCategories' => '审计类别', + 'Menu:AuditCategories+' => '审计类别', + 'Menu:Notifications:Title' => '审计类别', + 'Menu:RunQueriesMenu' => '运行查询', + 'Menu:RunQueriesMenu+' => '运行任何查询', + 'Menu:QueryMenu' => '查询手册', + 'Menu:QueryMenu+' => '查询手册', + 'Menu:UniversalSearchMenu' => '全局搜索', + 'Menu:UniversalSearchMenu+' => '搜索所有...', + 'Menu:UserManagementMenu' => '用户管理', + 'Menu:UserManagementMenu+' => '用户管理', + 'Menu:ProfilesMenu' => '角色', + 'Menu:ProfilesMenu+' => '角色', + 'Menu:ProfilesMenu:Title' => '角色', + 'Menu:UserAccountsMenu' => '用户帐户', + 'Menu:UserAccountsMenu+' => '用户帐户', + 'Menu:UserAccountsMenu:Title' => '用户帐户', + 'Menu:MyShortcuts' => '我的快捷方式', + 'Menu:UserManagement' => '用户管理', + 'Menu:Queries' => '查询', + 'Menu:ConfigurationTools' => '配置', +)); diff --git a/js/dashboard.js b/js/dashboard.js index 4c8350bcc..bcf20dd00 100644 --- a/js/dashboard.js +++ b/js/dashboard.js @@ -154,8 +154,10 @@ $(function() var sDashletUniqueId = $(this).attr("id"); var sDashletIdParts = sDashletUniqueId.split('_'); var sDashletOrigId = sDashletIdParts[sDashletIdParts.length - 1]; - return isNaN(sDashletOrigId) ? 0 : parseInt(sDashletOrigId); + return isNaN(parseInt(sDashletOrigId)) ? 0 : parseInt(sDashletOrigId); }).get(); + // avoid empty array for IE + aDashletsIds.push(0); // Note: Use of .apply() to be compatible with IE10 var iHighestDashletOrigId = Math.max.apply(null, aDashletsIds); diff --git a/js/datatable.js b/js/datatable.js index 27fcf9dd6..37c0efd27 100644 --- a/js/datatable.js +++ b/js/datatable.js @@ -53,7 +53,7 @@ $(function() oParams.operation = 'datatable'; oParams.filter = this.options.sFilter; - oParams.extra_param = this.options.oExtraParams; + oParams.extra_param = JSON.stringify(this.options.oExtraParams); oParams.start = 0; oParams.end = this.options.iPageSize; oParams.select_mode = this.options.sSelectMode; diff --git a/js/extkeywidget.js b/js/extkeywidget.js index 9d58634bf..8a258437f 100644 --- a/js/extkeywidget.js +++ b/js/extkeywidget.js @@ -37,8 +37,99 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper // make sure that the form is clean $('#'+this.id+'_btnRemove').prop('disabled',true); $('#'+this.id+'_linksToRemove').val(''); + } + this.AddAutocomplete = function(iMinChars, sWizHelperJSON) + { + var hasFocus = 0; + var cache = {}; + $('#label_'+me.id).autocomplete({ + source: function (request, response) { + term = request.term.toLowerCase().latinise().replace(/[\u0300-\u036f]/g, ""); + + if (term in cache) + { + response(cache[term]); + return; + } + if (term.indexOf(this.previous) >= 0 && cache[this.previous] != null && cache[this.previous].length < 120) + { + //we have already all the possibility in cache + var data = []; + $.each(cache[this.previous], function (key, value) { + if (value.label.toLowerCase().latinise().replace(/[\u0300-\u036f]/g, "").indexOf(term) >= 0) + { + data.push(value); + } + }); + cache[term] = data; + response(data); + } + else + { + $.post({ + url: GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', + dataType: "json", + data: { + q: request.term, + operation: 'ac_extkey', + sTargetClass: me.sTargetClass, + sFilter: me.sFilter, + bSearchMode: me.bSearchMode, + sOutputFormat: 'json', + json: function () { + return sWizHelperJSON; + } + }, + success: function (data) { + cache[term] = data; + response(data); + } + }); + + } + }, + autoFocus: true, + minLength: iMinChars, + focus: function (event, ui) { + // $('#label_$this->iId').val( ui.item.label ); + return false; + }, + select: function (event, ui) { + $('#'+me.id).val(ui.item.value); + $('#label_'+me.id).val(ui.item.label); + $('#'+me.id).trigger('validate'); + $('#'+me.id).trigger('extkeychange'); + $('#'+me.id).trigger('change'); + return false; + } + }) + .autocomplete("instance")._renderItem = function (ul, item) { + var term = this.term.replace("/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi", "\\$1"); + var val = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)("+term+")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>"); + if (item.obsolete == 'yes') + { + val = val+' <b>old</b>'; + } + return $("<li>") + .append(val) + .appendTo(ul); + }; + + $('#label_'+me.id).focus(function(){ + // track whether the field has focus, we shouldn't process any + // results if the field no longer has focus + hasFocus++; + }).blur(function() { + hasFocus = 0; + }).click( + function() { + if(hasFocus++>1) + { + $('#label_'+me.id).autocomplete( "search"); + } + }); }; - + this.StopPendingRequest = function() { if (me.ajax_request) @@ -600,5 +691,5 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper return false; // Do NOT submit the form in case we are called by OnSubmit... }; - + } \ No newline at end of file diff --git a/js/forms-json-utils.js b/js/forms-json-utils.js index c1eb96c36..4e5e39bd0 100644 --- a/js/forms-json-utils.js +++ b/js/forms-json-utils.js @@ -543,6 +543,7 @@ function UpdateDuration(iId) } // Called when filling an autocomplete field +//deprecated in 2.8 function OnAutoComplete(id, event, data, formatted) { if (data) diff --git a/js/jquery.autocomplete.js b/js/jquery.autocomplete.js index 49683c287..ced30419b 100644 --- a/js/jquery.autocomplete.js +++ b/js/jquery.autocomplete.js @@ -13,6 +13,7 @@ * patched by sabello to : replace .size() to length * set the row response split to "\t" * + * @since 2.8.0 N°2284 replaced by JQuery UI Autocomplete Widget, but kept for legacy code in modules */ ;(function($) { diff --git a/js/jquery.min.js b/js/jquery.min.js index a1c07fd80..b0614034a 100644 --- a/js/jquery.min.js +++ b/js/jquery.min.js @@ -1,2 +1,2 @@ -/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}k.fn=k.prototype={jquery:f,constructor:k,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=k.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return k.each(this,e)},map:function(n){return this.pushStack(k.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},k.extend=k.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(k.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||k.isPlainObject(n)?n:{},i=!1,a[t]=k.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},k.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t){b(e,{nonce:t&&t.nonce})},each:function(e,t){var n,r=0;if(d(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},trim:function(e){return null==e?"":(e+"").replace(p,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(d(Object(e))?k.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(d(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g.apply([],a)},guid:1,support:y}),"function"==typeof Symbol&&(k.fn[Symbol.iterator]=t[Symbol.iterator]),k.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var h=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,k="sizzle"+1*new Date,m=n.document,S=0,r=0,p=ue(),x=ue(),N=ue(),A=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",$=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",F=new RegExp(M+"+","g"),B=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="<a id='"+k+"'></a><select id='"+k+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!==C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!==C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(F," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[k]||(a[k]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===S&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[S,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[k]||(a[k]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===S&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[k]||(a[k]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[S,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[k]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace(B,"$1"));return s[k]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[S,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[k]||(e[k]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===S&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[k]&&(v=Ce(v)),y&&!y[k]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[k]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(B,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace(B," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=N[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[k]?i.push(a):o.push(a);(a=N(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=S+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t===C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument===C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(S=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(S=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=k.split("").sort(D).join("")===k,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);k.find=h,k.expr=h.selectors,k.expr[":"]=k.expr.pseudos,k.uniqueSort=k.unique=h.uniqueSort,k.text=h.getText,k.isXMLDoc=h.isXML,k.contains=h.contains,k.escapeSelector=h.escape;var T=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&k(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},N=k.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var D=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1<i.call(n,e)!==r}):k.filter(n,e,r)}k.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?k.find.matchesSelector(r,e)?[r]:[]:k.find.matches(e,k.grep(t,function(e){return 1===e.nodeType}))},k.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(k(e).filter(function(){for(t=0;t<r;t++)if(k.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)k.find(e,i[t],n);return 1<r?k.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&N.test(e)?k(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(k.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&k(e);if(!N.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&k.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?k.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(k(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(k.uniqueSort(k.merge(this.get(),k(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),k.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return T(e,"parentNode")},parentsUntil:function(e,t,n){return T(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return T(e,"nextSibling")},prevAll:function(e){return T(e,"previousSibling")},nextUntil:function(e,t,n){return T(e,"nextSibling",n)},prevUntil:function(e,t,n){return T(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return"undefined"!=typeof e.contentDocument?e.contentDocument:(A(e,"template")&&(e=e.content||e),k.merge([],e.childNodes))}},function(r,i){k.fn[r]=function(e,t){var n=k.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=k.filter(t,n)),1<this.length&&(O[r]||k.uniqueSort(n),H.test(r)&&n.reverse()),this.pushStack(n)}});var R=/[^\x20\t\r\n\f]+/g;function M(e){return e}function I(e){throw e}function W(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}k.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},k.each(e.match(R)||[],function(e,t){n[t]=!0}),n):k.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){k.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return k.each(arguments,function(e,t){var n;while(-1<(n=k.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<k.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},k.extend({Deferred:function(e){var o=[["notify","progress",k.Callbacks("memory"),k.Callbacks("memory"),2],["resolve","done",k.Callbacks("once memory"),k.Callbacks("once memory"),0,"resolved"],["reject","fail",k.Callbacks("once memory"),k.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return k.Deferred(function(r){k.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,M,s),l(u,o,I,s)):(u++,t.call(e,l(u,o,M,s),l(u,o,I,s),l(u,o,M,o.notifyWith))):(a!==M&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){k.Deferred.exceptionHook&&k.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==I&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(k.Deferred.getStackHook&&(t.stackTrace=k.Deferred.getStackHook()),C.setTimeout(t))}}return k.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:M,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:M)),o[2][3].add(l(0,e,m(n)?n:I))}).promise()},promise:function(e){return null!=e?k.extend(e,a):a}},s={};return k.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=k.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(W(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)W(i[t],a(t),o.reject);return o.promise()}});var $=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;k.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&$.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},k.readyException=function(e){C.setTimeout(function(){throw e})};var F=k.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),k.ready()}k.fn.ready=function(e){return F.then(e)["catch"](function(e){k.readyException(e)}),this},k.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--k.readyWait:k.isReady)||(k.isReady=!0)!==e&&0<--k.readyWait||F.resolveWith(E,[k])}}),k.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(k.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var _=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)_(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(k(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},z=/^-ms-/,U=/-([a-z])/g;function X(e,t){return t.toUpperCase()}function V(e){return e.replace(z,"ms-").replace(U,X)}var G=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function Y(){this.expando=k.expando+Y.uid++}Y.uid=1,Y.prototype={cache:function(e){var t=e[this.expando];return t||(t={},G(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[V(t)]=n;else for(r in t)i[V(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][V(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(V):(t=V(t))in r?[t]:t.match(R)||[]).length;while(n--)delete r[t[n]]}(void 0===t||k.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!k.isEmptyObject(t)}};var Q=new Y,J=new Y,K=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Z=/[A-Z]/g;function ee(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(Z,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:K.test(i)?JSON.parse(i):i)}catch(e){}J.set(e,t,n)}else n=void 0;return n}k.extend({hasData:function(e){return J.hasData(e)||Q.hasData(e)},data:function(e,t,n){return J.access(e,t,n)},removeData:function(e,t){J.remove(e,t)},_data:function(e,t,n){return Q.access(e,t,n)},_removeData:function(e,t){Q.remove(e,t)}}),k.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=J.get(o),1===o.nodeType&&!Q.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=V(r.slice(5)),ee(o,r,i[r]));Q.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){J.set(this,n)}):_(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=J.get(o,n))?t:void 0!==(t=ee(o,n))?t:void 0;this.each(function(){J.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){J.remove(this,e)})}}),k.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Q.get(e,t),n&&(!r||Array.isArray(n)?r=Q.access(e,t,k.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=k.queue(e,t),r=n.length,i=n.shift(),o=k._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){k.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Q.get(e,n)||Q.access(e,n,{empty:k.Callbacks("once memory").add(function(){Q.remove(e,[t+"queue",n])})})}}),k.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?k.queue(this[0],t):void 0===n?this:this.each(function(){var e=k.queue(this,t,n);k._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&k.dequeue(this,t)})},dequeue:function(e){return this.each(function(){k.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=k.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Q.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var te=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ne=new RegExp("^(?:([+-])=|)("+te+")([a-z%]*)$","i"),re=["Top","Right","Bottom","Left"],ie=E.documentElement,oe=function(e){return k.contains(e.ownerDocument,e)},ae={composed:!0};ie.getRootNode&&(oe=function(e){return k.contains(e.ownerDocument,e)||e.getRootNode(ae)===e.ownerDocument});var se=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&oe(e)&&"none"===k.css(e,"display")},ue=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];for(o in i=n.apply(e,r||[]),t)e.style[o]=a[o];return i};function le(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return k.css(e,t,"")},u=s(),l=n&&n[3]||(k.cssNumber[t]?"":"px"),c=e.nodeType&&(k.cssNumber[t]||"px"!==l&&+u)&&ne.exec(k.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)k.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,k.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ce={};function fe(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Q.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&se(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ce[s])||(o=a.body.appendChild(a.createElement(s)),u=k.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ce[s]=u)))):"none"!==n&&(l[c]="none",Q.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}k.fn.extend({show:function(){return fe(this,!0)},hide:function(){return fe(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){se(this)?k(this).show():k(this).hide()})}});var pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Q.set(e[n],"globalEval",!t||Q.get(t[n],"globalEval"))}ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;var me,xe,be=/<|&#?\w+;/;function we(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))k.merge(p,o.nodeType?[o]:o);else if(be.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+k.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;k.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<k.inArray(o,r))i&&i.push(o);else if(l=oe(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}me=E.createDocumentFragment().appendChild(E.createElement("div")),(xe=E.createElement("input")).setAttribute("type","radio"),xe.setAttribute("checked","checked"),xe.setAttribute("name","t"),me.appendChild(xe),y.checkClone=me.cloneNode(!0).cloneNode(!0).lastChild.checked,me.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t<arguments.length;t++)u[t]=arguments[t];if(s.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,s)){a=k.event.handlers.call(this,s,l),t=0;while((i=a[t++])&&!s.isPropagationStopped()){s.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!s.isImmediatePropagationStopped())s.rnamespace&&!1!==o.namespace&&!s.rnamespace.test(o.namespace)||(s.handleObj=o,s.data=o.data,void 0!==(r=((k.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,u))&&!1===(s.result=r)&&(s.preventDefault(),s.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,s),s.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<k(i,this).index(l):k.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(k.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[k.expando]?e:new k.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&De(t,"click",ke),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&De(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Q.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},k.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},k.Event=function(e,t){if(!(this instanceof k.Event))return new k.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?ke:Se,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&k.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[k.expando]=!0},k.Event.prototype={constructor:k.Event,isDefaultPrevented:Se,isPropagationStopped:Se,isImmediatePropagationStopped:Se,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=ke,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=ke,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=ke,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},k.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&Te.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&Ce.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},k.event.addProp),k.each({focus:"focusin",blur:"focusout"},function(e,t){k.event.special[e]={setup:function(){return De(this,e,Ne),!1},trigger:function(){return De(this,e),!0},delegateType:t}}),k.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){k.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||k.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),k.fn.extend({on:function(e,t,n,r){return Ae(this,e,t,n,r)},one:function(e,t,n,r){return Ae(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,k(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Se),this.each(function(){k.event.remove(this,e,n,t)})}});var je=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/<script|<style|<link/i,Le=/checked\s*(?:[^=]|=\s*.checked.)/i,He=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n<r;n++)k.event.add(t,i,l[i][n]);J.hasData(e)&&(s=J.access(e),u=k.extend({},s),J.set(t,u))}}function Ie(n,r,i,o){r=g.apply([],r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&Le.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Ie(t,r,i,o)});if(f&&(t=(e=we(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=k.map(ve(e,"script"),Pe)).length;c<f;c++)u=e,c!==p&&(u=k.clone(u,!0,!0),s&&k.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,k.map(a,Re),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Q.access(u,"globalEval")&&k.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?k._evalUrl&&!u.noModule&&k._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")}):b(u.textContent.replace(He,""),u,l))}return n}function We(e,t,n){for(var r,i=t?k.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||k.cleanData(ve(r)),r.parentNode&&(n&&oe(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}k.extend({htmlPrefilter:function(e){return e.replace(je,"<$1></$2>")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Me(o[r],a[r]);else Me(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=k.event.special,o=0;void 0!==(n=e[o]);o++)if(G(n)){if(t=n[Q.expando]){if(t.events)for(r in t.events)i[r]?k.event.remove(n,r):k.removeEvent(n,r,t.handle);n[Q.expando]=void 0}n[J.expando]&&(n[J.expando]=void 0)}}}),k.fn.extend({detach:function(e){return We(this,e,!0)},remove:function(e){return We(this,e)},text:function(e){return _(this,function(e){return void 0===e?k.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Ie(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Oe(this,e).appendChild(e)})},prepend:function(){return Ie(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Oe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Ie(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Ie(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(k.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return k.clone(this,e,t)})},html:function(e){return _(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!qe.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=k.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(k.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Ie(this,arguments,function(e){var t=this.parentNode;k.inArray(this,n)<0&&(k.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),k.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){k.fn[e]=function(e){for(var t,n=[],r=k(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),k(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var $e=new RegExp("^("+te+")(?!px)[a-z%]+$","i"),Fe=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},Be=new RegExp(re.join("|"),"i");function _e(e,t,n){var r,i,o,a,s=e.style;return(n=n||Fe(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||oe(e)||(a=k.style(e,t)),!y.pixelBoxStyles()&&$e.test(a)&&Be.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function ze(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(u){s.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",u.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",ie.appendChild(s).appendChild(u);var e=C.getComputedStyle(u);n="1%"!==e.top,a=12===t(e.marginLeft),u.style.right="60%",o=36===t(e.right),r=36===t(e.width),u.style.position="absolute",i=12===t(u.offsetWidth/3),ie.removeChild(s),u=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s=E.createElement("div"),u=E.createElement("div");u.style&&(u.style.backgroundClip="content-box",u.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===u.style.backgroundClip,k.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),a},scrollboxSize:function(){return e(),i}}))}();var Ue=["Webkit","Moz","ms"],Xe=E.createElement("div").style,Ve={};function Ge(e){var t=k.cssProps[e]||Ve[e];return t||(e in Xe?e:Ve[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=Ue.length;while(n--)if((e=Ue[n]+t)in Xe)return e}(e)||e)}var Ye=/^(none|table(?!-c[ea]).+)/,Qe=/^--/,Je={position:"absolute",visibility:"hidden",display:"block"},Ke={letterSpacing:"0",fontWeight:"400"};function Ze(e,t,n){var r=ne.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function et(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=k.css(e,n+re[a],!0,i)),r?("content"===n&&(u-=k.css(e,"padding"+re[a],!0,i)),"margin"!==n&&(u-=k.css(e,"border"+re[a]+"Width",!0,i))):(u+=k.css(e,"padding"+re[a],!0,i),"padding"!==n?u+=k.css(e,"border"+re[a]+"Width",!0,i):s+=k.css(e,"border"+re[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function tt(e,t,n){var r=Fe(e),i=(!y.boxSizingReliable()||n)&&"border-box"===k.css(e,"boxSizing",!1,r),o=i,a=_e(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if($e.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||"auto"===a||!parseFloat(a)&&"inline"===k.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===k.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+et(e,t,n||(i?"border":"content"),o,r,a)+"px"}function nt(e,t,n,r,i){return new nt.prototype.init(e,t,n,r,i)}k.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=_e(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=V(t),u=Qe.test(t),l=e.style;if(u||(t=Ge(s)),a=k.cssHooks[t]||k.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=ne.exec(n))&&i[1]&&(n=le(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(k.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=V(t);return Qe.test(t)||(t=Ge(s)),(a=k.cssHooks[t]||k.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=_e(e,t,r)),"normal"===i&&t in Ke&&(i=Ke[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),k.each(["height","width"],function(e,u){k.cssHooks[u]={get:function(e,t,n){if(t)return!Ye.test(k.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?tt(e,u,n):ue(e,Je,function(){return tt(e,u,n)})},set:function(e,t,n){var r,i=Fe(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===k.css(e,"boxSizing",!1,i),s=n?et(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-et(e,u,"border",!1,i)-.5)),s&&(r=ne.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=k.css(e,u)),Ze(0,t,s)}}}),k.cssHooks.marginLeft=ze(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(_e(e,"marginLeft"))||e.getBoundingClientRect().left-ue(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),k.each({margin:"",padding:"",border:"Width"},function(i,o){k.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+re[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(k.cssHooks[i+o].set=Ze)}),k.fn.extend({css:function(e,t){return _(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Fe(e),i=t.length;a<i;a++)o[t[a]]=k.css(e,t[a],!1,r);return o}return void 0!==n?k.style(e,t,n):k.css(e,t)},e,t,1<arguments.length)}}),((k.Tween=nt).prototype={constructor:nt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||k.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(k.cssNumber[n]?"":"px")},cur:function(){var e=nt.propHooks[this.prop];return e&&e.get?e.get(this):nt.propHooks._default.get(this)},run:function(e){var t,n=nt.propHooks[this.prop];return this.options.duration?this.pos=t=k.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):nt.propHooks._default.set(this),this}}).init.prototype=nt.prototype,(nt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=k.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){k.fx.step[e.prop]?k.fx.step[e.prop](e):1!==e.elem.nodeType||!k.cssHooks[e.prop]&&null==e.elem.style[Ge(e.prop)]?e.elem[e.prop]=e.now:k.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=nt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},k.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},k.fx=nt.prototype.init,k.fx.step={};var rt,it,ot,at,st=/^(?:toggle|show|hide)$/,ut=/queueHooks$/;function lt(){it&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(lt):C.setTimeout(lt,k.fx.interval),k.fx.tick())}function ct(){return C.setTimeout(function(){rt=void 0}),rt=Date.now()}function ft(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=re[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function pt(e,t,n){for(var r,i=(dt.tweeners[t]||[]).concat(dt.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function dt(o,e,t){var n,a,r=0,i=dt.prefilters.length,s=k.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=rt||ct(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:k.extend({},e),opts:k.extend(!0,{specialEasing:{},easing:k.easing._default},t),originalProperties:e,originalOptions:t,startTime:rt||ct(),duration:t.duration,tweens:[],createTween:function(e,t){var n=k.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=V(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=k.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=dt.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(k._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return k.map(c,pt,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),k.fx.timer(k.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}k.Animation=k.extend(dt,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return le(n.elem,e,ne.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(R);for(var n,r=0,i=e.length;r<i;r++)n=e[r],dt.tweeners[n]=dt.tweeners[n]||[],dt.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&se(e),v=Q.get(e,"fxshow");for(r in n.queue||(null==(a=k._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,k.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],st.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||k.style(e,r)}if((u=!k.isEmptyObject(t))||!k.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Q.get(e,"display")),"none"===(c=k.css(e,"display"))&&(l?c=l:(fe([e],!0),l=e.style.display||l,c=k.css(e,"display"),fe([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===k.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Q.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&fe([e],!0),p.done(function(){for(r in g||fe([e]),Q.remove(e,"fxshow"),d)k.style(e,r,d[r])})),u=pt(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?dt.prefilters.unshift(e):dt.prefilters.push(e)}}),k.speed=function(e,t,n){var r=e&&"object"==typeof e?k.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return k.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in k.fx.speeds?r.duration=k.fx.speeds[r.duration]:r.duration=k.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&k.dequeue(this,r.queue)},r},k.fn.extend({fadeTo:function(e,t,n,r){return this.filter(se).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=k.isEmptyObject(t),o=k.speed(e,n,r),a=function(){var e=dt(this,k.extend({},t),o);(i||Q.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&!1!==i&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=k.timers,r=Q.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&ut.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||k.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Q.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=k.timers,o=n?n.length:0;for(t.finish=!0,k.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),k.each(["toggle","show","hide"],function(e,r){var i=k.fn[r];k.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(ft(r,!0),e,t,n)}}),k.each({slideDown:ft("show"),slideUp:ft("hide"),slideToggle:ft("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){k.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),k.timers=[],k.fx.tick=function(){var e,t=0,n=k.timers;for(rt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||k.fx.stop(),rt=void 0},k.fx.timer=function(e){k.timers.push(e),k.fx.start()},k.fx.interval=13,k.fx.start=function(){it||(it=!0,lt())},k.fx.stop=function(){it=null},k.fx.speeds={slow:600,fast:200,_default:400},k.fn.delay=function(r,e){return r=k.fx&&k.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},ot=E.createElement("input"),at=E.createElement("select").appendChild(E.createElement("option")),ot.type="checkbox",y.checkOn=""!==ot.value,y.optSelected=at.selected,(ot=E.createElement("input")).value="t",ot.type="radio",y.radioValue="t"===ot.value;var ht,gt=k.expr.attrHandle;k.fn.extend({attr:function(e,t){return _(this,k.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){k.removeAttr(this,e)})}}),k.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?k.prop(e,t,n):(1===o&&k.isXMLDoc(e)||(i=k.attrHooks[t.toLowerCase()]||(k.expr.match.bool.test(t)?ht:void 0)),void 0!==n?null===n?void k.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=k.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(R);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),ht={set:function(e,t,n){return!1===t?k.removeAttr(e,n):e.setAttribute(n,n),n}},k.each(k.expr.match.bool.source.match(/\w+/g),function(e,t){var a=gt[t]||k.find.attr;gt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=gt[o],gt[o]=r,r=null!=a(e,t,n)?o:null,gt[o]=i),r}});var vt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;function mt(e){return(e.match(R)||[]).join(" ")}function xt(e){return e.getAttribute&&e.getAttribute("class")||""}function bt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(R)||[]}k.fn.extend({prop:function(e,t){return _(this,k.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[k.propFix[e]||e]})}}),k.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&k.isXMLDoc(e)||(t=k.propFix[t]||t,i=k.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=k.find.attr(e,"tabindex");return t?parseInt(t,10):vt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(k.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),k.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){k.propFix[this.toLowerCase()]=this}),k.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){k(this).addClass(t.call(this,e,xt(this)))});if((e=bt(t)).length)while(n=this[u++])if(i=xt(n),r=1===n.nodeType&&" "+mt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=mt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){k(this).removeClass(t.call(this,e,xt(this)))});if(!arguments.length)return this.attr("class","");if((e=bt(t)).length)while(n=this[u++])if(i=xt(n),r=1===n.nodeType&&" "+mt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=mt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){k(this).toggleClass(i.call(this,e,xt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=k(this),r=bt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=xt(this))&&Q.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Q.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+mt(xt(n))+" ").indexOf(t))return!0;return!1}});var wt=/\r/g;k.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,k(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=k.map(t,function(e){return null==e?"":e+""})),(r=k.valHooks[this.type]||k.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=k.valHooks[t.type]||k.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(wt,""):null==e?"":e:void 0}}),k.extend({valHooks:{option:{get:function(e){var t=k.find.attr(e,"value");return null!=t?t:mt(k.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=k(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=k.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<k.inArray(k.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),k.each(["radio","checkbox"],function(){k.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<k.inArray(k(e).val(),t)}},y.checkOn||(k.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var Tt=/^(?:focusinfocus|focusoutblur)$/,Ct=function(e){e.stopPropagation()};k.extend(k.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!Tt.test(d+k.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[k.expando]?e:new k.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:k.makeArray(t,[e]),c=k.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,Tt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Q.get(o,"events")||{})[e.type]&&Q.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&G(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!G(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),k.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,Ct),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,Ct),k.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=k.extend(new k.Event,n,{type:e,isSimulated:!0});k.event.trigger(r,null,t)}}),k.fn.extend({trigger:function(e,t){return this.each(function(){k.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return k.event.trigger(e,t,n,!0)}}),y.focusin||k.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){k.event.simulate(r,e.target,k.event.fix(e))};k.event.special[r]={setup:function(){var e=this.ownerDocument||this,t=Q.access(e,r);t||e.addEventListener(n,i,!0),Q.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this,t=Q.access(e,r)-1;t?Q.access(e,r,t):(e.removeEventListener(n,i,!0),Q.remove(e,r))}}});var Et=C.location,kt=Date.now(),St=/\?/;k.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||k.error("Invalid XML: "+e),t};var Nt=/\[\]$/,At=/\r?\n/g,Dt=/^(?:submit|button|image|reset|file)$/i,jt=/^(?:input|select|textarea|keygen)/i;function qt(n,e,r,i){var t;if(Array.isArray(e))k.each(e,function(e,t){r||Nt.test(n)?i(n,t):qt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)qt(n+"["+t+"]",e[t],r,i)}k.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!k.isPlainObject(e))k.each(e,function(){i(this.name,this.value)});else for(n in e)qt(n,e[n],t,i);return r.join("&")},k.fn.extend({serialize:function(){return k.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=k.prop(this,"elements");return e?k.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!k(this).is(":disabled")&&jt.test(this.nodeName)&&!Dt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=k(this).val();return null==n?null:Array.isArray(n)?k.map(n,function(e){return{name:t.name,value:e.replace(At,"\r\n")}}):{name:t.name,value:n.replace(At,"\r\n")}}).get()}});var Lt=/%20/g,Ht=/#.*$/,Ot=/([?&])_=[^&]*/,Pt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Rt=/^(?:GET|HEAD)$/,Mt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Ft=E.createElement("a");function Bt(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(R)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function _t(t,i,o,a){var s={},u=t===Wt;function l(e){var r;return s[e]=!0,k.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function zt(e,t){var n,r,i=k.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&k.extend(!0,e,r),e}Ft.href=Et.href,k.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Et.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Et.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":k.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,k.ajaxSettings),t):zt(k.ajaxSettings,e)},ajaxPrefilter:Bt(It),ajaxTransport:Bt(Wt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=k.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?k(y):k.event,x=k.Deferred(),b=k.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Pt.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Et.href)+"").replace(Mt,Et.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(R)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Ft.protocol+"//"+Ft.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=k.param(v.data,v.traditional)),_t(It,v,t,T),h)return T;for(i in(g=k.event&&v.global)&&0==k.active++&&k.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Rt.test(v.type),f=v.url.replace(Ht,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(Lt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(St.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Ot,"$1"),o=(St.test(f)?"&":"?")+"_="+kt+++o),v.url=f+o),v.ifModified&&(k.lastModified[f]&&T.setRequestHeader("If-Modified-Since",k.lastModified[f]),k.etag[f]&&T.setRequestHeader("If-None-Match",k.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+$t+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=_t(Wt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(k.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(k.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--k.active||k.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return k.get(e,t,n,"json")},getScript:function(e,t){return k.get(e,void 0,t,"script")}}),k.each(["get","post"],function(e,i){k[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),k.ajax(k.extend({url:e,type:i,dataType:r,data:t,success:n},k.isPlainObject(e)&&e))}}),k._evalUrl=function(e,t){return k.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){k.globalEval(e,t)}})},k.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=k(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){k(this).wrapInner(n.call(this,e))}):this.each(function(){var e=k(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){k(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){k(this).replaceWith(this.childNodes)}),this}}),k.expr.pseudos.hidden=function(e){return!k.expr.pseudos.visible(e)},k.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},k.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var Ut={0:200,1223:204},Xt=k.ajaxSettings.xhr();y.cors=!!Xt&&"withCredentials"in Xt,y.ajax=Xt=!!Xt,k.ajaxTransport(function(i){var o,a;if(y.cors||Xt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(Ut[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),k.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),k.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return k.globalEval(e),e}}}),k.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),k.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=k("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=mt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&k.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?k("<div>").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}}),k.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),k.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),k.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||k.guid++,i},k.holdReady=function(e){e?k.readyWait++:k.ready(!0)},k.isArray=Array.isArray,k.parseJSON=JSON.parse,k.nodeName=A,k.isFunction=m,k.isWindow=x,k.camelCase=V,k.type=w,k.now=Date.now,k.isNumeric=function(e){var t=k.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},"function"==typeof define&&define.amd&&define("jquery",[],function(){return k});var Qt=C.jQuery,Jt=C.$;return k.noConflict=function(e){return C.$===k&&(C.$=Jt),e&&C.jQuery===k&&(C.jQuery=Qt),k},e||(C.jQuery=C.$=k),k}); +/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S}); diff --git a/js/jquery.treeview.js b/js/jquery.treeview.js index 67906016f..0e8fe5d4e 100644 --- a/js/jquery.treeview.js +++ b/js/jquery.treeview.js @@ -38,7 +38,7 @@ animated ? this.animate({ height: "toggle" }, animated, callback) : this.each(function(){ - jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ](); + $(this)[ $(this).is(":hidden") ? "show" : "hide" ](); if(callback) callback.apply(this, arguments); }); @@ -52,6 +52,32 @@ this.each(callback); } }, + heightToggleShow: function(animated, callback) { + if (animated) { + this.animate({ height: "show" }, animated, callback); + } + else + { + this.each(function() { + $(this).show(); + if (callback) + callback.apply(this, arguments); + }); + } + }, + heightToggleHide: function(animated, callback) { + if (animated) { + this.animate({ height: "hide" }, animated, callback); + } + else + { + this.each(function() { + $(this).hide(); + if (callback) + callback.apply(this, arguments); + }); + } + }, prepareBranches: function(settings) { if (!settings.prerendered) { // mark last tree items @@ -132,20 +158,42 @@ // handle toggle event function toggler() { - $(this) + //with if it's better + if($(this).parent().find(".treeview").first().is(":hidden") ) + { + $(this) .parent() // swap classes for hitarea .find(">.hitarea") - .swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea ) - .swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea ) + .replaceClass( CLASSES.expandableHitarea, CLASSES.collapsableHitarea) + .replaceClass( CLASSES.lastExpandableHitarea, CLASSES.lastCollapsableHitarea) .end() // swap classes for parent li - .swapClass( CLASSES.collapsable, CLASSES.expandable ) - .swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable ) + .replaceClass( CLASSES.expandable, CLASSES.collapsable ) + .replaceClass( CLASSES.lastExpandable, CLASSES.lastCollapsable ) // find child lists .find( ">ul" ) - // toggle them - .heightToggle( settings.animated, settings.toggle ); + // toggle them + .heightToggleShow( settings.animated, settings.toggle ); + } + else + { + $(this) + .parent() + // swap classes for hitarea + .find(">.hitarea") + .replaceClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea ) + .replaceClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea ) + .end() + // swap classes for parent li + .replaceClass( CLASSES.collapsable, CLASSES.expandable ) + .replaceClass( CLASSES.lastCollapsable, CLASSES.lastExpandable ) + // find child lists + .find( ">ul" ) + // toggle them + .heightToggleHide( settings.animated, settings.toggle ); + } + if ( settings.unique ) { $(this).parent() .siblings() @@ -157,10 +205,9 @@ .replaceClass( CLASSES.collapsable, CLASSES.expandable ) .replaceClass( CLASSES.lastCollapsable, CLASSES.lastExpandable ) .find( ">ul" ) - .heightHide( settings.animated, settings.toggle ); + .heightHide( settings.animated, settings.toggle ); } } - function serialize() { function binary(arg) { return arg ? 1 : 0; diff --git a/js/search/search_form_criteria_string.js b/js/search/search_form_criteria_string.js index 2391ee4cc..7a2a3ee92 100644 --- a/js/search/search_form_criteria_string.js +++ b/js/search/search_form_criteria_string.js @@ -38,8 +38,26 @@ $(function() }, }, }, - - + _getOperatorValues: function(oOpElem) + { + var aValues = []; + oOpElem.find('.sfc_op_content input').each(function(){ + var sValue = $(this).val(); + aValues.push({value: sValue.replace('_','\\_'), label: sValue}); + }); + return aValues; + }, + _setOperatorValues: function(oOpElem, aValues) + { + if(aValues.length === 0) + { + return false; + } + oOpElem.find('.sfc_op_content input').each(function(){ + $(this).val(aValues[0].value.replace('\\_','_')).trigger('non_interactive_change'); + }); + return true; + }, // the constructor _create: function() { diff --git a/js/search/search_form_handler.js b/js/search/search_form_handler.js index 6d031a6b6..5007eff10 100644 --- a/js/search/search_form_handler.js +++ b/js/search/search_form_handler.js @@ -408,7 +408,7 @@ $(function() var oFilterElem = $('<div></div>') .addClass('sf_filter') .addClass('sfm_filter') - .append('<span class="sff_input_wrapper"><input type="text" placeholder="' + Dict.S('UI:Search:Value:Filter:Placeholder') + '" /><span class="sff_picto sff_filter fas fa-filter"></span><span class="sff_picto sff_reset fa fa-times"></span></span>') + .append('<span class="sff_input_wrapper"><input type="text" placeholder="' + Dict.S('UI:Search:Value:Filter:Placeholder') + '" /><span class="sff_picto sff_filter fas fa-filter"></span><span class="sff_picto sff_reset fas fa-times"></span></span>') .appendTo(oContentElem); // - Lists container diff --git a/lib/combodo/tcpdf/README.md b/lib/combodo/tcpdf/README.md index 734b9879f..db0149f69 100644 --- a/lib/combodo/tcpdf/README.md +++ b/lib/combodo/tcpdf/README.md @@ -6,7 +6,7 @@ * **category** Library * **author** Nicola Asuni <info@tecnick.com> -* **copyright** 2002-2019 Nicola Asuni - Tecnick.com LTD +* **copyright** 2002-2020 Nicola Asuni - Tecnick.com LTD * **license** http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT) * **link** http://www.tcpdf.org * **source** https://github.com/tecnickcom/TCPDF diff --git a/lib/combodo/tcpdf/VERSION b/lib/combodo/tcpdf/VERSION index 8ac3c4451..b98d1d3fa 100644 --- a/lib/combodo/tcpdf/VERSION +++ b/lib/combodo/tcpdf/VERSION @@ -1 +1 @@ -6.3.4 +6.3.5 diff --git a/lib/combodo/tcpdf/composer.json b/lib/combodo/tcpdf/composer.json index fe90ae9eb..d9ac48435 100644 --- a/lib/combodo/tcpdf/composer.json +++ b/lib/combodo/tcpdf/composer.json @@ -6,13 +6,8 @@ "homepage": "https://github.com/combodo-itop-libs/TCPDF", "type": "library", "description": "TCPDF fork adding requirements for iTop: Specific fonts.", - "license": "LGPL-3.0-only", + "license": "LGPL-3.0", "authors": [ - { - "name": "Nicola Asuni", - "email": "info@tecnick.com", - "role": "lead" - }, { "name": "Combodo", "email": "contact@combodo.com" diff --git a/lib/combodo/tcpdf/include/barcodes/pdf417.php b/lib/combodo/tcpdf/include/barcodes/pdf417.php index 3b1774eaa..9a58a21f6 100644 --- a/lib/combodo/tcpdf/include/barcodes/pdf417.php +++ b/lib/combodo/tcpdf/include/barcodes/pdf417.php @@ -878,7 +878,7 @@ class PDF417 { $txtarr = array(); // array of characters and sub-mode switching characters $codelen = strlen($code); for ($i = 0; $i < $codelen; ++$i) { - $chval = ord($code{$i}); + $chval = ord($code[$i]); if (($k = array_search($chval, $this->textsubmodes[$submode])) !== false) { // we are on the same sub-mode $txtarr[] = $k; @@ -888,7 +888,7 @@ class PDF417 { // search new sub-mode if (($s != $submode) AND (($k = array_search($chval, $this->textsubmodes[$s])) !== false)) { // $s is the new submode - if (((($i + 1) == $codelen) OR ((($i + 1) < $codelen) AND (array_search(ord($code{($i + 1)}), $this->textsubmodes[$submode]) !== false))) AND (($s == 3) OR (($s == 0) AND ($submode == 1)))) { + if (((($i + 1) == $codelen) OR ((($i + 1) < $codelen) AND (array_search(ord($code[($i + 1)]), $this->textsubmodes[$submode]) !== false))) AND (($s == 3) OR (($s == 0) AND ($submode == 1)))) { // shift (temporary change only for this char) if ($s == 3) { // shift to puntuaction @@ -952,7 +952,7 @@ class PDF417 { $cw = array_merge($cw, $cw6); } else { for ($i = 0; $i < $sublen; ++$i) { - $cw[] = ord($code{$i}); + $cw[] = ord($code[$i]); } } $code = $rest; diff --git a/lib/combodo/tcpdf/include/tcpdf_static.php b/lib/combodo/tcpdf/include/tcpdf_static.php index 8e9686953..06a1dddcc 100644 --- a/lib/combodo/tcpdf/include/tcpdf_static.php +++ b/lib/combodo/tcpdf/include/tcpdf_static.php @@ -55,7 +55,7 @@ class TCPDF_STATIC { * Current TCPDF version. * @private static */ - private static $tcpdf_version = '6.3.4'; + private static $tcpdf_version = '6.3.5'; /** * String alias for total number of pages. @@ -1859,7 +1859,7 @@ class TCPDF_STATIC { public static function encodeUrlQuery($url) { $urlData = parse_url($url); if (isset($urlData['query']) && $urlData['query']) { - $urlQueryData = []; + $urlQueryData = array(); parse_str(urldecode($urlData['query']), $urlQueryData); $updatedUrl = $urlData['scheme'] . '://' . $urlData['host'] . $urlData['path'] . '?' . http_build_query($urlQueryData); } else { diff --git a/lib/combodo/tcpdf/tcpdf.php b/lib/combodo/tcpdf/tcpdf.php index be32cae7a..a71afc3a8 100644 --- a/lib/combodo/tcpdf/tcpdf.php +++ b/lib/combodo/tcpdf/tcpdf.php @@ -7178,7 +7178,7 @@ class TCPDF { } else { $ximg = $x; } - + if ($ismask OR $hidden) { // image is not displayed return $info['i']; @@ -12384,7 +12384,8 @@ class TCPDF { $x = $this->w; } $fixed = false; - if ((string)$page && (((string)$page)[0] == '*')) { + $pageAsString = (string) $page; + if ($pageAsString && $pageAsString[0] == '*') { $page = intval(substr($page, 1)); // this page number will not be changed when moving/add/deleting pages $fixed = true; diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 49be98bdb..8f8e6b200 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -197,6 +197,7 @@ return array( 'DataTableSettings' => $baseDir . '/application/datatable.class.inc.php', 'Datamatrix' => $vendorDir . '/combodo/tcpdf/include/barcodes/datamatrix.php', 'DateTimeFormat' => $baseDir . '/core/datetimeformat.class.inc.php', + 'DeadLockLog' => $baseDir . '/core/log.class.inc.php', 'DefaultLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php', 'DefaultMetricComputer' => $baseDir . '/core/computing.inc.php', 'DefaultWorkingTimeComputer' => $baseDir . '/core/computing.inc.php', @@ -231,6 +232,7 @@ return array( 'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', 'EMail' => $baseDir . '/core/email.class.inc.php', 'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php', + 'ErrorPage' => $baseDir . '/application/errorpage.class.inc.php', 'Event' => $baseDir . '/core/event.class.inc.php', 'EventIssue' => $baseDir . '/core/event.class.inc.php', 'EventLoginUsage' => $baseDir . '/core/event.class.inc.php', @@ -266,7 +268,6 @@ return array( 'HistoryBlock' => $baseDir . '/application/displayblock.class.inc.php', 'Html2Text\\Html2Text' => $baseDir . '/application/Html2Text.php', 'Html2Text\\Html2TextException' => $baseDir . '/application/Html2TextException.php', - 'ILogFileNameBuilder' => $baseDir . '/core/log.class.inc.php', 'ITopArchiveTar' => $baseDir . '/core/tar-itop.class.inc.php', 'InlineImage' => $baseDir . '/core/inlineimage.class.inc.php', 'InlineImageGC' => $baseDir . '/core/inlineimage.class.inc.php', @@ -285,6 +286,7 @@ return array( 'ListOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', 'LogAPI' => $baseDir . '/core/log.class.inc.php', 'LogFileNameBuilderFactory' => $baseDir . '/core/log.class.inc.php', + 'LogFileRotationProcess' => $baseDir . '/core/log.class.inc.php', 'LoginBlockExtension' => $baseDir . '/application/logintwig.class.inc.php', 'LoginTwigContext' => $baseDir . '/application/logintwig.class.inc.php', 'LoginTwigRenderer' => $baseDir . '/application/logintwig.class.inc.php', @@ -302,6 +304,7 @@ return array( 'ModuleDesign' => $baseDir . '/core/moduledesign.class.inc.php', 'ModuleHandlerAPI' => $baseDir . '/core/modulehandler.class.inc.php', 'ModuleHandlerApiInterface' => $baseDir . '/core/modulehandler.class.inc.php', + 'MonthlyRotatingLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php', 'MyHelpers' => $baseDir . '/core/MyHelpers.class.inc.php', 'MySQLException' => $baseDir . '/core/cmdbsource.class.inc.php', 'MySQLHasGoneAwayException' => $baseDir . '/core/cmdbsource.class.inc.php', @@ -603,9 +606,6 @@ return array( 'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php', 'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php', 'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php', - 'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', - 'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', - 'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php', 'Psr\\SimpleCache\\CacheException' => $vendorDir . '/psr/simple-cache/src/CacheException.php', 'Psr\\SimpleCache\\CacheInterface' => $vendorDir . '/psr/simple-cache/src/CacheInterface.php', 'Psr\\SimpleCache\\InvalidArgumentException' => $vendorDir . '/psr/simple-cache/src/InvalidArgumentException.php', @@ -844,9 +844,6 @@ return array( 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser' => $vendorDir . '/symfony/framework-bundle/Templating/TemplateNameParser.php', 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference' => $vendorDir . '/symfony/framework-bundle/Templating/TemplateReference.php', 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TimedPhpEngine' => $vendorDir . '/symfony/framework-bundle/Templating/TimedPhpEngine.php', - 'Symfony\\Bundle\\FrameworkBundle\\Test\\ForwardCompatTestTrait' => $vendorDir . '/symfony/framework-bundle/Test/ForwardCompatTestTrait.php', - 'Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase' => $vendorDir . '/symfony/framework-bundle/Test/KernelTestCase.php', - 'Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase' => $vendorDir . '/symfony/framework-bundle/Test/WebTestCase.php', 'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpExtractor' => $vendorDir . '/symfony/framework-bundle/Translation/PhpExtractor.php', 'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpStringTokenParser' => $vendorDir . '/symfony/framework-bundle/Translation/PhpStringTokenParser.php', 'Symfony\\Bundle\\FrameworkBundle\\Translation\\TranslationLoader' => $vendorDir . '/symfony/framework-bundle/Translation/TranslationLoader.php', @@ -1659,7 +1656,6 @@ return array( 'Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface' => $vendorDir . '/symfony/var-dumper/Dumper/DataDumperInterface.php', 'Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper' => $vendorDir . '/symfony/var-dumper/Dumper/HtmlDumper.php', 'Symfony\\Component\\VarDumper\\Exception\\ThrowingCasterException' => $vendorDir . '/symfony/var-dumper/Exception/ThrowingCasterException.php', - 'Symfony\\Component\\VarDumper\\Test\\VarDumperTestTrait' => $vendorDir . '/symfony/var-dumper/Test/VarDumperTestTrait.php', 'Symfony\\Component\\VarDumper\\VarDumper' => $vendorDir . '/symfony/var-dumper/VarDumper.php', 'Symfony\\Component\\Yaml\\Command\\LintCommand' => $vendorDir . '/symfony/yaml/Command/LintCommand.php', 'Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Dumper.php', @@ -1863,8 +1859,6 @@ return array( 'Twig\\Source' => $vendorDir . '/twig/twig/src/Source.php', 'Twig\\Template' => $vendorDir . '/twig/twig/src/Template.php', 'Twig\\TemplateWrapper' => $vendorDir . '/twig/twig/src/TemplateWrapper.php', - 'Twig\\Test\\IntegrationTestCase' => $vendorDir . '/twig/twig/src/Test/IntegrationTestCase.php', - 'Twig\\Test\\NodeTestCase' => $vendorDir . '/twig/twig/src/Test/NodeTestCase.php', 'Twig\\Token' => $vendorDir . '/twig/twig/src/Token.php', 'Twig\\TokenParser\\AbstractTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/AbstractTokenParser.php', 'Twig\\TokenParser\\ApplyTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/ApplyTokenParser.php', @@ -2062,11 +2056,6 @@ return array( 'Twig_Test' => $vendorDir . '/twig/twig/lib/Twig/Test.php', 'Twig_TestCallableInterface' => $vendorDir . '/twig/twig/lib/Twig/TestCallableInterface.php', 'Twig_TestInterface' => $vendorDir . '/twig/twig/lib/Twig/TestInterface.php', - 'Twig_Test_Function' => $vendorDir . '/twig/twig/lib/Twig/Test/Function.php', - 'Twig_Test_IntegrationTestCase' => $vendorDir . '/twig/twig/lib/Twig/Test/IntegrationTestCase.php', - 'Twig_Test_Method' => $vendorDir . '/twig/twig/lib/Twig/Test/Method.php', - 'Twig_Test_Node' => $vendorDir . '/twig/twig/lib/Twig/Test/Node.php', - 'Twig_Test_NodeTestCase' => $vendorDir . '/twig/twig/lib/Twig/Test/NodeTestCase.php', 'Twig_Token' => $vendorDir . '/twig/twig/lib/Twig/Token.php', 'Twig_TokenParser' => $vendorDir . '/twig/twig/lib/Twig/TokenParser.php', 'Twig_TokenParserBroker' => $vendorDir . '/twig/twig/lib/Twig/TokenParserBroker.php', @@ -2138,6 +2127,7 @@ return array( 'iDBObjectSetIterator' => $baseDir . '/core/dbobjectiterator.php', 'iDBObjectURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php', 'iDisplay' => $baseDir . '/core/dbobject.class.php', + 'iLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php', 'iLoginExtension' => $baseDir . '/application/applicationextension.inc.php', 'iLoginFSMExtension' => $baseDir . '/application/applicationextension.inc.php', 'iLoginUIExtension' => $baseDir . '/application/applicationextension.inc.php', diff --git a/lib/composer/autoload_real.php b/lib/composer/autoload_real.php index e8c595bf1..ac16a9508 100644 --- a/lib/composer/autoload_real.php +++ b/lib/composer/autoload_real.php @@ -13,6 +13,9 @@ class ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b } } + /** + * @return \Composer\Autoload\ClassLoader + */ public static function getLoader() { if (null !== self::$loader) { diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index 386977fc9..818c364ae 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -427,6 +427,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'DataTableSettings' => __DIR__ . '/../..' . '/application/datatable.class.inc.php', 'Datamatrix' => __DIR__ . '/..' . '/combodo/tcpdf/include/barcodes/datamatrix.php', 'DateTimeFormat' => __DIR__ . '/../..' . '/core/datetimeformat.class.inc.php', + 'DeadLockLog' => __DIR__ . '/../..' . '/core/log.class.inc.php', 'DefaultLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php', 'DefaultMetricComputer' => __DIR__ . '/../..' . '/core/computing.inc.php', 'DefaultWorkingTimeComputer' => __DIR__ . '/../..' . '/core/computing.inc.php', @@ -461,6 +462,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', 'EMail' => __DIR__ . '/../..' . '/core/email.class.inc.php', 'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php', + 'ErrorPage' => __DIR__ . '/../..' . '/application/errorpage.class.inc.php', 'Event' => __DIR__ . '/../..' . '/core/event.class.inc.php', 'EventIssue' => __DIR__ . '/../..' . '/core/event.class.inc.php', 'EventLoginUsage' => __DIR__ . '/../..' . '/core/event.class.inc.php', @@ -496,7 +498,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'HistoryBlock' => __DIR__ . '/../..' . '/application/displayblock.class.inc.php', 'Html2Text\\Html2Text' => __DIR__ . '/../..' . '/application/Html2Text.php', 'Html2Text\\Html2TextException' => __DIR__ . '/../..' . '/application/Html2TextException.php', - 'ILogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php', 'ITopArchiveTar' => __DIR__ . '/../..' . '/core/tar-itop.class.inc.php', 'InlineImage' => __DIR__ . '/../..' . '/core/inlineimage.class.inc.php', 'InlineImageGC' => __DIR__ . '/../..' . '/core/inlineimage.class.inc.php', @@ -515,6 +516,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'ListOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', 'LogAPI' => __DIR__ . '/../..' . '/core/log.class.inc.php', 'LogFileNameBuilderFactory' => __DIR__ . '/../..' . '/core/log.class.inc.php', + 'LogFileRotationProcess' => __DIR__ . '/../..' . '/core/log.class.inc.php', 'LoginBlockExtension' => __DIR__ . '/../..' . '/application/logintwig.class.inc.php', 'LoginTwigContext' => __DIR__ . '/../..' . '/application/logintwig.class.inc.php', 'LoginTwigRenderer' => __DIR__ . '/../..' . '/application/logintwig.class.inc.php', @@ -532,6 +534,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'ModuleDesign' => __DIR__ . '/../..' . '/core/moduledesign.class.inc.php', 'ModuleHandlerAPI' => __DIR__ . '/../..' . '/core/modulehandler.class.inc.php', 'ModuleHandlerApiInterface' => __DIR__ . '/../..' . '/core/modulehandler.class.inc.php', + 'MonthlyRotatingLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php', 'MyHelpers' => __DIR__ . '/../..' . '/core/MyHelpers.class.inc.php', 'MySQLException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php', 'MySQLHasGoneAwayException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php', @@ -833,9 +836,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php', 'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php', 'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php', - 'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', - 'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', - 'Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php', 'Psr\\SimpleCache\\CacheException' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheException.php', 'Psr\\SimpleCache\\CacheInterface' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheInterface.php', 'Psr\\SimpleCache\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/simple-cache/src/InvalidArgumentException.php', @@ -1074,9 +1074,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/TemplateNameParser.php', 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/TemplateReference.php', 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TimedPhpEngine' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/TimedPhpEngine.php', - 'Symfony\\Bundle\\FrameworkBundle\\Test\\ForwardCompatTestTrait' => __DIR__ . '/..' . '/symfony/framework-bundle/Test/ForwardCompatTestTrait.php', - 'Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase' => __DIR__ . '/..' . '/symfony/framework-bundle/Test/KernelTestCase.php', - 'Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase' => __DIR__ . '/..' . '/symfony/framework-bundle/Test/WebTestCase.php', 'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpExtractor' => __DIR__ . '/..' . '/symfony/framework-bundle/Translation/PhpExtractor.php', 'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpStringTokenParser' => __DIR__ . '/..' . '/symfony/framework-bundle/Translation/PhpStringTokenParser.php', 'Symfony\\Bundle\\FrameworkBundle\\Translation\\TranslationLoader' => __DIR__ . '/..' . '/symfony/framework-bundle/Translation/TranslationLoader.php', @@ -1889,7 +1886,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/DataDumperInterface.php', 'Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/HtmlDumper.php', 'Symfony\\Component\\VarDumper\\Exception\\ThrowingCasterException' => __DIR__ . '/..' . '/symfony/var-dumper/Exception/ThrowingCasterException.php', - 'Symfony\\Component\\VarDumper\\Test\\VarDumperTestTrait' => __DIR__ . '/..' . '/symfony/var-dumper/Test/VarDumperTestTrait.php', 'Symfony\\Component\\VarDumper\\VarDumper' => __DIR__ . '/..' . '/symfony/var-dumper/VarDumper.php', 'Symfony\\Component\\Yaml\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/yaml/Command/LintCommand.php', 'Symfony\\Component\\Yaml\\Dumper' => __DIR__ . '/..' . '/symfony/yaml/Dumper.php', @@ -2093,8 +2089,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'Twig\\Source' => __DIR__ . '/..' . '/twig/twig/src/Source.php', 'Twig\\Template' => __DIR__ . '/..' . '/twig/twig/src/Template.php', 'Twig\\TemplateWrapper' => __DIR__ . '/..' . '/twig/twig/src/TemplateWrapper.php', - 'Twig\\Test\\IntegrationTestCase' => __DIR__ . '/..' . '/twig/twig/src/Test/IntegrationTestCase.php', - 'Twig\\Test\\NodeTestCase' => __DIR__ . '/..' . '/twig/twig/src/Test/NodeTestCase.php', 'Twig\\Token' => __DIR__ . '/..' . '/twig/twig/src/Token.php', 'Twig\\TokenParser\\AbstractTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/AbstractTokenParser.php', 'Twig\\TokenParser\\ApplyTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/ApplyTokenParser.php', @@ -2292,11 +2286,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'Twig_Test' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test.php', 'Twig_TestCallableInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TestCallableInterface.php', 'Twig_TestInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TestInterface.php', - 'Twig_Test_Function' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/Function.php', - 'Twig_Test_IntegrationTestCase' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/IntegrationTestCase.php', - 'Twig_Test_Method' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/Method.php', - 'Twig_Test_Node' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/Node.php', - 'Twig_Test_NodeTestCase' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/NodeTestCase.php', 'Twig_Token' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Token.php', 'Twig_TokenParser' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser.php', 'Twig_TokenParserBroker' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParserBroker.php', @@ -2368,6 +2357,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'iDBObjectSetIterator' => __DIR__ . '/../..' . '/core/dbobjectiterator.php', 'iDBObjectURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php', 'iDisplay' => __DIR__ . '/../..' . '/core/dbobject.class.php', + 'iLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php', 'iLoginExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', 'iLoginFSMExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', 'iLoginUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', diff --git a/lib/composer/installed.json b/lib/composer/installed.json index 9ff117e69..1ec9d2a9a 100644 --- a/lib/composer/installed.json +++ b/lib/composer/installed.json @@ -1,17 +1,17 @@ [ { "name": "combodo/tcpdf", - "version": "6.3.4", - "version_normalized": "6.3.4.0", + "version": "6.3.5", + "version_normalized": "6.3.5.0", "source": { "type": "git", "url": "https://github.com/combodo-itop-libs/TCPDF.git", - "reference": "fe1c625d33e8f7d872d6fb69fb0255fd0e5cee2d" + "reference": "abbfedb8ca59843dec11c97ca3f308742265c3fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/combodo-itop-libs/TCPDF/zipball/fe1c625d33e8f7d872d6fb69fb0255fd0e5cee2d", - "reference": "fe1c625d33e8f7d872d6fb69fb0255fd0e5cee2d", + "url": "https://api.github.com/repos/combodo-itop-libs/TCPDF/zipball/abbfedb8ca59843dec11c97ca3f308742265c3fc", + "reference": "abbfedb8ca59843dec11c97ca3f308742265c3fc", "shasum": "" }, "require": { @@ -20,7 +20,7 @@ "replace": { "tecnickcom/tcpdf": "self.version" }, - "time": "2020-02-12T14:16:56+00:00", + "time": "2020-06-05T13:06:44+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -45,21 +45,22 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0-only" + "LGPL-3.0" ], "authors": [ - { - "name": "Nicola Asuni", - "email": "info@tecnick.com", - "role": "lead" - }, { "name": "Combodo", "email": "contact@combodo.com" } ], "description": "TCPDF fork adding requirements for iTop: Specific fonts.", - "homepage": "https://github.com/combodo-itop-libs/TCPDF" + "homepage": "https://github.com/combodo-itop-libs/TCPDF", + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tcpdf%20project", + "type": "custom" + } + ] }, { "name": "nikic/php-parser", diff --git a/lib/web.config b/lib/web.config index 599a5f260..58c9c3ac3 100644 --- a/lib/web.config +++ b/lib/web.config @@ -1,8 +1,13 @@ -<?xml version="1.0" encoding="utf-8" ?> +<?xml version="1.0" encoding="utf-8"?> <configuration> - <system.web> - <authorization> - <deny users="*" /> <!-- Denies all users --> - </authorization> - </system.web> + <system.webServer> + <security> + <requestFiltering> + <fileExtensions applyToWebDAV="false" allowUnlisted="false"></fileExtensions> + </requestFiltering> + <authorization> + <deny users="*" /> <!-- Denies all users --> + </authorization> + </security> + </system.webServer> </configuration> \ No newline at end of file diff --git a/log/web.config b/log/web.config index 599a5f260..58c9c3ac3 100644 --- a/log/web.config +++ b/log/web.config @@ -1,8 +1,13 @@ -<?xml version="1.0" encoding="utf-8" ?> +<?xml version="1.0" encoding="utf-8"?> <configuration> - <system.web> - <authorization> - <deny users="*" /> <!-- Denies all users --> - </authorization> - </system.web> + <system.webServer> + <security> + <requestFiltering> + <fileExtensions applyToWebDAV="false" allowUnlisted="false"></fileExtensions> + </requestFiltering> + <authorization> + <deny users="*" /> <!-- Denies all users --> + </authorization> + </security> + </system.webServer> </configuration> \ No newline at end of file diff --git a/pages/UI.php b/pages/UI.php index 7bf8491e8..0fb6d19ea 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -92,6 +92,10 @@ function ApplyNextAction(Webpage $oP, CMDBObject $oObj, $sNextAction) { $oObj->DBUpdate(); } + else + { + throw new ApplicationException(Dict::S('UI:FailedToApplyStimuli')); + } ReloadAndDisplay($oP, $oObj); } else @@ -384,7 +388,7 @@ try /////////////////////////////////////////////////////////////////////////////////////////// case 'details': // Details of an object - $sClass = utils::ReadParam('class', ''); + $sClass = utils::ReadParam('class', '', false, 'class'); $id = utils::ReadParam('id', ''); if ( empty($sClass) || empty($id)) { @@ -455,7 +459,7 @@ try case 'release_lock_and_details': $oP->DisableBreadCrumb(); - $sClass = utils::ReadParam('class', ''); + $sClass = utils::ReadParam('class', '', false, 'class'); $id = utils::ReadParam('id', ''); $oObj = MetaModel::GetObject($sClass, $id); $sToken = utils::ReadParam('token', ''); @@ -564,7 +568,6 @@ try $sFullText = trim($aMatches[2]); } } - $sFullText = str_replace('_', '\_', $sFullText); if (preg_match('/^"(.*)"$/', $sFullText, $aMatches)) { // The text is surrounded by double-quotes, remove the quotes and treat it as one single expression @@ -596,7 +599,7 @@ try break; } $sFullText = implode(' ', $aFullTextNeedles); - $sFullText = str_replace('\_', '_', $sFullText); + // Sanity check of the accelerators /** @var array $aAccelerators */ $aAccelerators = MetaModel::GetConfig()->Get('full_text_accelerators'); @@ -903,13 +906,22 @@ HTML case 'apply_modify': // Applying the modifications to an existing object $oP->DisableBreadCrumb(); - $sClass = utils::ReadPostedParam('class', ''); + $sClass = utils::ReadPostedParam('class', '', 'class'); $sClassLabel = MetaModel::GetName($sClass); $id = utils::ReadPostedParam('id', ''); $sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id'); if ( empty($sClass) || empty($id)) // TO DO: check that the class name is valid ! { - throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'id')); + IssueLog::Trace('Object not updated (empty class or id)', $sClass, array( + '$operation' => $operation, + '$id' => $id, + '$sTransactionId' => $sTransactionId, + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); + + throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'id')); } $bDisplayDetails = true; $oObj = MetaModel::GetObject($sClass, $id, false); @@ -918,13 +930,34 @@ HTML $bDisplayDetails = false; $oP->set_title(Dict::S('UI:ErrorPageTitle')); $oP->P(Dict::S('UI:ObjectDoesNotExist')); + + IssueLog::Trace('Object not updated (id not found)', $sClass, array( + '$operation' => $operation, + '$id' => $id, + '$sTransactionId' => $sTransactionId, + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); } elseif (!utils::IsTransactionValid($sTransactionId, false)) { + //TODO: since $bDisplayDetails= true, there will be an redirection, thus, the content generated here is ignored, only the $sMessage and $sSeverity are used afeter the redirection $sUser = UserRights::GetUser(); IssueLog::Error("UI.php '$operation' : invalid transaction_id ! data: user='$sUser', class='$sClass'"); $oP->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $oObj->GetRawName(), $sClassLabel)); // Set title will take care of the encoding $oP->p("<strong>".Dict::S('UI:Error:ObjectAlreadyUpdated')."</strong>\n"); + $sMessage = Dict::Format('UI:Error:ObjectAlreadyUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName()); + $sSeverity = 'error'; + + IssueLog::Trace('Object not updated (invalid transaction_id)', $sClass, array( + '$operation' => $operation, + '$id' => $id, + '$sTransactionId' => $sTransactionId, + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); } else { @@ -937,9 +970,31 @@ HTML $oP->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $oObj->GetRawName(), $sClassLabel)); // Set title will take care of the encoding $sMessage = Dict::Format('UI:Class_Object_NotUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName()); $sSeverity = 'info'; + + IssueLog::Trace('Object not updated (see either $aErrors or IsModified)', $sClass, array( + '$operation' => $operation, + '$id' => $id, + '$sTransactionId' => $sTransactionId, + '$aErrors' => $aErrors, + 'IsModified' => $oObj->IsModified(), + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); } else { + IssueLog::Trace('Object updated', $sClass, array( + '$operation' => $operation, + '$id' => $id, + '$sTransactionId' => $sTransactionId, + '$aErrors' => $aErrors, + 'IsModified' => $oObj->IsModified(), + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); + try { if (!empty($aErrors)) @@ -972,7 +1027,6 @@ HTML // - 2) Ok, there was some trouble indeed $sMessage = $e->getMessage(); $sSeverity = 'error'; - $bDisplayDetails = true; } utils::RemoveTransaction($sTransactionId); } @@ -983,11 +1037,22 @@ HTML $sNextAction = utils::ReadPostedParam('next_action', ''); if (!empty($sNextAction)) { - ApplyNextAction($oP, $oObj, $sNextAction); + try + { + ApplyNextAction($oP, $oObj, $sNextAction); + } + catch (ApplicationException $e) + { + $sMessage = $e->getMessage(); + $sSeverity = 'info'; + ReloadAndDisplay($oP, $oObj, 'update', $sMessage, $sSeverity); + } } else { // Nothing more to do + $sMessage = isset($sMessage) ? $sMessage : ''; + $sSeverity = isset($sSeverity) ? $sSeverity : null; ReloadAndDisplay($oP, $oObj, 'update', $sMessage, $sSeverity); } @@ -1102,6 +1167,14 @@ HTML $aWarnings = array(); if ( empty($sClass) ) // TO DO: check that the class name is valid ! { + IssueLog::Trace('Object not created (empty class)', $sClass, array( + '$operation' => $operation, + '$sTransactionId' => $sTransactionId, + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); + throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'class')); } if (!utils::IsTransactionValid($sTransactionId, false)) @@ -1109,6 +1182,14 @@ HTML $sUser = UserRights::GetUser(); IssueLog::Error("UI.php '$operation' : invalid transaction_id ! data: user='$sUser', class='$sClass'"); $oP->p("<strong>".Dict::S('UI:Error:ObjectAlreadyCreated')."</strong>\n"); + + IssueLog::Trace('Object not created (invalid transaction_id)', $sClass, array( + '$operation' => $operation, + '$sTransactionId' => $sTransactionId, + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); } else { @@ -1139,11 +1220,30 @@ HTML { if (!empty($aErrors) || !empty($aWarnings)) { + IssueLog::Trace('Object not created (see $aErrors)', $sClass, array( + '$operation' => $operation, + '$sTransactionId' => $sTransactionId, + '$aErrors' => $aErrors, + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); + throw new CoreCannotSaveObjectException(array('id' => $oObj->GetKey(), 'class' => $sClass, 'issues' => $aErrors)); } $oObj->DBInsertNoReload();// No need to reload + IssueLog::Trace('Object created', $sClass, array( + '$operation' => $operation, + '$id' => $oObj->GetKey(), + '$sTransactionId' => $sTransactionId, + '$aErrors' => $aErrors, + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); + utils::RemoveTransaction($sTransactionId); $oP->set_title(Dict::S('UI:PageTitle:ObjectCreated')); @@ -1156,7 +1256,16 @@ HTML if (!empty($sNextAction)) { $oP->add("<h1>$sMessage</h1>"); - ApplyNextAction($oP, $oObj, $sNextAction); + try + { + ApplyNextAction($oP, $oObj, $sNextAction); + } + catch (ApplicationException $e) + { + $sMessage = $e->getMessage(); + $sSeverity = 'info'; + ReloadAndDisplay($oP, $oObj, 'create', $sMessage, $sSeverity); + } } else { @@ -1384,7 +1493,7 @@ HTML } $iFieldsCount = count($aFieldsMap); $sJsonFieldsMap = json_encode($aFieldsMap); - + $oP->add_script( <<<EOF // Initializes the object once at the beginning of the page... @@ -1555,7 +1664,8 @@ EOF throw new ApplicationException(Dict::S('UI:Error:ActionNotAllowed')); } - $oObj = MetaModel::GetObject($sClass, $id, false); + /** @var \cmdbAbstractObject $oObj */ + $oObj = MetaModel::GetObject($sClass, $id, false); if ($oObj != null) { $aPrefillFormParam = array( 'user' => $_SESSION["auth_user"], @@ -1563,7 +1673,16 @@ EOF 'stimulus' => $sStimulus, 'origin' => 'console' ); - $oObj->DisplayStimulusForm($oP, $sStimulus, $aPrefillFormParam); + try + { + $oObj->DisplayStimulusForm($oP, $sStimulus, $aPrefillFormParam); + } + catch(ApplicationException $e) + { + $sMessage = $e->getMessage(); + $sSeverity = 'info'; + ReloadAndDisplay($oP, $oObj, 'stimulus', $sMessage, $sSeverity); + } } else { @@ -1576,7 +1695,7 @@ EOF case 'apply_stimulus': // Actual state change $oP->DisableBreadCrumb(); - $sClass = utils::ReadPostedParam('class', ''); + $sClass = utils::ReadPostedParam('class', '', 'class'); $id = utils::ReadPostedParam('id', ''); $sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id'); $sStimulus = utils::ReadPostedParam('stimulus', ''); @@ -1584,6 +1703,7 @@ EOF { throw new ApplicationException(Dict::Format('UI:Error:3ParametersMissing', 'class', 'id', 'stimulus')); } + /** @var \cmdbAbstractObject $oObj */ $oObj = MetaModel::GetObject($sClass, $id, false); if ($oObj != null) { @@ -1683,7 +1803,15 @@ EOF $bDisplayDetails = false; // Found issues, explain and give the user a second chance // - $oObj->DisplayStimulusForm($oP, $sStimulus); + try + { + $oObj->DisplayStimulusForm($oP, $sStimulus); + } + catch(ApplicationException $e) + { + $sMessage = $e->getMessage(); + $sSeverity = 'info'; + } $sIssueDesc = Dict::Format('UI:ObjectCouldNotBeWritten',$sIssues); $oP->add_ready_script("alert('".addslashes($sIssueDesc)."');"); } @@ -1810,7 +1938,7 @@ EOF case 'kill_lock': $oP->DisableBreadCrumb(); - $sClass = utils::ReadParam('class', ''); + $sClass = utils::ReadParam('class', '', false, 'class'); $id = utils::ReadParam('id', ''); iTopOwnershipLock::KillLock($sClass, $id); $oObj = MetaModel::GetObject($sClass, $id); @@ -1845,7 +1973,7 @@ EOF catch(CoreException $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); if ($e instanceof SecurityException) { $oP->add("<h1>".Dict::S('UI:SystemIntrusion')."</h1>\n"); @@ -1888,7 +2016,7 @@ catch(CoreException $e) catch(Exception $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); $oP->output(); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 77b5e0dd2..25910adde 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -1020,7 +1020,18 @@ try { $bReleaseLock = iTopOwnershipLock::ReleaseLock($sObjClass, $iObjKey, $sToken); } - break; + + IssueLog::Trace('on_form_cancel', $sObjClass, array( + '$iObjKey' => $iObjKey, + '$sTransactionId' => $iTransactionId, + '$sTempId' => $sTempId, + '$sToken' => $sToken, + '$sUser' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); + + break; case 'dashboard': $oPage->SetContentType('text/html'); @@ -1196,7 +1207,7 @@ EOF break; case 'dashboard_editor': - $sId = utils::ReadParam('id', '', false, 'element_identifier'); + $sId = utils::ReadParam('id', '', false, 'context_param'); $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); $aExtraParams['dashboard_div_id'] = utils::Sanitize($sId, '', 'element_identifier'); $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); @@ -2649,6 +2660,17 @@ EOF $aResult['width'] = $aDimensions['width']; $aResult['height'] = $aDimensions['height']; } + + IssueLog::Trace('InlineImage created', 'InlineImage', array( + '$operation' => $operation, + '$aResult' => $aResult, + 'secret' => $oAttachment->Get('secret'), + 'temp_id' => $sTempId, + 'item_class' => $sObjClass, + 'user' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); } else { @@ -2689,6 +2711,16 @@ EOF $oAttachment->Set('contents', $oDoc); $oAttachment->Set('secret', sprintf('%06x', mt_rand(0, 0xFFFFFF))); // something not easy to guess $iAttId = $oAttachment->DBInsert(); + + IssueLog::Trace('InlineImage created', 'InlineImage', array( + '$operation' => $operation, + 'secret' => $oAttachment->Get('secret'), + 'temp_id' => $sTempId, + 'item_class' => $sObjClass, + 'user' => UserRights::GetUser(), + 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], + 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], + )); } } catch (FileUploadException $e) diff --git a/pages/audit.php b/pages/audit.php index 306ba613f..7a055b479 100644 --- a/pages/audit.php +++ b/pages/audit.php @@ -282,6 +282,7 @@ try { try { + $iCount = 0; $oDefinitionFilter = DBObjectSearch::FromOQL($oAuditCategory->Get('definition_set')); $oDefinitionFilter->UpdateContextFromUser(); FilterByContext($oDefinitionFilter, $oAppContext); @@ -348,7 +349,7 @@ try $aRow['nb_errors'] = "n/a"; $aRow['percent_ok'] = ''; $aRow['class'] = 'red'; - $sMessage = Dict::Format('UI:Audit:ErrorIn_Category_Reason', $oAuditCategory->GetHyperlink(), $e->getMessage()); + $sMessage = Dict::Format('UI:Audit:ErrorIn_Category_Reason', $oAuditCategory->GetHyperlink(), utils::HtmlEntities($e->getMessage())); $oP->p("<img style=\"vertical-align:middle\" src=\"../images/stop-mid.png\"/> ".$sMessage); $aResults[] = $aRow; @@ -375,7 +376,7 @@ try catch(CoreException $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getHtmlDesc())); $oP->output(); @@ -404,7 +405,7 @@ catch(CoreException $e) catch(Exception $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); $oP->output(); diff --git a/pages/csvimport.php b/pages/csvimport.php index cf618ce23..0cfcc4cd5 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -313,10 +313,9 @@ try // We're doing it for real, let's create a change $sUserString = CMDBChange::GetCurrentUserName().' (CSV)'; CMDBObject::SetTrackInfo($sUserString); - CMDBObject::SetTrackOrigin('csv-interactive'); - $oMyChange = CMDBObject::GetCurrentChange(); } + CMDBObject::SetTrackOrigin('csv-interactive'); $oBulk = new BulkChange( $sClassName, @@ -1491,7 +1490,7 @@ EOF catch(CoreException $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getHtmlDesc())); $oP->output(); @@ -1520,7 +1519,7 @@ catch(CoreException $e) catch(Exception $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); $oP->output(); diff --git a/pages/graphviz.php b/pages/graphviz.php index ace9e1f9f..3ad2298d2 100644 --- a/pages/graphviz.php +++ b/pages/graphviz.php @@ -112,7 +112,7 @@ $sDotExecutable = MetaModel::GetConfig()->Get('graphviz_path'); if (file_exists($sDotExecutable)) { // create the file with Graphviz - $sImageFilePath = APPROOT."data/lifecycle/".$sClass.".png"; + $sImageFilePath = APPROOT."data/lifecycle/".$sClass.".svg"; if (!is_dir(APPROOT."data")) { @mkdir(APPROOT."data"); @@ -128,7 +128,7 @@ if (file_exists($sDotExecutable)) @fwrite($rFile, $sDotDescription); @fclose($rFile); $aOutput = array(); - $CommandLine = "\"$sDotExecutable\" -v -Tpng < \"$sDotFilePath\" -o \"$sImageFilePath\" 2>&1"; + $CommandLine = "\"$sDotExecutable\" -v -Tsvg < \"$sDotFilePath\" -o \"$sImageFilePath\" 2>&1"; exec($CommandLine, $aOutput, $iRetCode); if ($iRetCode != 0) @@ -142,15 +142,17 @@ if (file_exists($sDotExecutable)) } else { - header('Content-type: image/png'); - echo file_get_contents($sImageFilePath); + header('Content-type: image/svg+xml'); + header('Content-Disposition: inline; filename="'.$sClass.'.svg"'); + readfile($sImageFilePath); } @unlink($sDotFilePath); } else { header('Content-type: image/png'); - echo file_get_contents($sImageFilePath); + header('Content-Disposition: inline; filename="'.$sClass.'.png"'); + readfile($sImageFilePath); } ?> diff --git a/pages/navigator.php b/pages/navigator.php index a20e753fe..70b8d9d74 100755 --- a/pages/navigator.php +++ b/pages/navigator.php @@ -36,7 +36,7 @@ $oAppContext = new ApplicationContext(); $oP = new iTopWebPage("iTop - Navigator"); // Main program -$sClass = utils::ReadParam('class', ''); +$sClass = utils::ReadParam('class', '', false, 'class'); $id = utils::ReadParam('id', 0); $sRelation = utils::ReadParam('relation', 'neighbours'); diff --git a/pages/preferences.php b/pages/preferences.php index 5cdc3d4a1..3f093e47e 100644 --- a/pages/preferences.php +++ b/pages/preferences.php @@ -516,7 +516,7 @@ try catch(CoreException $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getHtmlDesc())); $oP->output(); @@ -545,7 +545,7 @@ catch(CoreException $e) catch(Exception $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); $oP->output(); diff --git a/pages/schema.php b/pages/schema.php index 0254e86af..d608de8d9 100644 --- a/pages/schema.php +++ b/pages/schema.php @@ -247,32 +247,31 @@ function DisplayClassesList($oPage, $sContext) $oPage->add("<div id=\"delDataModelSearch\"> <i class=\"fas fa-times-circle\"></i></div>"); $oPage->add("<ul id=\"ClassesList\" class=\"treeview fileview\">\n"); $oPage->add_ready_script( - <<<EOF - $("#search-model").result(function(e,f,g,h){ - //$(this).trigger(jQuery.Event('input')); + <<<JS + function getListClass (request, response,aListe) { + var results = $.ui.autocomplete.filter(aListe, request.term); + var top_suggestions = $.grep(results, function (n,i) { + return (n.label.substr(0, request.term.length).toLowerCase() == request.term.toLowerCase()); + }); + response($.merge(top_suggestions,results)); + } + $("#search-model").autocomplete({ + source: function (request, response) { + getListClass (request, response,autocompleteClassLabelAndCode); + }, + select: function( event, ui ) { var preUrl = "?operation=details_class&class="; var sufUrl = "&c[menu]=DataModelMenu"; - var code = ''; - switch($("#displaySelector").val()){ - case 'labelandcode': - var id = autocompleteClassLabelAndCode.indexOf(g); - if(id != undefined) - code = autocompleteClassCode[id]; - break; - case 'label': - var id = autocompleteClassLabel.indexOf(g); - if(id != undefined) - code = autocompleteClassCode[id]; - break; - case 'code': - var id = autocompleteClassCode.indexOf(g); - if(id != undefined) - code = autocompleteClassCode[id]; - break; - } - if(code != '') - window.location = preUrl + code + sufUrl; - }); + window.location = preUrl + ui.item.value + sufUrl; + }, + focus: true + }) + .autocomplete( "instance" )._renderItem = function( ul, item ) { + var term = this.term.replace("/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi", "\\$1"); + var val = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>"); + return $( "<li>" ).append( val ).appendTo( ul ); + }; + $("#search-model").on('input', function() { var search_result = []; $("#ClassesList").find("li").each(function(){ @@ -293,11 +292,14 @@ function DisplayClassesList($oPage, $sContext) $("#search-model").val(""); $("#search-model").trigger('input'); }); -EOF +JS ); // Get all the "root" classes for display $aRootClasses = array(); + $aClassLabelAndCodeAsJSON = []; + $aClassLabelAsJSON = array(); + $aClassCodeAsJSON = array(); foreach (MetaModel::GetClasses() as $sClassName) { if (MetaModel::IsRootClass($sClassName)) @@ -312,20 +314,19 @@ EOF //Fetch classes names for autocomplete purpose // - Encode as JSON to escape quotes and other characters - $sClassLabelAndCodeAsJSON = json_encode("$sLabelClassName ($sClassName)"); - $sClassLabelAsJSON = json_encode($sLabelClassName); - $sClassCodeAsJSON = json_encode($sClassName); - // - Push to autocomplete - $oPage->add_script( - <<<EOF - autocompleteClassLabelAndCode.push($sClassLabelAndCodeAsJSON); - autocompleteClassLabel.push($sClassLabelAsJSON); - autocompleteClassCode.push($sClassCodeAsJSON); -EOF - ); + array_push ($aClassLabelAndCodeAsJSON, ["value"=>$sClassName,"label"=>"$sLabelClassName ($sClassName)"]); + array_push ($aClassLabelAsJSON, ["value"=>$sClassName,"label"=>"$sLabelClassName"]); + array_push ($aClassCodeAsJSON, ["value"=>$sClassName,"label"=>"$sClassName"]); } + usort($aClassLabelAndCodeAsJSON, "Label_sort"); + // - Push to autocomplete + $oPage->add_script("autocompleteClassLabelAndCode=".json_encode($aClassLabelAndCodeAsJSON)."; console.warn(autocompleteClassLabelAndCode);"); + $oPage->add_script("autocompleteClassLabel=".json_encode($aClassLabelAsJSON).";"); + $oPage->add_script("autocompleteClassCode=".json_encode($aClassCodeAsJSON).";"); + // Sort them alphabetically on their display name - asort($aRootClasses); + asort($aClassLabelAndCodeAsJSON); + //usort($aRootClasses,"Label_sort"); foreach ($aRootClasses as $sClassName => $sDisplayName) { if (MetaModel::IsRootClass($sClassName)) @@ -343,6 +344,9 @@ EOF $oPage->add_ready_script('$("#ClassesList").treeview();'); } +function Label_sort($building_a, $building_b) { + return strnatcmp ($building_a["label"], $building_b["label"]); +} /** * Helper for the list of classes related to the given class in a graphical way @@ -1055,19 +1059,28 @@ function DisplayGranularityDisplayer($oPage) $('.attrCode').show(); $('.attrLabel').show(); $('.parenthesis').show(); - $("#search-model").autocomplete(autocompleteClassLabelAndCode, {scroll:true, matchContains:true}); + $("#search-model").autocomplete({ + source: function (request, response) { + getListClass (request, response,autocompleteClassLabelAndCode); + }}); break; case 'label': $('.attrCode').hide(); $('.attrLabel').show(); $('.parenthesis').hide(); - $("#search-model").autocomplete(autocompleteClassLabel, {scroll:true, matchContains:true}); + $("#search-model").autocomplete({ + source: function (request, response) { + getListClass (request, response,autocompleteClassLabel); + }}); break; case 'code': $('.attrCode').show(); $('.attrLabel').hide(); $('.parenthesis').hide(); - $("#search-model").autocomplete(autocompleteClassCode, {scroll:true, matchContains:true}); + $("#search-model").autocomplete({ + source: function (request, response) { + getListClass (request, response,autocompleteClassCode); + }}); break; } SetUserPreference("datamodel_viewer_display_granularity", $('#displaySelector').val(), true); @@ -1144,13 +1157,28 @@ switch ($operation) if ($sClass != '') { $oPage->add_ready_script( - <<<EOF + <<<JS $('#search-model').val('$sClass'); -$('#search-model').trigger("input"); +var search_result = []; +$("#ClassesList").find("li").each(function(){ + if( ! ~$(this).children("a").text().toLowerCase().indexOf('$sClass'.toLowerCase())){ + $(this).hide(); + } + else{ + search_result.push($(this)); + } +}); +search_result.forEach(function(e){ + e.show(); + e.find('ul > li').show(); + e.parents().show(); +}); +//$('#search-model').trigger("input"); -EOF +JS ); DisplayClassDetails($oPage, $sClass, $sContext); + break; } default: diff --git a/pages/tagadmin.php b/pages/tagadmin.php index cecf2dcb0..afa0a5c1e 100644 --- a/pages/tagadmin.php +++ b/pages/tagadmin.php @@ -132,10 +132,10 @@ catch (Exception $e) { require_once(APPROOT.'setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); //$oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); $oP->output(); IssueLog::Error($e->getMessage()); -} \ No newline at end of file +} diff --git a/portal/index.php b/portal/index.php index 7e2d048b1..1ef673a02 100644 --- a/portal/index.php +++ b/portal/index.php @@ -30,7 +30,7 @@ try catch(Exception $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); $oP->output(); diff --git a/readme.txt b/readme.txt index 3cdfaed3c..b737c1bc7 100644 --- a/readme.txt +++ b/readme.txt @@ -4,6 +4,7 @@ iTop stands for *IT Operations Portal*. It is a complete open source, ITIL, web based service management tool including a fully customizable CMDB, a helpdesk system and a document management tool. iTop also offers mass import tools and web services to integrate with your IT + ## Features - Fully configurable **CMDB** - **HelpDesk** and Incident Management @@ -17,34 +18,21 @@ iTop also offers mass import tools and web services to integrate with your IT - **Data synchronization** (for data federation) -## Resources +## Latest release + - [Changes since the previous version](https://wiki.openitop.org/doku.php?id=latest:release:change_log) + - [New features](https://wiki.openitop.org/doku.php?id=latest:release:start) + - [Migration notes](https://wiki.openitop.org/doku.php?id=latest:install:start) + - [Download](https://sourceforge.net/projects/itop/files/latest/download) + +## Resources - [iTop Forums](https://sourceforge.net/p/itop/discussion/): for support request - [iTop Tickets](https://sourceforge.net/p/itop/tickets/): for feature requests and bug reports - [Releases download](https://sourceforge.net/projects/itop/files/itop/) + - [Software requirements](https://wiki.openitop.org/doku.php?id=latest:install:upgrading_itop) - [iTop documentation](https://www.itophub.io/wiki/page) for iTop and official extensions - [iTop extensions](https://store.itophub.io/en_US/) for discovering ans installing extensions -## Releases -### Version 2.7 - - [Changes since the previous version](https://wiki.openitop.org/doku.php?id=2_7_0:release:change_log) - - [New features](https://wiki.openitop.org/doku.php?id=2_7_0:release:2_7_whats_new) - - [Migration notes](https://wiki.openitop.org/doku.php?id=2_7_0:install:260_to_270_migration_notes) - -### Version 2.6 - - [Changes since the previous version](https://wiki.openitop.org/doku.php?id=2_6_0:release:change_log) - - [New features](https://wiki.openitop.org/doku.php?id=2_6_0:release:2_6_whats_new) - - [Migration notes](https://wiki.openitop.org/doku.php?id=2_6_0:install:250_to_260_migration_notes) -### Version 2.5 - - [Changes since the previous version](https://wiki.openitop.org/doku.php?id=2_5_0:release:change_log) - - [New features](https://wiki.openitop.org/doku.php?id=2_5_0:release:2_5_whats_new) - - [Migration notes](https://wiki.openitop.org/doku.php?id=2_5_0:install:240_to_250_migration_notes) - - -### Version 2.4 - - [Changes since the previous version](https://wiki.openitop.org/doku.php?id=2_4_0:release:change_log) - - [New features](https://wiki.openitop.org/doku.php?id=2_4_0:release:2_4_whats_new) - - [Migration notes](https://wiki.openitop.org/doku.php?id=2_4_0:install:230_to_240_migration_notes) diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index 5552570d0..154bdb5cf 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -318,14 +318,27 @@ class MFCompiler } else { - /** @var \DOMElement $oClass */ + /** @var \MFElement $oClass */ foreach($oClasses as $oClass) { $sClass = $oClass->getAttribute("id"); $aAllClasses[] = $sClass; try { - $sCompiledCode .= $this->CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir); + $aCompiledClasses = $this->CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir); + + foreach ($aCompiledClasses['required_files'] as $sIncludeFile) + { + $sCompiledCode .= "require_once('$sIncludeFile');\n"; + } + + foreach ($aCompiledClasses['code'] as $sClass => $sCompiledClass) + { + $sClassFileName = DIRECTORY_SEPARATOR.$sRelativeDir.DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.'Model'.DIRECTORY_SEPARATOR.$sClass.'.php'; + $sClassFile = "{$sTempTargetDir}{$sClassFileName}"; + $this->WritePHPFile($sClassFile, $sModuleName, $sModuleVersion, $sCompiledClass); + $sCompiledCode .= "require_once ('{$sFinalTargetDir}{$sClassFileName}');\n"; + } } catch (DOMFormatException $e) { @@ -466,49 +479,7 @@ EOF; // Write the code into the given module as model.<module>.php // $sResultFile = $sTempTargetDir.'/'.$sRelativeDir.'/model.'.$sModuleName.'.php'; - if (is_file($sResultFile)) - { - $this->Log("Updating $sResultFile for module $sModuleName in version $sModuleVersion ($iClassCount classes)"); - } else - { - $sResultDir = dirname($sResultFile); - if (!is_dir($sResultDir)) - { - $this->Log("Creating directory $sResultDir"); - mkdir($sResultDir, 0777, true); - } - $this->Log("Creating $sResultFile for module $sModuleName in version $sModuleVersion ($iClassCount classes)"); - } - - // Compile the module into a single file - // - $sCurrDate = date(DATE_ISO8601); - $sAuthor = 'iTop compiler'; - $sLicence = 'http://opensource.org/licenses/AGPL-3.0'; - $sFileHeader = - <<<EOF -<?php -// -// File generated by ... on the $sCurrDate -// Please do not edit manually -// - -/** - * Classes and menus for $sModuleName (version $sModuleVersion) - * - * @author $sAuthor - * @license $sLicence - */ - -EOF; - $ret = file_put_contents($sResultFile, $sFileHeader.$sCompiledCode); - if ($ret === false) - { - $iLen = strlen($sFileHeader.$sCompiledCode); - $fFree = @disk_free_space(dirname($sResultFile)); - $aErr = error_get_last(); - throw new Exception("Failed to write '$sResultFile'. Last error: '{$aErr['message']}', content to write: $iLen bytes, available free space on disk: $fFree."); - } + $this->WritePHPFile($sResultFile, $sModuleName, $sModuleVersion, $sCompiledCode); } else { @@ -611,8 +582,8 @@ EOF; SetupUtils::builddir($sTempTargetDir.'/core'); $sPHPFile = $sTempTargetDir.'/core/main.php'; file_put_contents($sPHPFile, $this->sMainPHPCode); - + $sCurrDate = date(DATE_ISO8601); // Autoload $sPHPFile = $sTempTargetDir.'/autoload.php'; $sPHPFileContent = @@ -1018,7 +989,7 @@ EOF * @param string $sFinalTargetDir * @param string $sModuleRelativeDir * - * @return string + * @return array * @throws \DOMFormatException */ protected function CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sModuleRelativeDir) @@ -1997,7 +1968,7 @@ EOF $sClassName = $oClass->getAttribute('id'); $bIsAbstractClass = ($oProperties->GetChildText('abstract') == 'true'); $oPhpParent = $oClass->GetUniqueElement('php_parent', false); - $aRequiredFiles = array(); + $aRequiredFiles = []; if ($oPhpParent) { $sParentClass = $oPhpParent->GetChildText('name', ''); @@ -2030,8 +2001,7 @@ $sZlists; EOF; // some other stuff (magical attributes like friendlyName) are done in MetaModel::InitClasses and though not present in the // generated PHP - $sPHP = $this->GeneratePhpCodeForClass($sClassName, $sParentClass, $sClassParams, $sInitMethodCalls, - $bIsAbstractClass, $sMethods, $aRequiredFiles, $sCodeComment); + $aPHP[$sClassName] = $this->GeneratePhpCodeForClass($sClassName, $sParentClass, $sClassParams, $sInitMethodCalls, $bIsAbstractClass, $sMethods, $sCodeComment); // N°931 generates TagFieldData classes for AttributeTag fields if (!empty($aTagFieldsInfo)) @@ -2060,11 +2030,11 @@ EOF { $sTagClassName = static::GetTagDataClassName($sClassName, $sTagFieldName); $sTagClassParams = var_export($aTagClassParams, true); - $sPHP .= $this->GeneratePhpCodeForClass($sTagClassName, $sTagClassParentClass, $sTagClassParams, $sTagInitMethodCalls); + $aPHP[$sTagClassName] = $this->GeneratePhpCodeForClass($sTagClassName, $sTagClassParentClass, $sTagClassParams, $sTagInitMethodCalls); } } - return $sPHP; + return ['code' => $aPHP, 'required_files' => $aRequiredFiles]; } private static function GetTagDataClassName($sClass, $sAttCode) @@ -3008,22 +2978,21 @@ EOF; * @param bool $bIsAbstractClass * @param string $sMethods * - * @param array $aRequiredFiles * @param string $sCodeComment * * @return string php code for the class */ private function GeneratePhpCodeForClass( - $sClassName, $sParentClassName, $sClassParams, $sInitMethodCalls = '', $bIsAbstractClass = false, $sMethods = '', - $aRequiredFiles = array(), $sCodeComment = '' + $sClassName, + $sParentClassName, + $sClassParams, + $sInitMethodCalls = '', + $bIsAbstractClass = false, + $sMethods = '', + $sCodeComment = '' ) { $sPHP = "\n\n$sCodeComment\n"; - foreach ($aRequiredFiles as $sIncludeFile) - { - $sPHP .= "\nrequire_once('$sIncludeFile');\n"; - } - if ($bIsAbstractClass) { $sPHP .= 'abstract class '.$sClassName; @@ -3126,7 +3095,7 @@ EOF; */ protected function WriteStaticOnlyWebConfig($sTempTargetDir) { - $sContent = <<<EOF + $sContent = <<<XML <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.webServer> @@ -3159,8 +3128,57 @@ EOF; </system.webServer> </configuration> -EOF; +XML; $this->WriteFile("$sTempTargetDir/web.config", $sContent); } + + /** + * @param $sResultFile + * @param $sModuleName + * @param $sModuleVersion + * @param $sCompiledCode + * + * @throws \Exception + */ + protected function WritePHPFile($sResultFile, $sModuleName, $sModuleVersion, $sCompiledCode) + { + if (is_file($sResultFile)) + { + $this->Log("Updating $sResultFile for module $sModuleName in version $sModuleVersion"); + } + else + { + $sResultDir = dirname($sResultFile); + if (!is_dir($sResultDir)) + { + $this->Log("Creating directory $sResultDir"); + mkdir($sResultDir, 0777, true); + } + $this->Log("Creating $sResultFile for module $sModuleName in version $sModuleVersion"); + } + + // Compile the module into a single file + // + $sCurrDate = date(DATE_ISO8601); + $sAuthor = 'iTop compiler'; + $sLicence = 'http://opensource.org/licenses/AGPL-3.0'; + $sFileHeader = + <<<EOF +<?php +// +// File generated by ... on the $sCurrDate +// Please do not edit manually +// + +/** + * Classes and menus for $sModuleName (version $sModuleVersion) + * + * @author $sAuthor + * @license $sLicence + */ + +EOF; + $this->WriteFile($sResultFile, $sFileHeader.$sCompiledCode); + } } diff --git a/setup/extensionsmap.class.inc.php b/setup/extensionsmap.class.inc.php index 4401175e6..53ef0392b 100644 --- a/setup/extensionsmap.class.inc.php +++ b/setup/extensionsmap.class.inc.php @@ -277,7 +277,6 @@ class iTopExtensionsMap $oExtension->sVersion = $oXml->Get('version'); $oExtension->bMandatory = ($oXml->Get('mandatory') == 'true'); $oExtension->sMoreInfoUrl = $oXml->Get('more_info_url'); - $oExtension->sVersion = $oXml->Get('version'); $oExtension->sSource = $sSource; $oExtension->sSourceDir = $sSearchDir; diff --git a/setup/modelfactory.class.inc.php b/setup/modelfactory.class.inc.php index 14880065d..2bc507c99 100644 --- a/setup/modelfactory.class.inc.php +++ b/setup/modelfactory.class.inc.php @@ -256,18 +256,21 @@ class MFModule public function GetDictionaryFiles() { $aDictionaries = array(); - if ($hDir = opendir($this->sRootDir)) + foreach (array($this->sRootDir, $this->sRootDir.'/dictionaries') as $sRootDir) { - while (($sFile = readdir($hDir)) !== false) + if ($hDir = @opendir($sRootDir)) { - $aMatches = array(); - if (preg_match("/^[^\\.]+.dict.".$this->sName.'.php$/i', $sFile, - $aMatches)) // Dictionary files are named like <Lang>.dict.<ModuleName>.php + while (($sFile = readdir($hDir)) !== false) { - $aDictionaries[] = $this->sRootDir.'/'.$sFile; + $aMatches = array(); + if (preg_match("/^[^\\.]+.dict.".$this->sName.'.php$/i', $sFile, + $aMatches)) // Dictionary files are named like <Lang>.dict.<ModuleName>.php + { + $aDictionaries[] = $sRootDir.'/'.$sFile; + } } + closedir($hDir); } - closedir($hDir); } return $aDictionaries; @@ -466,18 +469,21 @@ class MFDictModule extends MFModule public function GetDictionaryFiles() { $aDictionaries = array(); - if ($hDir = opendir($this->sRootDir)) + foreach (array($this->sRootDir, $this->sRootDir.'/dictionaries') as $sRootDir) { - while (($sFile = readdir($hDir)) !== false) + if ($hDir = @opendir($sRootDir)) { - $aMatches = array(); - if (preg_match("/^.*dictionary\\.itop.*.php$/i", $sFile, - $aMatches)) // Dictionary files are named like <Lang>.dict.<ModuleName>.php + while (($sFile = readdir($hDir)) !== false) { - $aDictionaries[] = $this->sRootDir.'/'.$sFile; + $aMatches = array(); + if (preg_match("/^.*dictionary\\.itop.*.php$/i", $sFile, + $aMatches)) // Dictionary files are named like <Lang>.dict.<ModuleName>.php + { + $aDictionaries[] = $sRootDir.'/'.$sFile; + } } + closedir($hDir); } - closedir($hDir); } return $aDictionaries; @@ -2898,4 +2904,4 @@ class MFParameters return $merged; } -} \ No newline at end of file +} diff --git a/setup/modulediscovery.class.inc.php b/setup/modulediscovery.class.inc.php index a77db0ecd..07d78c618 100644 --- a/setup/modulediscovery.class.inc.php +++ b/setup/modulediscovery.class.inc.php @@ -134,17 +134,20 @@ class ModuleDiscovery { $sModuleName = $aMatches[1]; $sDir = dirname($sFilePath); - if ($hDir = opendir($sDir)) + foreach (array($sDir, $sDir.'/dictionaries') as $sRootDir) { - while (($sFile = readdir($hDir)) !== false) + if ($hDir = @opendir($sRootDir)) { - $aMatches = array(); - if (preg_match("/^[^\\.]+.dict.$sModuleName.php$/i", $sFile, $aMatches)) // Dictionary files named like <Lang>.dict.<ModuleName>.php are loaded automatically + while (($sFile = readdir($hDir)) !== false) { - self::$m_aModules[$sId]['dictionary'][] = self::$m_sModulePath.'/'.$sFile; + $aMatches = array(); + if (preg_match("/^[^\\.]+.dict.$sModuleName.php$/i", $sFile, $aMatches)) // Dictionary files named like <Lang>.dict.<ModuleName>.php are loaded automatically + { + self::$m_aModules[$sId]['dictionary'][] = $sRootDir.'/'.$sFile; + } } + closedir($hDir); } - closedir($hDir); } } } diff --git a/setup/setuputils.class.inc.php b/setup/setuputils.class.inc.php index 70911820c..30b95c1bb 100644 --- a/setup/setuputils.class.inc.php +++ b/setup/setuputils.class.inc.php @@ -39,6 +39,50 @@ class CheckResult $this->sLabel = $sLabel; $this->sDescription = $sDescription; } + + /** + * @return string + * @since 2.8.0 N°2214 + */ + public function __toString() + { + $sPrintDesc = (empty($this->sDescription)) ? '' : " ({$this->sDescription})"; + return "{$this->sLabel}$sPrintDesc"; + } + + /** + * @param \CheckResult[] $aResults + * + * @return \CheckResult[] only elements that are error (iSeverity===ERROR) + * + * @since 2.8.0 N°2214 + */ + public static function KeepOnlyErrors($aResults) + { + return array_filter($aResults, + static function ($v) + { + if ($v->iSeverity === CheckResult::ERROR) { + return $v; + } + }, + ARRAY_FILTER_USE_BOTH); + } + + /** + * @param \CheckResult[] $aResults + * + * @return string[] + * @uses \CheckResult::__toString + * + * @since 2.8.0 N°2214 + */ + public static function FromObjectsToStrings($aResults) + { + return array_map(function ($value) { + return $value->__toString(); + }, $aResults); + } } /** @@ -50,15 +94,15 @@ class CheckResult class SetupUtils { // -- Minimum versions (requirements : forbids installation if not met) - const PHP_MIN_VERSION = '5.6.0'; // 5.6 will be supported until the end of 2018 (see http://php.net/supported-versions.php) + const PHP_MIN_VERSION = '7.1.3'; // 7 will be supported until the end of 2019 (see http://php.net/supported-versions.php) const MYSQL_MIN_VERSION = '5.6.0'; // 5.6 to have fulltext on InnoDB for Tags fields (N°931) const MYSQL_NOT_VALIDATED_VERSION = ''; // MySQL 8 is now OK (N°2010 in 2.7.0) but has no query cache so mind the perf on large volumes ! // -- versions that will be the minimum in next iTop major release (warning if not met) - const PHP_NEXT_MIN_VERSION = '7.1.3'; // we are aiming on switching to Symfony 4 in iTop 2.8 + const PHP_NEXT_MIN_VERSION = ''; // const MYSQL_NEXT_MIN_VERSION = ''; // no new MySQL requirement for next iTop version // -- First recent version that is not yet validated by Combodo (warning) - const PHP_NOT_VALIDATED_VERSION = '7.5.0'; + const PHP_NOT_VALIDATED_VERSION = '8.0.0'; const MIN_MEMORY_LIMIT = 33554432; // 32 * 1024 * 1024 - we can use expressions in const since PHP 5.6 but we are in the setup ! const SUHOSIN_GET_MAX_VALUE_LENGTH = 2048; @@ -76,7 +120,7 @@ class SetupUtils * @internal SetupPage $oP The page used only for its 'log' method * @return CheckResult[] */ - static function CheckPhpAndExtensions() + public static function CheckPhpAndExtensions() { $aResult = array(); @@ -370,6 +414,37 @@ class SetupUtils return $aResult; } + /** + * @param \CLIPage $oCliPage + * @param int $iExitCode + * + * @since 2.8.0 N°2214 + */ + public static function CheckPhpAndExtensionsForCli($oCliPage, $iExitCode = -1) + { + $aPhpCheckResults = self::CheckPhpAndExtensions(); + $aPhpCheckErrors = CheckResult::KeepOnlyErrors($aPhpCheckResults); + if (empty($aPhpCheckErrors)) + { + return; + } + + $sMessageTitle = 'Error: PHP minimum requirements are not met !'; + $oCliPage->p($sMessageTitle); + $aPhpCheckErrorsForPrint = CheckResult::FromObjectsToStrings($aPhpCheckErrors); + foreach ($aPhpCheckErrorsForPrint as $sError) + { + $oCliPage->p(' * '.$sError); + } + $oCliPage->output(); + + // some CLI scripts are launched automatically + // we need a log so that we don't miss errors after migration ! + IssueLog::Error($oCliPage->s_title.' '.$sMessageTitle, 'CLI', $aPhpCheckErrorsForPrint); + + exit($iExitCode); + } + /** * @param CheckResult[] $aResult checks log */ @@ -419,7 +494,7 @@ class SetupUtils * @param $aSelectedModules * @return array */ - static function CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules) + public static function CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules) { $aResult = array(); SetupPage::log('Info - CheckSelectedModules'); @@ -450,7 +525,7 @@ class SetupUtils * @return array An array of CheckResults objects * @internal param Page $oP The page used only for its 'log' method */ - static function CheckBackupPrerequisites($sDBBackupPath, $sMySQLBinDir = null) + public static function CheckBackupPrerequisites($sDBBackupPath, $sMySQLBinDir = null) { $aResult = array(); SetupPage::log('Info - CheckBackupPrerequisites'); @@ -540,7 +615,7 @@ class SetupUtils * @return CheckResult The result of the check * @internal param string $GraphvizPath The path where graphviz' dot program is installed */ - static function CheckGraphviz($sGraphvizPath) + public static function CheckGraphviz($sGraphvizPath) { $oResult = null; SetupPage::log('Info - CheckGraphviz'); @@ -589,7 +664,7 @@ class SetupUtils * Emulates sys_get_temp_dir if needed (PHP < 5.2.1) * @return string Path to the system's temp directory */ - static function GetTmpDir() + public static function GetTmpDir() { return realpath(sys_get_temp_dir()); } @@ -598,7 +673,7 @@ class SetupUtils * Helper function to retrieve the directory where files are to be uploaded * @return string Path to the temp directory used for uploading files */ - static function GetUploadTmpDir() + public static function GetUploadTmpDir() { $sPath = ini_get('upload_tmp_dir'); if (empty($sPath)) @@ -821,7 +896,7 @@ class SetupUtils } } - static function GetPreviousInstance($sDir) + public static function GetPreviousInstance($sDir) { $sSourceDir = ''; $sSourceEnvironment = ''; @@ -875,7 +950,7 @@ class SetupUtils * @return bool|float false if failure * @uses \disk_free_space() */ - static function CheckDiskSpace($sDir) + public static function CheckDiskSpace($sDir) { while(($f = @disk_free_space($sDir)) == false) { @@ -887,7 +962,7 @@ class SetupUtils return $f; } - static function HumanReadableSize($fBytes) + public static function HumanReadableSize($fBytes) { $aSizes = array('bytes', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Hb'); $index = 0; @@ -912,7 +987,7 @@ class SetupUtils * @param string $sTlsCA * @param string $sNewDBName */ - static function DisplayDBParameters( + public static function DisplayDBParameters( $oPage, $bIsItopInstall, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $bTlsEnabled, $sTlsCA, $sNewDBName = '' ) { @@ -1155,7 +1230,7 @@ EOF * @return bool|array false if the connection failed or array('checks' => Array of CheckResult, 'databases' => * Array of database names (as strings) or null if not allowed) */ - static function CheckDbServer( + public static function CheckDbServer( $sDBServer, $sDBUser, $sDBPwd, $bTlsEnabled = false, $sTlsCA = null ) { @@ -1606,29 +1681,16 @@ JS } $sManualInstallModulesFullPath = APPROOT.$sExtensionsDir.DIRECTORY_SEPARATOR; - $aManualInstallModules = array_filter($aModules, - static function ($v, $k) use ($sManualInstallModulesFullPath) { - if (!isset($v['root_dir'])) // avoid index undefined for the _Root_ entry - { - return false; - } - // calling realpath to avoid problems with dir separator (almost everywhere we are adding '/' instead of DIRECTORY_SEPARATOR) - $return = utils::RealPath($v['root_dir'], $sManualInstallModulesFullPath); - if ($return === false) - { - return false; - } - - return true; - }, - ARRAY_FILTER_USE_BOTH); - - if (empty($aManualInstallModules)) + //simple test in order to prevent install iTop pro with module in extension folder + $aFileInfo = scandir($sManualInstallModulesFullPath); + foreach ($aFileInfo as $sFolder) { - return ''; + if ($sFolder != "." && $sFolder != ".." && is_dir($sManualInstallModulesFullPath.$sFolder) === true) + { + return "Some modules are present in the '$sExtensionsDir' directory, this is not allowed when using ".ITOP_APPLICATION; + } } - - return "Some modules are present in the '$sExtensionsDir' directory, this is not allowed when using ".ITOP_APPLICATION; + return ''; } /** @@ -2067,7 +2129,7 @@ JS */ class SetupInfo { - static $aSelectedModules = array(); + public static $aSelectedModules = array(); /** * Called by the setup process to initializes the list of selected modules. Do not call this method @@ -2075,7 +2137,7 @@ class SetupInfo * @param hash $aModules * @return void */ - static function SetSelectedModules($aModules) + public static function SetSelectedModules($aModules) { self::$aSelectedModules = $aModules; } @@ -2086,7 +2148,7 @@ class SetupInfo * @param string $sModuleId The identifier of the module (without the version number. Example: itop-config-mgmt) * @return boolean True if the module is already selected, false otherwise */ - static function ModuleIsSelected($sModuleId) + public static function ModuleIsSelected($sModuleId) { return (array_key_exists($sModuleId, self::$aSelectedModules)); } diff --git a/sources/application/TwigBase/Controller/Controller.php b/sources/application/TwigBase/Controller/Controller.php index 64907aadb..7ff7eb49d 100644 --- a/sources/application/TwigBase/Controller/Controller.php +++ b/sources/application/TwigBase/Controller/Controller.php @@ -23,6 +23,7 @@ use ajax_page; use ApplicationMenu; use Combodo\iTop\Application\TwigBase\Twig\TwigHelper; use Dict; +use ErrorPage; use Exception; use IssueLog; use iTopWebPage; @@ -160,7 +161,7 @@ abstract class Controller require_once(APPROOT."/setup/setuppage.class.inc.php"); http_response_code(500); - $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n"); $oP->add(get_class($e).' : '.htmlentities($e->GetMessage(), ENT_QUOTES, 'utf-8')); $oP->output(); diff --git a/sources/application/TwigBase/Twig/TwigHelper.php b/sources/application/TwigBase/Twig/TwigHelper.php index ad1bcfb39..98f1560aa 100644 --- a/sources/application/TwigBase/Twig/TwigHelper.php +++ b/sources/application/TwigBase/Twig/TwigHelper.php @@ -22,6 +22,14 @@ class TwigHelper $oLoader = new Twig_Loader_Filesystem($sViewPath); $oTwig = new Twig_Environment($oLoader); Extension::RegisterTwigExtensions($oTwig); + if (!utils::IsDevelopmentEnvironment()) + { + // Disable the cache in development environment + $sLocalPath = utils::LocalPath($sViewPath); + $sLocalPath = str_replace('env-'.utils::GetCurrentEnvironment(), 'twig', $sLocalPath); + $sCachePath = utils::GetCachePath().$sLocalPath; + $oTwig->setCache($sCachePath); + } return $oTwig; } diff --git a/sources/application/search/criterionconversion/criteriontooql.class.inc.php b/sources/application/search/criterionconversion/criteriontooql.class.inc.php index cc90ae6f8..cf3e84c8c 100644 --- a/sources/application/search/criterionconversion/criteriontooql.class.inc.php +++ b/sources/application/search/criterionconversion/criteriontooql.class.inc.php @@ -123,7 +123,6 @@ class CriterionToOQL extends CriterionConversionAbstract { return "1"; } - $sValue = str_replace('_', '\_', $sValue); return "({$sRef} LIKE '%{$sValue}%')"; } @@ -412,7 +411,7 @@ class CriterionToOQL extends CriterionConversionAbstract // Use the 'below' operator by default $oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode); $oCriteria = $oSearch->GetCriteria(); - $aArgs = MetaModel::PrepareQueryArguments(array(), $oSearch->GetInternalParams()); + $aArgs = MetaModel::PrepareQueryArguments(array(), $oSearch->GetInternalParams(), $oSearch->GetExpectedArguments() ); $oSearch->ResetCondition(); $sCondition = $oCriteria->Render($aArgs); } diff --git a/sources/application/search/searchform.class.inc.php b/sources/application/search/searchform.class.inc.php index 7b77e5f2c..86da53990 100644 --- a/sources/application/search/searchform.class.inc.php +++ b/sources/application/search/searchform.class.inc.php @@ -530,7 +530,7 @@ class SearchForm { $aOrCriterion = array(); $bIsEmptyExpression = true; - $aArgs = MetaModel::PrepareQueryArguments($aArgs, $oSearch->GetInternalParams()); + $aArgs = MetaModel::PrepareQueryArguments($aArgs, $oSearch->GetInternalParams(), $oSearch->GetExpectedArguments()); if ($oSearch instanceof DBObjectSearch) { diff --git a/sources/renderer/bootstrap/fieldrenderer/bsfileuploadfieldrenderer.class.inc.php b/sources/renderer/bootstrap/fieldrenderer/bsfileuploadfieldrenderer.class.inc.php index 642d6b375..299bba55c 100644 --- a/sources/renderer/bootstrap/fieldrenderer/bsfileuploadfieldrenderer.class.inc.php +++ b/sources/renderer/bootstrap/fieldrenderer/bsfileuploadfieldrenderer.class.inc.php @@ -143,7 +143,7 @@ var oTable_{$this->oField->GetGlobalId()}; var buildTable_{$this->oField->GetGlobalId()} = function() { oTable_{$this->oField->GetGlobalId()} = $("table#$sAttachmentTableId").DataTable( { - "dom": "t", + "dom": "tp", "order": [[3, "asc"]], "columnDefs": [ $sDeleteColumnDef diff --git a/sources/renderer/bootstrap/fieldrenderer/bssimplefieldrenderer.class.inc.php b/sources/renderer/bootstrap/fieldrenderer/bssimplefieldrenderer.class.inc.php index 947f3b310..7449aa05f 100644 --- a/sources/renderer/bootstrap/fieldrenderer/bssimplefieldrenderer.class.inc.php +++ b/sources/renderer/bootstrap/fieldrenderer/bssimplefieldrenderer.class.inc.php @@ -247,7 +247,7 @@ EOF name: $(me).closest(".form_field").attr("data-field-id"), value: $(me).val() }); - }); + }).on("mouseup", function(){this.focus();}); EOF ); break; diff --git a/synchro/replica.php b/synchro/replica.php index bbfc50da9..166b015ec 100644 --- a/synchro/replica.php +++ b/synchro/replica.php @@ -67,6 +67,7 @@ try $oBlock->Display($oP, 1); break; + case 'delete': case 'select_for_deletion': // Redirect to the page that implements bulk delete $sDelete = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?'.$_SERVER['QUERY_STRING']; diff --git a/synchro/synchro_exec.php b/synchro/synchro_exec.php index d212ca776..df22e9219 100644 --- a/synchro/synchro_exec.php +++ b/synchro/synchro_exec.php @@ -72,6 +72,7 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter = 'parameter') if (utils::IsModeCLI()) { $oP = new CLIPage(Dict::S("TitleSynchroExecution")); + SetupUtils::CheckPhpAndExtensionsForCli($oP, -2); } else { diff --git a/synchro/synchro_import.php b/synchro/synchro_import.php index 0c065d03a..84b905fc0 100644 --- a/synchro/synchro_import.php +++ b/synchro/synchro_import.php @@ -255,6 +255,7 @@ class CLILikeWebPage extends WebPage if (utils::IsModeCLI()) { $oP = new CLIPage(Dict::S('TitleSynchroExecution')); + SetupUtils::CheckPhpAndExtensionsForCli($oP, -2); } else { diff --git a/test/ItopDataTestCase.php b/test/ItopDataTestCase.php index eddbffcc6..9a26e7c10 100644 --- a/test/ItopDataTestCase.php +++ b/test/ItopDataTestCase.php @@ -713,7 +713,7 @@ class ItopDataTestCase extends ItopTestCase $iId = $oLnk->Get('functionalci_id'); if (!empty($aWaitedCIList)) { - $this->assertTrue(array_key_exists($iId, $aWaitedCIList)); + $this->assertArrayHasKey($iId, $aWaitedCIList); $this->assertEquals($aWaitedCIList[$iId], $oLnk->Get('impact_code')); } } @@ -737,7 +737,7 @@ class ItopDataTestCase extends ItopTestCase $iId = $oLnk->Get('contact_id'); if (!empty($aWaitedContactList)) { - $this->assertTrue(array_key_exists($iId, $aWaitedContactList)); + $this->assertArrayHasKey($iId, $aWaitedContactList); foreach ($aWaitedContactList[$iId] as $sAttCode => $oValue) { if (MetaModel::IsValidAttCode(get_class($oTicket), $sAttCode)) @@ -756,5 +756,29 @@ class ItopDataTestCase extends ItopTestCase $this->iTestOrgId = $oOrg->GetKey(); } - + /** + * Assert that a series of operations will trigger a given number of MySL queries + * + * @param $iExpectedCount Number of MySQL queries that should be executed + * @param callable $oFunction Operations to perform + * + * @throws \MySQLException + * @throws \MySQLQueryHasNoResultException + */ + protected static function assertDBQueryCount($iExpectedCount, callable $oFunction) + { + $iInitialCount = (int) CMDBSource::QueryToScalar("SHOW SESSION STATUS LIKE 'Queries'", 1); + $oFunction(); + $iFinalCount = (int) CMDBSource::QueryToScalar("SHOW SESSION STATUS LIKE 'Queries'", 1); + $iCount = $iFinalCount - 1 - $iInitialCount; + if ($iCount != $iExpectedCount) + { + static::fail("Expected $iExpectedCount queries. $iCount have been executed."); + } + else + { + // Otherwise PHP Unit will consider that no assertion has been made + static::assertTrue(true); + } + } } diff --git a/test/OQL/OQLToSQLTest.php b/test/OQL/OQLToSQLTest.php index d12b03f39..dec00c31e 100644 --- a/test/OQL/OQLToSQLTest.php +++ b/test/OQL/OQLToSQLTest.php @@ -341,6 +341,7 @@ class OQLToSQLTest extends ItopDataTestCase ); $aData = array( + "SELECT UNION 5.6" => array("SELECT `Organization` AS `Organization` WHERE ((`Organization`.`status` != 'inactive') AND (`Organization`.`id` = '1')) UNION SELECT `Organization` AS `Organization` WHERE ((`Organization`.`id` = '1') AND (`Organization`.`id` = '1'))", array(), array(), array(), null, null, 3, 0), "SELECT WebServer 150" => array("SELECT `WebServer` FROM WebServer AS `WebServer` WHERE 1", array(), array(), $aAttToLoad150, null, null, 3, 0), "SELECT WebServer 151" => array("SELECT `WebServer` FROM WebServer AS `WebServer` WHERE 1", array(), array(), array(), null, null, 3, 0), "SELECT L JOIN 176" => array("SELECT `L` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id WHERE 1", unserialize('a:1:{s:14:"L.friendlyname";b:1;}'), array(), null, null, 3, 0), diff --git a/test/application/UtilsTest.php b/test/application/UtilsTest.php index 9fe2bd0f3..be2819237 100644 --- a/test/application/UtilsTest.php +++ b/test/application/UtilsTest.php @@ -103,6 +103,48 @@ class UtilsTest extends \Combodo\iTop\Test\UnitTest\ItopTestCase ]; } + /** + * @dataProvider LocalPathProvider + * + * @param $sAbsolutePath + * @param $expected + */ + public function testLocalPath($sAbsolutePath, $expected) + { + $this->assertSame($expected, utils::LocalPath($sAbsolutePath)); + + } + + public function LocalPathProvider() + { + return array( + 'index.php' => array( + 'sAbsolutePath' => APPROOT.'index.php', + 'expected' => 'index.php', + ), + 'non existing' => array( + 'sAbsolutePath' => APPROOT.'nonexisting/nonexisting', + 'expected' => false, + ), + 'outside' => array( + 'sAbsolutePath' => '/tmp', + 'expected' => false, + ), + 'application/cmdbabstract.class.inc.php' => array( + 'sAbsolutePath' => APPROOT.'application/cmdbabstract.class.inc.php', + 'expected' => 'application/cmdbabstract.class.inc.php', + ), + 'dir' => array( + 'sAbsolutePath' => APPROOT.'application/.', + 'expected' => 'application', + ), + 'root' => array( + 'sAbsolutePath' => APPROOT.'.', + 'expected' => '', + ), + ); + } + /** * @dataProvider appRootUrlProvider * @covers utils::GetAppRootUrl diff --git a/test/core/CMDBSourceTest.php b/test/core/CMDBSourceTest.php index 6901da313..010d825ce 100644 --- a/test/core/CMDBSourceTest.php +++ b/test/core/CMDBSourceTest.php @@ -101,6 +101,11 @@ class CMDBSourceTest extends ItopTestCase "ENUM('1','2','3') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '1'", "enum('1','2','3') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '1'", ), + 'ENUM with values containing parenthesis' => array( + true, + "ENUM('CSP A','CSP M','NA','OEM(ROC)','OPEN(VL)','RETAIL (Boite)') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", + "enum('CSP A','CSP M','NA','OEM(ROC)','OPEN(VL)','RETAIL (Boite)') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", + ), ); } } diff --git a/test/core/DBObjectTest.php b/test/core/DBObjectTest.php index 6c514f72a..9e21de2c4 100644 --- a/test/core/DBObjectTest.php +++ b/test/core/DBObjectTest.php @@ -48,6 +48,7 @@ class DBObjectTest extends ItopDataTestCase /** * Test default page name + * @covers DBObject::GetUIPage */ public function testGetUIPage() { @@ -81,6 +82,9 @@ class DBObjectTest extends ItopDataTestCase array('PHP_INT_MIN', false)); } + /** + * @covers DBObject::GetOriginal + */ public function testGetOriginal() { $oObject = $this->CreateUserRequest(190664); @@ -88,4 +92,114 @@ class DBObjectTest extends ItopDataTestCase static::assertNull($oObject->GetOriginal('sla_tto_passed')); } + /** + * @covers DBObject::NewObject + * @covers DBObject::Get + * @covers DBObject::Set + */ + public function testAttributeRefresh_FriendlyName() + { + $oObject = \MetaModel::NewObject('Person', array('name' => 'Foo', 'first_name' => 'John', 'org_id' => 3, 'location_id' => 2)); + + static::assertEquals('John Foo', $oObject->Get('friendlyname')); + $oObject->Set('name', 'Who'); + static::assertEquals('John Who', $oObject->Get('friendlyname')); + } + + /** + * @covers MetaModel::GetObject + * @covers DBObject::Get + * @covers DBObject::Set + */ + public function testAttributeRefresh_FriendlyNameFromDB() + { + $oObject = \MetaModel::NewObject('Person', array('name' => 'Gary', 'first_name' => 'Romain', 'org_id' => 3, 'location_id' => 2)); + $oObject->DBInsert(); + $iObjKey = $oObject->GetKey(); + + $oObject = \MetaModel::GetObject('Person', $iObjKey); + + static::assertEquals('Romain Gary', $oObject->Get('friendlyname')); + $oObject->Set('name', 'Duris'); + static::assertEquals('Romain Duris', $oObject->Get('friendlyname')); + } + + /** + * @covers DBObject::NewObject + * @covers DBObject::Get + * @covers DBObject::Set + */ + public function testAttributeRefresh_ObsolescenceFlag() + { + $oObject = \MetaModel::NewObject('Person', array('name' => 'Foo', 'first_name' => 'John', 'org_id' => 3, 'location_id' => 2)); + + static::assertEquals(false, (bool)$oObject->Get('obsolescence_flag')); + $oObject->Set('status', 'inactive'); + static::assertEquals(true, (bool)$oObject->Get('obsolescence_flag')); + } + + /** + * @covers DBObject::NewObject + * @covers DBObject::Get + * @covers DBObject::Set + */ + public function testAttributeRefresh_ExternalKeysAndFields() + { + static::assertDBQueryCount(0, function() use (&$oObject){ + $oObject = \MetaModel::NewObject('Person', array('name' => 'Foo', 'first_name' => 'John', 'org_id' => 3, 'location_id' => 2)); + }); + static::assertDBQueryCount(2, function() use (&$oObject){ + static::assertEquals('Demo', $oObject->Get('org_id_friendlyname')); + static::assertEquals('Grenoble', $oObject->Get('location_id_friendlyname')); + }); + + // External key given as an id + static::assertDBQueryCount(1, function() use (&$oObject){ + $oObject->Set('org_id', 2); + static::assertEquals('IT Department', $oObject->Get('org_id_friendlyname')); + }); + + // External key given as an object + static::assertDBQueryCount(1, function() use (&$oBordeaux){ + $oBordeaux = \MetaModel::GetObject('Location', 1); + }); + + static::assertDBQueryCount(0, function() use (&$oBordeaux, &$oObject){ + $oObject->Set('location_id', $oBordeaux); + static::assertEquals('IT Department', $oObject->Get('org_id_friendlyname')); + static::assertEquals('IT Department', $oObject->Get('org_name')); + static::assertEquals('Bordeaux', $oObject->Get('location_id_friendlyname')); + }); + } + + public function testSetExtKeyUnsetDependentAttribute() + { + $oObject = \MetaModel::NewObject('Person', array('name' => 'Foo', 'first_name' => 'John', 'org_id' => 3, 'location_id' => 2)); + $oOrg = \MetaModel::GetObject('Organization', 2); + $oObject->Set('org_id', $oOrg); + static::assertEquals(0, $oObject->Get('location_id')); + } + + /** + * @group Integration + */ + public function testModelExpressions() + { + foreach (\MetaModel::GetClasses() as $sClass) + { + if (\MetaModel::IsAbstract($sClass)) continue; + + $oObject = \MetaModel::NewObject($sClass); + foreach (\MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + { + if ($oAttDef->IsBasedOnOQLExpression()) + { + $this->debug("$sClass::$sAttCode"); + static::assertDBQueryCount(0, function() use (&$oObject, &$oAttDef){ + $oObject->EvaluateExpression($oAttDef->GetOQLExpression()); + }); + } + } + } + } } diff --git a/test/core/DBSearchIntersectTest.php b/test/core/DBSearchIntersectTest.php index cd11f97ba..96379b2e4 100644 --- a/test/core/DBSearchIntersectTest.php +++ b/test/core/DBSearchIntersectTest.php @@ -3,7 +3,7 @@ namespace Combodo\iTop\Test\UnitTest\Core; use CMDBSource; -use Combodo\iTop\Test\UnitTest\ItopDataTestCase; +use Combodo\iTop\Test\UnitTest\ItopTestCase; use DBSearch; /** @@ -15,9 +15,8 @@ use DBSearch; * @preserveGlobalState disabled * @backupGlobals disabled */ -class DBSearchIntersectTest extends ItopDataTestCase +class DBSearchIntersectTest extends ItopTestCase { - const USE_TRANSACTION = false; protected function setUp() { @@ -52,6 +51,19 @@ class DBSearchIntersectTest extends ItopDataTestCase { $aTests = array(); + $aTests['Union filtered by parent class'] = array( + 'left' => "SELECT ApplicationSolution UNION SELECT BusinessProcess", + 'right' => "SELECT FunctionalCI WHERE org_id = 3", + 'alias' => "ApplicationSolution", + 'result' => "SELECT `ApplicationSolution` FROM ApplicationSolution AS `ApplicationSolution` WHERE (`ApplicationSolution`.`org_id` = 3) UNION SELECT `BusinessProcess` FROM BusinessProcess AS `BusinessProcess` WHERE (`BusinessProcess`.`org_id` = 3)"); + +// Bug to fix +// $aTests['Test union #2902'] = array( +// 'left' => "SELECT `L-1` FROM ServiceFamily AS `L-1` WHERE 1", +// 'right' => "SELECT `sf` FROM ServiceFamily AS `sf` JOIN Service AS `s` ON `s`.servicefamily_id = `sf`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `s`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id WHERE (`cc`.`org_id` = 3) UNION SELECT `sf` FROM ServiceFamily AS `sf` JOIN Service AS `s` ON `s`.servicefamily_id = `sf`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `s`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN Organization AS `child` ON `cc`.org_id = `child`.id JOIN Organization AS `root` ON `child`.parent_id BELOW `root`.id WHERE (`root`.`id` = 3)", +// 'alias' => "L-1", +// 'result' => "SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `s` ON `s`.servicefamily_id = `L-1`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `s`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id WHERE (`cc`.`org_id` = 3) UNION SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `s` ON `s`.servicefamily_id = `L-1`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `s`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN Organization AS `child` ON `cc`.org_id = `child`.id JOIN Organization AS `root` ON `child`.parent_id BELOW `root`.id WHERE (`root`.`id` = 3)"); + $aTests['Multiple selected classes inverted'] = array( 'left' => "SELECT `L`, `P` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id WHERE 1", 'right' => "SELECT Person WHERE org_id = 3", @@ -112,6 +124,26 @@ class DBSearchIntersectTest extends ItopDataTestCase 'alias' => "P", 'result' => "SELECT `L` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id JOIN Location AS `L1` ON `P`.location_id = `L1`.id WHERE (`L1`.`org_id` = 3)"); + $aTests['Test Subclass1'] = array( + 'left' => "SELECT `U` FROM UserRequest AS `U` WHERE `U`.agent_id = 3", + 'right' => "SELECT `Ticket` WHERE org_id = 3", + 'alias' => "U", + 'result' => "SELECT `U` FROM UserRequest AS `U` WHERE ((`U`.`agent_id` = 3) AND (`U`.`org_id` = 3))"); + + $aTests['Test Subclass and join'] = array( + 'left' => "SELECT `UserRequest` FROM UserRequest AS `UserRequest` JOIN Person AS `P` ON `UserRequest`.agent_id = `P`.id WHERE `UserRequest`.agent_id = 3", + 'right' => "SELECT `Ticket` WHERE org_id = 3", + 'alias' => "UserRequest", + 'result' => "SELECT `UserRequest` FROM UserRequest AS `UserRequest` JOIN Person AS `P` ON `UserRequest`.agent_id = `P`.id WHERE ((`UserRequest`.`agent_id` = 3) AND (`UserRequest`.`org_id` = 3))"); + + $aTests['Test Subclass and union'] = array( + 'left' => "SELECT `U` FROM UserRequest AS `U` WHERE `U`.agent_id = 3 UNION SELECT `T` FROM Ticket AS `T` WHERE `T`.agent_id = 3 ", + 'right' => "SELECT `Ticket` WHERE org_id = 3", + 'alias' => "U", + 'result' => "SELECT `U` FROM UserRequest AS `U` WHERE ((`U`.`agent_id` = 3) AND (`U`.`org_id` = 3)) UNION SELECT `T` FROM Ticket AS `T` WHERE ((`T`.`agent_id` = 3) AND (`T`.`org_id` = 3))"); + + + return $aTests; } @@ -422,4 +454,53 @@ class DBSearchIntersectTest extends ItopDataTestCase return $aQueries; } + + /** + * Bug #2970 + * @throws \CoreException + * @throws \CoreWarning + * @throws \OQLException + */ + public function testFilterOnJoin() + { + $sReq1 = "SELECT `L-1` FROM Organization AS `L-1` WHERE (`L-1`.`id` = :current_contact->org_id)"; + $sReq2 = "SELECT `L-1-1` FROM CustomerContract AS `L-1-1` JOIN Organization AS `O` ON `L-1-1`.org_id = `O`.id WHERE (((`L-1-1`.`status` = 'active') OR (`L-1-1`.`status` = 'standby')) AND (`O`.`id` = :current_contact->org_id))"; + + $oFilter1 = DBSearch::FromOQL($sReq1); + $oFilter2 = DBSearch::FromOQL($sReq2); + $aRealiasingMap = array(); + $oFilter1 = $oFilter1->Join($oFilter2, + DBSearch::JOIN_REFERENCED_BY, + 'org_id', + TREE_OPERATOR_EQUALS, $aRealiasingMap); + + $sRes1 = $oFilter1->ToOQL(); + $this->debug($sRes1); + + foreach($oFilter1->GetCriteria_ReferencedBy() as $sForeignClass => $aReferences) + { + foreach ($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator) + { + foreach ($aFiltersByOperator as $iOperatorCode => $aFilters) + { + foreach ($aFilters as $index => $oForeignFilter) + { + $this->debug($oForeignFilter->ToOQL()); + } + } + } + } + + $this->assertFalse(strpos($sRes1, '`O`.')); + + $sReq3 = "SELECT `CustomerContract` FROM CustomerContract AS `CustomerContract` WHERE (`CustomerContract`.`org_id` IN ('2'))"; + $oFilter3 = DBSearch::FromOQL($sReq3); + + $oFilter1 = $oFilter1->Filter('L-1-1', $oFilter3); + + $sRes1 = $oFilter1->ToOQL(); + $this->debug($sRes1); + + $this->assertFalse(strpos($sRes1, '`O`.')); + } } diff --git a/test/core/ExpressionEvaluateTest.php b/test/core/ExpressionEvaluateTest.php new file mode 100644 index 000000000..79d9bc38b --- /dev/null +++ b/test/core/ExpressionEvaluateTest.php @@ -0,0 +1,535 @@ +<?php + + +namespace Combodo\iTop\Test\UnitTest\Core; + + +use CMDBSource; +use Combodo\iTop\Test\UnitTest\iTopDataTestCase; +use DateInterval; +use DateTime; +use Expression; +use FunctionExpression; +use MetaModel; +use ScalarExpression; + +class ExpressionEvaluateTest extends iTopDataTestCase +{ + const USE_TRANSACTION = false; + + /** + * @covers Expression::GetParameters() + * @dataProvider GetParametersProvider + * + * @param $sExpression + * @param $sParentFilter + * @param $aExpectedParameters + * + * @throws \OQLException + */ + public function testGetParameters($sExpression, $sParentFilter, $aExpectedParameters) + { + $oExpression = Expression::FromOQL($sExpression); + $aParameters = $oExpression->GetParameters($sParentFilter); + sort($aExpectedParameters); + sort($aParameters); + static::assertEquals($aExpectedParameters, $aParameters); + } + + public function GetParametersProvider() + { + return array( + array('1 AND 0 OR :hello + :world', null, array('hello', 'world')), + array('1 AND 0 OR :hello + :world', 'this', array()), + array(':this->left + :this->right', null, array('this->left', 'this->right')), + array(':this->left + :this->right', 'this', array('left', 'right')), + array(':this->left + :this->right', 'that', array()), + array(':this_left + :this_right', 'this', array()), + ); + } + + /** + * 100x quicker to execute than testExpressionEvaluate + * + * @covers Expression::Evaluate() + * @covers Expression::FromOQL() + * @relies-on-dataProvider VariousExpressions + * @throws \OQLException + */ + public function _testExpressionEvaluateAllAtOnce() + { + $aTestCases = $this->VariousExpressionsProvider(); + foreach ($aTestCases as $sCaseId => $aTestArgs) + { + $this->debug("Case $sCaseId:"); + $this->testVariousExpressions($aTestArgs[0], $aTestArgs[1]); + } + } + + /** + * @covers Expression::Evaluate() + * @covers Expression::FromOQL() + * @dataProvider VariousExpressionsProvider + * + * @param string $sExpression + * @param string $expectedValue + * + * @throws \OQLException + * @throws \Exception + */ + public function testVariousExpressions($sExpression, $expectedValue) + { + $oExpression = Expression::FromOQL($sExpression); + $value = $oExpression->Evaluate(array()); + static::assertEquals($expectedValue, $value); + } + + public function VariousExpressionsProvider() + { + if (false) + { + $aExpressions = array( + // Test case to isolate for troubleshooting purposes + array('1+1', 2), + ); + } + else + { + $aExpressions = array( + // The bare minimum + array('"blah"', 'blah'), + array('"\\\\"', '\\'), + // Arithmetics + array('2+2', 4), + array('2+2-2', 2), + array('2*(3+4)', 14), + array('(2*3)+4', 10), + array('2*3+4', 10), + // Strings + array("CONCAT('hello', 'world')", 'helloworld'), + // Not yet parsed - array("CONCAT_WS(' ', 'hello', 'world')", 'hello world'), + array("SUBSTR('abcdef', 2, 3)", 'bcd'), + array("TRIM(' Sin dolor ')", 'Sin dolor'), + // Comparison operators + array('1 = 1', 1), + array('1 != 1', 0), + array('0 = 1', 0), + array('0 != 1', 1), + array('2 > 1', 1), + array('2 < 1', 0), + array('1 > 2', 0), + array('2 > 1', 1), + array('2 >= 1', 1), + array('2 >= 2', 1), + array("'the quick brown dog' LIKE '%QUICK%'", 1), + array("'the quick brown dog' LIKE '%SLOW%'", 0), + array("'the quick brown dog' LIKE '%QU_CK%'", 1), + array("'the quick brown dog' LIKE '%QU_ICK%'", 0), + array('"400 (km/h)" LIKE "400%"', 1), + array('"400 (km/h)" LIKE "100%"', 0), + array('"2020-06-12" > "2020-06-11"', 1), + array('"2020-06-12" < "2020-06-11"', 0), + array('" 2020-06-12" > "2020-06-11"', 0), // Leading spaces => a string + array('" 2020-06-12 " > "2020-06-11"', 0), // Trailing spaces => a string + array('"2020-06-12 17:35:13" > "2020-06-12 17:35:12"', 1), + array('"2020-06-12 17:35:13" < "2020-06-12 17:35:12"', 0), + array('"2020-06-12 17:35:13" > "2020-06-12"', 1), + array('"2020-06-12 17:35:13" < "2020-06-12"', 0), + array('"2020-06-12 00:00:00" = "2020-06-12"', 0), + // Logical operators + array('0 AND 0', 0), + array('1 AND 0', 0), + array('0 AND 1', 0), + array('1 AND 1', 1), + array('0 OR 0', 0), + array('0 OR 1', 1), + array('1 OR 0', 1), + array('1 OR 1', 1), + array('1 AND 0 OR 1', 1), + // Casting + array('1 AND "blah"', 0), + array('1 AND "1"', 1), + array('1 AND "2"', 1), + array('1 AND "0"', 0), + array('1 AND "-1"', 1), + // Null + array('NULL', null), + array('1 AND NULL', null), + array('CONCAT("Great but...", NULL)', null), + array('COALESCE(NULL, 123)', 123), + array('COALESCE(321, 123)', 321), + array('ISNULL(NULL)', 1), + array('ISNULL(123)', 0), + // Date functions + array("DATE('2020-03-12 13:18:30')", '2020-03-12'), + array("DATE_FORMAT('2009-10-04 22:23:00', '%Y %m %d %H %i %s')", '2009 10 04 22 23 00'), + array("DATE(NOW()) = CURRENT_DATE()", 1), // Could fail if executed around midnight! + array("TO_DAYS('2020-01-02')", 737791), + array("FROM_DAYS(737791)", '2020-01-02'), + array("YEAR('2020-05-03')", 2020), + array("MONTH('2020-05-03')", 5), + array("DAY('2020-05-03')", 3), + array("DATE_ADD('2020-02-28 18:00:00', INTERVAL 1 HOUR)", '2020-02-28 19:00:00'), + array("DATE_ADD('2020-02-28 18:00:00', INTERVAL 1 DAY)", '2020-02-29 18:00:00'), + array("DATE_SUB('2020-03-01 18:00:00', INTERVAL 1 HOUR)", '2020-03-01 17:00:00'), + array("DATE_SUB('2020-03-01 18:00:00', INTERVAL 1 DAY)", '2020-02-29 18:00:00'), + // Misc. functions + array('IF(1, 123, 567)', 123), + array('IF(0, 123, 567)', 567), + array('ELT(3, "a", "b", "c")', 'c'), + array('ELT(0, "a", "b", "c")', null), + array('ELT(4, "a", "b", "c")', null), + array('INET_ATON("128.0.0.1")', 2147483649), + array('INET_NTOA(2147483649)', '128.0.0.1'), + ); + } + + // Build a comprehensive index + $aRet = array(); + foreach ($aExpressions as $aExp) + { + $aRet[$aExp[0]] = $aExp; + } + return $aRet; + } + + /** + * @covers Expression::Evaluate() + * @dataProvider NotYetParsableExpressionsProvider + * + * @param string $sExpression + * @param string $expectedValue + */ + public function testNotYetParsableExpressions($sExpression, $expectedValue) + { + $sNewExpression = "return $sExpression;"; + $oExpression = eval($sNewExpression); + $res = $oExpression->Evaluate(array()); + static::assertEquals($expectedValue, $res); + } + + public function NotYetParsableExpressionsProvider() + { + $aExpressions = array( + array("new \\FunctionExpression('CONCAT_WS', array(new \\ScalarExpression(' '), new \\ScalarExpression('Hello'), new \ScalarExpression('world!')))", 'Hello world!'), + array("new \\ScalarExpression('windows\\system32')", 'windows\\system32'), + array("new \\BinaryExpression(new \\ScalarExpression('100%'), 'LIKE', new \\ScalarExpression('___\%'))", 1), + array("new \\BinaryExpression(new \ScalarExpression('1000'), 'LIKE', new \ScalarExpression('___\%'))", 0), + // Net yet parsed - array("TIME(NOW()) = CURRENT_TIME()", 1), // Not relevant + // Not yet parsed - array("DATE_ADD('2020-02-28 18:00:00', INTERVAL 1 WEEK)", '2020-03-06 18:00:00'), + // Not yet parsed - array("DATE_SUB('2020-03-01 18:00:00', INTERVAL 1 WEEK)", '2020-02-23 18:00:00'), + // Not yet parsed - array('ROUND(1.2345, 2)', 1.23), + // Not yet parsed - array('FLOOR(1.2)', 1), + ); + // Build a comprehensive index + $aRet = array(); + foreach ($aExpressions as $aExp) + { + $aRet[$aExp[0]] = $aExp; + } + return $aRet; + } + + /** + * Check that the test data would give the same result when evaluated by MySQL + * It uses the data provider ExpressionProvider, and checks every test case in one single query + * + * @throws \MySQLException + */ + public function testMySQLEvaluateAllAtOnce() + { + // Expressions given as an OQL + $aTests = array_values($this->VariousExpressionsProvider()); + + // Expressions given as a PHP statement + foreach (array_values($this->NotYetParsableExpressionsProvider()) as $i => $aTest) + { + $sNewExpression = "return {$aTest[0]};"; + $oExpression = eval($sNewExpression); + $sExpression = $oExpression->RenderExpression(true); + $aTests[] = array($sExpression, $aTest[1]); + } + + $aExpressions = array(); + foreach ($aTests as $i => $aTest) + { + $aExpressions[] = "{$aTest[0]} as test_$i"; + } + + $sSelects = implode(', ', $aExpressions); + $sQuery = "SELECT $sSelects"; + + $this->debug($sQuery); + $aResults = CMDBSource::QueryToArray($sQuery); + + foreach ($aTests as $i => $aTest) + { + $value = $aResults[0]["test_$i"]; + $expectedValue = $aTest[1]; + $this->debug("Test #$i: {$aTests[$i][0]} => ".var_export($value, true)); + static::assertEquals($expectedValue, $value); + } + } + + /** + * @covers DBObject::EvaluateExpression + * @dataProvider ExpressionsWithObjectFieldsProvider + * + * @param $sClass + * @param $aValues + * @param $sExpression + * @param $expected + * + * @throws \CoreException + * @throws \OQLException + */ + public function testExpressionsWithObjectFields($sClass, $aValues, $sExpression, $expected) + { + $oObject = MetaModel::NewObject($sClass, $aValues); + $oExpression = Expression::FromOQL($sExpression); + + $res = $oObject->EvaluateExpression($oExpression); + + static::assertEquals($expected, $res); + } + + public function ExpressionsWithObjectFieldsProvider() + { + return array( + array('Location', array('name' => 'Grenoble', 'org_id' => 2), 'org_id', 2), + array('Location', array('name' => 'Grenoble', 'org_id' => 2), 'CONCAT(SUBSTR(name, 4), " cause")', 'noble cause'), + ); + } + + /** + * @dataProvider ExpressionWithParametersProvider + * + * @param $sExpression + * @param $aParameters + * @param $expected + * + * @throws \OQLException + * @throws \Exception + */ + public function testExpressionWithParameters($sExpression, $aParameters, $expected) + { + $oExpression = Expression::FromOQL($sExpression); + $res = $oExpression->Evaluate($aParameters); + static::assertEquals($expected, $res); + } + + public function ExpressionWithParametersProvider() + { + return array( + array('CONCAT(SUBSTR(name, 4), " cause")', array('name' => 'noble'), 'le cause'), + ); + } + + /** + * Check Expression::IfTrue + * + * @covers Expression::FromOQL + * @covers Expression::IsTrue + * @dataProvider TrueExpressionsProvider + * + * @param $sExpression + * @param $bExpectTrue + * + * @throws \OQLException + */ + public function testTrueExpressions($sExpression, $bExpectTrue) + { + $oExpression = Expression::FromOQL($sExpression); + + $res = $oExpression->IsTrue(); + if ($bExpectTrue) + { + static::assertTrue($res, 'arg: '.$sExpression); + } + else + { + static::assertFalse($res, 'arg: '.$sExpression); + } + } + + public function TrueExpressionsProvider() + { + $aExpressions = array( + array('1', true), + array('0 OR 0', false), + array('1 AND 1', true), + array('1 AND (1 OR 0)', true) + ); + // Build a comprehensive index + $aRet = array(); + foreach ($aExpressions as $aExp) + { + $aRet[$aExp[0]] = $aExp; + } + return $aRet; + } + + /** + * @covers FunctionExpression::Evaluate() + * @dataProvider TimeFormatsProvider + * + * @param $sFormat + * @param $bProcessed + * @param $sValueOrException + * + * @throws \CoreException + * @throws \MySQLException + * @throws \MySQLQueryHasNoResultException + * @throws \Exception + */ + public function testTimeFormat($sFormat, $bProcessed, $sValueOrException) + { + $sDate = '2009-06-04 21:23:24'; + $oExpression = new FunctionExpression('DATE_FORMAT', array(new ScalarExpression($sDate), new ScalarExpression("%$sFormat"))); + if ($bProcessed) + { + $sqlValue = CMDBSource::QueryToScalar("SELECT DATE_FORMAT('$sDate', '%$sFormat')"); + static::assertEquals($sqlValue, $sValueOrException, 'Check test against MySQL'); + + $res = $oExpression->Evaluate(array()); + static::assertEquals($sValueOrException, $res, 'Check evaluation'); + } + else + { + static::expectException($sValueOrException); + $oExpression->Evaluate(array()); + } + } + + public function TimeFormatsProvider() + { + $aTests = array( + array('a', true, 'Thu'), + array('b', true, 'Jun'), + array('c', true, '6'), + array('D', true, '4th'), + array('d', true, '04'), + array('e', true, '4'), + array('f', false, 'NotYetEvaluatedExpression'), // microseconds: no way! + array('H', true, '21'), + array('h', true, '09'), + array('I', true, '09'), + array('i', true, '23'), + array('j', true, '155'), // day of the year + array('k', true, '21'), + array('l', true, '9'), + array('M', true, 'June'), + array('m', true, '06'), + array('p', true, 'PM'), + array('r', true, '09:23:24 PM'), + array('S', true, '24'), + array('s', true, '24'), + array('T', true, '21:23:24'), + array('U', false, 'NotYetEvaluatedExpression'), // Week sunday based (mode 0) + array('u', false, 'NotYetEvaluatedExpression'), // Week monday based (mode 1) + array('V', false, 'NotYetEvaluatedExpression'), // Week sunday based (mode 2) + array('v', true, '23'), // Week monday based (mode 3 - ISO-8601) + array('W', true, 'Thursday'), + array('w', true, '4'), + array('X', false, 'NotYetEvaluatedExpression'), + array('x', true, '2009'), // to be used with %v (ISO - 8601) + array('Y', true, '2009'), + array('y', true, '09'), + ); + $aRes = array(); + foreach ($aTests as $aTest) + { + $aRes["Format %{$aTest[0]}"] = $aTest; + } + return $aRes; + } + + /** + * Systematically check all supported format specs, for a given date + * + * @covers FunctionExpression::Evaluate() + * @dataProvider EveryTimeFormatProvider + * + * @param $sDate + * + * @throws \CoreException + * @throws \MySQLException + * @throws \Exception + */ + public function testEveryTimeFormat($sDate) + { + $aFormats = $this->TimeFormatsProvider(); + $aSelects = array(); + foreach ($aFormats as $sFormatDesc => $aFormatSpec) + { + $sFormat = $aFormatSpec[0]; + $bProcessed = $aFormatSpec[1]; + if ($bProcessed) + { + $aSelects["%$sFormat"] = "DATE_FORMAT('$sDate', '%$sFormat') AS `$sFormat`"; + } + } + $sSelects = "SELECT ".implode(', ', $aSelects); + $aRes = CMDBSource::QueryToArray($sSelects); + $aRow = $aRes[0]; + foreach ($aFormats as $sFormatDesc => $aFormatSpec) + { + $sFormat = $aFormatSpec[0]; + $bProcessed = $aFormatSpec[1]; + if ($bProcessed) + { + $oExpression = new FunctionExpression('DATE_FORMAT', array(new ScalarExpression($sDate), new ScalarExpression("%$sFormat"))); + $res = $oExpression->Evaluate(array()); + static::assertEquals($aRow[$sFormat], $res, "Format %$sFormat not matching MySQL for '$sDate'"); + } + } + } + public function EveryTimeFormatProvider() + { + return array( + array('1971-07-19 8:40:00'), + array('1999-12-31 23:59:59'), + array('2000-01-01 00:00:00'), + array('2009-06-04 21:23:24'), + array('2020-02-29 23:59:59'), + array('2030-10-21 23:59:59'), + array('2050-12-21 23:59:59'), + ); + } + + /** + * Systematically check all supported format specs, for a range of dates + * + * @covers FunctionExpression::Evaluate() + * @dataProvider EveryTimeFormatOnDateRangeProvider + * + * @param $sStartDate + * @param $sInterval + * @param $iRepeat + * + * @throws \CoreException + * @throws \MySQLException + * @throws \Exception + */ + public function testEveryTimeFormatOnDateRange($sStartDate, $sInterval, $iRepeat) + { + $oDate = new DateTime($sStartDate); + for ($i = 0 ; $i < $iRepeat ; $i++) + { + $sDate = date_format($oDate, 'Y-m-d, H:i:s'); + $this->debug("Checking '$sDate'"); + $this->testEveryTimeFormat($sDate); + $oDate->add(new DateInterval($sInterval)); + } + } + + public function EveryTimeFormatOnDateRangeProvider() + { + return array( + '10 years, day by day' => array('2000-01-01', 'P1D', 365 * 10), + '1 day, hour by hour' => array('2000-01-01 00:01:02', 'PT1H', 24), + '1 hour, minute by minute' => array('2000-01-01 00:01:02', 'PT1M', 60), + '1 minute, second by second' => array('2000-01-01 00:01:02', 'PT1S', 60), + ); + } +} \ No newline at end of file diff --git a/test/core/ExpressionTest.php b/test/core/ExpressionTest.php new file mode 100644 index 000000000..2ceef3944 --- /dev/null +++ b/test/core/ExpressionTest.php @@ -0,0 +1,47 @@ +<?php + +namespace Combodo\iTop\Test\UnitTest\Core; + +use Combodo\iTop\Test\UnitTest\ItopDataTestCase; +use Expression; + +class ExpressionTest extends ItopDataTestCase +{ + const USE_TRANSACTION = false; + + /** + * @dataProvider ListParametersProvider + * @param $sOQL + * @param $aExpected + * + * @throws \OQLException + */ + public function testListParameters($sOQL, $aExpected) + { + $oExpression = Expression::FromOQL($sOQL); + $aParameters = $oExpression->ListParameters(); + $aResult = array(); + foreach ($aParameters as $oVarExpr) + { + /** var \VariableExpression $oVarExpr */ + $aResult[] = $oVarExpr->RenderExpression(); + } + $this->debug($aResult); + $this->assertSame(array_diff($aExpected, $aResult), array_diff($aResult, $aExpected)); + } + + public function ListParametersProvider() + { + return array( + array('1', array()), + array(':id = 2', array(':id')), + array('expiration_date < DATE_SUB(NOW(), INTERVAL :expiration_days DAY)', array(':expiration_days')), + array('id IN (SELECT Organization WHERE :id = 2)', array(':id')), + array('id IN (:id, 2)', array(':id')), + array("B.name LIKE :name", array(':name')), + array("name REGEXP :regexp", array(':regexp')), + array(" t.agent_id = :current_contact_id", array(':current_contact_id')), + array("INET_ATON(dev.managementip) > INET_ATON('10.22.32.224') AND INET_ATON(:ip) < INET_ATON('10.22.32.255')", array(':ip')), + ); + } +} diff --git a/test/core/HTMLDOMSanitizerTest.php b/test/core/HTMLDOMSanitizerTest.php index 004b1cc74..46e3de462 100644 --- a/test/core/HTMLDOMSanitizerTest.php +++ b/test/core/HTMLDOMSanitizerTest.php @@ -202,5 +202,117 @@ class HTMLDOMSanitizerTest extends ItopTestCase return true; } + + /** + * @dataProvider RemoveBlackListedTagContentProvider + */ + public function testDoSanitizeRemoveBlackListedTagContent($html, $expected) + { + $oSanitizer = new HTMLDOMSanitizer(); + $sSanitizedHtml = $oSanitizer->DoSanitize($html); + + $this->assertEquals($expected, str_replace("\n", '', $sSanitizedHtml)); + } + + public function RemoveBlackListedTagContentProvider() + { + return array( + 'basic' => array( + 'html' => 'foo<iframe>bar</iframe>baz', + 'expected' => '<p>foobaz</p>', + ), + 'basic with body' => array( + 'html' => '<body>foo<iframe>bar</iframe>baz</body>', + 'expected' => 'foobaz', + ), + 'basic with html and body tags' => array( + 'html' => '<html><body lang="EN-GB" link="#0563C1" vlink="#954F72">foo<iframe>bar</iframe>baz</body></html>', + 'expected' => 'foobaz', + ), + 'basic with attributes' => array( + 'html' => 'foo<iframe baz="1">bar</iframe>baz', + 'expected' => '<p>foobaz</p>', + ), + 'basic with comment' => array( + 'html' => 'foo<iframe baz="1">bar<!-- foo --></iframe>baz', + 'expected' => '<p>foobaz</p>', + ), + 'basic with contentRemovable tag' => array( + 'html' => 'foo<iframe baz="1">bar<style>foo</style><script>boo</script></iframe>baz', + 'expected' => '<p>foobaz</p>', + ), + 'nested' => array( + 'html' => 'before<iframe>foo<article>baz</article>oof<article><iframe>bar</iframe>oof</article></iframe>after', + 'expected' => '<p>beforeafter</p>', + ), + 'nested with not closed br' => array( + 'html' => 'before<iframe>foo<article>baz</article>oof<br><article><iframe>bar</iframe>oof</article></iframe>after', + 'expected' => '<p>beforeafter</p>', + ), + 'nested with allowed' => array( + 'html' => 'before<iframe><div><article><p>baz</p>zab</article></div>oof</iframe>after', + 'expected' => '<p>beforeafter</p>', + ), + 'nested with spaces' => array( + 'html' => 'before<iframe><article>baz</article> oof</iframe>after', + 'expected' => '<p>beforeafter</p>', + ), + 'nested with attributes' => array( + 'html' => 'before<iframe baz="1"><article baz="1" biz="2">baz</article>oof</iframe>after', + 'expected' => '<p>beforeafter</p>', + ), + 'nested with allowed and attributes and spaces ' => array( + 'html' => '<html><body>before<iframe baz="1"><div baz="baz"><article baz="1" biz="2">baz</article>rab</div> oof</iframe>after</body></html>', + 'expected' => 'beforeafter', + ), + 'nested with allowed and contentRemovable tags' => array( + 'html' => '<html><body>before<iframe baz="1"><div ><article>baz</article>rab</div> oof<embed>embedTExt</embed></iframe>middle<style>foo</style>after<script>boo</script></body></html>', + 'expected' => 'beforemiddleafter', + ), + + 'regression: if head present => body is not trimmed' => array( + 'html' => '<html><head></head><body lang="EN-GB" link="#0563C1" vlink="#954F72">bar</body></html>', + 'expected' => 'bar', + ), + ); + } + + /** + * @dataProvider CallInlineImageProcessImageTagProvider + */ + public function testDoSanitizeCallInlineImageProcessImageTag($sHtml, $iExpectedCount) + { + require_once APPROOT.'test/core/sanitizer/InlineImageMock.php'; + + $oSanitizer = new HTMLDOMSanitizer(); + $oSanitizer->DoSanitize($sHtml); + + $iCalledCount = \InlineImage::GetCallCounter(); + $this->assertEquals($iExpectedCount, $iCalledCount); + } + + public function CallInlineImageProcessImageTagProvider() + { + return array( + 'no image' => array( + 'html' => '<p>bar</p>', + 'expected' => 0, + ), + 'basic image' => array( + 'html' => '<img />', + 'expected' => 1, + ), + 'nested images within forbidden tags' => array( + 'html' => '<html><body><img /><iframe baz="1"><div baz="baz"><article baz="1" biz="2">baz<img /><img /></article>rab</div> oof<img /></iframe><img /></body></html>', + 'expected' => 2, + ), +// This test will be restored with the ticket n°2556 +// 'nested images within forbidden and removed tags' => array( +// 'html' => '<html><body><img /><iframe baz="1"><div baz="baz"><object baz="1" biz="2">baz<img /><img /></object>rab</div> oof<img /></iframe><img /></body></html>', +// 'expected' => 2, +// ), + ); + } + } diff --git a/test/core/MetaModelTest.php b/test/core/MetaModelTest.php index c0bd95fa9..3ec4e47f9 100644 --- a/test/core/MetaModelTest.php +++ b/test/core/MetaModelTest.php @@ -83,4 +83,100 @@ class MetaModelTest extends ItopDataTestCase ), ); } + + /** + * @covers MetaModel::GetDependentAttributes() + * @dataProvider GetDependentAttributesProvider + * + * @param string $sClass + * @param string $sAttCode + * @param array $aExpectedAttCodes + * + * @throws \Exception + */ + public function testGetDependentAttributes($sClass, $sAttCode, array $aExpectedAttCodes) + { + $aRes = MetaModel::GetDependentAttributes($sClass, $sAttCode); + // The order doesn't matter + sort($aRes); + sort($aExpectedAttCodes); + static::assertEquals($aExpectedAttCodes, $aRes); + } + + public function GetDependentAttributesProvider() + { + $aRawCases = array( + array('Person', 'org_id', array('location_id', 'org_name', 'org_id_friendlyname', 'org_id_obsolescence_flag')), + array('Person', 'name', array('friendlyname')), + array('Person', 'status', array('obsolescence_flag')), + ); + $aRet = array(); + foreach ($aRawCases as $i => $aData) + { + $aRet[$aData[0].'::'.$aData[1]] = $aData; + } + return $aRet; + } + + /** + * @covers MetaModel::GetPrerequisiteAttributes() + * @dataProvider GetPrerequisiteAttributesProvider + * + * @param string $sClass + * @param string $sAttCode + * @param array $aExpectedAttCodes + * + * @throws \Exception + */ + public function testGetPrerequisiteAttributes($sClass, $sAttCode, array $aExpectedAttCodes) + { + $aRes = MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode); + // The order doesn't matter + sort($aRes); + sort($aExpectedAttCodes); + static::assertEquals($aRes, $aExpectedAttCodes); + } + + public function GetPrerequisiteAttributesProvider() + { + $aRawCases = array( + array('Person', 'friendlyname', array('name', 'first_name')), + array('Person', 'obsolescence_flag', array('status')), + array('Person', 'org_id_friendlyname', array('org_id')), + array('Person', 'org_id', array()), + array('Person', 'org_name', array('org_id')), + ); + $aRet = array(); + foreach ($aRawCases as $i => $aData) + { + $aRet[$aData[0].'::'.$aData[1]] = $aData; + } + return $aRet; + } + + /** + * To be removed as soon as the dependencies on external fields are obsoleted + * @Group Integration + */ + public function testManualVersusAutomaticDependenciesOnExtKeys() + { + foreach (\MetaModel::GetClasses() as $sClass) + { + if (\MetaModel::IsAbstract($sClass)) continue; + + foreach (\MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + { + if (\MetaModel::GetAttributeOrigin($sClass, $sAttCode) != $sClass) continue; + if (!$oAttDef instanceof \AttributeExternalKey) continue; + + $aManual = $oAttDef->Get('depends_on'); + $aAuto = \MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode); + // The order doesn't matter + sort($aAuto); + sort($aManual); + static::assertEquals($aManual, $aAuto, "Class: $sClass, Attribute: $sAttCode"); + } + } + } + } diff --git a/test/core/OQLTest.php b/test/core/OQLTest.php index e1f74f879..eb7bf7230 100644 --- a/test/core/OQLTest.php +++ b/test/core/OQLTest.php @@ -402,6 +402,7 @@ class OQLTest extends ItopDataTestCase } catch (Exception $e) { + $this->debug($e->getMessage()); $sExceptionClass = get_class($e); } diff --git a/test/core/WeeklyScheduledProcessMockConfig.php b/test/core/WeeklyScheduledProcessMockConfig.php new file mode 100644 index 000000000..236f83797 --- /dev/null +++ b/test/core/WeeklyScheduledProcessMockConfig.php @@ -0,0 +1,36 @@ +<?php + +/** + * Tool class to mock the config in {@link AbstractWeeklyScheduledProcess} + * + * The weekdays will be set to working week days (all except saturday & sunday) + * + * @package Combodo\iTop\Test\UnitTest\Core + */ +class WeeklyScheduledProcessMockConfig extends AbstractWeeklyScheduledProcess +{ + const MODULE_NAME = 'TEST_SCHEDULED_PROCESS'; + + public function __construct($bEnabledValue, $sTimeValue) + { + $this->oConfig = new Config(); + $this->oConfig->SetModuleSetting(self::MODULE_NAME, self::MODULE_SETTING_ENABLED, $bEnabledValue); + $this->oConfig->SetModuleSetting(self::MODULE_NAME, self::MODULE_SETTING_TIME, $sTimeValue); + $this->oConfig->SetModuleSetting(self::MODULE_NAME, self::MODULE_SETTING_WEEKDAYS, 'monday, tuesday, wednesday, thursday, friday'); + } + + protected function GetModuleName() + { + return self::MODULE_NAME; + } + + protected function GetDefaultModuleSettingTime() + { + return null; // config mock injected in the constructor + } + + public function Process($iUnixTimeLimit) + { + // nothing to do here (not tested) + } +} \ No newline at end of file diff --git a/test/core/WeeklyScheduledProcessTest.php b/test/core/WeeklyScheduledProcessTest.php new file mode 100644 index 000000000..ffd879b64 --- /dev/null +++ b/test/core/WeeklyScheduledProcessTest.php @@ -0,0 +1,67 @@ +<?php +namespace Combodo\iTop\Test\UnitTest\Core; + +use Combodo\iTop\Test\UnitTest\ItopTestCase; +use Config; +use DateTime; + + +/** + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + * @backupGlobals disabled + * + * @package Combodo\iTop\Test\UnitTest\Core + */ +class WeeklyScheduledProcessTest extends ItopTestCase +{ + protected function setUp() + { + parent::setUp(); + require_once(APPROOT.'core/backgroundprocess.inc.php'); + require_once(APPROOT.'test/core/WeeklyScheduledProcessMockConfig.php'); + } + + + /** + * @dataProvider GetNextOccurrenceProvider + * @test + * + * @param boolean $bEnabledValue true if task is enabled + * @param string $sCurrentTime Date string for current time, eg '2020-01-01 23:30' + * @param string $sTimeValue time to run that is set in the config, eg '23:30' + * @param string $sExpectedTime next occurrence that should be returned + * + * @throws \ProcessInvalidConfigException + */ + public function TestGetNextOccurrence($bEnabledValue, $sCurrentTime, $sTimeValue, $sExpectedTime) + { + $oWeeklyImpl = new \WeeklyScheduledProcessMockConfig($bEnabledValue, $sTimeValue); + + $sItopTimeZone = $oWeeklyImpl->getOConfig()->Get('timezone'); + $timezone = new \DateTimeZone($sItopTimeZone); + $oExpectedDateTime = new DateTime($sExpectedTime, $timezone); + + $this->assertEquals($oExpectedDateTime, $oWeeklyImpl->GetNextOccurrence($sCurrentTime)); + } + + public function GetNextOccurrenceProvider() + { + return array( + 'Disabled process' => array(false, 'now', null, '3000-01-01'), + 'working day same day, prog noon and current before noon' => array(true, '2020-05-11 11:00', '12:00', '2020-05-11 12:00'), + 'working day same day, prog noon and current is noon' => array(true, '2020-05-11 12:00', '12:00', '2020-05-12 12:00'), + 'working day same day, prog noon and current after noon' => array(true, '2020-05-11 13:00', '12:00', '2020-05-12 12:00'), + 'saturday, prog noon and current before noon' => array(true, '2020-05-09 11:00', '12:00', '2020-05-11 12:00'), + 'saturday, prog noon and current is noon' => array(true, '2020-05-09 12:00', '12:00', '2020-05-11 12:00'), + 'saturday, prog noon and current after noon' => array(true, '2020-05-09 13:00', '12:00', '2020-05-11 12:00'), + 'sunday, prog noon and current before noon' => array(true, '2020-05-10 11:00', '12:00', '2020-05-11 12:00'), + 'sunday, prog noon and current is noon' => array(true, '2020-05-10 12:00', '12:00', '2020-05-11 12:00'), + 'sunday, prog noon and current after noon' => array(true, '2020-05-10 13:00', '12:00', '2020-05-11 12:00'), + 'working day, day before, prog noon and current before midnight' => array(true, '2020-05-11 23:59', '00:00', '2020-05-12 00:00'), + 'working day same day, prog noon and current is midnight' => array(true, '2020-05-12 00:00', '00:00', '2020-05-13 00:00'), + 'working day same day, prog noon and current after midnight' => array(true, '2020-05-12 00:01', '00:00', '2020-05-13 00:00'), + ); + } +} + diff --git a/test/core/sanitizer/InlineImageMock.php b/test/core/sanitizer/InlineImageMock.php new file mode 100644 index 000000000..d5ad33218 --- /dev/null +++ b/test/core/sanitizer/InlineImageMock.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright (C) 2010-2020 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/> + * + */ + +/** + * Mock class used by @see \Combodo\iTop\Test\UnitTest\Core\HTMLDOMSanitizerTest + * + */ +class InlineImage +{ + private static $iCallCounter = 0; + + public static function ProcessImageTag(DOMNode $oNode) + { + self::$iCallCounter++; + } + + public static function GetCallCounter() + { + return self::$iCallCounter; + } +} \ No newline at end of file diff --git a/test/testlist.inc.php b/test/testlist.inc.php index b02e9ddc7..0a8bfed61 100644 --- a/test/testlist.inc.php +++ b/test/testlist.inc.php @@ -485,7 +485,7 @@ class TestMyBizModel extends TestBizModel echo "Max depth = $iMaxDepth</br>\n"; $oObj = MetaModel::GetObject("cmdbContact", 18); - $aRels = $oObj->GetRelatedObjects("Potes", $iMaxDepth); + $aRels = $oObj->GetRelatedObjectsDown("Potes", $iMaxDepth); echo $oObj->Get('name')." has some 'Potes'...</br>\n"; foreach ($aRels as $sClass => $aObjs) { @@ -581,391 +581,6 @@ class TestMyBizModel extends TestBizModel } -/////////////////////////////////////////////////////////////////////////// -// Test a complex biz model on the fly -/////////////////////////////////////////////////////////////////////////// - -abstract class MyFarm extends TestBizModel -{ - static public function GetConfigFile() {return '/config-test-farm.php';} - - protected function DoPrepare() - { - parent::DoPrepare(); - $this->ResetDB(); - MetaModel::DBCheckIntegrity(); - } - - protected function InsertMammal($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $sName, $iHeight, $sBirth) - { - $oNew = MetaModel::NewObject('Mammal'); - $oNew->Set('species', $sSpecies); - $oNew->Set('sex', $sSex); - $oNew->Set('speed', $iSpeed); - $oNew->Set('mother', $iMotherid); - $oNew->Set('father', $iFatherId); - $oNew->Set('name', $sName); - $oNew->Set('height', $iHeight); - $oNew->Set('birth', $sBirth); - return $this->ObjectToDB($oNew); - } - - protected function InsertBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId) - { - $oNew = MetaModel::NewObject('Bird'); - $oNew->Set('species', $sSpecies); - $oNew->Set('sex', $sSex); - $oNew->Set('speed', $iSpeed); - $oNew->Set('mother', $iMotherid); - $oNew->Set('father', $iFatherId); - return $this->ObjectToDB($oNew); - } - - protected function InsertFlyingBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $iFlyingSpeed) - { - $oNew = MetaModel::NewObject('FlyingBird'); - $oNew->Set('species', $sSpecies); - $oNew->Set('sex', $sSex); - $oNew->Set('speed', $iSpeed); - $oNew->Set('mother', $iMotherid); - $oNew->Set('father', $iFatherId); - $oNew->Set('flyingspeed', $iFlyingSpeed); - return $this->ObjectToDB($oNew); - } - - private function InsertGroup($sName, $iLeaderId) - { - $oNew = MetaModel::NewObject('Group'); - $oNew->Set('name', $sName); - $oNew->Set('leader', $iLeaderId); - $iId = $oNew->DBInsertNoReload(); - return $iId; - } -} - - -class TestQueriesOnFarm extends MyFarm -{ - static public function GetName() - { - return 'Farm test'; - } - - static public function GetDescription() - { - return 'A series of tests on the farm business model (SQL generation)'; - } - - protected function CheckQuery($sQuery, $bIsCorrectQuery) - { - if ($bIsCorrectQuery) - { - echo "<h4 style=\"color:green;\">$sQuery</h4>\n"; - } - else - { - echo "<h4 style=\"color:red;\">$sQuery</h3>\n"; - } - try - { - //$oOql = new OqlInterpreter($sQuery); - //$oTrash = $oOql->ParseQuery(); - //self::DumpVariable($oTrash, true); - $oMyFilter = DBObjectSearch::FromOQL($sQuery); - } - catch (OQLException $oOqlException) - { - if ($bIsCorrectQuery) - { - echo "<p>More info on this unexpected failure:<br/>".$oOqlException->getHtmlDesc()."</p>\n"; - throw $oOqlException; - return false; - } - else - { - // Everything is fine :-) - echo "<p>More info on this expected failure:\n"; - echo "<ul>\n"; - echo "<li>".get_class($oOqlException)."</li>\n"; - echo "<li>".$oOqlException->getMessage()."</li>\n"; - echo "<li>".$oOqlException->getHtmlDesc()."</li>\n"; - echo "</ul>\n"; - echo "</p>\n"; - return true; - } - } - // The query was correctly parsed, was it expected to be correct ? - if (!$bIsCorrectQuery) - { - throw new UnitTestException("The query '$sQuery' was parsed with success, while it shouldn't (?)"); - return false; - } - echo "<p>To OQL: ".$oMyFilter->ToOQL()."</p>"; - - $this->search_and_show_list($oMyFilter); - - //echo "<p>first pass<p>\n"; - //self::DumpVariable($oMyFilter, true); - $sQuery1 = $oMyFilter->MakeSelectQuery(); - //echo "<p>second pass<p>\n"; - //self::DumpVariable($oMyFilter, true); - //$sQuery1 = $oMyFilter->MakeSelectQuery(); - - $sSerialize = $oMyFilter->serialize(); - echo "<p>Serialized:$sSerialize</p>\n"; - $oFilter2 = DBObjectSearch::unserialize($sSerialize); - try - { - $sQuery2 = $oFilter2->MakeSelectQuery(); - } - catch (Exception $e) - { - echo "<p>Could not compute the query after unserialize</p>\n"; - echo "<p>Query 1: $sQuery1</p>\n"; - MyHelpers::var_cmp_html($oMyFilter, $oFilter2); - throw $e; - } - //if ($oFilter2 != $oMyFilter) no, they may differ while the resulting query is the same! - if ($sQuery1 != $sQuery2) - { - echo "<p>serialize/unserialize mismatch :-(</p>\n"; - MyHelpers::var_cmp_html($sQuery1, $sQuery2); - MyHelpers::var_cmp_html($oMyFilter, $oFilter2); - return false; - } - return true; - } - - protected function DoExecute() - { -// $this->ReportError("Found two different OQL expression out of the (same?) filter: <em>$sExpr1</em> != <em>$sExpr2</em>"); -// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); - echo "<h3>Create protagonists...</h3>"; - - $iId1 = $this->InsertMammal('human', 'male', 10, 0, 0, 'romanoff', 192, '1971-07-19'); - $iId2 = $this->InsertMammal('human', 'female', 9, 0, 0, 'rouanita', 165, '1983-01-23'); - $this->InsertMammal('human', 'female', 3, $iId2, $iId1, 'pomme', 169, '2008-02-23'); - $this->InsertMammal('pig', 'female', 3, 0, 0, 'grouinkette', 85, '2006-06-01'); - $this->InsertMammal('donkey', 'female', 3, 0, 0, 'muleta', 124, '2003-11-11'); - - $this->InsertBird('rooster', 'male', 12, 0, 0); - $this->InsertFlyingBird('pie', 'female', 11, 0, 0, 35); - - // Benchmarking - // - if (false) - { - define ('COUNT_BENCHMARK', 10); - echo "<h3>Parsing a long query, ".COUNT_BENCHMARK." times</h3>"; - $sQuery = "SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.birth < DATE_SUB(CURRENT_DATE(), INTERVAL 10 YEAR) AND Dad.height * 2 <= ROUND(TO_DAYS(Dad.birth) / (3 + 1) * 5 - 3)"; - - $fStart = MyHelpers::getmicrotime(); - for($i=0 ; $i < COUNT_BENCHMARK ; $i++) - { - $oMyFilter = DBObjectSearch::FromOQL($sQuery); - } - $fDuration = MyHelpers::getmicrotime() - $fStart; - $fParsingDuration = $fDuration / COUNT_BENCHMARK; - echo "<p>Mean time by op: $fParsingDuration</p>"; - } - - echo "<h3>Test queries...</h3>"; - - $aQueries = array( - 'SELECT Animal' => true, - 'SELECT Animal WHERE Animal.pkey = 1' => false, - 'SELECT Animal WHERE Animal.id = 1' => true, - 'SELECT Aniiimal' => false, - 'SELECTe Animal' => false, - 'SELECT * FROM Animal' => false, - 'SELECT Animal AS zoo WHERE zoo.species = \'human\'' => true, - 'SELECT Animal AS zoo WHERE species = \'human\'' => true, - 'SELECT Animal AS zoo WHERE espece = \'human\'' => false, - 'SELECT Animal AS zoo WHERE zoo.species IN (\'human\', "pig")' => true, - 'SELECT Animal AS zoo WHERE CONCATENATION(zoo.species, zoo.sex) LIKE "hum%male"' => false, - 'SELECT Animal AS zoo WHERE CONCAT(zoo.species, zoo.sex) LIKE "hum%male"' => true, - 'SELECT Animal AS zoo WHERE zoo.species NOT IN (\'human\', "pig")' => true, - 'SELECT Animal AS zoo WHERE zoo.kind = \'human\'' => false, - 'SELECT Animal WHERE Animal.species = \'human\' AND Animal.sex = \'female\'' => true, - 'SELECT Mammal AS x WHERE (x.species = \'human\' AND x.name LIKE \'ro%\') OR (x.species = \'donkey\' AND x.name LIKE \'po%\')' => true, - 'SELECT Mammal AS x WHERE x.species = \'human\' AND x.name LIKE \'ro%\' OR x.species = \'donkey\' AND x.name LIKE \'po%\'' => true, - 'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true, - 'SELECT Mammal AS m WHERE DAY(m.birth) = 19' => true, - 'SELECT Mammal AS m WHERE YEAR(m.birth) = 1971' => true, - 'SELECT Mammal AS m WHERE m.birth < DATE_SUB(CURRENT_DATE(), INTERVAL 10 YEAR)' => true, - 'SELECT Mammal AS m WHERE m.birth > DATE_SUB(NOW(), INTERVAL 2000 DAY)' => true, - 'SELECT Mammal AS m WHERE (TO_DAYS(NOW()) - TO_DAYS(m.birth)) > 2000' => true, - 'SELECT Mammal AS m WHERE m.name = IF(FLOOR(ROUND(m.height)) > 2, "pomme", "romain")' => true, - 'SELECT Mammal AS m WHERE (1 + 2' => false, - 'SELECT Mammal AS m WHERE (1 + 2 * 4 / 23) > 0' => true, - 'SELECT Mammal AS m WHERE (4 / 23 * 2 + 1) > 0' => true, - 'SELECT Mammal AS m WHERE 1/0' => true, - 'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true, - 'SELECT Animal JOIN Group ON Group.leader = Animal.id' => true, - 'SELECT Group JOIN Animal ON Group.leader = Animal.id' => true, - 'SELECT Animal AS A JOIN Group AS G1 ON G1.leader = A.id' => true, - 'SELECT Animal AS A JOIN Group AS G ON FooClass.leader = A.id' => false, - 'SELECT Animal AS A JOIN Group AS G ON G.leader = FooClass.id' => false, - 'SELECT Animal AS A JOIN Group AS G ON G.masterchief = A.id' => false, - 'SELECT Animal AS A JOIN Group AS G ON A.id = G.leader' => false, - 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.qwerty = 123' => false, - 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.name LIKE "a%"' => true, - 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.id = 1' => true, - 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE id = 1' => false, - 'SELECT Animal AS A JOIN Group AS G ON A.member = G.id' => false, - 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id' => true, - 'SELECT Mammal AS M JOIN Group AS G ON A.member = G.id' => false, - 'SELECT Mammal AS myAlias JOIN Group AS myAlias ON myAlias.member = myAlias.id' => false, - 'SELECT Mammal AS Mammal JOIN Group AS Mammal ON Mammal.member = Mammal.id' => false, - 'SELECT Group AS G WHERE G.leader_name LIKE "%"' => true, - 'SELECT Group AS G WHERE G.leader_speed < 100000' => true, - 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true, - 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_speed < 100000' => true, - 'SELECT Mammal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, - 'SELECT Mammal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true, - 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, - 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true, - 'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id' => true, - 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true, - 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.id = 1' => true, - 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\'' => false, - 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, - 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.speed = 0' => true, - 'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true, - 'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id' => true, - 'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id JOIN Mammal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.name=\'chloe\' OR Child.name=\'bizounours\'' => true, - // Specifying multiple objects - 'SELECT Animal FROM Animal' => true, - 'SELECT yelele FROM Animal' => false, - 'SELECT Animal FROM Animal AS A' => false, - 'SELECT A FROM Animal AS A' => true, - ); - //$aQueries = array( - // 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true, - //); - foreach($aQueries as $sQuery => $bIsCorrect) - { - $this->CheckQuery($sQuery, $bIsCorrect); - } - return true; - } -} - - -/////////////////////////////////////////////////////////////////////////// -// Test data load -/////////////////////////////////////////////////////////////////////////// - -class TestBulkChangeOnFarm extends TestBizModel -{ - static public function GetName() - { - return 'Farm test - data load'; - } - - static public function GetDescription() - { - return 'Bulk load'; - } - - static public function GetConfigFile() {return '/config-test-farm.php';} - - protected function DoPrepare() - { - parent::DoPrepare(); - $this->ResetDB(); - MetaModel::DBCheckIntegrity(); - } - - protected function DoExecute() - { -// $this->ReportError("Found two different OQL expression out of the (same?) filter: <em>$sExpr1</em> != <em>$sExpr2</em>"); -// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); - - $oParser = new CSVParser("denomination,hauteur,age - suzy,123,2009-01-01 - chita,456, - ", ',', '"'); - $aData = $oParser->ToArray(1, array('_name', '_height', '_birth')); - self::DumpVariable($aData); - - $oBulk = new BulkChange( - 'Mammal', - $aData, - // attributes - array('name' => '_name', 'height' => '_height', 'birth' => '_birth'), - // ext keys - array(), - // reconciliation - array('name') - ); - - $oMyChange = MetaModel::NewObject("CMDBChange"); - $oMyChange->Set("date", time()); - $oMyChange->Set("userinfo", "Testor"); - $iChangeId = $oMyChange->DBInsert(); -// echo "Created new change: $iChangeId</br>"; - - echo "<h3>Planned for loading...</h3>"; - $aRes = $oBulk->Process(); - self::DumpVariable($aRes); - echo "<h3>Go for loading...</h3>"; - $aRes = $oBulk->Process($oMyChange); - self::DumpVariable($aRes); - - return; - - $oRawData = array( - 'Mammal', - array('species', 'sex', 'speed', 'mother', 'father', 'name', 'height', 'birth'), - "human,male,23,0,0,romulus,192,1971 - human,male,23,0,0,remus,154,-50 - human,male,23,0,0,julius,160,-49 - human,female,23,0,0,cleopatra,142,-50 - pig,female,23,0,0,confucius,50,2003" - ); - } -} - - -/////////////////////////////////////////////////////////////////////////// -// Test data load -/////////////////////////////////////////////////////////////////////////// - -class TestFullTextSearchOnFarm extends MyFarm -{ - static public function GetName() - { - return 'Farm test - full text search'; - } - - static public function GetDescription() - { - return 'Focus on the full text search feature'; - } - - protected function DoExecute() - { - echo "<h3>Create protagonists...</h3>"; - - $iId1 = $this->InsertMammal('human', 'male', 10, 0, 0, 'romanoff', 192, '1971-07-19'); - $iId2 = $this->InsertMammal('human', 'female', 9, 0, 0, 'rouanita', 165, '1983-01-23'); - $this->InsertMammal('human', 'female', 3, $iId2, $iId1, 'pomme', 169, '2008-02-23'); - $this->InsertMammal('pig', 'female', 3, 0, 0, 'grouinkette', 85, '2006-06-01'); - $this->InsertMammal('donkey', 'female', 3, 0, 0, 'muleta', 124, '2003-11-11'); - - $this->InsertBird('rooster', 'male', 12, 0, 0); - $this->InsertFlyingBird('pie', 'female', 11, 0, 0, 35); - - echo "<h3>Search...</h3>"; - $oSearch = new DBObjectSearch('Mammal'); - $oSearch->AddCondition_FullText('manof'); - //$oResultSet = new DBObjectSet($oSearch); - $this->search_and_show_list($oSearch); - } -} - - /////////////////////////////////////////////////////////////////////////// // Test queries /////////////////////////////////////////////////////////////////////////// diff --git a/webservices/cron.php b/webservices/cron.php index 145c758fe..b7717cede 100644 --- a/webservices/cron.php +++ b/webservices/cron.php @@ -60,7 +60,7 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter = 'parameter') function UsageAndExit($oP) { - $bModeCLI = utils::IsModeCLI(); + $bModeCLI = ($oP instanceof CLIPage); if ($bModeCLI) { @@ -477,9 +477,12 @@ function ReorderProcesses(&$aProcesses, $aTasks, $oNow, $bVerbose, &$oP) set_time_limit(0); // Some background actions may really take long to finish (like backup) -if (utils::IsModeCLI()) +$bIsModeCLI = utils::IsModeCLI(); +if ($bIsModeCLI) { $oP = new CLIPage("iTop - cron"); + + SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL); } else { @@ -497,7 +500,7 @@ catch (Exception $e) exit(EXIT_CODE_FATAL); } -if (utils::IsModeCLI()) +if ($bIsModeCLI) { // Next steps: // specific arguments: 'csvfile' diff --git a/webservices/export-v2.php b/webservices/export-v2.php index 1fd7d6f01..8b193fc9a 100644 --- a/webservices/export-v2.php +++ b/webservices/export-v2.php @@ -32,6 +32,12 @@ require_once(APPROOT.'/core/bulkexport.class.inc.php'); require_once(APPROOT.'/application/startup.inc.php'); + +const EXIT_CODE_ERROR = -1; +const EXIT_CODE_FATAL = -2; + + + function ReportErrorAndExit($sErrorMessage) { if (utils::IsModeCLI()) @@ -39,14 +45,14 @@ function ReportErrorAndExit($sErrorMessage) $oP = new CLIPage("iTop - Export"); $oP->p('ERROR: '.$sErrorMessage); $oP->output(); - exit(-1); + exit(EXIT_CODE_ERROR); } else { $oP = new WebPage("iTop - Export"); $oP->p('ERROR: '.$sErrorMessage); $oP->output(); - exit(-1); + exit(EXIT_CODE_ERROR); } } @@ -58,7 +64,7 @@ function ReportErrorAndUsage($sErrorMessage) $oP->p('ERROR: '.$sErrorMessage); Usage($oP); $oP->output(); - exit(-1); + exit(EXIT_CODE_ERROR); } else { @@ -66,7 +72,7 @@ function ReportErrorAndUsage($sErrorMessage) $oP->p('ERROR: '.$sErrorMessage); Usage($oP); $oP->output(); - exit(-1); + exit(EXIT_CODE_ERROR); } } @@ -565,6 +571,8 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false) ///////////////////////////////////////////////////////////////////////////// if (utils::IsModeCLI()) { + SetupUtils::CheckPhpAndExtensionsForCli(new CLIPage('iTop - Export')); + try { // Do this before loging, in order to allow setting user credentials from within the file @@ -573,7 +581,7 @@ if (utils::IsModeCLI()) catch(Exception $e) { echo "Error: ".$e->GetMessage()."<br/>\n"; - exit(-2); + exit(EXIT_CODE_FATAL); } $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data'); diff --git a/webservices/export.php b/webservices/export.php index ee127ca32..f19d03eaf 100644 --- a/webservices/export.php +++ b/webservices/export.php @@ -29,6 +29,11 @@ require_once(APPROOT.'/application/excelexporter.class.inc.php'); require_once(APPROOT.'/application/startup.inc.php'); + +const EXIT_CODE_ERROR = -1; +const EXIT_CODE_FATAL = -2; + + try { // Do this before loging, in order to allow setting user credentials from within the file @@ -37,12 +42,15 @@ try catch(Exception $e) { echo "Error: ".$e->GetMessage()."<br/>\n"; - exit -2; + exit(EXIT_CODE_FATAL); } if (utils::IsModeCLI()) { - $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data'); + $oP = new CLIPage("iTop - Export"); + SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL); + + $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data'); $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data'); if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) @@ -51,10 +59,9 @@ if (utils::IsModeCLI()) } else { - $oP = new CLIPage("iTop - Export"); - $oP->p("Access restricted or wrong credentials ('$sAuthUser')"); + $oP->p("Access restricted or wrong credentials ('$sAuthUser')"); $oP->output(); - exit -1; + exit(EXIT_CODE_ERROR); } } else @@ -74,7 +81,7 @@ if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) $oP = new CLIPage("iTop - Export"); $oP->p("The user account is not authorized to access the archives"); $oP->output(); - exit -1; + exit(EXIT_CODE_ERROR); } $bLocalize = (utils::ReadParam('no_localize', 0) != 1); diff --git a/webservices/import.php b/webservices/import.php index c43071822..7907bcfcd 100644 --- a/webservices/import.php +++ b/webservices/import.php @@ -212,6 +212,7 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter) if (utils::IsModeCLI()) { $oP = new CLIPage("iTop - Bulk import"); + SetupUtils::CheckPhpAndExtensionsForCli($oP, -2); } else {