diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 5e100852e..d0298dc2c 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[] = "
"; - $aRanks[] = $aMessageData['rank']; + if(!in_array("",$aMessages)) + { + $aMessages[] = ""; + $aRanks[] = $aMessageData['rank']; + } } unset($_SESSION['obj_messages'][$sMessageKey]); } @@ -3012,10 +3015,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++; } diff --git a/application/dashlet.class.inc.php b/application/dashlet.class.inc.php index 1e0e76f25..f43f232bb 100644 --- a/application/dashlet.class.inc.php +++ b/application/dashlet.class.inc.php @@ -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])) diff --git a/application/loginbasic.class.inc.php b/application/loginbasic.class.inc.php index 9f56aaf88..660b45cba 100644 --- a/application/loginbasic.class.inc.php +++ b/application/loginbasic.class.inc.php @@ -40,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; 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/composer.json b/composer.json index 240f68395..e363d44da 100644 --- a/composer.json +++ b/composer.json @@ -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", 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/core/backgroundprocess.inc.php b/core/backgroundprocess.inc.php index 8b25c70b3..05ec4572e 100644 --- a/core/backgroundprocess.inc.php +++ b/core/backgroundprocess.inc.php @@ -1,29 +1,23 @@ - - /** - * 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 { /** @@ -94,6 +88,11 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess 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 * @@ -107,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 * @@ -125,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 @@ -158,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) @@ -181,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() @@ -190,7 +208,8 @@ 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; $sDay = $oNow->format('N'); for ($iDay = (int) $sDay; $iDay <= 7; $iDay++) diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index 8b8bdef71..d7a48bf4c 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -1248,9 +1248,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/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 95a78010d..492c503c0 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -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) diff --git a/core/dbsearch.class.php b/core/dbsearch.class.php index 6c7dc7459..66884183e 100644 --- a/core/dbsearch.class.php +++ b/core/dbsearch.class.php @@ -1179,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(); } } diff --git a/core/log.class.inc.php b/core/log.class.inc.php index 1590d26e3..dfb0fa3e6 100644 --- a/core/log.class.inc.php +++ b/core/log.class.inc.php @@ -686,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; } diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 50a46c0fa..fb09fab78 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -5747,6 +5747,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) { @@ -5763,9 +5772,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); } 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/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/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-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-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 %} {% endif %} 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 ad7912c31..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 @@ -1637,23 +1637,26 @@ public function PrefillSearchForm(&$aContextParam) parent::DoCheckToWrite(); $aCustomerContracts = $this->Get("customercontracts_list"); - foreach($aCustomerContracts as $sAttCode => $oCustomerContracts) + 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 AND ccs.sla_id!=:sla_id"; + $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"); - $aQueryParams['sla_id'] = $this->Get("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']); - while ($aCurrentRow = $oResultSql->Fetch()) + if ($aCurrentRow = $oResultSql->Fetch()) { $this->m_aCheckIssues[] = Dict::Format('Class:SLA/Error:UniqueLnkCustomerContractToService',$aCurrentRow->Get('customercontract_name'),$aCurrentRow->Get('service_name')); } } } - ]]> 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 19f1885bb..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 @@ -1631,23 +1631,26 @@ public function PrefillSearchForm(&$aContextParam) parent::DoCheckToWrite(); $aCustomerContracts = $this->Get("customercontracts_list"); - foreach($aCustomerContracts as $sAttCode => $oCustomerContracts) + 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 AND ccs.sla_id!=:sla_id"; + $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"); - $aQueryParams['sla_id'] = $this->Get("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']); - while ($aCurrentRow = $oResultSql->Fetch()) + if ($aCurrentRow = $oResultSql->Fetch()) { $this->m_aCheckIssues[] = Dict::Format('Class:SLA/Error:UniqueLnkCustomerContractToService',$aCurrentRow->Get('customercontract_name'),$aCurrentRow->Get('service_name')); } } } - ]]> 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/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