mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-21 01:28:47 +02:00
Merge remote-tracking branch 'origin/develop' into feature/backoffice-full-moon-design
# Conflicts: # application/datamodel.application.xml # application/itopwebpage.class.inc.php # css/light-grey.scss
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -146,3 +146,4 @@ local.properties
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
/css/setup.css
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
# create target dirs
|
||||
mkdir -p var
|
||||
mkdir -p toolkit
|
||||
|
||||
# cleanup target dirs
|
||||
rm -rf toolkit/*
|
||||
|
||||
# fill target dirs
|
||||
curl https://www.combodo.com/documentation/iTopDataModelToolkit-2.3.zip > toolkit.zip
|
||||
unzip toolkit.zip
|
||||
rm toolkit.zip
|
||||
cp -r .jenkins/configuration/default-environment/unattended_install/* toolkit
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
# on the root dir
|
||||
# composer install -a # => Not needed anymore (libs were added to git with N°2435)
|
||||
|
||||
|
||||
# under the test dir
|
||||
cd test
|
||||
composer install
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
|
||||
|
||||
whoami
|
||||
pwd
|
||||
ls
|
||||
|
||||
echo "$BRANCH_NAME:${BRANCH_NAME}"
|
||||
|
||||
echo "printenv :"
|
||||
printenv
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -x
|
||||
|
||||
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
|
||||
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 $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 $OPTION --exclude-group OQL --teamcity
|
||||
fi
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
chmod 666 conf/production/config-itop.php
|
||||
|
||||
cd toolkit
|
||||
php unattended_install.php --response_file=default-params.xml --clean=true
|
||||
@@ -1,284 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* Configuration file, generated by the iTop configuration wizard
|
||||
*
|
||||
* The file is used in MetaModel::LoadConfig() which does all the necessary initialization job
|
||||
*
|
||||
*/
|
||||
$MySettings = array(
|
||||
|
||||
// access_message: Message displayed to the users when there is any access restriction
|
||||
// default: 'iTop is temporarily frozen, please wait... (the admin team)'
|
||||
'access_message' => 'iTop is temporarily frozen, please wait... (the admin team)',
|
||||
|
||||
// access_mode: Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3
|
||||
// default: 3
|
||||
'access_mode' => 3,
|
||||
|
||||
'allowed_login_types' => 'form|basic|external',
|
||||
|
||||
// apc_cache.enabled: If set, the APC cache is allowed (the PHP extension must also be active)
|
||||
// default: true
|
||||
'apc_cache.enabled' => true,
|
||||
|
||||
// apc_cache.query_ttl: Time to live set in APC for the prepared queries (seconds - 0 means no timeout)
|
||||
// default: 3600
|
||||
'apc_cache.query_ttl' => 3600,
|
||||
|
||||
// app_root_url: Root URL used for navigating within the application, or from an email to the application (you can put $SERVER_NAME$ as a placeholder for the server's name)
|
||||
// default: ''
|
||||
'app_root_url' => 'http://127.0.0.1/itop/svn/trunk/',
|
||||
|
||||
// buttons_position: Position of the forms buttons: bottom | top | both
|
||||
// default: 'both'
|
||||
'buttons_position' => 'both',
|
||||
|
||||
// cas_include_path: The path where to find the phpCAS library
|
||||
// default: '/usr/share/php'
|
||||
'cas_include_path' => '/usr/share/php',
|
||||
|
||||
// cron_max_execution_time: Duration (seconds) of the page cron.php, must be shorter than php setting max_execution_time and shorter than the web server response timeout
|
||||
// default: 600
|
||||
'cron_max_execution_time' => 600,
|
||||
|
||||
// csv_file_default_charset: Character set used by default for downloading and uploading data as a CSV file. Warning: it is case sensitive (uppercase is preferable).
|
||||
// default: 'ISO-8859-1'
|
||||
'csv_file_default_charset' => 'ISO-8859-1',
|
||||
|
||||
'csv_import_charsets' => array (
|
||||
),
|
||||
|
||||
// csv_import_history_display: Display the history tab in the import wizard
|
||||
// default: false
|
||||
'csv_import_history_display' => false,
|
||||
|
||||
// date_and_time_format: Format for date and time display (per language)
|
||||
// default: array (
|
||||
// 'default' =>
|
||||
// array (
|
||||
// 'date' => 'Y-m-d',
|
||||
// 'time' => 'H:i:s',
|
||||
// 'date_time' => '$date $time',
|
||||
// ),
|
||||
// )
|
||||
'date_and_time_format' => array (
|
||||
'default' =>
|
||||
array (
|
||||
'date' => 'Y-m-d',
|
||||
'time' => 'H:i:s',
|
||||
'date_time' => '$date $time',
|
||||
),
|
||||
'FR FR' =>
|
||||
array (
|
||||
'date' => 'd/m/Y',
|
||||
'time' => 'H:i:s',
|
||||
'date_time' => '$date $time',
|
||||
),
|
||||
),
|
||||
|
||||
'db_host' => '',
|
||||
|
||||
'db_name' => 'itop_ci',
|
||||
|
||||
'db_pwd' => 'IKnowYouSeeMeInJenkinsConf',
|
||||
|
||||
'db_subname' => '',
|
||||
|
||||
'db_user' => 'jenkins_itop',
|
||||
|
||||
// deadline_format: The format used for displaying "deadline" attributes: any string with the following placeholders: $date$, $difference$
|
||||
// default: '$difference$'
|
||||
'deadline_format' => '$difference$',
|
||||
|
||||
'default_language' => 'EN US',
|
||||
|
||||
// draft_attachments_lifetime: Lifetime (in seconds) of drafts' attachments and inline images: after this duration, the garbage collector will delete them.
|
||||
// default: 3600
|
||||
'draft_attachments_lifetime' => 3600,
|
||||
|
||||
// email_asynchronous: If set, the emails are sent off line, which requires cron.php to be activated. Exception: some features like the email test utility will force the serialized mode
|
||||
// default: false
|
||||
'email_asynchronous' => false,
|
||||
|
||||
// email_default_sender_address: Default address provided in the email from header field.
|
||||
// default: ''
|
||||
'email_default_sender_address' => '',
|
||||
|
||||
// email_default_sender_label: Default label provided in the email from header field.
|
||||
// default: ''
|
||||
'email_default_sender_label' => '',
|
||||
|
||||
// email_transport: Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocole)
|
||||
// default: 'PHPMail'
|
||||
'email_transport' => 'SMTP',
|
||||
|
||||
// email_transport_smtp.host: host name or IP address (optional)
|
||||
// default: 'localhost'
|
||||
'email_transport_smtp.host' => 'smtp.combodo.com',
|
||||
|
||||
// email_transport_smtp.password: Authentication password (optional)
|
||||
// default: ''
|
||||
'email_transport_smtp.password' => 'IDoNotWork',
|
||||
|
||||
// email_transport_smtp.port: port number (optional)
|
||||
// default: 25
|
||||
'email_transport_smtp.port' => 25,
|
||||
|
||||
// email_transport_smtp.username: Authentication user (optional)
|
||||
// default: ''
|
||||
'email_transport_smtp.username' => 'test2@combodo.com',
|
||||
|
||||
// email_validation_pattern: Regular expression to validate/detect the format of an eMail address
|
||||
// default: '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}'
|
||||
'email_validation_pattern' => '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}',
|
||||
|
||||
'encryption_key' => '@iT0pEncr1pti0n!',
|
||||
|
||||
'ext_auth_variable' => '$_SERVER[\'REMOTE_USER\']',
|
||||
|
||||
'fast_reload_interval' => '60',
|
||||
|
||||
// graphviz_path: Path to the Graphviz "dot" executable for graphing objects lifecycle
|
||||
// default: '/usr/bin/dot'
|
||||
'graphviz_path' => '/usr/bin/dot',
|
||||
|
||||
// inline_image_max_display_width: The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.
|
||||
// default: '250'
|
||||
'inline_image_max_display_width' => 250,
|
||||
|
||||
// inline_image_max_storage_width: The maximum width (in pixels) when uploading images to be used inside an HTML formatted attribute. Images larger than the given size will be downsampled before storing them in the database.
|
||||
// default: '1600'
|
||||
'inline_image_max_storage_width' => 1600,
|
||||
|
||||
// link_set_attribute_qualifier: Link set from string: attribute qualifier (encloses both the attcode and the value)
|
||||
// default: '\''
|
||||
'link_set_attribute_qualifier' => '\'',
|
||||
|
||||
// link_set_attribute_separator: Link set from string: attribute separator
|
||||
// default: ';'
|
||||
'link_set_attribute_separator' => ';',
|
||||
|
||||
// link_set_item_separator: Link set from string: line separator
|
||||
// default: '|'
|
||||
'link_set_item_separator' => '|',
|
||||
|
||||
// link_set_value_separator: Link set from string: value separator (between the attcode and the value itself
|
||||
// default: ':'
|
||||
'link_set_value_separator' => ':',
|
||||
|
||||
'log_global' => true,
|
||||
|
||||
'log_issue' => true,
|
||||
|
||||
'log_notification' => true,
|
||||
|
||||
'log_web_service' => true,
|
||||
|
||||
// max_combo_length: The maximum number of elements in a drop-down list. If more then an autocomplete will be used
|
||||
// default: 50
|
||||
'max_combo_length' => 50,
|
||||
|
||||
'max_display_limit' => '15',
|
||||
|
||||
// max_linkset_output: Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.
|
||||
// default: 100
|
||||
'max_linkset_output' => 100,
|
||||
|
||||
'min_display_limit' => '10',
|
||||
|
||||
// online_help: Hyperlink to the online-help web page
|
||||
// default: 'http://www.combodo.com/itop-help'
|
||||
'online_help' => 'http://www.combodo.com/itop-help',
|
||||
|
||||
// php_path: Path to the php executable in CLI mode
|
||||
// default: 'php'
|
||||
'php_path' => 'php',
|
||||
|
||||
// portal_tickets: CSV list of classes supported in the portal
|
||||
// default: 'UserRequest'
|
||||
'portal_tickets' => 'UserRequest',
|
||||
|
||||
'query_cache_enabled' => true,
|
||||
|
||||
// search_manual_submit: Force manual submit of search requests (class => true)
|
||||
// default: false
|
||||
'search_manual_submit' => array (
|
||||
'Person' => true,
|
||||
),
|
||||
|
||||
'secure_connection_required' => false,
|
||||
|
||||
// session_name: The name of the cookie used to store the PHP session id
|
||||
// default: 'iTop'
|
||||
'session_name' => 'iTop',
|
||||
|
||||
// shortcut_actions: Actions that are available as direct buttons next to the "Actions" menu
|
||||
// default: 'UI:Menu:Modify,UI:Menu:New'
|
||||
'shortcut_actions' => 'UI:Menu:Modify,UI:Menu:New',
|
||||
|
||||
// source_dir: Source directory for the datamodel files. (which gets compiled to env-production).
|
||||
// default: ''
|
||||
'source_dir' => 'datamodels/2.x/',
|
||||
|
||||
'standard_reload_interval' => '300',
|
||||
|
||||
// synchro_trace: Synchronization details: none, display, save (includes 'display')
|
||||
// default: 'none'
|
||||
'synchro_trace' => 'none',
|
||||
|
||||
// timezone: Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitely configured in PHP
|
||||
// default: 'Europe/Paris'
|
||||
'timezone' => 'Europe/Paris',
|
||||
|
||||
// tracking_level_linked_set_default: Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)
|
||||
// default: 1
|
||||
'tracking_level_linked_set_default' => 0,
|
||||
|
||||
// url_validation_pattern: Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)
|
||||
// default: '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?'
|
||||
'url_validation_pattern' => '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?',
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* Modules specific settings
|
||||
*
|
||||
*/
|
||||
$MyModuleSettings = array(
|
||||
'authent-local' => array (
|
||||
'password_validation.pattern' => '',
|
||||
),
|
||||
'itop-attachments' => array (
|
||||
'allowed_classes' => array (
|
||||
0 => 'Ticket',
|
||||
),
|
||||
'position' => 'relations',
|
||||
'preview_max_width' => 290,
|
||||
),
|
||||
'itop-backup' => array (
|
||||
'mysql_bindir' => '',
|
||||
'week_days' => 'monday, tuesday, wednesday, thursday, friday',
|
||||
'time' => '23:30',
|
||||
'retention_count' => 5,
|
||||
'enabled' => true,
|
||||
'debug' => false,
|
||||
),
|
||||
'molkobain-console-tooltips' => array (
|
||||
'decoration_class' => 'fas fa-question',
|
||||
'enabled' => true,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* Data model modules to be loaded. Names are specified as relative paths
|
||||
*
|
||||
*/
|
||||
$MyModules = array(
|
||||
'addons' => array (
|
||||
'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
|
||||
),
|
||||
);
|
||||
?>
|
||||
@@ -1,208 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2019 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
|
||||
*/
|
||||
|
||||
//this scrit will be run under the ./toolkit directory, relatively to the document root
|
||||
|
||||
require_once('../approot.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/application/clipage.class.inc.php');
|
||||
require_once(APPROOT.'/core/config.class.inc.php');
|
||||
require_once(APPROOT.'/core/log.class.inc.php');
|
||||
require_once(APPROOT.'/core/kpi.class.inc.php');
|
||||
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
require_once(APPROOT.'/setup/wizardcontroller.class.inc.php');
|
||||
require_once(APPROOT.'/setup/wizardsteps.class.inc.php');
|
||||
require_once(APPROOT.'/setup/applicationinstaller.class.inc.php');
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
$sParamFile = utils::ReadParam('response_file', 'default-params.xml', true /* CLI allowed */, 'raw_data');
|
||||
$bCheckConsistency = (utils::ReadParam('check_consistency', '0', true /* CLI allowed */) == '1');
|
||||
|
||||
$oParams = new XMLParameters($sParamFile);
|
||||
$sMode = $oParams->Get('mode');
|
||||
|
||||
if ($sMode == 'install')
|
||||
{
|
||||
echo "Installation mode detected.\n";
|
||||
$bClean = utils::ReadParam('clean', false, true /* CLI allowed */);
|
||||
if ($bClean)
|
||||
{
|
||||
echo "Cleanup mode detected.\n";
|
||||
$sTargetEnvironment = $oParams->Get('target_env', '');
|
||||
if ($sTargetEnvironment == '')
|
||||
{
|
||||
$sTargetEnvironment = 'production';
|
||||
}
|
||||
$sTargetDir = APPROOT.'env-'.$sTargetEnvironment;
|
||||
|
||||
// Configuration file
|
||||
$sConfigFile = APPCONF.$sTargetEnvironment.'/'.ITOP_CONFIG_FILE;
|
||||
if (file_exists($sConfigFile))
|
||||
{
|
||||
echo "Trying to delete the configuration file: '$sConfigFile'.\n";
|
||||
@chmod($sConfigFile, 0770); // RWX for owner and group, nothing for others
|
||||
unlink($sConfigFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No config file to delete ($sConfigFile does not exist).\n";
|
||||
}
|
||||
|
||||
// env-xxx directory
|
||||
if (file_exists($sTargetDir))
|
||||
{
|
||||
if (is_dir($sTargetDir))
|
||||
{
|
||||
echo "Emptying the target directory '$sTargetDir'.\n";
|
||||
SetupUtils::tidydir($sTargetDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
die("ERROR the target dir '$sTargetDir' exists, but is NOT a directory !!!\nExiting.\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No target directory to delete ($sTargetDir does not exist).\n";
|
||||
}
|
||||
|
||||
// Database
|
||||
$aDBSettings = $oParams->Get('database', array());
|
||||
$sDBServer = $aDBSettings['server'];
|
||||
$sDBUser = $aDBSettings['user'];
|
||||
$sDBPwd = $aDBSettings['pwd'];
|
||||
$sDBName = $aDBSettings['name'];
|
||||
$sDBPrefix = $aDBSettings['prefix'];
|
||||
|
||||
if ($sDBPrefix != '')
|
||||
{
|
||||
die("Cleanup not implemented for a partial database (prefix= '$sDBPrefix')\nExiting.");
|
||||
}
|
||||
|
||||
$oMysqli = new mysqli($sDBServer, $sDBUser, $sDBPwd);
|
||||
if ($oMysqli->connect_errno)
|
||||
{
|
||||
die("Cannot connect to the MySQL server (".$oMysqli->connect_errno . ") ".$oMysqli->connect_error."\nExiting");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($oMysqli->select_db($sDBName))
|
||||
{
|
||||
echo "Deleting database '$sDBName'\n";
|
||||
$oMysqli->query("DROP DATABASE `$sDBName`");
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "The database '$sDBName' does not seem to exist. Nothing to cleanup.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$bHasErrors = false;
|
||||
$aChecks = SetupUtils::CheckBackupPrerequisites(APPROOT.'data'); // mmm should be the backup destination dir
|
||||
|
||||
$aSelectedModules = $oParams->Get('selected_modules');
|
||||
$sSourceDir = $oParams->Get('source_dir', 'datamodels/latest');
|
||||
$sExtensionDir = $oParams->Get('extensions_dir', 'extensions');
|
||||
$aChecks = array_merge($aChecks, SetupUtils::CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules));
|
||||
|
||||
|
||||
foreach($aChecks as $oCheckResult)
|
||||
{
|
||||
switch($oCheckResult->iSeverity)
|
||||
{
|
||||
case CheckResult::ERROR:
|
||||
$bHasErrors = true;
|
||||
$sHeader = "Error";
|
||||
break;
|
||||
|
||||
case CheckResult::WARNING:
|
||||
$sHeader = "Warning";
|
||||
break;
|
||||
|
||||
case CheckResult::INFO:
|
||||
default:
|
||||
$sHeader = "Info";
|
||||
break;
|
||||
}
|
||||
echo $sHeader.": ".$oCheckResult->sLabel;
|
||||
if (strlen($oCheckResult->sDescription))
|
||||
{
|
||||
echo ' - '.$oCheckResult->sDescription;
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
if ($bHasErrors)
|
||||
{
|
||||
echo "Encountered stopper issues. Aborting...\n";
|
||||
die;
|
||||
}
|
||||
|
||||
$bFoundIssues = false;
|
||||
|
||||
$bInstall = utils::ReadParam('install', true, true /* CLI allowed */);
|
||||
if ($bInstall)
|
||||
{
|
||||
echo "Starting the unattended installation...\n";
|
||||
$oWizard = new ApplicationInstaller($oParams);
|
||||
$bRes = $oWizard->ExecuteAllSteps();
|
||||
if (!$bRes)
|
||||
{
|
||||
echo "\nencountered installation issues!";
|
||||
$bFoundIssues = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No installation requested.\n";
|
||||
}
|
||||
if (!$bFoundIssues && $bCheckConsistency)
|
||||
{
|
||||
echo "Checking data model consistency.\n";
|
||||
ob_start();
|
||||
$sCheckRes = '';
|
||||
try
|
||||
{
|
||||
MetaModel::CheckDefinitions(false);
|
||||
$sCheckRes = ob_get_clean();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$sCheckRes = ob_get_clean()."\nException: ".$e->getMessage();
|
||||
}
|
||||
if (strlen($sCheckRes) > 0)
|
||||
{
|
||||
echo $sCheckRes;
|
||||
echo "\nfound consistency issues!";
|
||||
$bFoundIssues = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bFoundIssues)
|
||||
{
|
||||
// last line: used to check the install
|
||||
// the only way to track issues in case of Fatal error or even parsing error!
|
||||
echo "\ninstalled!";
|
||||
exit;
|
||||
}
|
||||
90
.make/build/afterBuild.php
Normal file
90
.make/build/afterBuild.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
$iBeginTime = time();
|
||||
|
||||
chdir(__DIR__);
|
||||
|
||||
$aCommands = [
|
||||
'php composer/rmDeniedTestDir.php',
|
||||
'php build/commands/setupCssCompiler.php',
|
||||
// 'bash /tmp/gabuzomeu.sh',
|
||||
];
|
||||
|
||||
$aFailedCommands=[];
|
||||
foreach ($aCommands as $sCommand)
|
||||
{
|
||||
if (!ExecCommand($sCommand))
|
||||
{
|
||||
$aFailedCommands[] = $sCommand;
|
||||
}
|
||||
}
|
||||
|
||||
$iElapsed = time() - $iBeginTime;
|
||||
|
||||
if (count($aFailedCommands))
|
||||
{
|
||||
fwrite(STDERR, "\nafterBuild execution failed! (in ${iElapsed}s)\n");
|
||||
fwrite(STDERR, "List of failling commands:\n - " . implode("\n - ", $aFailedCommands) . "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
echo "\nDone (${iElapsed}s)\n";
|
||||
exit(0);
|
||||
|
||||
/**
|
||||
* Executes a command and returns an array with exit code, stdout and stderr content
|
||||
*
|
||||
* @param string $cmd - Command to execute
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
function ExecCommand($cmd) {
|
||||
$iBeginTime = time();
|
||||
|
||||
|
||||
echo sprintf("command: %s", str_pad("$cmd ", 50));
|
||||
|
||||
$descriptorspec = array(
|
||||
0 => array("pipe", "r"), // stdin
|
||||
1 => array("pipe", "w"), // stdout
|
||||
2 => array("pipe", "w"), // stderr
|
||||
);
|
||||
$process = proc_open($cmd, $descriptorspec, $pipes, __DIR__ . '/..', null);
|
||||
|
||||
$stdout = stream_get_contents($pipes[1]);
|
||||
fclose($pipes[1]);
|
||||
|
||||
$stderr = stream_get_contents($pipes[2]);
|
||||
fclose($pipes[2]);
|
||||
|
||||
$iCode = proc_close($process);
|
||||
$bSuccess = (0 === $iCode);
|
||||
|
||||
$iElapsed = time() - $iBeginTime;
|
||||
if (!$bSuccess) {
|
||||
fwrite(STDERR, sprintf(
|
||||
"\nCOMMAND FAILED! (%s) \n - status:%s \n - stderr:%s \n - stdout: %s\n - elapsed:%ss\n\n",
|
||||
$cmd,
|
||||
$iCode,
|
||||
rtrim($stderr),
|
||||
rtrim($stdout),
|
||||
$iElapsed
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "| elapsed:${iElapsed}s \n";
|
||||
}
|
||||
|
||||
if (!empty($stderr))
|
||||
{
|
||||
fwrite(STDERR, "$stderr\n");
|
||||
}
|
||||
if (!empty($stdout))
|
||||
{
|
||||
echo "stdout :$stdout\n\n";
|
||||
}
|
||||
|
||||
return $bSuccess;
|
||||
}
|
||||
51
.make/build/commands/setupCssCompiler.php
Normal file
51
.make/build/commands/setupCssCompiler.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?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/>
|
||||
*
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Composer\iTopComposer;
|
||||
|
||||
$iTopFolder = __DIR__."/../../../";
|
||||
|
||||
require_once("$iTopFolder/approot.inc.php");
|
||||
require_once(APPROOT."/application/utils.inc.php");
|
||||
|
||||
if (php_sapi_name() !== 'cli')
|
||||
{
|
||||
throw new \Exception('This script can only run from CLI');
|
||||
}
|
||||
|
||||
$sCssFile = APPROOT.'/css/setup.css';
|
||||
if (file_exists($sCssFile))
|
||||
{
|
||||
fwrite(STDERR, "$sCssFile already exists (it should not), removing it.");
|
||||
if (!unlink($sCssFile))
|
||||
{
|
||||
fwrite(STDERR, "Failed to remove $sCssFile, exiting.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
$sCssRelPath = utils::GetCSSFromSASS('css/setup.scss');
|
||||
|
||||
if (!file_exists($sCssFile))
|
||||
{
|
||||
fwrite(STDERR, "Failed to compile $sCssFile, exiting.");
|
||||
exit(1);
|
||||
}
|
||||
78
Jenkinsfile
vendored
78
Jenkinsfile
vendored
@@ -1,73 +1,11 @@
|
||||
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 {
|
||||
def infra
|
||||
|
||||
stage('init') {
|
||||
parallel {
|
||||
stage('debug') {
|
||||
steps {
|
||||
sh './.jenkins/bin/init/debug.sh'
|
||||
}
|
||||
}
|
||||
stage('append files to project') {
|
||||
steps {
|
||||
sh './.jenkins/bin/init/append_files.sh'
|
||||
}
|
||||
}
|
||||
stage('composer install') {
|
||||
steps {
|
||||
sh './.jenkins/bin/init/composer_install.sh'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node(){
|
||||
checkout scm
|
||||
|
||||
stage('unattended_install') {
|
||||
parallel {
|
||||
stage('unattended_install default env') {
|
||||
steps {
|
||||
sh './.jenkins/bin/unattended_install/default_env.sh'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('test') {
|
||||
parallel {
|
||||
stage('phpunit') {
|
||||
steps {
|
||||
sh './.jenkins/bin/tests/phpunit.sh ${debugMode} ${runNonRegOQLTests} "${coverture}" "${testFile}"'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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_UNESCAPED} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
|
||||
}
|
||||
fixed {
|
||||
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')
|
||||
}
|
||||
infra = load '/var/lib/jenkins/workspace/itop-test-infra_master/src/Infra.groovy'
|
||||
}
|
||||
|
||||
|
||||
infra.call()
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ responsible disclosure and will make every effort to acknowledge your contributi
|
||||
### iTop vulnerabilities
|
||||
Please send a procedure to reproduce iTop vulnerabilities to [itop-security@combodo.com](mailto:itop-security@combodo.com).
|
||||
|
||||
You can send us a standard "given / then / when" report, including iTop version, impacts, and maybe installed modules or data if they are
|
||||
You can send us a standard "given / when / then" report, including iTop version, impacts, and maybe installed modules or data if they are
|
||||
needed to reproduce.
|
||||
|
||||
### Dependencies vulnerabilities
|
||||
|
||||
@@ -200,7 +200,15 @@ class ApplicationContext
|
||||
}
|
||||
return implode("&", $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.8.0 N°2534 - dashboard: bug with autorefresh that deactivates filtering on organisation
|
||||
* Returns the params as c[menu]:..., c[org_id]:....
|
||||
* @return string The params
|
||||
*/
|
||||
public function GetForPostParams()
|
||||
{
|
||||
return json_encode( $this->aValues);
|
||||
}
|
||||
/**
|
||||
* Returns the context as sequence of input tags to be inserted inside a <form> tag
|
||||
* @return string The context as a sequence of <input type="hidden" /> tags
|
||||
|
||||
@@ -3819,19 +3819,24 @@ EOF
|
||||
break;
|
||||
|
||||
case 'Image':
|
||||
$value = null;
|
||||
$oImage = utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents');
|
||||
$aSize = utils::GetImageSize($oImage->GetData());
|
||||
$oImage = utils::ResizeImageToFit($oImage, $aSize[0], $aSize[1], $oAttDef->Get('storage_max_width'),
|
||||
$oAttDef->Get('storage_max_height'));
|
||||
if (!is_null($oImage->GetData()))
|
||||
{
|
||||
$aSize = utils::GetImageSize($oImage->GetData());
|
||||
$oImage = utils::ResizeImageToFit(
|
||||
$oImage,
|
||||
$aSize[0],
|
||||
$aSize[1],
|
||||
$oAttDef->Get('storage_max_width'),
|
||||
$oAttDef->Get('storage_max_height')
|
||||
);
|
||||
}
|
||||
$aOtherData = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null, 'raw_data');
|
||||
if (is_array($aOtherData))
|
||||
{
|
||||
$value = array('fcontents' => $oImage, 'remove' => $aOtherData['remove']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'RedundancySetting':
|
||||
|
||||
@@ -928,6 +928,9 @@ class RuntimeDashboard extends Dashboard
|
||||
$sExtraParams = json_encode($aAjaxParams);
|
||||
$iReloadInterval = 1000 * $this->GetAutoReloadInterval();
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext=$oAppContext->GetForPostParams();
|
||||
//$sContext is named "c" because it use the existing code for context parameters c[org_id] and c[menu]
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
if (typeof(AutoReloadDashboardId$sDivId) !== 'undefined')
|
||||
@@ -945,7 +948,7 @@ class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
$('.dashboard_contents#$sDivId').block();
|
||||
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
{ operation: 'reload_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, reload_url: '$sReloadURL'},
|
||||
{ operation: 'reload_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, c: $sContext, reload_url: '$sReloadURL'},
|
||||
function(data){
|
||||
$('.dashboard_contents#$sDivId').html(data);
|
||||
$('.dashboard_contents#$sDivId').unblock();
|
||||
|
||||
@@ -109,6 +109,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/all.min.css');
|
||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-combodo/font-combodo.css');
|
||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'js/ckeditor/plugins/codesnippet/lib/highlight/styles/obsidian.css');
|
||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/selectize.default.css');
|
||||
|
||||
// TODO: Add only what's necessary
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.layout.min.js');
|
||||
@@ -761,21 +762,18 @@ JS
|
||||
break;
|
||||
|
||||
default:
|
||||
$sHtml = '';
|
||||
$oAppContext = new ApplicationContext();
|
||||
$iCurrentOrganization = $oAppContext->GetCurrentValue('org_id');
|
||||
$sHtml = '<div id="SiloSelection">';
|
||||
$sHtml .= '<form style="display:inline" action="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php">'; //<select class="org_combo" name="c[org_id]" title="Pick an organization" onChange="this.form.submit();">';
|
||||
|
||||
$sFavoriteOrgs = '';
|
||||
$oWidget = new UIExtKeyWidget('Organization', 'org_id', '', true /* search mode */);
|
||||
$sHtml .= $oWidget->Display($this, 50, false, '', $oSet, $iCurrentOrganization, 'org_id', false, 'c[org_id]', '',
|
||||
$sHtml .= $oWidget->DisplaySelect($this, 50, false, '', $oSet, $iCurrentOrganization, false, 'c[org_id]', '',
|
||||
array(
|
||||
'iFieldSize' => 20,
|
||||
'iMinChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'),
|
||||
'sDefaultValue' => Dict::S('UI:AllOrganizations'),
|
||||
),
|
||||
null, 'select', false /* bSearchMultiple */);
|
||||
));
|
||||
$this->add_ready_script('$("#org_id").bind("extkeychange", function() { $("#SiloSelection form").submit(); } )');
|
||||
$this->add_ready_script("$('#label_org_id').click( function() { if ($('#org_id').val() == '') { $(this).val(''); } } );\n");
|
||||
// Add other dimensions/context information to this form
|
||||
|
||||
@@ -35,7 +35,17 @@ register_shutdown_function(function()
|
||||
$sReservedMemory = null;
|
||||
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
|
||||
{
|
||||
IssueLog::error($err['message']);
|
||||
// Remove stack trace from MySQLException
|
||||
$sMessage = $err['message'];
|
||||
if (strpos($sMessage, 'MySQLException') !== false)
|
||||
{
|
||||
$iStackTracePos = strpos($sMessage, 'Stack trace:');
|
||||
if ($iStackTracePos !== false)
|
||||
{
|
||||
$sMessage = substr($sMessage, 0, $iStackTracePos);
|
||||
}
|
||||
}
|
||||
IssueLog::error($sMessage);
|
||||
if (strpos($err['message'], 'Allowed memory size of') !== false)
|
||||
{
|
||||
$sLimit = ini_get('memory_limit');
|
||||
|
||||
@@ -234,7 +234,14 @@ class privUITransactionFile
|
||||
*/
|
||||
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
||||
{
|
||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
||||
// Constraint the transaction file within APPROOT.'data/transactions'
|
||||
$sTransactionDir = realpath(APPROOT.'data/transactions');
|
||||
$sFilepath = utils::RealPath($sTransactionDir.'/'.$id, $sTransactionDir);
|
||||
if (($sFilepath === false) || (strlen($sTransactionDir) == strlen($sFilepath)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
clearstatcache(true, $sFilepath);
|
||||
$bResult = file_exists($sFilepath);
|
||||
if ($bResult)
|
||||
|
||||
@@ -68,6 +68,7 @@ class UIExtKeyWidget
|
||||
protected $iId;
|
||||
protected $sTargetClass;
|
||||
protected $sAttCode;
|
||||
//@deprecated
|
||||
protected $bSearchMode;
|
||||
|
||||
//public function __construct($sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
|
||||
@@ -86,7 +87,29 @@ class UIExtKeyWidget
|
||||
$sDisplayStyle = 'select'; // In search mode, always use a drop-down list
|
||||
}
|
||||
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode);
|
||||
return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix, $aArgs, null, $sDisplayStyle);
|
||||
if(!$bSearchMode)
|
||||
{
|
||||
switch($sDisplayStyle)
|
||||
{
|
||||
case 'radio':
|
||||
case 'radio_horizontal':
|
||||
case 'radio_vertical':
|
||||
|
||||
return $oWidget->DisplayRadio($oPage, $iMaxComboLength, $bAllowTargetCreation, $oAllowedValues, $value, $sFieldName, $sDisplayStyle);
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
case 'list':
|
||||
default:
|
||||
return $oWidget->DisplaySelect($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value,
|
||||
$bMandatory, $sFieldName, $sFormPrefix, $aArgs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix, $aArgs, null, $sDisplayStyle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function __construct($sTargetClass, $iInputId, $sAttCode = '', $bSearchMode = false)
|
||||
@@ -98,6 +121,270 @@ class UIExtKeyWidget
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.8.0 N°2508 - Include Obsolescence icon within list and autocomplete
|
||||
* Get the HTML fragment corresponding to the ext key editing widget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @param array $aArgs Extra context arguments
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function DisplaySelect(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array())
|
||||
{
|
||||
$sTitle = addslashes($sTitle);
|
||||
$oPage->add_linked_script('../js/extkeywidget.js');
|
||||
$oPage->add_linked_script('../js/forms-json-utils.js');
|
||||
|
||||
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
|
||||
$bExtensions = true;
|
||||
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
|
||||
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey\">";
|
||||
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
|
||||
if($this->bSearchMode)
|
||||
{
|
||||
$sWizHelper = 'null';
|
||||
$sWizHelperJSON = "''";
|
||||
$sJSSearchMode = 'true';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($aArgs['wizHelper']))
|
||||
{
|
||||
$sWizHelper = $aArgs['wizHelper'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
}
|
||||
$sWizHelperJSON = $sWizHelper.'.UpdateWizardToJSON()';
|
||||
$sJSSearchMode = 'false';
|
||||
}
|
||||
if (is_null($oAllowedValues))
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
$oAllowedValues->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->sTargetClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
|
||||
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
|
||||
if (!$oAllowedValues->CountExceeds($iMaxComboLength))
|
||||
{
|
||||
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
|
||||
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
|
||||
//$sHTMLValue .= "<div class=\"field_select_wrapper\">\n";
|
||||
$aOptions = [];
|
||||
$sDisplayValue = "";
|
||||
|
||||
$aOption = [];
|
||||
$aOption['value'] = "";
|
||||
$aOption['label'] = Dict::S('UI:SelectOne');
|
||||
array_push($aOptions,$aOption);
|
||||
|
||||
$oAllowedValues->Rewind();
|
||||
while($oObj = $oAllowedValues->Fetch())
|
||||
{
|
||||
$aOption=[];
|
||||
$aOption['value'] = $oObj->GetKey();
|
||||
$aOption['label'] = $oObj->GetName();//.'<span class=\"object-ref-icon fas fa-eye-slash object-obsolete fa-1x fa-fw\"></span>';
|
||||
|
||||
if (($oAllowedValues->Count() == 1) && ($bMandatory == 'true') )
|
||||
{
|
||||
// When there is only once choice, select it by default
|
||||
$sDisplayValue=$oObj->GetName();
|
||||
if($value != $oObj->GetKey())
|
||||
{
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$this->iId').attr('data-validate','dependencies');
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((is_array($value) && in_array($oObj->GetKey(), $value)) || ($value == $oObj->GetKey()))
|
||||
{
|
||||
$sDisplayValue=$oObj->GetKey();
|
||||
// $sHTMLValue.="<div class='item' data-value='".$oObj->GetKey)."'>".$oObj->GetName()."</div>";
|
||||
}
|
||||
}
|
||||
if ($oObj->IsObsolete()){
|
||||
$aOption['obsolescence_flag'] ="1";
|
||||
}
|
||||
array_push($aOptions,$aOption);
|
||||
}
|
||||
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\"></select>";
|
||||
$sJsonOptions=json_encode($aOptions);
|
||||
$oPage->add_ready_script(
|
||||
<<<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>";
|
||||
oACWidget_{$this->iId}.AddSelectize('$sJsonOptions','$sDisplayValue');
|
||||
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
|
||||
$('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } );
|
||||
|
||||
JS
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Too many choices, use an autocomplete
|
||||
// Check that the given value is allowed
|
||||
$oSearch = $oAllowedValues->GetFilter();
|
||||
$oSearch->AddCondition('id', $value);
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
if ($oSet->Count() == 0)
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
|
||||
if (is_null($value) || ($value == 0)) // Null values are displayed as ''
|
||||
{
|
||||
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDisplayValue = $this->GetObjectName($value);
|
||||
}
|
||||
$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 .= "<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
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
|
||||
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
|
||||
// Scripts to start the autocomplete and bind some events to it
|
||||
$oPage->add_ready_script(
|
||||
<<<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>";
|
||||
oACWidget_{$this->iId}.AddAutocomplete($iMinChars, $sWizHelperJSON);
|
||||
if ($('#ac_dlg_{$this->iId}').length == 0)
|
||||
{
|
||||
$('body').append('<div id="ac_dlg_{$this->iId}"></div>');
|
||||
}
|
||||
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(
|
||||
<<<JS
|
||||
if ($('#ac_tree_{$this->iId}').length == 0)
|
||||
{
|
||||
$('body').append('<div id="ac_tree_{$this->iId}"></div>');
|
||||
}
|
||||
JS
|
||||
);
|
||||
}
|
||||
if ($bCreate && $bExtensions)
|
||||
{
|
||||
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
|
||||
|
||||
$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(
|
||||
<<<JS
|
||||
if ($('#ajax_{$this->iId}').length == 0)
|
||||
{
|
||||
$('body').append('<div id="ajax_{$this->iId}"></div>');
|
||||
}
|
||||
JS
|
||||
);
|
||||
}
|
||||
$sHTMLValue .= "</div>";
|
||||
$sHTMLValue .= "<span class=\"form_validation\" id=\"v_{$this->iId}\"></span><span class=\"field_status\" id=\"fstatus_{$this->iId}\"></span>";
|
||||
|
||||
return $sHTMLValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.8.0 N°2508 - Include Obsolescence icon within list and autocomplete
|
||||
* Get the HTML fragment corresponding to the ext key editing widget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @param array $aArgs Extra context arguments
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function DisplayRadio(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, DBObjectset $oAllowedValues, $value, $sFieldName, $sDisplayStyle)
|
||||
{
|
||||
$oPage->add_linked_script('../js/forms-json-utils.js');
|
||||
|
||||
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
|
||||
$bExtensions = true;
|
||||
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
|
||||
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey\">";
|
||||
|
||||
if (is_null($oAllowedValues))
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
$oAllowedValues->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
|
||||
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
|
||||
if (!$oAllowedValues->CountExceeds($iMaxComboLength))
|
||||
{
|
||||
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
|
||||
$sValidationField = null;
|
||||
|
||||
$bVertical = ($sDisplayStyle != 'radio_horizontal');
|
||||
$bExtensions = false;
|
||||
$oAllowedValues->Rewind();
|
||||
$aAllowedValues = array();
|
||||
while($oObj = $oAllowedValues->Fetch())
|
||||
{
|
||||
$aAllowedValues[$oObj->GetKey()] = $oObj->GetName();
|
||||
}
|
||||
$sHTMLValue .= $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", false /* $bMandatory will be placed manually */, $bVertical, $sValidationField);
|
||||
$aEventsList[] ='change';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHTMLValue .= "unable to display. Too much values";
|
||||
}
|
||||
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(
|
||||
<<<JS
|
||||
if ($('#ac_tree_{$this->iId}').length == 0)
|
||||
{
|
||||
$('body').append('<div id="ac_tree_{$this->iId}"></div>');
|
||||
}
|
||||
JS
|
||||
);
|
||||
}
|
||||
if ($bCreate && $bExtensions)
|
||||
{
|
||||
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
|
||||
|
||||
$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(
|
||||
<<<JS
|
||||
if ($('#ajax_{$this->iId}').length == 0)
|
||||
{
|
||||
$('body').append('<div id="ajax_{$this->iId}"></div>');
|
||||
}
|
||||
JS
|
||||
);
|
||||
}
|
||||
$sHTMLValue .= "</div>";
|
||||
|
||||
// Note: This test is no longer necessary as we changed the markup to extract validation decoration in the standard .field_input_xxx container
|
||||
//if (($sDisplayStyle == 'select') || ($sDisplayStyle == 'list'))
|
||||
//{
|
||||
$sHTMLValue .= "<span class=\"form_validation\" id=\"v_{$this->iId}\"></span><span class=\"field_status\" id=\"fstatus_{$this->iId}\"></span>";
|
||||
//}
|
||||
|
||||
return $sHTMLValue;
|
||||
}
|
||||
/**
|
||||
* @deprecated Use DisplayBob
|
||||
* Get the HTML fragment corresponding to the ext key editing widget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @param array $aArgs Extra context arguments
|
||||
@@ -319,12 +606,7 @@ JS
|
||||
);
|
||||
}
|
||||
$sHTMLValue .= "</div>";
|
||||
|
||||
// Note: This test is no longer necessary as we changed the markup to extract validation decoration in the standard .field_input_xxx container
|
||||
//if (($sDisplayStyle == 'select') || ($sDisplayStyle == 'list'))
|
||||
//{
|
||||
$sHTMLValue .= "<span class=\"form_validation\" id=\"v_{$this->iId}\"></span><span class=\"field_status\" id=\"fstatus_{$this->iId}\"></span>";
|
||||
//}
|
||||
$sHTMLValue .= "<span class=\"form_validation\" id=\"v_{$this->iId}\"></span><span class=\"field_status\" id=\"fstatus_{$this->iId}\"></span>";
|
||||
|
||||
return $sHTMLValue;
|
||||
}
|
||||
@@ -440,12 +722,12 @@ EOF
|
||||
$oValuesSet->SetSort(false);
|
||||
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'start_with');
|
||||
$aValuesContains = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'start_with');
|
||||
asort($aValuesContains);
|
||||
$aValues = $aValuesContains;
|
||||
if (sizeof($aValues) < $iMax)
|
||||
{
|
||||
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
|
||||
$aValuesContains = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
|
||||
asort($aValuesContains);
|
||||
$iSize = sizeof($aValuesContains);
|
||||
foreach ($aValuesContains as $sKey => $sFriendlyName)
|
||||
@@ -462,7 +744,7 @@ EOF
|
||||
}
|
||||
elseif (!in_array($sContains, $aValues))
|
||||
{
|
||||
$aValuesEquals = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals');
|
||||
$aValuesEquals = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals');
|
||||
$aValues = array_merge($aValuesEquals, $aValues);
|
||||
}
|
||||
|
||||
@@ -471,9 +753,9 @@ EOF
|
||||
case static::ENUM_OUTPUT_FORMAT_JSON:
|
||||
|
||||
$aJsonMap = array();
|
||||
foreach ($aValues as $sKey => $sLabel)
|
||||
foreach ($aValues as $sKey => $aValue)
|
||||
{
|
||||
$aJsonMap[] = array('value' => $sKey, 'label' => $sLabel);
|
||||
$aJsonMap[] = array('value' => $sKey, 'label' => $aValue['label'], 'obsolescence_flag' => $aValue['obsolescence_flag']);
|
||||
}
|
||||
|
||||
$oP->SetContentType('application/json');
|
||||
@@ -481,9 +763,9 @@ EOF
|
||||
break;
|
||||
|
||||
case static::ENUM_OUTPUT_FORMAT_CSV:
|
||||
foreach($aValues as $sKey => $sFriendlyName)
|
||||
foreach($aValues as $sKey => $aValue)
|
||||
{
|
||||
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
|
||||
$oP->add(trim($aValue['label'])."\t".$sKey."\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -309,6 +309,7 @@ class utils
|
||||
case 'context_param':
|
||||
case 'parameter':
|
||||
case 'field_name':
|
||||
case 'transaction_id':
|
||||
if (is_array($value))
|
||||
{
|
||||
$retValue = array();
|
||||
@@ -2112,6 +2113,41 @@ class utils
|
||||
return COMPILATION_TIMESTAMP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string eg : '2_7_0' ITOP_VERSION is '2.7.1-dev'
|
||||
*/
|
||||
public static function GetItopVersionWikiSyntax()
|
||||
{
|
||||
$sMinorVersion = self::GetItopMinorVersion();
|
||||
return str_replace('.', '_', $sMinorVersion).'_0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string eg 2.7 if ITOP_VERSION is '2.7.0-dev'
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function GetItopMinorVersion()
|
||||
{
|
||||
$sPatchVersion = self::GetItopPatchVersion();
|
||||
$aExplodedVersion = explode('.', $sPatchVersion);
|
||||
|
||||
if (empty($aExplodedVersion[0]) || empty($aExplodedVersion[1]))
|
||||
{
|
||||
throw new Exception('iTop version is wrongfully configured!');
|
||||
}
|
||||
|
||||
return sprintf('%d.%d', $aExplodedVersion[0], $aExplodedVersion[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string eg '2.7.0' if ITOP_VERSION is '2.7.0-dev'
|
||||
*/
|
||||
public static function GetItopPatchVersion()
|
||||
{
|
||||
$aExplodedVersion = explode('-', ITOP_VERSION);
|
||||
return $aExplodedVersion[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given class if configured as a high cardinality class.
|
||||
*
|
||||
@@ -2224,7 +2260,7 @@ class utils
|
||||
* @param string $sPath for example '/var/www/html/itop/data/backups/manual/itop_27-2019-10-03_15_35.tar.gz'
|
||||
* @param string $sBasePath for example '/var/www/html/itop/data/'
|
||||
*
|
||||
* @return bool false if path :
|
||||
* @return bool|string false if path :
|
||||
* * invalid
|
||||
* * not allowed
|
||||
* * not contained in base path
|
||||
|
||||
@@ -345,10 +345,10 @@ abstract class BulkExport
|
||||
$this->oBulkExportResult->Set('format', $this->sFormatCode);
|
||||
$this->oBulkExportResult->Set('search', $this->oSearch->serialize());
|
||||
$this->oBulkExportResult->Set('chunk_size', $this->iChunkSize);
|
||||
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
|
||||
$this->oBulkExportResult->Set('localize_output', $this->bLocalizeOutput);
|
||||
}
|
||||
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
|
||||
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
|
||||
utils::PushArchiveMode(false);
|
||||
$ret = $this->oBulkExportResult->DBWrite();
|
||||
utils::PopArchiveMode();
|
||||
@@ -420,6 +420,11 @@ abstract class BulkExport
|
||||
public function GetStatistics()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function SetFields($sFields)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function GetDownloadFileName()
|
||||
|
||||
@@ -95,11 +95,17 @@ abstract class CMDBObject extends DBObject
|
||||
protected static $m_oCurrChange = null;
|
||||
protected static $m_sInfo = null; // null => the information is built in a standard way
|
||||
protected static $m_sOrigin = null; // null => the origin is 'interactive'
|
||||
|
||||
|
||||
/**
|
||||
* Specify another change (this is mainly for backward compatibility)
|
||||
* Specify the change to be used by the API to attach any CMDBChangeOp* object created
|
||||
*
|
||||
* @see SetTrackInfo if CurrentChange is null, then a new one will be create using trackinfo
|
||||
*
|
||||
* @param CMDBChange|null $oChange use null so that the API will recreate a new CMDBChange using TrackInfo & TrackOrigin
|
||||
*
|
||||
* @since 2.7.2 N°3219 can now reset CMDBChange by passing null
|
||||
*/
|
||||
public static function SetCurrentChange(CMDBChange $oChange)
|
||||
public static function SetCurrentChange($oChange)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
}
|
||||
@@ -126,11 +132,15 @@ abstract class CMDBObject extends DBObject
|
||||
/**
|
||||
* Override the additional information (defaulting to user name)
|
||||
* A call to this verb should replace every occurence of
|
||||
* $oMyChange = MetaModel::NewObject("CMDBChange");
|
||||
* $oMyChange = MetaModel::NewObject("CMDBChange");
|
||||
* $oMyChange->Set("date", time());
|
||||
* $oMyChange->Set("userinfo", 'this is done by ... for ...');
|
||||
* $iChangeId = $oMyChange->DBInsert();
|
||||
*/
|
||||
*
|
||||
* @see SetCurrentChange to specify a CMDBObject instance instead
|
||||
*
|
||||
* @param string $sInfo
|
||||
*/
|
||||
public static function SetTrackInfo($sInfo)
|
||||
{
|
||||
self::$m_sInfo = $sInfo;
|
||||
@@ -138,8 +148,13 @@ abstract class CMDBObject extends DBObject
|
||||
|
||||
/**
|
||||
* Provides information about the origin of the change
|
||||
* @param $sOrigin String: one of: interactive, csv-interactive, csv-import.php, webservice-soap, webservice-rest, syncho-data-source, email-processing, custom-extension
|
||||
*/
|
||||
*
|
||||
* @see SetTrackInfo
|
||||
* @see SetCurrentChange to specify a CMDBObject instance instead
|
||||
*
|
||||
* @param $sOrigin String: one of: interactive, csv-interactive, csv-import.php, webservice-soap, webservice-rest, syncho-data-source,
|
||||
* email-processing, custom-extension
|
||||
*/
|
||||
public static function SetTrackOrigin($sOrigin)
|
||||
{
|
||||
self::$m_sOrigin = $sOrigin;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
define('ITOP_APPLICATION', 'iTop');
|
||||
define('ITOP_APPLICATION_SHORT', 'iTop');
|
||||
define('ITOP_VERSION', '2.8.0-dev');
|
||||
define('ITOP_VERSION', '2.8.0-dev'); // @see utils::GetItopVersionShort() and utils::GetItopVersionWikiSyntax()
|
||||
define('ITOP_REVISION', 'svn');
|
||||
define('ITOP_BUILD_DATE', '$WCNOW$');
|
||||
define('ITOP_VERSION_FULL', ITOP_VERSION.'-'.ITOP_REVISION);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.7">
|
||||
<user_rights>
|
||||
<profiles>
|
||||
<profile id="1024" _delta="define">
|
||||
|
||||
@@ -792,10 +792,11 @@ class DBObjectSearch extends DBSearch
|
||||
* Helper to
|
||||
* - convert a translation table (format optimized for the translation in an expression tree) into simple hash
|
||||
* - compile over an eventually existing map
|
||||
* - accept multiple translations for the same alias for unions
|
||||
*
|
||||
* @param array $aRealiasingMap Map to update
|
||||
* @param array $aAliasTranslation Translation table resulting from calls to MergeWith_InNamespace
|
||||
* @return void of <old-alias> => <new-alias>
|
||||
* @return void of [old-alias][] => new-alias (@since 2.7.2)
|
||||
*/
|
||||
protected function UpdateRealiasingMap(&$aRealiasingMap, $aAliasTranslation)
|
||||
{
|
||||
@@ -803,17 +804,33 @@ class DBObjectSearch extends DBSearch
|
||||
{
|
||||
foreach ($aAliasTranslation as $sPrevAlias => $aRules)
|
||||
{
|
||||
if (isset($aRules['*']))
|
||||
if (!isset($aRules['*']))
|
||||
{
|
||||
$sNewAlias = $aRules['*'];
|
||||
$sOriginalAlias = array_search($sPrevAlias, $aRealiasingMap);
|
||||
if ($sOriginalAlias !== false)
|
||||
continue;
|
||||
}
|
||||
|
||||
$sNewAlias = $aRules['*'];
|
||||
$bOriginalFound = false;
|
||||
$iIndex = 0;
|
||||
foreach ($aRealiasingMap as $sOriginalAlias => $aAliases)
|
||||
{
|
||||
$iIndex = array_search($sPrevAlias, $aAliases);
|
||||
if ($iIndex !== false)
|
||||
{
|
||||
$aRealiasingMap[$sOriginalAlias] = $sNewAlias;
|
||||
$bOriginalFound = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
||||
}
|
||||
if ($bOriginalFound)
|
||||
{
|
||||
$aRealiasingMap[$sOriginalAlias][$iIndex] = $sNewAlias;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isset($aRealiasingMap[$sPrevAlias]) || !in_array($sNewAlias, $aRealiasingMap[$sPrevAlias]))
|
||||
{
|
||||
$aRealiasingMap[$sPrevAlias] = $sNewAlias;
|
||||
$aRealiasingMap[$sPrevAlias][] = $sNewAlias;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -861,7 +878,7 @@ class DBObjectSearch extends DBSearch
|
||||
/**
|
||||
* Add a link to another filter, using an extkey already present in current filter
|
||||
*
|
||||
* @param DBObjectSearch $oFilter filter to join to
|
||||
* @param DBObjectSearch $oFilter filter to join to (can be modified)
|
||||
* @param string $sExtKeyAttCode extkey present in current filter, that allows to points to $oFilter
|
||||
* @param int $iOperatorCode
|
||||
* @param array $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed.
|
||||
@@ -955,7 +972,7 @@ class DBObjectSearch extends DBSearch
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObjectSearch $oFilter
|
||||
* @param DBObjectSearch $oFilter (can be modified)
|
||||
* @param $sForeignExtKeyAttCode
|
||||
* @param int $iOperatorCode
|
||||
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
|
||||
|
||||
@@ -631,7 +631,7 @@ abstract class DBSearch
|
||||
}
|
||||
|
||||
$sOql = $this->ToOql($bDevelopParams, $aContextParams);
|
||||
return json_encode(array($sOql, $aQueryParams, $this->m_aModifierProperties));
|
||||
return urlencode(json_encode(array($sOql, $aQueryParams, $this->m_aModifierProperties)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -648,7 +648,7 @@ abstract class DBSearch
|
||||
*/
|
||||
static public function unserialize($sValue)
|
||||
{
|
||||
$aData = json_decode($sValue, true);
|
||||
$aData = json_decode(urldecode($sValue), true);
|
||||
if (is_null($aData))
|
||||
{
|
||||
throw new CoreException("Invalid filter parameter");
|
||||
@@ -1112,6 +1112,8 @@ abstract class DBSearch
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @since 2.7.0 N°2555
|
||||
*/
|
||||
public function GetFirstResult($bMustHaveOneResultMax = true, $aOrderBy = array(), $aSearchParams = array())
|
||||
{
|
||||
|
||||
@@ -380,15 +380,14 @@ class DBUnionSearch extends DBSearch
|
||||
* @param DBObjectSearch $oFilter
|
||||
* @param $sExtKeyAttCode
|
||||
* @param int $iOperatorCode
|
||||
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
|
||||
* @throws CoreException
|
||||
* @throws CoreWarning
|
||||
* @param null $aRealiasingMap array of [old-alias][] => <new-alias>, for each alias that has changed (@since 2.7.2)
|
||||
*/
|
||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
|
||||
$oConditionFilter = $oFilter->DeepClone();
|
||||
$oSearch->AddCondition_PointingTo($oConditionFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,13 +395,14 @@ class DBUnionSearch extends DBSearch
|
||||
* @param DBObjectSearch $oFilter
|
||||
* @param $sForeignExtKeyAttCode
|
||||
* @param int $iOperatorCode
|
||||
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
|
||||
* @param null $aRealiasingMap array of [old-alias][] => <new-alias>, for each alias that has changed (@since 2.7.2)
|
||||
*/
|
||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
|
||||
$oConditionFilter = $oFilter->DeepClone();
|
||||
$oSearch->AddCondition_ReferencedBy($oConditionFilter, $sForeignExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -353,7 +353,8 @@ EOF
|
||||
|
||||
$fStartExcel = microtime(true);
|
||||
$writer = new XLSXWriter();
|
||||
$oDateTimeFormat = new DateTimeFormat($this->aStatusInfo['date_format']);
|
||||
$sDateFormat = isset($this->aStatusInfo['date_format']) ? $this->aStatusInfo['date_format'] : (string)AttributeDateTime::GetFormat();
|
||||
$oDateTimeFormat = new DateTimeFormat($sDateFormat);
|
||||
$writer->setDateTimeFormat($oDateTimeFormat->ToExcel());
|
||||
$oDateFormat = new DateTimeFormat($oDateTimeFormat->ToDateFormat());
|
||||
$writer->setDateFormat($oDateFormat->ToExcel());
|
||||
|
||||
@@ -126,6 +126,13 @@ abstract class MetaModel
|
||||
/** @var string */
|
||||
protected static $m_sEnvironment = 'production';
|
||||
|
||||
/**
|
||||
* MetaModel constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
@@ -2766,7 +2773,7 @@ abstract class MetaModel
|
||||
$aInterfaces = array('iApplicationUIExtension', 'iPreferencesExtension', 'iApplicationObjectExtension', 'iLoginFSMExtension', 'iLoginUIExtension', 'iLogoutExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider', 'iModuleExtension');
|
||||
foreach($aInterfaces as $sInterface)
|
||||
{
|
||||
self::$m_aExtensionClasses[$sInterface] = array();
|
||||
self::$m_aExtensionClassNames[$sInterface] = array();
|
||||
}
|
||||
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
@@ -2777,11 +2784,7 @@ abstract class MetaModel
|
||||
{
|
||||
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable())
|
||||
{
|
||||
if (is_null($oExtensionInstance))
|
||||
{
|
||||
$oExtensionInstance = new $sPHPClass;
|
||||
}
|
||||
self::$m_aExtensionClasses[$sInterface][$sPHPClass] = $oExtensionInstance;
|
||||
self::$m_aExtensionClassNames[$sInterface][$sPHPClass] = $sPHPClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5506,7 +5509,7 @@ abstract class MetaModel
|
||||
{
|
||||
$sIndexName = $sField;
|
||||
$sColumns = '`'.$sField.'`';
|
||||
if (!is_null($aLength[0]))
|
||||
if (isset($aLength[0]))
|
||||
{
|
||||
$sColumns .= ' ('.$aLength[0].')';
|
||||
}
|
||||
@@ -6366,7 +6369,7 @@ abstract class MetaModel
|
||||
if (is_array($result))
|
||||
{
|
||||
// todo - verifier que toutes les classes mentionnees ici sont chargees dans InitClasses()
|
||||
self::$m_aExtensionClasses = $result['m_aExtensionClasses'];
|
||||
self::$m_aExtensionClassNames = $result['m_aExtensionClassNames'];
|
||||
self::$m_Category2Class = $result['m_Category2Class'];
|
||||
self::$m_aRootClasses = $result['m_aRootClasses'];
|
||||
self::$m_aParentClasses = $result['m_aParentClasses'];
|
||||
@@ -6403,7 +6406,7 @@ abstract class MetaModel
|
||||
$oKPI = new ExecutionKPI();
|
||||
|
||||
$aCache = array();
|
||||
$aCache['m_aExtensionClasses'] = self::$m_aExtensionClasses;
|
||||
$aCache['m_aExtensionClassNames'] = self::$m_aExtensionClassNames;
|
||||
$aCache['m_Category2Class'] = self::$m_Category2Class;
|
||||
$aCache['m_aRootClasses'] = self::$m_aRootClasses; // array of "classname" => "rootclass"
|
||||
$aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass")
|
||||
@@ -6489,6 +6492,8 @@ abstract class MetaModel
|
||||
|
||||
/** @var array */
|
||||
protected static $m_aExtensionClasses = array();
|
||||
/** @var array */
|
||||
protected static $m_aExtensionClassNames = array();
|
||||
|
||||
/**
|
||||
* @param string $sToInclude
|
||||
@@ -7283,25 +7288,9 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function EnumPlugins($sInterface, $sFilterInstanceOf = null)
|
||||
{
|
||||
if (!array_key_exists($sInterface, self::$m_aExtensionClasses))
|
||||
{
|
||||
return array();
|
||||
}
|
||||
$pluginManager = new PluginManager(self::$m_aExtensionClassNames, self::$m_aExtensionClasses);
|
||||
|
||||
if (is_null($sFilterInstanceOf))
|
||||
{
|
||||
return self::$m_aExtensionClasses[$sInterface];
|
||||
}
|
||||
|
||||
$fFilterCallback = function ($instance) use ($sFilterInstanceOf)
|
||||
{
|
||||
return $instance instanceof $sFilterInstanceOf;
|
||||
};
|
||||
|
||||
return array_filter(
|
||||
self::$m_aExtensionClasses[$sInterface],
|
||||
$fFilterCallback
|
||||
);
|
||||
return $pluginManager->EnumPlugins($sInterface, $sFilterInstanceOf);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7312,16 +7301,9 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetPlugins($sInterface, $sClassName)
|
||||
{
|
||||
$oInstance = null;
|
||||
if (array_key_exists($sInterface, self::$m_aExtensionClasses))
|
||||
{
|
||||
if (array_key_exists($sClassName, self::$m_aExtensionClasses[$sInterface]))
|
||||
{
|
||||
return self::$m_aExtensionClasses[$sInterface][$sClassName];
|
||||
}
|
||||
}
|
||||
$pluginManager = new PluginManager(self::$m_aExtensionClassNames, self::$m_aExtensionClasses);
|
||||
|
||||
return $oInstance;
|
||||
return $pluginManager->GetPlugins($sInterface, $sClassName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7471,6 +7453,119 @@ abstract class MetaModel
|
||||
}
|
||||
}
|
||||
|
||||
class PluginManager
|
||||
{
|
||||
|
||||
private $m_aExtensionClassNames;
|
||||
private $m_aExtensionClasses;
|
||||
private $m_pluginInstantiationManager ;
|
||||
|
||||
public function __construct($m_aExtensionClassNames, $m_aExtensionClasses, $m_pluginInstanciationManager=null)
|
||||
{
|
||||
$this->m_aExtensionClasses = $m_aExtensionClasses;
|
||||
$this->m_aExtensionClassNames = $m_aExtensionClassNames;
|
||||
|
||||
if ($m_pluginInstanciationManager==null)
|
||||
{
|
||||
$this->m_pluginInstantiationManager = new PluginInstanciationManager();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_pluginInstantiationManager = $m_pluginInstanciationManager;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sInterface
|
||||
* @param bool $bCanInstantiatePlugins internal use, let this value to true
|
||||
* @param string|null $sFilterInstanceOf [optional] if given, only instance of this string will be returned
|
||||
* @return array classes=>instance implementing the given interface
|
||||
*/
|
||||
public function EnumPlugins($sInterface, $sFilterInstanceOf = null, $bCanInstantiatePlugins = true)
|
||||
{
|
||||
$aPlugins = array();
|
||||
if (array_key_exists($sInterface, $this->m_aExtensionClasses))
|
||||
{
|
||||
$aAllPlugins = array_values($this->m_aExtensionClasses[$sInterface]);
|
||||
|
||||
if (is_null($sFilterInstanceOf))
|
||||
{
|
||||
return $aAllPlugins;
|
||||
};
|
||||
|
||||
$aPlugins = array();
|
||||
foreach ($aAllPlugins as $instance)
|
||||
{
|
||||
if ($instance instanceof $sFilterInstanceOf)
|
||||
{
|
||||
$aPlugins[] = $instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ($bCanInstantiatePlugins && array_key_exists($sInterface, $this->m_aExtensionClassNames))
|
||||
{
|
||||
$this->InstantiatePlugins($sInterface);
|
||||
|
||||
return $this->EnumPlugins($sInterface, $sFilterInstanceOf, false);
|
||||
}
|
||||
return $aPlugins;
|
||||
}
|
||||
|
||||
public function InstantiatePlugins($sInterface)
|
||||
{
|
||||
$this->m_aExtensionClasses[$sInterface] = $this->m_pluginInstantiationManager->InstantiatePlugins($this->m_aExtensionClassNames, $sInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sInterface
|
||||
* @param string $sClassName
|
||||
* @param bool $bCanInstantiatePlugins internal use, let this value to true
|
||||
*
|
||||
* @return mixed the instance of the specified plug-ins for the given interface
|
||||
*/
|
||||
public function GetPlugins($sInterface, $sClassName, $bCanInstantiatePlugins = true)
|
||||
{
|
||||
$oInstance = null;
|
||||
if (array_key_exists($sInterface, $this->m_aExtensionClasses))
|
||||
{
|
||||
if (array_key_exists($sClassName, $this->m_aExtensionClasses[$sInterface]))
|
||||
{
|
||||
return $this->m_aExtensionClasses[$sInterface][$sClassName];
|
||||
}
|
||||
}
|
||||
else if ($bCanInstantiatePlugins && array_key_exists($sInterface, $this->m_aExtensionClassNames))
|
||||
{
|
||||
$this->InstantiatePlugins($sInterface);
|
||||
return $this->GetPlugins($sInterface, $sClassName, false);
|
||||
}
|
||||
|
||||
return $oInstance;
|
||||
}
|
||||
} //PluginManager class
|
||||
|
||||
class PluginInstanciationManager
|
||||
{
|
||||
public function InstantiatePlugins($m_aExtensionClassNames, $sInterface)
|
||||
{
|
||||
$newPerInstanceClasses = array();
|
||||
if (array_key_exists($sInterface, $m_aExtensionClassNames))
|
||||
{
|
||||
foreach ($m_aExtensionClassNames[$sInterface] as $sClassName)
|
||||
{
|
||||
if (class_exists($sClassName))
|
||||
{
|
||||
$class = new ReflectionClass($sClassName);
|
||||
|
||||
if ($class->isInstantiable())
|
||||
{
|
||||
$newPerInstanceClasses[$sClassName] = new $sClassName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $newPerInstanceClasses;
|
||||
}
|
||||
}
|
||||
|
||||
// Standard attribute lists
|
||||
MetaModel::RegisterZList("noneditable", array("description" => "non editable fields", "type" => "attributes"));
|
||||
|
||||
@@ -2235,7 +2235,15 @@ class ListExpression extends Expression
|
||||
{
|
||||
if ($oExpr instanceof VariableExpression)
|
||||
{
|
||||
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar($aArgs);
|
||||
$oVarExpr = $oExpr->GetAsScalar($aArgs);
|
||||
if ($oVarExpr instanceof ListExpression)
|
||||
{
|
||||
$this->m_aExpressions = $oVarExpr->GetItems();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aExpressions[$idx] = $oVarExpr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -30,8 +30,8 @@ require_once('dbobjectiterator.php');
|
||||
|
||||
class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
{
|
||||
public const LINK_ALIAS = 'Link';
|
||||
public const REMOTE_ALIAS = 'Remote';
|
||||
const LINK_ALIAS = 'Link';
|
||||
const REMOTE_ALIAS = 'Remote';
|
||||
|
||||
protected $sHostClass; // subclass of DBObject
|
||||
protected $sAttCode; // xxxxxx_list
|
||||
@@ -812,6 +812,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
// 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(
|
||||
@@ -821,6 +822,18 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
);
|
||||
$oRemoteClassSearch->AddConditionExpression($oNotObsolete);
|
||||
}
|
||||
|
||||
if (!utils::IsArchiveMode() && MetaModel::IsArchivable($sTargetClass))
|
||||
{
|
||||
$oNotArchived = new BinaryExpression(
|
||||
new FieldExpression('archive_flag', $sTargetClass),
|
||||
'=',
|
||||
new ScalarExpression(0)
|
||||
);
|
||||
|
||||
$oRemoteClassSearch->AddConditionExpression($oNotArchived);
|
||||
}
|
||||
|
||||
$oLinkSearch->AddCondition_PointingTo($oRemoteClassSearch, $sExtKeyToRemote);
|
||||
$oLinkSearch->RenameAlias($oLinkSearch->GetClassAlias(), self::LINK_ALIAS);
|
||||
$oLinkSearch->RenameAlias($sTargetClass, self::REMOTE_ALIAS);
|
||||
@@ -835,4 +848,4 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
|
||||
return $oLinkSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,29 +365,37 @@ EOF
|
||||
{
|
||||
throw new BulkExportMissingParameterException('fields');
|
||||
}
|
||||
else if(($sQueryId !== null) && ($sQueryId !== null))
|
||||
else
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
|
||||
$oQueries = new DBObjectSet($oSearch);
|
||||
if ($oQueries->Count() > 0)
|
||||
if (($sQueryId !== null) && ($sQueryId !== null))
|
||||
{
|
||||
$oQuery = $oQueries->Fetch();
|
||||
if (($sFields === null) || ($sFields === ''))
|
||||
$oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
|
||||
$oQueries = new DBObjectSet($oSearch);
|
||||
if ($oQueries->Count() > 0)
|
||||
{
|
||||
// No 'fields' parameter supplied, take the fields from the query phrasebook definition
|
||||
$sFields = trim($oQuery->Get('fields'));
|
||||
if ($sFields === '')
|
||||
$oQuery = $oQueries->Fetch();
|
||||
if (($sFields === null) || ($sFields === ''))
|
||||
{
|
||||
throw new BulkExportMissingParameterException('fields');
|
||||
// No 'fields' parameter supplied, take the fields from the query phrasebook definition
|
||||
$sFields = trim($oQuery->Get('fields'));
|
||||
if ($sFields === '')
|
||||
{
|
||||
throw new BulkExportMissingParameterException('fields');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw BulkExportException('Invalid value for the parameter: query. There is no Query Phrasebook with id = '.$sQueryId, Dict::Format('Core:BulkExport:InvalidParameter_Query', $sQueryId));
|
||||
else
|
||||
{
|
||||
throw BulkExportException('Invalid value for the parameter: query. There is no Query Phrasebook with id = '.$sQueryId, Dict::Format('Core:BulkExport:InvalidParameter_Query', $sQueryId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->SetFields($sFields);
|
||||
}
|
||||
|
||||
public function SetFields($sFields)
|
||||
{
|
||||
// Interpret (and check) the list of fields
|
||||
//
|
||||
$aSelectedClasses = $this->oSearch->GetSelectedClasses();
|
||||
|
||||
@@ -220,7 +220,7 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
$this->m_sOperation = $sOperation;
|
||||
|
||||
$this->m_aValues = array();
|
||||
|
||||
|
||||
if ($this->m_bAllowAllData)
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL_AllData($this->m_sFilterExpr);
|
||||
@@ -348,6 +348,141 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
{
|
||||
$this->m_bSort = $bSort;
|
||||
}
|
||||
|
||||
public function GetValuesForAutocomplete($aArgs, $sContains = '', $sOperation = 'contains', $aAdditionalFields = array())
|
||||
{
|
||||
if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains) || ($sOperation != $this->m_sOperation))
|
||||
{
|
||||
$this->LoadValuesForAutocomplete($aArgs, $sContains, $sOperation, $aAdditionalFields);
|
||||
$this->m_bIsLoaded = true;
|
||||
}
|
||||
// The results are already filtered and sorted (on friendly name)
|
||||
$aRet = $this->m_aValues;
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aArgs
|
||||
* @param string $sContains
|
||||
* @param string $sOperation 'contains' or 'equals_start_with'
|
||||
*
|
||||
* @return bool
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
protected function LoadValuesForAutocomplete($aArgs, $sContains = '', $sOperation = 'contains', $aAdditionalFields = array())
|
||||
{
|
||||
$this->m_sContains = $sContains;
|
||||
$this->m_sOperation = $sOperation;
|
||||
|
||||
$this->m_aValues = array();
|
||||
|
||||
if ($this->m_bAllowAllData)
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL_AllData($this->m_sFilterExpr);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL($this->m_sFilterExpr);
|
||||
$oFilter->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
}
|
||||
if (!$oFilter) return false;
|
||||
if (!is_null($this->m_oExtraCondition))
|
||||
{
|
||||
$oFilter = $oFilter->Intersect($this->m_oExtraCondition);
|
||||
}
|
||||
foreach($this->m_aModifierProperties as $sPluginClass => $aProperties)
|
||||
{
|
||||
foreach ($aProperties as $sProperty => $value)
|
||||
{
|
||||
$oFilter->SetModifierProperty($sPluginClass, $sProperty, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$oExpression = DBObjectSearch::GetPolymorphicExpression($oFilter->GetClass(), 'friendlyname');
|
||||
$sClass = $oFilter->GetClass();
|
||||
|
||||
switch ($sOperation)
|
||||
{
|
||||
case 'equals':
|
||||
$aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($sClass);
|
||||
$sClassAlias = $oFilter->GetClassAlias();
|
||||
$aFilters = array();
|
||||
$oValueExpr = new ScalarExpression($sContains);
|
||||
foreach($aAttributes as $sAttribute)
|
||||
{
|
||||
$oNewFilter = $oFilter->DeepClone();
|
||||
$oNameExpr = new FieldExpression($sAttribute, $sClassAlias);
|
||||
$oCondition = new BinaryExpression($oNameExpr, '=', $oValueExpr);
|
||||
$oNewFilter->AddConditionExpression($oCondition);
|
||||
$aFilters[] = $oNewFilter;
|
||||
}
|
||||
// Unions are much faster than OR conditions
|
||||
$oFilter = new DBUnionSearch($aFilters);
|
||||
break;
|
||||
case 'start_with':
|
||||
$aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($sClass);
|
||||
$sClassAlias = $oFilter->GetClassAlias();
|
||||
$aFilters = array();
|
||||
$oValueExpr = new ScalarExpression($sContains.'%');
|
||||
foreach($aAttributes as $sAttribute)
|
||||
{
|
||||
$oNewFilter = $oFilter->DeepClone();
|
||||
$oNameExpr = new FieldExpression($sAttribute, $sClassAlias);
|
||||
$oCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
|
||||
$oNewFilter->AddConditionExpression($oCondition);
|
||||
$aFilters[] = $oNewFilter;
|
||||
}
|
||||
// Unions are much faster than OR conditions
|
||||
$oFilter = new DBUnionSearch($aFilters);
|
||||
break;
|
||||
|
||||
default:
|
||||
$oValueExpr = new ScalarExpression('%'.$sContains.'%');
|
||||
$oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias());
|
||||
$oNewCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
|
||||
$oFilter->AddConditionExpression($oNewCondition);
|
||||
break;
|
||||
}
|
||||
|
||||
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs, null, $this->m_iLimit, 0, $this->m_bSort);
|
||||
if (empty($this->m_sValueAttCode))
|
||||
{
|
||||
$aAttToLoad = array($oFilter->GetClassAlias() => array('friendlyname'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAttToLoad = array($oFilter->GetClassAlias() => array($this->m_sValueAttCode));
|
||||
}
|
||||
$aAttToLoad=array_merge($aAttToLoad,$aAdditionalFields);
|
||||
$oObjects->OptimizeColumnLoad($aAttToLoad);
|
||||
while ($oObject = $oObjects->Fetch())
|
||||
{
|
||||
$aData=[];
|
||||
if (empty($this->m_sValueAttCode))
|
||||
{
|
||||
$aData['label'] = $oObject->GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aData['label'] = $oObject->Get($this->m_sValueAttCode);
|
||||
}
|
||||
if($oObject->IsObsolete())
|
||||
{
|
||||
$aData['obsolescence_flag']='1';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aData['obsolescence_flag']='0';
|
||||
}
|
||||
foreach ($aAdditionalFields as $sFieldName)
|
||||
{
|
||||
$aData[$sFieldName] = $oObject->Get($sFieldName);
|
||||
}
|
||||
$this->m_aValues[$oObject->GetKey()] = $aData;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2376,16 +2376,19 @@
|
||||
noborder-bottom: 2px #fff solid;
|
||||
nowidth: 100%;
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
fieldset .details > .field_container {
|
||||
fieldset .details>.field_container {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.field_input_zone {
|
||||
.selectize-input {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.field_container {
|
||||
display: table;
|
||||
width: 100%;
|
||||
@@ -2477,8 +2480,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.field_input_zone {
|
||||
width: 100%; /* auto; */
|
||||
.field_input_zone{
|
||||
width: 100%; /* auto; */
|
||||
align-items:center;
|
||||
|
||||
&.field_input_string,
|
||||
&.field_input_password {
|
||||
@@ -2615,9 +2619,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.field_input_extkey {
|
||||
display: table;
|
||||
width: 100%;
|
||||
&.field_input_extkey{
|
||||
display: inline-table;
|
||||
width: 100%;
|
||||
|
||||
> .field_select_wrapper {
|
||||
display: table-cell;
|
||||
|
||||
267
css/setup.css
267
css/setup.css
@@ -1,267 +0,0 @@
|
||||
/*!
|
||||
* Copyright (C) 2013-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
|
||||
*/
|
||||
/* Helpers classes */
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
/* Animations */
|
||||
@keyframes progress_bar_color_ongoing {
|
||||
from {
|
||||
background-color: #FBD38D;
|
||||
}
|
||||
to {
|
||||
background-color: #FEEBC8;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes bg-pan-left {
|
||||
0% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0 50%;
|
||||
}
|
||||
}
|
||||
@keyframes bg-pan-left {
|
||||
0% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0 50%;
|
||||
}
|
||||
}
|
||||
/* Theme */
|
||||
body {
|
||||
background-color: #eee;
|
||||
color: #1A202C;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 10pt;
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica, serif;
|
||||
overflow-y: auto;
|
||||
}
|
||||
h1 {
|
||||
color: #555555;
|
||||
font-size: 16pt;
|
||||
}
|
||||
h2 {
|
||||
color: #1A202C;
|
||||
font-size: 14pt;
|
||||
font-weight: normal;
|
||||
}
|
||||
h3 {
|
||||
color: #1C94C4;
|
||||
font-size: 12pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
a {
|
||||
color: #1c94c4;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: #EA7D1E;
|
||||
}
|
||||
#header {
|
||||
width: 600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 50px;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
height: 54px;
|
||||
border: 3px solid #CBD2D9;
|
||||
border-bottom: none;
|
||||
}
|
||||
#header img {
|
||||
border: 0;
|
||||
vertical-align: middle;
|
||||
margin-right: 20px;
|
||||
}
|
||||
#header h1 {
|
||||
height: 54px;
|
||||
margin: 0;
|
||||
}
|
||||
#setup {
|
||||
width: 600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border: 3px solid #CBD2D9;
|
||||
border-top: none;
|
||||
}
|
||||
.next {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
.v-spacer {
|
||||
padding-top: 1em;
|
||||
}
|
||||
button {
|
||||
margin-top: 1em;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
p.info {
|
||||
padding-left: 50px;
|
||||
background: url(../images/info-mid.png) no-repeat left -5px;
|
||||
min-height: 48px;
|
||||
}
|
||||
p.ok {
|
||||
padding-left: 50px;
|
||||
background: url(../images/clean-mid.png) no-repeat left -8px;
|
||||
min-height: 48px;
|
||||
}
|
||||
p.warning {
|
||||
padding-left: 50px;
|
||||
background: url(../images/messagebox_warning-mid.png) no-repeat left -5px;
|
||||
min-height: 48px;
|
||||
}
|
||||
p.error {
|
||||
padding-left: 50px;
|
||||
background: url(../images/stop-mid.png) no-repeat left -5px;
|
||||
min-height: 48px;
|
||||
}
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
td.label {
|
||||
text-align: left;
|
||||
}
|
||||
label.read-only {
|
||||
color: #666;
|
||||
cursor: text;
|
||||
}
|
||||
td.input {
|
||||
text-align: left;
|
||||
}
|
||||
table.formTable {
|
||||
border: 0;
|
||||
}
|
||||
.wizlabel, .wizinput {
|
||||
color: #000;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.wizhelp {
|
||||
color: #333;
|
||||
font-size: 8pt;
|
||||
}
|
||||
#progress {
|
||||
border: none;
|
||||
width: 210px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
margin: 5px;
|
||||
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06) !important;
|
||||
border-radius: 2px;
|
||||
background-color: #EDF2F7 !important;
|
||||
}
|
||||
#progress .progress {
|
||||
color: #000000 !important;
|
||||
/* !important to overload jQuery UI style */
|
||||
background-image: linear-gradient(270deg, #FBD38D 50%, #FEEBC8 55%, #FBD38D 80%) !important;
|
||||
/* !important to overload jQuery UI style */
|
||||
animation: bg-pan-left 3s infinite both;
|
||||
background-size: 600% 100%;
|
||||
border-radius: inherit;
|
||||
}
|
||||
#progress .progress.progress-error {
|
||||
background-image: none !important;
|
||||
background-color: #F56565 !important;
|
||||
/* !important to overload jQuery UI style */
|
||||
animation: none;
|
||||
}
|
||||
h3.clickable {
|
||||
background: url(../images/plus.gif) no-repeat left;
|
||||
padding-left: 16px;
|
||||
cursor: hand;
|
||||
}
|
||||
h3.clickable.open {
|
||||
background: url(../images/minus.gif) no-repeat left;
|
||||
padding-left: 16px;
|
||||
cursor: hand;
|
||||
}
|
||||
.message {
|
||||
color: #1A202C;
|
||||
background-color: #F7FAFC;
|
||||
border-left: 4px solid #4A5568;
|
||||
padding: 10px;
|
||||
}
|
||||
.message > .message-title {
|
||||
font-weight: bold;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.message ~ .message {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.message.message-valid {
|
||||
color: #276749;
|
||||
background-color: #F0FFF4;
|
||||
border-color: #48BB78;
|
||||
}
|
||||
.message.message-warning {
|
||||
color: #C05621;
|
||||
background-color: #FFFAF0;
|
||||
border-color: #ED8936;
|
||||
}
|
||||
.message.message-error {
|
||||
color: #C53030;
|
||||
background-color: #FFF5F5;
|
||||
border-color: #E53E3E;
|
||||
}
|
||||
.text-valid {
|
||||
color: #276749;
|
||||
}
|
||||
.text-warning {
|
||||
color: #C05621;
|
||||
}
|
||||
.text-error {
|
||||
color: #C53030;
|
||||
}
|
||||
fieldset {
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
fieldset > legend {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 7px;
|
||||
padding-bottom: 7px;
|
||||
width: 100%;
|
||||
color: #3C3C3C;
|
||||
font-size: 11pt;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #D2D2D2;
|
||||
}
|
||||
.module-selection-banner img {
|
||||
max-height: 48px;
|
||||
}
|
||||
.module-selection-body {
|
||||
height: 28em;
|
||||
overflow: auto;
|
||||
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06) !important;
|
||||
background-color: #F7FAFC;
|
||||
padding: 10px;
|
||||
}
|
||||
.module-selection-body .wiz-choice:checked ~ .description #itop-ticket-mgmt-simple-ticket-enhanced-portal:not(:checked) ~ .description::after, .module-selection-body .wiz-choice:checked ~ .description #itop-ticket-mgmt-itil-enhanced-portal:not(:checked) ~ .description::after {
|
||||
content: "Legacy portal is no longer part of iTop, by leaving this option unchecked your portal users won't be able to access iTop anymore.";
|
||||
display: block;
|
||||
margin-top: 0.5em;
|
||||
font-weight: bold;
|
||||
color: #e60000b8;
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
/* integrityCheck: begin (do not remove/edit) */
|
||||
|
||||
/////////
|
||||
// Colors
|
||||
$content-border-color: #CBD2D9 !default;
|
||||
@@ -314,3 +316,4 @@ fieldset{
|
||||
}
|
||||
}
|
||||
|
||||
/* integrityCheck: end (do not remove/edit) */
|
||||
29
datamodels/2.x/combodo-db-tools/bin/report.php
Normal file
29
datamodels/2.x/combodo-db-tools/bin/report.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2020 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\DBTools\Service\DBAnalyzerUtils;
|
||||
|
||||
@include_once('../approot.inc.php');
|
||||
@include_once('../../approot.inc.php');
|
||||
@include_once('../../../approot.inc.php');
|
||||
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
|
||||
require_once('../db_analyzer.class.inc.php');
|
||||
require_once('../src/Service/DBAnalyzerUtils.php');
|
||||
|
||||
$oDBAnalyzer = new DatabaseAnalyzer(0);
|
||||
$aResults = $oDBAnalyzer->CheckIntegrity([]);
|
||||
|
||||
if (empty($aResults))
|
||||
{
|
||||
echo "Database OK\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$sReportFile = DBAnalyzerUtils::GenerateReport($aResults);
|
||||
|
||||
echo "Report generated: {$sReportFile}.log\n";
|
||||
@@ -17,6 +17,8 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
use Combodo\iTop\DBTools\Service\DBAnalyzerUtils;
|
||||
|
||||
@include_once('../../approot.inc.php');
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
|
||||
@@ -53,7 +55,7 @@ function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppCon
|
||||
$bRunAnalysis = intval(utils::ReadParam('run_analysis', '0'));
|
||||
if ($bRunAnalysis)
|
||||
{
|
||||
$oDBAnalyzer = new DatabaseAnalyzer();
|
||||
$oDBAnalyzer = new DatabaseAnalyzer(0);
|
||||
$aResults = $oDBAnalyzer->CheckIntegrity($aClassSelection);
|
||||
if (empty($aResults))
|
||||
{
|
||||
@@ -199,88 +201,23 @@ function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppCon
|
||||
*/
|
||||
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)
|
||||
{
|
||||
fwrite($fReport, '');
|
||||
foreach($aErrorList as $sErrorLabel => $aError)
|
||||
{
|
||||
fwrite($fReport, "\r\n----------\r\n");
|
||||
fwrite($fReport, 'Class: '.MetaModel::GetName($sClass).' ('.$sClass.")\r\n");
|
||||
$iCount = $aError['count'];
|
||||
fwrite($fReport, 'Count: '.$iCount."\r\n");
|
||||
fwrite($fReport, 'Error: '.$sErrorLabel."\r\n");
|
||||
$sQuery = $aError['query'];
|
||||
fwrite($fReport, 'Query: '.$sQuery."\r\n");
|
||||
|
||||
if (isset($aError['fixit']))
|
||||
{
|
||||
fwrite($fReport, "\r\nFix it (indication):\r\n\r\n");
|
||||
$aFixitQueries = $aError['fixit'];
|
||||
foreach($aFixitQueries as $sFixitQuery)
|
||||
{
|
||||
fwrite($fReport, "$sFixitQuery\r\n");
|
||||
}
|
||||
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)
|
||||
{
|
||||
foreach($aRes as $sKey => $sValue)
|
||||
{
|
||||
$sQueryResult .= "'$sKey'='$sValue' ";
|
||||
if ($sKey == 'id')
|
||||
{
|
||||
$aIdList[] = $sValue;
|
||||
}
|
||||
}
|
||||
$sQueryResult .= "\r\n";
|
||||
|
||||
}
|
||||
fwrite($fReport, "Result: \r\n".$sQueryResult);
|
||||
$sIdList = '('.implode(',', $aIdList).')';
|
||||
fwrite($fReport, 'Ids: '.$sIdList."\r\n");
|
||||
}
|
||||
}
|
||||
fclose($fReport);
|
||||
fclose($fCleanUp);
|
||||
$sReportFile = DBAnalyzerUtils::GenerateReport($aResults);
|
||||
|
||||
$sZipReport = "{$sReportFile}.zip";
|
||||
$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->open($sZipReport, ZipArchive::CREATE);
|
||||
$oArchive->addFile($sReportFile.'.log', basename($sReportFile.'.log'));
|
||||
$oArchive->close();
|
||||
unlink($sDBToolsFolder.$sReportFile.'.txt');
|
||||
unlink($sDBToolsFolder.$sCleanupFile.'.txt');
|
||||
$sReportFile = $sDBToolsFolder.$sReportFile.'.zip';
|
||||
|
||||
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Type: multipart/x-zip');
|
||||
header('Content-Disposition: inline; filename="'.basename($sReportFile).'"');
|
||||
header('Content-Disposition: inline; filename="'.basename($sZipReport).'"');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
header('Content-Length: '.filesize($sReportFile));
|
||||
readfile($sReportFile);
|
||||
unlink($sReportFile);
|
||||
header('Content-Length: '.filesize($sZipReport));
|
||||
readfile($sZipReport);
|
||||
unlink($sZipReport);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ SetupWebPage::AddModule(
|
||||
//
|
||||
'datamodel' => array(
|
||||
'model.combodo-db-tools.php',
|
||||
'src/Service/DBToolsUtils.php'
|
||||
'src/Service/DBToolsUtils.php',
|
||||
'src/Service/DBAnalyzerUtils.php',
|
||||
),
|
||||
'webservice' => array(),
|
||||
'data.struct' => array(),
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2020 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
namespace Combodo\iTop\DBTools\Service;
|
||||
|
||||
use CoreException;
|
||||
use DictExceptionMissingString;
|
||||
use MetaModel;
|
||||
|
||||
class DBAnalyzerUtils
|
||||
{
|
||||
/**
|
||||
* @param $aResults
|
||||
*
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
public static function GenerateReport($aResults)
|
||||
{
|
||||
$sDBToolsFolder = str_replace("\\", '/', APPROOT.'log/');
|
||||
$sReportFile = 'dbtools-report';
|
||||
|
||||
$fReport = fopen($sDBToolsFolder.$sReportFile.'.log', 'w');
|
||||
fwrite($fReport, 'Database Maintenance tools: '.date('Y-m-d H:i:s')."\r\n");
|
||||
foreach ($aResults as $sClass => $aErrorList)
|
||||
{
|
||||
fwrite($fReport, '');
|
||||
foreach ($aErrorList as $sErrorLabel => $aError)
|
||||
{
|
||||
fwrite($fReport, "\r\n----------\r\n");
|
||||
fwrite($fReport, 'Class: '.MetaModel::GetName($sClass).' ('.$sClass.")\r\n");
|
||||
$iCount = $aError['count'];
|
||||
fwrite($fReport, 'Count: '.$iCount."\r\n");
|
||||
fwrite($fReport, 'Error: '.$sErrorLabel."\r\n");
|
||||
$sQuery = $aError['query'];
|
||||
fwrite($fReport, 'Query: '.$sQuery."\r\n");
|
||||
|
||||
if (isset($aError['fixit']))
|
||||
{
|
||||
fwrite($fReport, "\r\nFix it (indication):\r\n\r\n");
|
||||
$aFixitQueries = $aError['fixit'];
|
||||
foreach ($aFixitQueries as $sFixitQuery)
|
||||
{
|
||||
fwrite($fReport, "$sFixitQuery\r\n");
|
||||
}
|
||||
fwrite($fReport, "\r\n");
|
||||
}
|
||||
|
||||
$sQueryResult = '';
|
||||
$aIdList = array();
|
||||
foreach ($aError['res'] as $aRes)
|
||||
{
|
||||
foreach ($aRes as $sKey => $sValue)
|
||||
{
|
||||
$sQueryResult .= "'$sKey'='$sValue' ";
|
||||
if ($sKey == 'id')
|
||||
{
|
||||
$aIdList[] = $sValue;
|
||||
}
|
||||
}
|
||||
$sQueryResult .= "\r\n";
|
||||
|
||||
}
|
||||
fwrite($fReport, "Result: \r\n".$sQueryResult);
|
||||
$sIdList = '('.implode(',', $aIdList).')';
|
||||
fwrite($fReport, 'Ids: '.$sIdList."\r\n");
|
||||
}
|
||||
}
|
||||
fclose($fReport);
|
||||
|
||||
|
||||
$sReportFile = $sDBToolsFolder.$sReportFile;
|
||||
|
||||
return $sReportFile;
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,8 @@ function ExportStartExport() {
|
||||
var oParams = {};
|
||||
oParams.operation = 'export_build';
|
||||
oParams.format = sFormat;
|
||||
oParams.expression = sOQL;
|
||||
oParams.fields = sFields;
|
||||
oParams.token = sToken;
|
||||
oParams.start = 1;
|
||||
$.post(GetAbsoluteUrlAppRoot() + 'pages/ajax.render.php', oParams, function (data) {
|
||||
if (data == null) {
|
||||
ExportError('Export failed (no data provided), please contact your administrator');
|
||||
|
||||
@@ -326,6 +326,10 @@ class ManageBrick extends PortalBrick
|
||||
return $this->sTileMode;
|
||||
}
|
||||
|
||||
public function GetDecorationCssClass()
|
||||
{
|
||||
return static::$aPresentationData[$this->sTileMode]['decorationCssClass'];
|
||||
}
|
||||
/**
|
||||
* Sets the tile mode (display)
|
||||
*
|
||||
|
||||
@@ -20,22 +20,22 @@
|
||||
|
||||
namespace Combodo\iTop\Portal\Controller;
|
||||
|
||||
use AttributeExternalKey;
|
||||
use AttributeLinkedSetIndirect;
|
||||
use BinaryExpression;
|
||||
use Combodo\iTop\Portal\Brick\AbstractBrick;
|
||||
use Combodo\iTop\Portal\Brick\BrowseBrick;
|
||||
use Combodo\iTop\Portal\Helper\BrowseBrickHelper;
|
||||
use DBObjectSearch;
|
||||
use DBObjectSet;
|
||||
use DBSearch;
|
||||
use FieldExpression;
|
||||
use MetaModel;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use MetaModel;
|
||||
use DBSearch;
|
||||
use DBObjectSet;
|
||||
use BinaryExpression;
|
||||
use FieldExpression;
|
||||
use VariableExpression;
|
||||
use AttributeExternalKey;
|
||||
use Combodo\iTop\Portal\Brick\AbstractBrick;
|
||||
use Combodo\iTop\Portal\Brick\BrowseBrick;
|
||||
|
||||
/**
|
||||
* Class BrowseBrickController
|
||||
@@ -156,8 +156,11 @@ class BrowseBrickController extends BrickController
|
||||
{
|
||||
if (array_key_exists($sLevelAlias, $aRealiasingMap))
|
||||
{
|
||||
$aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search']->RenameAlias($aRealiasingMap[$sLevelAlias],
|
||||
$sLevelAlias);
|
||||
/** @since 2.7.2 */
|
||||
foreach ($aRealiasingMap[$sLevelAlias] as $sAliasToChange)
|
||||
{
|
||||
$aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search']->RenameAlias($sAliasToChange, $sLevelAlias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ use AttributeImage;
|
||||
use AttributeSet;
|
||||
use AttributeTagSet;
|
||||
use BinaryExpression;
|
||||
use BulkExport;
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Portal\Brick\AbstractBrick;
|
||||
use Combodo\iTop\Portal\Brick\ManageBrick;
|
||||
@@ -245,11 +246,18 @@ class ManageBrickController extends BrickController
|
||||
}
|
||||
|
||||
$sFields = implode(',', $aFields);
|
||||
$sFormat = 'xlsx';
|
||||
$oSearch->UpdateContextFromUser();
|
||||
$oExporter = BulkExport::FindExporter($sFormat, $oSearch);
|
||||
$oExporter->SetObjectList($oSearch);
|
||||
$oExporter->SetFormat($sFormat);
|
||||
$oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE);
|
||||
$oExporter->SetFields($sFields);
|
||||
|
||||
$aData = array(
|
||||
'oBrick' => $oBrick,
|
||||
'sBrickId' => $sBrickId,
|
||||
'sFields' => $sFields,
|
||||
'sOQL' => $oSearch->ToOQL(),
|
||||
'sToken' => $oExporter->SaveState(),
|
||||
);
|
||||
|
||||
return $this->render(static::EXCEL_EXPORT_TEMPLATE_PATH, $aData);
|
||||
|
||||
@@ -27,9 +27,8 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
var sDataState = 'not-yet-started';
|
||||
var sOQL = {{ sOQL|json_encode|raw }};
|
||||
var sToken = {{ sToken|raw }};
|
||||
var sFormat = 'xlsx';
|
||||
var sFields = "{{ sFields }}";
|
||||
|
||||
$(document).ready(function () {
|
||||
window.setTimeout(function () {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
id="brick-{{ oBrick.GetId }}"
|
||||
data-brick-id="{{ oBrick.GetId }}">
|
||||
<div>
|
||||
<div class="tile_title"><span class="icon fas fa-{{ oBrick.GetTileMode }}"></span> {{ oBrick.GetTitle()|dict_s }}
|
||||
<div class="tile_title"><span class="icon fas fa-{{ oBrick.GetDecorationCssClass }}"></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>
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Set>
|
||||
<ActionEmail id="1">
|
||||
<name>Notification to a Workgroup</name>
|
||||
<description>This action informs a team that a ticket has been assigned their workgroup</description>
|
||||
<status>disabled</status>
|
||||
<test_recipient></test_recipient>
|
||||
<from>test@test.com</from>
|
||||
<reply_to></reply_to>
|
||||
<to>SELECT Team WHERE id=:this->workgroup_id</to>
|
||||
<cc></cc>
|
||||
<bcc></bcc>
|
||||
<subject>The ticket $this->name()$, priority $this->label(priority)$ has been assigned to the workgroup $this->workgroup_name$</subject>
|
||||
<body><html>
|
||||
<body>
|
||||
<p>The ticket $this->name()$ has been assigned to the workgroup $this->workgroup_name$.</p>
|
||||
<p>Description: $this->title$</p>
|
||||
<p>Title: $this->title$</p>
|
||||
<hr/>
|
||||
<p>for more information on this ticket, click here: $this->hyperlink()$</p>
|
||||
</body>
|
||||
</html></body>
|
||||
<importance>normal</importance>
|
||||
</ActionEmail>
|
||||
<ActionEmail id="2">
|
||||
<name>Notification to Agent</name>
|
||||
<description>This action informs an agent that a ticket has been assigned to her/him</description>
|
||||
<status>disabled</status>
|
||||
<test_recipient></test_recipient>
|
||||
<from>test@test.com</from>
|
||||
<reply_to></reply_to>
|
||||
<to>SELECT Person WHERE id=:this->agent_id</to>
|
||||
<cc></cc>
|
||||
<bcc></bcc>
|
||||
<subject>The ticket $this->name()$, priority $this->label(priority)$ has been assigned to you</subject>
|
||||
<body><html>
|
||||
<body>
|
||||
<p>The ticket $this->name()$ has been assigned to you.</p>
|
||||
<p>Description: $this->title$</p>
|
||||
<p>Title: $this->title$</p>
|
||||
<hr/>
|
||||
<p>for more information on this ticket, click here: $this->hyperlink()$</p>
|
||||
</body>
|
||||
</html></body>
|
||||
<importance>normal</importance>
|
||||
</ActionEmail>
|
||||
<ActionEmail id="3">
|
||||
<name>Notification to caller</name>
|
||||
<description>This action is used to inform the caller</description>
|
||||
<status>disabled</status>
|
||||
<test_recipient></test_recipient>
|
||||
<from>test@test.com</from>
|
||||
<reply_to></reply_to>
|
||||
<to>SELECT Person WHERE id=:this->caller_id</to>
|
||||
<cc></cc>
|
||||
<bcc></bcc>
|
||||
<subject>Ticket $this->name()$, priority $this->label(priority)$ - $this->status$</subject>
|
||||
<body><html>
|
||||
<body>
|
||||
<p>The ticket $this->name()$ has changed to status $this->status$</p>
|
||||
<p>Last update: $this->last_update$</p>
|
||||
<hr/>
|
||||
<p>for more information on this ticket, click here: $this->hyperlink(portal)$</p>
|
||||
</body>
|
||||
</html></body>
|
||||
<importance>normal</importance>
|
||||
</ActionEmail>
|
||||
</Set>
|
||||
@@ -349,14 +349,14 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'BooleanLabel:yes' => 'Ja',
|
||||
'BooleanLabel:no' => 'Nee',
|
||||
'UI:Login:Title' => 'Aanmelden in ITOP_APPLICATION_SHORT',
|
||||
'UI:Login:Title' => 'Aanmelden in '.ITOP_APPLICATION_SHORT,
|
||||
'Menu:WelcomeMenu' => 'Welkom', // Duplicated into itop-welcome-itil (will be removed from here...)
|
||||
'Menu:WelcomeMenu+' => 'Welkom in ITOP_APPLICATION_SHORT', // Duplicated into itop-welcome-itil (will be removed from here...)
|
||||
'Menu:WelcomeMenu+' => 'Welkom in '.ITOP_APPLICATION_SHORT, // Duplicated into itop-welcome-itil (will be removed from here...)
|
||||
'Menu:WelcomeMenuPage' => 'Welkom', // Duplicated into itop-welcome-itil (will be removed from here...)
|
||||
'Menu:WelcomeMenuPage+' => 'Welkom in ITOP_APPLICATION_SHORT', // Duplicated into itop-welcome-itil (will be removed from here...)
|
||||
'UI:WelcomeMenu:Title' => 'Welkom in ITOP_APPLICATION_SHORT',
|
||||
'Menu:WelcomeMenuPage+' => 'Welkom in '.ITOP_APPLICATION_SHORT, // Duplicated into itop-welcome-itil (will be removed from here...)
|
||||
'UI:WelcomeMenu:Title' => 'Welkom in '.ITOP_APPLICATION_SHORT,
|
||||
|
||||
'UI:WelcomeMenu:LeftBlock' => '<p>ITOP_APPLICATION_SHORT is een compleet en open source portaal voor IT-operaties.</p>
|
||||
'UI:WelcomeMenu:LeftBlock' => '<p>'.ITOP_APPLICATION_SHORT.' is een compleet en open source portaal voor IT-operaties.</p>
|
||||
<ul>Op maat van jouw IT-omgeving:
|
||||
<li>Complete CMDB (Configuration Management Database) voor het documenteren en beheren van de IT-inventaris.</li>
|
||||
<li>Incident Management-module voor het vinden van en communiceren over alle problemen die optreden .</li>
|
||||
@@ -367,14 +367,14 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
</ul>
|
||||
<p>Alle modules kunnen volledig onafhankelijk van elkaar worden opgezet, stap voor stap.</p>',
|
||||
|
||||
'UI:WelcomeMenu:RightBlock' => '<p>ITOP_APPLICATION_SHORT is gericht op serviceproviders. Het zorgt ervoor dat IT-engineers gemakkelijk meerdere klanten of organisaties kunnen beheren.
|
||||
<ul>ITOP_APPLICATION_SHORT zorgt dankzij een uitgebreide set van bedrijfsprocessen voor een reeks voordelen:
|
||||
'UI:WelcomeMenu:RightBlock' => '<p>'.ITOP_APPLICATION_SHORT.' is gericht op serviceproviders. Het zorgt ervoor dat IT-engineers gemakkelijk meerdere klanten of organisaties kunnen beheren.
|
||||
<ul>'.ITOP_APPLICATION_SHORT.' zorgt dankzij een uitgebreide set van bedrijfsprocessen voor een reeks voordelen:
|
||||
<li>De efficientië van het IT-management versterkt.</li>
|
||||
<li>De prestaties van IT-operaties verbetert.</li>
|
||||
<li>De klanttevredenheid verhoogt en leidinggevenden inzicht biedt in hun bedrijfsperformantie.</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>ITOP_APPLICATION_SHORT is klaar om geïntegreerd te worden met jouw huidige infrastructuur rond IT-management.</p>
|
||||
<p>'.ITOP_APPLICATION_SHORT.' is klaar om geïntegreerd te worden met jouw huidige infrastructuur rond IT-management.</p>
|
||||
<p>
|
||||
<ul>De adoptie van dit IT-operationele portaal zal je helpen met:
|
||||
<li>Het beter beheren van een steeds complexere IT-omgeving.</li>
|
||||
@@ -441,7 +441,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'UI:Error:IncorrectLinkDefinition_LinkedClass_Class' => 'Incorrecte linkdefinitie: de klasse %1$s om objecten te beheren werd niet gevonden als externe sleutel (key) in de klasse %2$s',
|
||||
'UI:Error:Object_Class_Id_NotFound' => 'Object: %1$s:%2$d niet gevonden',
|
||||
'UI:Error:WizardCircularReferenceInDependencies' => 'Fout: cirkelverwijzing in de afhankelijke variabelen tussen de velden. Controleer het datamodel.',
|
||||
'UI:Error:UploadedFileTooBig' => 'Het geüploade bestand is te groot. De maximale grootte is %1$s. Contacteer jouw ITOP_APPLICATION_SHORT-beheerder om deze limiet aan te passen. (Controleer de PHP-configuratie voor "upload_max_filesize" en "post_max_size" op de server).',
|
||||
'UI:Error:UploadedFileTooBig' => 'Het geüploade bestand is te groot. De maximale grootte is %1$s. Contacteer jouw '.ITOP_APPLICATION_SHORT.'-beheerder om deze limiet aan te passen. (Controleer de PHP-configuratie voor "upload_max_filesize" en "post_max_size" op de server).',
|
||||
'UI:Error:UploadedFileTruncated.' => 'Het geüploade bestand is ingekort!',
|
||||
'UI:Error:NoTmpDir' => 'De tijdelijke opslagruimte is niet gedefinieerd.',
|
||||
'UI:Error:CannotWriteToTmp_Dir' => 'Niet mogelijk om het tijdelijke bestand naar een tijdelijke map weg te schrijven. upload_tmp_dir = "%1$s".',
|
||||
@@ -523,14 +523,14 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'UI:SearchValue:CheckAll' => 'Vink alles aan',
|
||||
'UI:SearchValue:UncheckAll' => 'Vink alles uit',
|
||||
'UI:SelectOne' => '-- selecteer --',
|
||||
'UI:Login:Welcome' => 'Welkom in ITOP_APPLICATION_SHORT!',
|
||||
'UI:Login:Welcome' => 'Welkom in '.ITOP_APPLICATION_SHORT.'!',
|
||||
'UI:Login:IncorrectLoginPassword' => 'Ongeldige gebruikersnaam of wachtwoord, probeer opnieuw.',
|
||||
'UI:Login:IdentifyYourself' => 'Identificeer jezelf voordat je verder gaat',
|
||||
'UI:Login:UserNamePrompt' => 'Gebruikersnaam',
|
||||
'UI:Login:PasswordPrompt' => 'Wachtwoord',
|
||||
'UI:Login:ForgotPwd' => 'Wachtwoord vergeten?',
|
||||
'UI:Login:ForgotPwdForm' => 'Wachtwoord vergeten',
|
||||
'UI:Login:ForgotPwdForm+' => 'ITOP_APPLICATION_SHORT kan je een e-mail sturen waarin de instructies voor het resetten van jouw account staan.',
|
||||
'UI:Login:ForgotPwdForm+' => ITOP_APPLICATION_SHORT.' kan je een e-mail sturen waarin de instructies voor het resetten van jouw account staan.',
|
||||
'UI:Login:ResetPassword' => 'Stuur nu!',
|
||||
'UI:Login:ResetPwdFailed' => 'E-mail sturen mislukt: %1$s',
|
||||
'UI:Login:SeparatorOr' => 'Of',
|
||||
@@ -543,8 +543,8 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'UI:ResetPwd-Error-NoEmail' => 'Er ontbreekt een e-mailadres. Neem contact op met jouw beheerder.',
|
||||
'UI:ResetPwd-Error-Send' => 'Er is een technisch probleem bij het verzenden van de e-mail. Neem contact op met jouw beheerder.',
|
||||
'UI:ResetPwd-EmailSent' => 'Kijk in jouw mailbox (eventueel bij ongewenste mail) en volg de instructies...',
|
||||
'UI:ResetPwd-EmailSubject' => 'Reset jouw ITOP_APPLICATION_SHORT-wachtwoord',
|
||||
'UI:ResetPwd-EmailBody' => '<body><p>Je hebt een reset van jouw ITOP_APPLICATION_SHORT-wachtwoord aangevraagd.</p><p>Klik op deze link (eenmalig te gebruiken) om <a href="%1$s">een nieuw wachtwoord in te voeren</a></p>.',
|
||||
'UI:ResetPwd-EmailSubject' => 'Reset jouw '.ITOP_APPLICATION_SHORT.'-wachtwoord',
|
||||
'UI:ResetPwd-EmailBody' => '<body><p>Je hebt een reset van jouw '.ITOP_APPLICATION_SHORT.'-wachtwoord aangevraagd.</p><p>Klik op deze link (eenmalig te gebruiken) om <a href="%1$s">een nieuw wachtwoord in te voeren</a></p>.',
|
||||
|
||||
'UI:ResetPwd-Title' => 'Reset wachtwoord',
|
||||
'UI:ResetPwd-Error-InvalidToken' => 'Sorry. Jouw wachtwoord is al gereset, of je hebt al meerdere e-mails ontvangen. Zorg ervoor dat je de link in de laatst ontvangen e-mail gebruikt.',
|
||||
@@ -552,24 +552,24 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'UI:ResetPwd-Ready' => 'Het wachtwoord is veranderd',
|
||||
'UI:ResetPwd-Login' => 'Klik hier om in te loggen',
|
||||
|
||||
'UI:Login:About' => 'ITOP_APPLICATION',
|
||||
'UI:Login:About' => ITOP_APPLICATION,
|
||||
'UI:Login:ChangeYourPassword' => 'Verander jouw wachtwoord',
|
||||
'UI:Login:OldPasswordPrompt' => 'Oud wachtwoord',
|
||||
'UI:Login:NewPasswordPrompt' => 'Nieuw wachtwoord',
|
||||
'UI:Login:RetypeNewPasswordPrompt' => 'Herhaal nieuwe wachtwoord',
|
||||
'UI:Login:IncorrectOldPassword' => 'Fout: het oude wachtwoord is incorrect',
|
||||
'UI:LogOffMenu' => 'Log uit',
|
||||
'UI:LogOff:ThankYou' => 'Bedankt voor het gebruiken van ITOP_APPLICATION',
|
||||
'UI:LogOff:ThankYou' => 'Bedankt voor het gebruiken van '.ITOP_APPLICATION,
|
||||
'UI:LogOff:ClickHereToLoginAgain' => 'Klik hier om in te loggen',
|
||||
'UI:ChangePwdMenu' => 'Verander wachtwoord',
|
||||
'UI:Login:PasswordChanged' => 'Wachtwoord met succes aangepast',
|
||||
'UI:AccessRO-All' => 'ITOP_APPLICATION is alleen-lezen',
|
||||
'UI:AccessRO-Users' => 'ITOP_APPLICATION is alleen-lezen voor eindgebruikers',
|
||||
'UI:AccessRO-All' => ITOP_APPLICATION.' is alleen-lezen',
|
||||
'UI:AccessRO-Users' => ITOP_APPLICATION.' is alleen-lezen voor eindgebruikers',
|
||||
'UI:ApplicationEnvironment' => 'Omgeving van de applicatie: %1$s',
|
||||
'UI:Login:RetypePwdDoesNotMatch' => 'Het nieuwe wachtwoord en de herhaling van het nieuwe wachtwoord komen niet overeen',
|
||||
'UI:Button:Login' => 'Ga naar ITOP_APPLICATION',
|
||||
'UI:Login:Error:AccessRestricted' => 'Geen toegang tot ITOP_APPLICATION_SHORT. Neem contact op met een ITOP_APPLICATION_SHORT-beheerder.',
|
||||
'UI:Login:Error:AccessAdmin' => 'Alleen toegankelijk voor mensen met beheerdersrechten. Neem contact op met een ITOP_APPLICATION_SHORT-beheerder',
|
||||
'UI:Button:Login' => 'Ga naar '.ITOP_APPLICATION,
|
||||
'UI:Login:Error:AccessRestricted' => 'Geen toegang tot '.ITOP_APPLICATION_SHORT.'. Neem contact op met een '.ITOP_APPLICATION_SHORT.'-beheerder.',
|
||||
'UI:Login:Error:AccessAdmin' => 'Alleen toegankelijk voor mensen met beheerdersrechten. Neem contact op met een '.ITOP_APPLICATION_SHORT.'-beheerder',
|
||||
'UI:Login:Error:WrongOrganizationName' => 'Onbekende organisatie',
|
||||
'UI:Login:Error:MultipleContactsHaveSameEmail' => 'Meerdere contacten hebben hetzelfde e-mailadres',
|
||||
'UI:Login:Error:NoValidProfiles' => 'Geen geldig profiel opgegeven',
|
||||
@@ -582,7 +582,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'UI:CSVImport:DataLine1' => 'Dataregel 1',
|
||||
'UI:CSVImport:DataLine2' => 'Dataregel 2',
|
||||
'UI:CSVImport:idField' => 'id (Primaire sleutel (key))',
|
||||
'UI:Title:BulkImport' => 'ITOP_APPLICATION_SHORT - Bulk import',
|
||||
'UI:Title:BulkImport' => ITOP_APPLICATION_SHORT.' - Bulk import',
|
||||
'UI:Title:BulkImport+' => 'CSV Import Wizard',
|
||||
'UI:Title:BulkSynchro_nbItem_ofClass_class' => 'Synchronisatie van %1$d objecten van klasse "%2$s"',
|
||||
'UI:CSVImport:ClassesSelectOne' => '-- selecteer een --',
|
||||
@@ -679,9 +679,9 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'UI:CSVExport:AdvancedMode' => 'Geavanceerde mode',
|
||||
'UI:CSVExport:AdvancedMode+' => 'In geavanceerde mode worden verscheidene kolommen toegevoegd aan de export: id van het object, id van de externe codes en hun reconciliation-attributen.',
|
||||
'UI:CSVExport:LostChars' => 'Tekstcoderingsprobleem',
|
||||
'UI:CSVExport:LostChars+' => 'Het gedownloade bestand zal worden gecodeerd in %1$s. ITOP_APPLICATION_SHORT heeft een aantal karakters gedetecteerd die niet compatibel zijn met dit formaat. Deze karakters zullen worden vervangen door een ander karakter (bijvoorbeeld karakters met accent kunnen het accent verliezen), of ze zullen worden verwijderd. Je kan data kopiëren en plakken van jouw webbrowser. Ook kan je de beheerder contacteren om de codes te veranderen (Zie parameter \'csv_file_default_charset\').',
|
||||
'UI:CSVExport:LostChars+' => 'Het gedownloade bestand zal worden gecodeerd in %1$s. '.ITOP_APPLICATION_SHORT.' heeft een aantal karakters gedetecteerd die niet compatibel zijn met dit formaat. Deze karakters zullen worden vervangen door een ander karakter (bijvoorbeeld karakters met accent kunnen het accent verliezen), of ze zullen worden verwijderd. Je kan data kopiëren en plakken van jouw webbrowser. Ook kan je de beheerder contacteren om de codes te veranderen (Zie parameter \'csv_file_default_charset\').',
|
||||
|
||||
'UI:Audit:Title' => 'ITOP_APPLICATION_SHORT - CMDB Audit',
|
||||
'UI:Audit:Title' => ITOP_APPLICATION_SHORT.' - CMDB Audit',
|
||||
'UI:Audit:InteractiveAudit' => 'Interactieve Audit',
|
||||
'UI:Audit:HeaderAuditRule' => 'Auditregel',
|
||||
'UI:Audit:HeaderNbObjects' => '# objecten',
|
||||
@@ -690,7 +690,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'UI:Audit:ErrorIn_Rule_Reason' => 'OQL-fout in de regel %1$s: %2$s.',
|
||||
'UI:Audit:ErrorIn_Category_Reason' => 'OQL-fout in de categorie %1$s: %2$s.',
|
||||
|
||||
'UI:RunQuery:Title' => 'ITOP_APPLICATION_SHORT - Evaluatie van OQL-query',
|
||||
'UI:RunQuery:Title' => ITOP_APPLICATION_SHORT.' - Evaluatie van OQL-query',
|
||||
'UI:RunQuery:QueryExamples' => 'Voorbeelden van query\'s',
|
||||
'UI:RunQuery:HeaderPurpose' => 'Doel',
|
||||
'UI:RunQuery:HeaderPurpose+' => 'Uitleg over de query',
|
||||
@@ -707,7 +707,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'UI:RunQuery:Error' => 'Er trad een fout op tijdens het uitvoeren van deze query: %1$s',
|
||||
'UI:Query:UrlForExcel' => 'URL om te gebruiken voor MS Excel-webquery\'s',
|
||||
'UI:Query:UrlV1' => 'De lijst van velden is leeg gelaten. De pagina <em>export-V2.php</em> kan niet aangeroepen worden zonder deze informatie.Daarom verwijst de onderstaande link naar de oude export-pagina: <em>export.php</em>. Deze verouderde versie heeft enkele beperkingen: de lijst van geëxporteerde velden kan verschillen afhankelijk van het gekozen export-formaat en het datamodel van iTop. Als je wil dat de lijst van geëxporteerde kolommen hetzelfde blijft over lange tijd, dan moet je een waarde opgeven voor het attribuut "Velden" en de pagina <em>export-V2.php</em> gebruiken.',
|
||||
'UI:Schema:Title' => 'ITOP_APPLICATION_SHORT objecten-schema',
|
||||
'UI:Schema:Title' => ITOP_APPLICATION_SHORT.' objecten-schema',
|
||||
'UI:Schema:CategoryMenuItem' => 'Categorie <b>%1$s</b>',
|
||||
'UI:Schema:Relationships' => 'Relaties',
|
||||
'UI:Schema:AbstractClass' => 'Abstracte klasse: objecten van deze klasse kunnen niet worden geïnstantieerd.',
|
||||
@@ -822,9 +822,9 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'UI:Delete:PleaseDoTheManualOperations' => 'Verricht eerst de handmatige handelingen die hierboven staan voordat je dit object verwijdert',
|
||||
'UI:Delect:Confirm_Object' => 'Bevestig dat je %1$s wil verwijderen.',
|
||||
'UI:Delect:Confirm_Count_ObjectsOf_Class' => 'Bevestig dat je de volgende %1$d objecten van klasse %2$s wilt verwijderen.',
|
||||
'UI:WelcomeToITop' => 'Welkom in ITOP_APPLICATION',
|
||||
'UI:DetailsPageTitle' => 'ITOP_APPLICATION_SHORT - %1$s - %2$s details',
|
||||
'UI:ErrorPageTitle' => 'ITOP_APPLICATION_SHORT - Fout',
|
||||
'UI:WelcomeToITop' => 'Welkom in '.ITOP_APPLICATION,
|
||||
'UI:DetailsPageTitle' => ITOP_APPLICATION_SHORT.' - %1$s - %2$s details',
|
||||
'UI:ErrorPageTitle' => ITOP_APPLICATION_SHORT.' - Fout',
|
||||
'UI:ObjectDoesNotExist' => 'Sorry, dit object bestaat niet (of je bent niet gemachtigd het te bekijken).',
|
||||
'UI:ObjectArchived' => 'Dit object werd gearchiveerd. Gelieve de Archief-mode in te schakelen of je beheerder te contacteren.',
|
||||
'Tag:Archived' => 'Gearchiveerd',
|
||||
@@ -834,7 +834,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'Tag:Synchronized' => 'Gesynchroniseerd',
|
||||
'ObjectRef:Archived' => 'Gearchiveerd',
|
||||
'ObjectRef:Obsolete' => 'Buiten dienst',
|
||||
'UI:SearchResultsPageTitle' => 'ITOP_APPLICATION_SHORT - Zoekresultaten',
|
||||
'UI:SearchResultsPageTitle' => ITOP_APPLICATION_SHORT.' - Zoekresultaten',
|
||||
'UI:SearchResultsTitle' => 'Zoekresultaten',
|
||||
'UI:SearchResultsTitle+' => 'Volledige tekst - zoekresultaten',
|
||||
'UI:Search:NoSearch' => 'Geen zoekopdracht',
|
||||
@@ -844,27 +844,28 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'UI:FullTextSearchTitle_Text' => 'Resultaten voor "%1$s":',
|
||||
'UI:Search:Count_ObjectsOf_Class_Found' => '%1$d object(en) van klasse %2$s gevonden.',
|
||||
'UI:Search:NoObjectFound' => 'Geen object gevonden.',
|
||||
'UI:ModificationPageTitle_Object_Class' => 'ITOP_APPLICATION_SHORT - %1$s - %2$s aanpassing',
|
||||
'UI:ModificationPageTitle_Object_Class' => ITOP_APPLICATION_SHORT.' - %1$s - %2$s aanpassing',
|
||||
'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: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 - %1$s aanmaken',
|
||||
'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.',
|
||||
'UI:Class_Object_Updated' => '%1$s (%2$s) aangepast.',
|
||||
'UI:BulkDeletePageTitle' => 'ITOP_APPLICATION_SHORT - Meerdere objecten verwijderen',
|
||||
'UI:BulkDeletePageTitle' => ITOP_APPLICATION_SHORT.' - Meerdere objecten verwijderen',
|
||||
'UI:BulkDeleteTitle' => 'Selecteer de objecten die je wilt verwijderen:',
|
||||
'UI:PageTitle:ObjectCreated' => 'Object Aangemaakt.',
|
||||
'UI:Title:Object_Of_Class_Created' => '%1$s - %2$s aangemaakt.',
|
||||
'UI:Apply_Stimulus_On_Object_In_State_ToTarget_State' => 'Bezig met het toepassen van %1$s op object: %2$s in fase %3$s tot doelfase: %4$s.',
|
||||
'UI:ObjectCouldNotBeWritten' => 'Het object kon niet geschreven worden: %1$s',
|
||||
'UI:PageTitle:FatalError' => 'ITOP_APPLICATION_SHORT - Fatale Fout',
|
||||
'UI:PageTitle:FatalError' => ITOP_APPLICATION_SHORT.' - Fatale Fout',
|
||||
'UI:SystemIntrusion' => 'Toegang geweigerd. Je hebt een actie aangevraagd waarvoor je niet gemachtigd bent.',
|
||||
'UI:FatalErrorMessage' => 'Fatale fout, ITOP_APPLICATION_SHORT kan niet doorgaan.',
|
||||
'UI:FatalErrorMessage' => 'Fatale fout, '.ITOP_APPLICATION_SHORT.' kan niet doorgaan.',
|
||||
'UI:Error_Details' => 'Fout: %1$s.',
|
||||
|
||||
'UI:PageTitle:ProfileProjections' => 'ITOP_APPLICATION_SHORT gebruikersbeheer - profiel-projecties',
|
||||
'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',
|
||||
'UI:UserManagement:ProjectedObject' => 'Object',
|
||||
@@ -962,8 +963,8 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'Menu:NotificationsMenu+' => 'Configuratie van de meldingen', // Duplicated into itop-welcome-itil (will be removed from here...)
|
||||
'UI:NotificationsMenu:Title' => 'Configuratie van <span class="hilite">Meldingen</span>',
|
||||
'UI:NotificationsMenu:Help' => 'Help',
|
||||
'UI:NotificationsMenu:HelpContent' => '<p>In ITOP_APPLICATION_SHORT zijn de meldingen volledig aan te passen. Ze zijn gebaseerd op twee sets van objecten: <i>triggers and actions</i>.</p>
|
||||
<p><i><b>Triggers</b></i> bepalen wanneer er een melding is. Er zijn verschillende triggers als onderdeel van ITOP_APPLICATION_SHORT core, maar andere kunnen door middel van uitbreidingen worden toegevoegd.
|
||||
'UI:NotificationsMenu:HelpContent' => '<p>In '.ITOP_APPLICATION_SHORT.' zijn de meldingen volledig aan te passen. Ze zijn gebaseerd op twee sets van objecten: <i>triggers and actions</i>.</p>
|
||||
<p><i><b>Triggers</b></i> bepalen wanneer er een melding is. Er zijn verschillende triggers als onderdeel van '.ITOP_APPLICATION_SHORT.' core, maar andere kunnen door middel van uitbreidingen worden toegevoegd.
|
||||
|
||||
<p>Sommige triggers worden uitgevoerd:</p>
|
||||
|
||||
@@ -1070,9 +1071,9 @@ Bij die koppeling wordt aan elke actie een volgorde-nummer gegeven. Dit bepaalt
|
||||
'UI:PageOrientation_Landscape' => 'Landschap',
|
||||
'UI:RelationTooltip:Redundancy' => 'Redundantie',
|
||||
'UI:RelationTooltip:ImpactedItems_N_of_M' => '# geïmpacteerde items: %1$d / %2$d',
|
||||
'UI:RelationTooltip:CriticalThreshold_N_of_M' => 'Kritische drempelwaarde: %1$d / %2$d',
|
||||
'Portal:Title' => 'ITOP_APPLICATION_SHORT gebruikersportaal',
|
||||
'Portal:NoRequestMgmt' => 'Beste %1$s, je bent naar deze pagina doorverwezen omdat jouw account is geconfigureerd met het profiel "Portal user". Helaas is ITOP_APPLICATION_SHORT niet geïnstalleerd met de optie "Request Management". Neem contact op met jouw beheerder.',
|
||||
'UI:RelationTooltip:CriticalThreshold_N_of_M' => 'Kritieke drempelwaarde: %1$d / %2$d',
|
||||
'Portal:Title' => ITOP_APPLICATION_SHORT.' gebruikersportaal',
|
||||
'Portal:NoRequestMgmt' => 'Beste %1$s, je bent naar deze pagina doorverwezen omdat jouw account is geconfigureerd met het profiel "Portal user". Helaas is '.ITOP_APPLICATION_SHORT.' niet geïnstalleerd met de optie "Request Management". Neem contact op met jouw beheerder.',
|
||||
'Portal:Refresh' => 'Herlaad',
|
||||
'Portal:Back' => 'Vorige',
|
||||
'Portal:WelcomeUserOrg' => 'Welkom %1$s, van %2$s',
|
||||
@@ -1156,7 +1157,7 @@ Bij die koppeling wordt aan elke actie een volgorde-nummer gegeven. Dit bepaalt
|
||||
'UI:Favorites:ShowObsoleteData+' => 'Toon "Buiten dienst"-data in zoekresultaten en in keuzelijsten.',
|
||||
'UI:NavigateAwayConfirmationMessage' => 'Bewerkingen zullen worden genegeerd.',
|
||||
'UI:CancelConfirmationMessage' => 'Je zult jouw aanpassingen verliezen. Wil je toch doorgaan?',
|
||||
'UI:AutoApplyConfirmationMessage' => 'Sommige veranderingen zijn nog niet doorgevoerd. Wil je dat ITOP_APPLICATION_SHORT deze meeneemt?',
|
||||
'UI:AutoApplyConfirmationMessage' => 'Sommige veranderingen zijn nog niet doorgevoerd. Wil je dat '.ITOP_APPLICATION_SHORT.' deze meeneemt?',
|
||||
'UI:Create_Class_InState' => 'Maak %1$s aan in deze fase: ',
|
||||
'UI:OrderByHint_Values' => 'Sorteervolgorde: %1$s',
|
||||
'UI:Menu:AddToDashboard' => 'Voeg toe aan dashboard...',
|
||||
@@ -1220,7 +1221,7 @@ Bij die koppeling wordt aan elke actie een volgorde-nummer gegeven. Dit bepaalt
|
||||
'UI:DashletUnknown:Label' => 'Onbekend',
|
||||
'UI:DashletUnknown:Description' => 'Onbekende dashlet (mogelijk verwijderd)',
|
||||
'UI:DashletUnknown:RenderText:View' => 'Kan deze dashlet niet weergeven.',
|
||||
'UI:DashletUnknown:RenderText:Edit' => 'Kan deze dashlet niet weergeven (klasse "%1$s"). Controleer bij je ITOP_APPLICATION_SHORT-beheerder of dit nog beschikbaar is.',
|
||||
'UI:DashletUnknown:RenderText:Edit' => 'Kan deze dashlet niet weergeven (klasse "%1$s"). Controleer bij je '.ITOP_APPLICATION_SHORT.'-beheerder of dit nog beschikbaar is.',
|
||||
'UI:DashletUnknown:RenderNoDataText:Edit' => 'Geen voorbeeld mogelijk van deze dashlet (klasse "%1$s").',
|
||||
'UI:DashletUnknown:Prop-XMLConfiguration' => 'Configuratie (getoond als ruwe XML)',
|
||||
|
||||
@@ -1400,8 +1401,8 @@ Bij die koppeling wordt aan elke actie een volgorde-nummer gegeven. Dit bepaalt
|
||||
'UI:AddAnExisting_Class' => 'Voeg objecten van type %1$s toe...',
|
||||
'UI:SelectionOf_Class' => 'Selectie van objecten van type %1$s',
|
||||
|
||||
'UI:AboutBox' => 'Over ITOP_APPLICATION_SHORT...',
|
||||
'UI:About:Title' => 'Over ITOP_APPLICATION_SHORT',
|
||||
'UI:AboutBox' => 'Over '.ITOP_APPLICATION_SHORT.'...',
|
||||
'UI:About:Title' => 'Over '.ITOP_APPLICATION_SHORT,
|
||||
'UI:About:DataModel' => 'Datamodel',
|
||||
'UI:About:Support' => 'Support informatie',
|
||||
'UI:About:Licenses' => 'Licenties',
|
||||
@@ -1426,7 +1427,7 @@ Bij die koppeling wordt aan elke actie een volgorde-nummer gegeven. Dit bepaalt
|
||||
'ExcelExport:PreparingExport' => 'Export aan het voorbereiden...',
|
||||
'ExcelExport:Statistics' => 'Statistieken',
|
||||
'portal:legacy_portal' => 'Portaal voor eindgebruikers',
|
||||
'portal:backoffice' => 'ITOP_APPLICATION_SHORT Back-Office User Interface',
|
||||
'portal:backoffice' => ITOP_APPLICATION_SHORT.' Back-Office User Interface',
|
||||
|
||||
'UI:CurrentObjectIsLockedBy_User' => 'Het object is vergrendeld omdat het momenteel aangepast wordt door %1$s.',
|
||||
'UI:CurrentObjectIsLockedBy_User_Explanation' => 'Het object wordt aangepast door %1$s. Jouw wijzigingen kunnen niet opgeslagen worden omdat ze een conflict kunnen veroorzaken.',
|
||||
|
||||
@@ -38,78 +38,116 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
|
||||
$('#'+this.id+'_btnRemove').prop('disabled',true);
|
||||
$('#'+this.id+'_linksToRemove').val('');
|
||||
}
|
||||
this.AddSelectize = function(options, initValue)
|
||||
{
|
||||
$('#'+me.id).selectize({
|
||||
render: {
|
||||
item: function(item) {
|
||||
if ( item.obsolescence_flag == 1)
|
||||
{
|
||||
val = '<span class="object-ref-icon fas fa-eye-slash object-obsolete fa-1x fa-fw"></span>'+item.label;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = item.label;
|
||||
}
|
||||
return $("<div>")
|
||||
.append(val);
|
||||
},
|
||||
option: function(item) {
|
||||
if ( item.obsolescence_flag == 1)
|
||||
{
|
||||
val = '<span class="object-ref-icon fas fa-eye-slash object-obsolete fa-1x fa-fw"></span>'+item.label;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = item.label;
|
||||
}
|
||||
return $("<div>")
|
||||
.append(val);
|
||||
}
|
||||
},
|
||||
items:[initValue],
|
||||
valueField: 'value',
|
||||
labelField: 'label',
|
||||
searchField: ['value'],
|
||||
options:JSON.parse(options),
|
||||
maxItems: 1,
|
||||
});
|
||||
}
|
||||
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, "");
|
||||
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;
|
||||
if (term in cache)
|
||||
{
|
||||
response(cache[term]);
|
||||
return;
|
||||
}
|
||||
})
|
||||
.autocomplete("instance")._renderItem = function (ul, item) {
|
||||
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')
|
||||
if (item.obsolescence_flag == '1')
|
||||
{
|
||||
val = val+' <b>old</b>';
|
||||
val = ' <span class="object-ref-icon fas fa-eye-slash object-obsolete fa-1x fa-fw"></span>'+val;
|
||||
}
|
||||
val='<d>'+val+'</d>';
|
||||
return $("<li>")
|
||||
.append(val)
|
||||
.appendTo(ul);
|
||||
@@ -520,9 +558,12 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
|
||||
else if (me.bSelectMode)
|
||||
{
|
||||
// Add the newly created object to the drop-down list and select it
|
||||
$('<option/>', { value : data.id }).html(data.name).appendTo('#'+me.id);
|
||||
/*$('<option/>', { value : data.id }).html(data.name).appendTo('#'+me.id);
|
||||
$('#'+me.id+' option[value="'+data.id+'"]').attr('selected', 'selected');
|
||||
$('#'+me.id).focus();
|
||||
$('#'+me.id).focus();*/
|
||||
var select = $('#'+me.id)[0].selectize;
|
||||
select.addOption({label: data.name, value: data.id});
|
||||
select.setValue(data.id);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -661,34 +702,43 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
|
||||
// Make sure that we cancel any pending request before issuing another
|
||||
// since responses may arrive in arbitrary order
|
||||
me.StopPendingRequest();
|
||||
|
||||
// Run the query and get the result back directly in JSON
|
||||
me.ajax_request = $.post( AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
|
||||
function(data)
|
||||
{
|
||||
var oTemp = $('<div>'+data.name+'</div>');
|
||||
var txt = oTemp.text(); // this causes HTML entities to be interpreted
|
||||
$('#label_'+me.id).val(txt);
|
||||
$('#label_'+me.id).removeClass('ac_dlg_loading');
|
||||
var prevValue = $('#'+me.id).val();
|
||||
$('#'+me.id).val(iObjectId);
|
||||
if (prevValue != iObjectId)
|
||||
{
|
||||
$('#'+me.id).trigger('validate');
|
||||
$('#'+me.id).trigger('extkeychange');
|
||||
$('#'+me.id).trigger('change');
|
||||
}
|
||||
if ( $('#'+me.id).hasClass('multiselect'))
|
||||
{
|
||||
$('#'+me.id+' option').each(function() { this.selected = ($(this).attr('value') == iObjectId); });
|
||||
$('#'+me.id).multiselect('refresh');
|
||||
}
|
||||
$('#label_'+me.id).focus();
|
||||
me.ajax_request = null;
|
||||
},
|
||||
'json'
|
||||
);
|
||||
|
||||
if ($('#label_'+me.id).size() == 0)
|
||||
{
|
||||
var prevValue =$('#'+me.id)[0].selectize.getValue();
|
||||
$('#'+me.id)[0].selectize.setValue(iObjectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Run the query and get the result back directly in JSON
|
||||
me.ajax_request = $.post(AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
|
||||
function (data) {
|
||||
var oTemp = $('<div>'+data.name+'</div>');
|
||||
var txt = oTemp.text(); // this causes HTML entities to be interpreted
|
||||
|
||||
$('#label_'+me.id).val(txt);
|
||||
$('#label_'+me.id).removeClass('ac_dlg_loading');
|
||||
|
||||
var prevValue = $('#'+me.id).val();
|
||||
$('#'+me.id).val(iObjectId);
|
||||
if (prevValue != iObjectId)
|
||||
{
|
||||
$('#'+me.id).trigger('validate');
|
||||
$('#'+me.id).trigger('extkeychange');
|
||||
$('#'+me.id).trigger('change');
|
||||
}
|
||||
if ($('#'+me.id).hasClass('multiselect'))
|
||||
{
|
||||
$('#'+me.id+' option').each(function () {
|
||||
this.selected = ($(this).attr('value') == iObjectId);
|
||||
});
|
||||
$('#'+me.id).multiselect('refresh');
|
||||
}
|
||||
$('#label_'+me.id).focus();
|
||||
me.ajax_request = null;
|
||||
},
|
||||
'json'
|
||||
);
|
||||
}
|
||||
return false; // Do NOT submit the form in case we are called by OnSubmit...
|
||||
};
|
||||
|
||||
|
||||
@@ -2464,6 +2464,10 @@ EOF
|
||||
$data = '';
|
||||
if ($token === null)
|
||||
{
|
||||
if (!ContextTag::Check('backoffice'))
|
||||
{
|
||||
throw new Exception('Missing token');
|
||||
}
|
||||
$sFormat = utils::ReadParam('format', '');
|
||||
$sExpression = utils::ReadParam('expression', null, false, 'raw_data');
|
||||
$iQueryId = utils::ReadParam('query', null);
|
||||
@@ -2499,6 +2503,11 @@ EOF
|
||||
else
|
||||
{
|
||||
$oExporter = BulkExport::FindExporterFromToken($token);
|
||||
if (utils::ReadParam('start', 0, false, 'integer') == 1)
|
||||
{
|
||||
// From portal, the first call is using a token
|
||||
$data .= $oExporter->GetHeader();
|
||||
}
|
||||
}
|
||||
|
||||
if ($oExporter)
|
||||
|
||||
@@ -282,8 +282,8 @@ class MFCompiler
|
||||
}
|
||||
else
|
||||
{
|
||||
$sRelativeDir = '';
|
||||
$sRealRelativeDir = '';
|
||||
$sRelativeDir = $sModuleName;
|
||||
$sRealRelativeDir = $sModuleName;
|
||||
}
|
||||
$aModulesInfo[$sModuleName] = array('root_dir' => $sRealRelativeDir, 'version' => $sModuleVersion);
|
||||
|
||||
@@ -334,10 +334,10 @@ class MFCompiler
|
||||
|
||||
foreach ($aCompiledClasses['code'] as $sClass => $sCompiledClass)
|
||||
{
|
||||
$sClassFileName = DIRECTORY_SEPARATOR.$sRelativeDir.DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.'Model'.DIRECTORY_SEPARATOR.$sClass.'.php';
|
||||
$sClassFileName = '/'.$sRelativeDir.'/src/Model/'.$sClass.'.php';
|
||||
$sClassFile = "{$sTempTargetDir}{$sClassFileName}";
|
||||
$this->WritePHPFile($sClassFile, $sModuleName, $sModuleVersion, $sCompiledClass);
|
||||
$sCompiledCode .= "require_once ('{$sFinalTargetDir}{$sClassFileName}');\n";
|
||||
$sCompiledCode .= "require_once (dirname(__DIR__).'{$sClassFileName}');\n";
|
||||
}
|
||||
}
|
||||
catch (DOMFormatException $e)
|
||||
@@ -474,7 +474,7 @@ EOF;
|
||||
{
|
||||
// We have compiled something: write the code somewhere
|
||||
//
|
||||
if (strlen($sRelativeDir) > 0)
|
||||
if (strlen($sModuleRootDir) > 0)
|
||||
{
|
||||
// Write the code into the given module as model.<module>.php
|
||||
//
|
||||
@@ -3020,6 +3020,10 @@ EOF;
|
||||
*/
|
||||
protected function WriteFile($sFilename, $sContent, $flags = null)
|
||||
{
|
||||
if (is_file($sFilename) || is_link($sFilename))
|
||||
{
|
||||
@unlink($sFilename);
|
||||
}
|
||||
$ret = file_put_contents($sFilename, $sContent, $flags);
|
||||
if ($ret === false)
|
||||
{
|
||||
|
||||
@@ -41,7 +41,7 @@ define('ITOP_DESIGN_LATEST_VERSION', '1.8'); // iTop >= 2.8.0
|
||||
*/
|
||||
class iTopDesignFormat
|
||||
{
|
||||
protected static $aVersions = array(
|
||||
public static $aVersions = array(
|
||||
'1.0' => array(
|
||||
'previous' => null,
|
||||
'go_to_previous' => null,
|
||||
@@ -95,7 +95,7 @@ class iTopDesignFormat
|
||||
'go_to_previous' => 'From18To17',
|
||||
'next' => null,
|
||||
'go_to_next' => null,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -991,7 +991,7 @@ class SetupUtils
|
||||
$oPage, $bIsItopInstall, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $bTlsEnabled, $sTlsCA,
|
||||
$sNewDBName = ''
|
||||
) {
|
||||
$sWikiVersion = '2_7_0';
|
||||
$sWikiVersion = utils::GetItopVersionWikiSyntax(); //eg : '2_7_0';
|
||||
$sMysqlTlsWikiPageUrl = 'https://wiki.openitop.org/doku.php?id='.$sWikiVersion.':install:php_and_mysql_tls';
|
||||
|
||||
$oPage->add('<tr><td colspan="2">');
|
||||
|
||||
@@ -91,11 +91,10 @@ class ConsoleSelectObjectFieldRenderer extends FieldRenderer
|
||||
$sFormPrefix = '';
|
||||
$oWidget = new \UIExtKeyWidget($sTargetClass, $sFieldId, '', true);
|
||||
$aArgs = array();
|
||||
$sDisplayStyle = 'select';
|
||||
$sTitle = $this->oField->GetLabel();
|
||||
require_once(APPROOT.'application/capturewebpage.class.inc.php');
|
||||
$oPage = new \CaptureWebPage();
|
||||
$sHTMLValue = $oWidget->Display($oPage, $iMaxComboLength, false /* $bAllowTargetCreation */, $sTitle, $oSet, $this->oField->GetCurrentValue(), $sFieldId, $this->oField->GetMandatory(), $sFieldName, $sFormPrefix, $aArgs, null, $sDisplayStyle);
|
||||
$sHTMLValue = $oWidget->DisplaySelect($oPage, $iMaxComboLength, false /* $bAllowTargetCreation */, $sTitle, $oSet, $this->oField->GetCurrentValue(), $this->oField->GetMandatory(), $sFieldName, $sFormPrefix, $aArgs);
|
||||
$oOutput->AddHtml($sHTMLValue);
|
||||
$oOutput->AddHtml($oPage->GetHtml());
|
||||
$oOutput->AddJs($oPage->GetJS());
|
||||
|
||||
@@ -534,9 +534,9 @@ try
|
||||
$aValues = array(); // Used to build the insert query
|
||||
foreach ($aRow as $iCol => $value)
|
||||
{
|
||||
if ($value === null)
|
||||
if ($value === null) // Source CSV: "<NULL>"
|
||||
{
|
||||
$aValues[] = 'NULL';
|
||||
$aValues[] = null;
|
||||
}
|
||||
elseif ($aIsDateToTransform[$iCol] !== false)
|
||||
{
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<div>
|
||||
{{ 'UI:Login:About'|dict_s }}
|
||||
</div>
|
||||
{{ 'UI:Login:About'|dict_s|raw }}
|
||||
</div>
|
||||
|
||||
@@ -68,7 +68,7 @@ class UtilsTest extends \Combodo\iTop\Test\UnitTest\ItopTestCase
|
||||
*/
|
||||
public function testRealPath($sPath, $sBasePath, $expected)
|
||||
{
|
||||
$this->assertSame($expected, utils::RealPath($sPath, $sBasePath));
|
||||
$this->assertSame($expected, utils::RealPath($sPath, $sBasePath), "utils::RealPath($sPath, $sBasePath) does not match $expected");
|
||||
}
|
||||
|
||||
public function realPathDataProvider()
|
||||
@@ -77,14 +77,19 @@ class UtilsTest extends \Combodo\iTop\Test\UnitTest\ItopTestCase
|
||||
|
||||
$sSep = DIRECTORY_SEPARATOR;
|
||||
$sItopRootRealPath = realpath(APPROOT).$sSep;
|
||||
$sLicenseFileName = 'license.txt';
|
||||
if (!is_file(APPROOT.$sLicenseFileName))
|
||||
{
|
||||
$sLicenseFileName = 'LICENSE';
|
||||
}
|
||||
|
||||
return [
|
||||
'licence.txt' => [APPROOT.'license.txt', APPROOT, $sItopRootRealPath.'license.txt'],
|
||||
$sLicenseFileName => [APPROOT.$sLicenseFileName, APPROOT, $sItopRootRealPath.$sLicenseFileName],
|
||||
'unexisting file' => [APPROOT.'license_DOES_NOT_EXIST.txt', APPROOT, false],
|
||||
'/license.txt' => [APPROOT.$sSep.'license.txt', APPROOT, $sItopRootRealPath.'license.txt'],
|
||||
'%2flicense.txt' => [APPROOT.'%2flicense.txt', APPROOT, false],
|
||||
'../license.txt' => [APPROOT.'..'.$sSep.'license.txt', APPROOT, false],
|
||||
'%2e%2e%2flicense.txt' => [APPROOT.'%2e%2e%2flicense.txt', APPROOT, false],
|
||||
'/'.$sLicenseFileName => [APPROOT.$sSep.$sLicenseFileName, APPROOT, $sItopRootRealPath.$sLicenseFileName],
|
||||
'%2f'.$sLicenseFileName => [APPROOT.'%2f'. $sLicenseFileName, APPROOT, false],
|
||||
'../'.$sLicenseFileName => [APPROOT.'..'.$sSep.$sLicenseFileName, APPROOT, false],
|
||||
'%2e%2e%2f'.$sLicenseFileName => [APPROOT.'%2e%2e%2f'.$sLicenseFileName, APPROOT, false],
|
||||
'application/utils.inc.php with basepath=APPROOT' => [
|
||||
APPROOT.'application/utils.inc.php',
|
||||
APPROOT,
|
||||
|
||||
BIN
test/backups/backup-itop.tar.gz
Normal file
BIN
test/backups/backup-itop.tar.gz
Normal file
Binary file not shown.
8
test/ci_description.ini
Normal file
8
test/ci_description.ini
Normal file
@@ -0,0 +1,8 @@
|
||||
[itop]
|
||||
itop_setup=test/setup_params/default-params.xml
|
||||
itop_backup=test/backups/backup-itop.tar.gz
|
||||
|
||||
[phpunit]
|
||||
; when empty phpunit_xml => no phpunit test performed
|
||||
; phpunit xml file description. required for phpunit testing
|
||||
phpunit_xml=test/phpunit.xml.dist
|
||||
68
test/core/CSVParserTest.php
Normal file
68
test/core/CSVParserTest.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use CSVParser;
|
||||
|
||||
|
||||
class CSVParserTest extends ItopTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
require_once(APPROOT.'core/csvparser.class.inc.php');
|
||||
require_once(APPROOT.'core/coreexception.class.inc.php');
|
||||
}
|
||||
|
||||
public function testFile()
|
||||
{
|
||||
$sSeparator = ';';
|
||||
$sDelimiter = '?';
|
||||
$sDataFile = '?field1?;?field2?;?field3?
|
||||
?line 0, col 0?;?line 0, col 1?;?line 0, col 2?
|
||||
a;b;c
|
||||
a;b;<NULL>
|
||||
? a ? ; ? b ? ; ? c ?
|
||||
a ; b ; c
|
||||
??;??;??
|
||||
;;
|
||||
?a"?;?b?;?c?
|
||||
?a1
|
||||
a2?;?b?;?c?
|
||||
?a1,a2?;?b?;?c?
|
||||
?a?;?b?;?c1,",c2
|
||||
,c3?
|
||||
?a?;?b?;?ouf !?
|
||||
spaces trimmed out ; 1234; mac@enroe.com ';
|
||||
|
||||
$aExpectedResult = array(
|
||||
array('line 0, col 0', 'line 0, col 1', 'line 0, col 2'),
|
||||
array('a', 'b', 'c'),
|
||||
array('a', 'b', null),
|
||||
array(' a ', ' b ', ' c '),
|
||||
array('a', 'b', 'c'),
|
||||
array('', '', ''),
|
||||
array('', '', ''),
|
||||
array('a"', 'b', 'c'),
|
||||
array("a1\na2", 'b', 'c'),
|
||||
array('a1,a2', 'b', 'c'),
|
||||
array('a', 'b', "c1,\",c2\n,c3"),
|
||||
array('a', 'b', 'ouf !'),
|
||||
array('spaces trimmed out', '1234', 'mac@enroe.com'),
|
||||
);
|
||||
|
||||
$oCSVParser = new CSVParser($sDataFile, $sSeparator, $sDelimiter);
|
||||
$aData = $oCSVParser->ToArray(1, null, 0);
|
||||
|
||||
foreach ($aData as $iRow => $aRow)
|
||||
{
|
||||
foreach ($aRow as $iCol => $cellValue)
|
||||
{
|
||||
$this->assertSame($aExpectedResult[$iRow][$iCol], $cellValue, "Line $iRow, Column $iCol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,52 +455,4 @@ class DBSearchIntersectTest extends ItopTestCase
|
||||
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`.'));
|
||||
}
|
||||
}
|
||||
|
||||
184
test/core/DBSearchJoinTest.php
Normal file
184
test/core/DBSearchJoinTest.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use DBSearch;
|
||||
|
||||
/**
|
||||
* Class DBSearchIntersectTest
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Core
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class DBSearchJoinTest extends ItopDataTestCase {
|
||||
|
||||
const USE_TRANSACTION = false;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider JoinProvider
|
||||
*
|
||||
* @param $sLeftSelect
|
||||
* @param $sRightSelect
|
||||
* @param $sParentAtt
|
||||
* @param $sResult
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreWarning
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function testJoin($sLeftSelect, $sRightSelect, $sParentAtt, $sResult)
|
||||
{
|
||||
$oLeftSearch = DBSearch::FromOQL($sLeftSelect);
|
||||
$oRightSearch = DBSearch::FromOQL($sRightSelect);
|
||||
|
||||
$aRealiasingMap = [];
|
||||
|
||||
$oResultSearch = $oLeftSearch->Join($oRightSearch,
|
||||
DBSearch::JOIN_REFERENCED_BY, $sParentAtt,
|
||||
TREE_OPERATOR_EQUALS, $aRealiasingMap);
|
||||
|
||||
$this->debug("\nRealiasing Map");
|
||||
$this->debug($aRealiasingMap);
|
||||
|
||||
CMDBSource::TestQuery($oResultSearch->MakeSelectQuery());
|
||||
$this->assertEquals($sResult, $oResultSearch->ToOQL());
|
||||
|
||||
// rename alias test
|
||||
$this->debug("\nBefore renaming");
|
||||
$this->debug($oResultSearch->ToOQL());
|
||||
$aLevelsPropertiesKeys = ['L-1', 'L-1-1', 'L-1-1-1'];
|
||||
foreach ($aLevelsPropertiesKeys as $sLevelAlias)
|
||||
{
|
||||
if (array_key_exists($sLevelAlias, $aRealiasingMap))
|
||||
{
|
||||
foreach ($aRealiasingMap[$sLevelAlias] as $sAliasToRename)
|
||||
{
|
||||
$oResultSearch->RenameAlias($sAliasToRename, $sLevelAlias);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->debug("\nAfter renaming");
|
||||
$this->debug($oResultSearch->ToOQL());
|
||||
|
||||
}
|
||||
|
||||
public function JoinProvider()
|
||||
{
|
||||
// Breakpoint in BrowseBrickController::DisplayAction()
|
||||
// $aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search'] = $aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search']->Join($aLevelsProperties[$aLevelsPropertiesKeys[$i + 1]]['search'],
|
||||
// DBSearch::JOIN_REFERENCED_BY, $aLevelsProperties[$aLevelsPropertiesKeys[$i + 1]]['parent_att'],
|
||||
// TREE_OPERATOR_EQUALS, $aRealiasingMap);
|
||||
return [
|
||||
'Bug 3176' => [
|
||||
'left' => "SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id WHERE ((`cc`.`org_id` = 2) AND (`L-1-1`.`status` != 'obsolete'))UNION SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l11` ON `l11`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc1` ON `l11`.customercontract_id = `cc1`.id JOIN Organization AS `child` ON `cc1`.org_id = `child`.id JOIN Organization AS `root` ON `child`.parent_id BELOW `root`.id WHERE ((`root`.`id` = 2) AND (`L-1-1`.`status` != 'obsolete'))",
|
||||
'right' => "SELECT `L-1-1-1` FROM ServiceSubcategory AS `L-1-1-1` WHERE (`L-1-1-1`.`status` != 'obsolete')",
|
||||
'parent_att' => 'service_id',
|
||||
'result' => "SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id WHERE (((`cc`.`org_id` = 2) AND (`L-1-1`.`status` != 'obsolete')) AND (`L-1-1-1`.`status` != 'obsolete')) UNION SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l11` ON `l11`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc1` ON `l11`.customercontract_id = `cc1`.id JOIN Organization AS `child` ON `cc1`.org_id = `child`.id JOIN Organization AS `root` ON `child`.parent_id BELOW `root`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id WHERE (((`root`.`id` = 2) AND (`L-1-1`.`status` != 'obsolete')) AND (`L-1-1-1`.`status` != 'obsolete'))",
|
||||
],
|
||||
'Bug 2970' => [
|
||||
'left' => "SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id WHERE ((`cc`.`org_id` = 2) AND (`L-1-1`.`status` != 'obsolete')) UNION SELECT `L-1-1` FROM Service AS `L-1-1` WHERE (`L-1-1`.`id` = 8)",
|
||||
'right' => "SELECT `L-1-1-1` FROM ServiceSubcategory AS `L-1-1-1` JOIN Service AS `s` ON `L-1-1-1`.service_id = `s`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `s`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id WHERE ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete'))) UNION SELECT `L-1-1-1` FROM ServiceSubcategory AS `L-1-1-1` JOIN Service AS `s1` ON `L-1-1-1`.service_id = `s1`.id WHERE ((`L-1-1-1`.`status` != 'obsolete') AND (`s1`.`id` = 8))",
|
||||
'parent_att' => 'service_id',
|
||||
'result' => "SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id WHERE (((`cc`.`org_id` = 2) AND (`L-1-1`.`status` != 'obsolete')) AND ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete')))) UNION SELECT `L-1-1` FROM Service AS `L-1-1` JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id JOIN lnkCustomerContractToService AS `l11` ON `l11`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc1` ON `l11`.customercontract_id = `cc1`.id WHERE ((`L-1-1`.`id` = 8) AND ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc1`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete')))) UNION SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id WHERE (((`cc`.`org_id` = 2) AND (`L-1-1`.`status` != 'obsolete')) AND ((`L-1-1-1`.`status` != 'obsolete') AND (`L-1-1`.`id` = 8))) UNION SELECT `L-1-1` FROM Service AS `L-1-1` JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id WHERE ((`L-1-1`.`id` = 8) AND ((`L-1-1-1`.`status` != 'obsolete') AND (`L-1-1`.`id` = 8)))",
|
||||
],
|
||||
'Bug 2585' => [
|
||||
'left' => "SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id WHERE ((`cc`.`org_id` = 2) AND (`L-1-1`.`status` != 'obsolete')) UNION SELECT `L-1-1` FROM Service AS `L-1-1` WHERE (`L-1-1`.`id` = 8)",
|
||||
'right' => "SELECT `L-1-1-1` FROM ServiceSubcategory AS `L-1-1-1` JOIN Service AS `s` ON `L-1-1-1`.service_id = `s`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `s`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id WHERE ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete'))) UNION SELECT `L-1-1-1` FROM ServiceSubcategory AS `L-1-1-1` JOIN Service AS `s1` ON `L-1-1-1`.service_id = `s1`.id WHERE ((`L-1-1-1`.`status` != 'obsolete') AND (`s1`.`id` = 8))",
|
||||
'parent_att' => 'service_id',
|
||||
'result' => "SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id WHERE (((`cc`.`org_id` = 2) AND (`L-1-1`.`status` != 'obsolete')) AND ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete')))) UNION SELECT `L-1-1` FROM Service AS `L-1-1` JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id JOIN lnkCustomerContractToService AS `l11` ON `l11`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc1` ON `l11`.customercontract_id = `cc1`.id WHERE ((`L-1-1`.`id` = 8) AND ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc1`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete')))) UNION SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id WHERE (((`cc`.`org_id` = 2) AND (`L-1-1`.`status` != 'obsolete')) AND ((`L-1-1-1`.`status` != 'obsolete') AND (`L-1-1`.`id` = 8))) UNION SELECT `L-1-1` FROM Service AS `L-1-1` JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id WHERE ((`L-1-1`.`id` = 8) AND ((`L-1-1-1`.`status` != 'obsolete') AND (`L-1-1`.`id` = 8)))",
|
||||
],
|
||||
'Bug 2585 K' => [
|
||||
'left' => "SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `SHOULD_BE_TRANSLATED` ON `SHOULD_BE_TRANSLATED`.servicefamily_id = `L-1`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `SHOULD_BE_TRANSLATED`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id WHERE (`cc`.`org_id` = 2) UNION SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `SHOULD_ALSO_BE_TRANSLATED` ON `SHOULD_ALSO_BE_TRANSLATED`.servicefamily_id = `L-1`.id WHERE (`SHOULD_ALSO_BE_TRANSLATED`.`id` = 8)",
|
||||
'right' => "SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id WHERE (((`cc`.`org_id` = 2) AND (`L-1-1`.`status` != 'obsolete')) AND ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete')))) UNION SELECT `L-1-1` FROM Service AS `L-1-1` JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id JOIN lnkCustomerContractToService AS `l11` ON `l11`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc1` ON `l11`.customercontract_id = `cc1`.id WHERE ((`L-1-1`.`id` = 8) AND ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc1`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete')))) UNION SELECT `L-1-1` FROM Service AS `L-1-1` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `L-1-1`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id WHERE (((`cc`.`org_id` = 2) AND (`L-1-1`.`status` != 'obsolete')) AND ((`L-1-1-1`.`status` != 'obsolete') AND (`L-1-1`.`id` = 8))) UNION SELECT `L-1-1` FROM Service AS `L-1-1` JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `L-1-1`.id WHERE ((`L-1-1`.`id` = 8) AND ((`L-1-1-1`.`status` != 'obsolete') AND (`L-1-1`.`id` = 8)))",
|
||||
'parent_att' => 'servicefamily_id',
|
||||
'result' => "SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `SHOULD_BE_TRANSLATED` ON `SHOULD_BE_TRANSLATED`.servicefamily_id = `L-1`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `SHOULD_BE_TRANSLATED`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `SHOULD_BE_TRANSLATED`.id WHERE ((`cc`.`org_id` = 2) AND (((`cc`.`org_id` = 2) AND (`SHOULD_BE_TRANSLATED`.`status` != 'obsolete')) AND ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete'))))) UNION SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `SHOULD_ALSO_BE_TRANSLATED` ON `SHOULD_ALSO_BE_TRANSLATED`.servicefamily_id = `L-1`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `SHOULD_ALSO_BE_TRANSLATED`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `SHOULD_ALSO_BE_TRANSLATED`.id WHERE ((`SHOULD_ALSO_BE_TRANSLATED`.`id` = 8) AND (((`cc`.`org_id` = 2) AND (`SHOULD_ALSO_BE_TRANSLATED`.`status` != 'obsolete')) AND ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete'))))) UNION SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `SHOULD_BE_TRANSLATED` ON `SHOULD_BE_TRANSLATED`.servicefamily_id = `L-1`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `SHOULD_BE_TRANSLATED`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `SHOULD_BE_TRANSLATED`.id WHERE ((`cc`.`org_id` = 2) AND ((`SHOULD_BE_TRANSLATED`.`id` = 8) AND ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete'))))) UNION SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `SHOULD_ALSO_BE_TRANSLATED` ON `SHOULD_ALSO_BE_TRANSLATED`.servicefamily_id = `L-1`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `SHOULD_ALSO_BE_TRANSLATED`.id JOIN lnkCustomerContractToService AS `l11` ON `l11`.service_id = `SHOULD_ALSO_BE_TRANSLATED`.id JOIN CustomerContract AS `cc1` ON `l11`.customercontract_id = `cc1`.id WHERE ((`SHOULD_ALSO_BE_TRANSLATED`.`id` = 8) AND ((`SHOULD_ALSO_BE_TRANSLATED`.`id` = 8) AND ((`L-1-1-1`.`status` != 'obsolete') AND ((`cc1`.`org_id` = 2) AND (`L-1-1-1`.`status` != 'obsolete'))))) UNION SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `SHOULD_BE_TRANSLATED` ON `SHOULD_BE_TRANSLATED`.servicefamily_id = `L-1`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `SHOULD_BE_TRANSLATED`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `SHOULD_BE_TRANSLATED`.id WHERE ((`cc`.`org_id` = 2) AND (((`cc`.`org_id` = 2) AND (`SHOULD_BE_TRANSLATED`.`status` != 'obsolete')) AND ((`L-1-1-1`.`status` != 'obsolete') AND (`SHOULD_BE_TRANSLATED`.`id` = 8)))) UNION SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `SHOULD_ALSO_BE_TRANSLATED` ON `SHOULD_ALSO_BE_TRANSLATED`.servicefamily_id = `L-1`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `SHOULD_ALSO_BE_TRANSLATED`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `SHOULD_ALSO_BE_TRANSLATED`.id WHERE ((`SHOULD_ALSO_BE_TRANSLATED`.`id` = 8) AND (((`cc`.`org_id` = 2) AND (`SHOULD_ALSO_BE_TRANSLATED`.`status` != 'obsolete')) AND ((`L-1-1-1`.`status` != 'obsolete') AND (`SHOULD_ALSO_BE_TRANSLATED`.`id` = 8)))) UNION SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `SHOULD_BE_TRANSLATED` ON `SHOULD_BE_TRANSLATED`.servicefamily_id = `L-1`.id JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `SHOULD_BE_TRANSLATED`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `SHOULD_BE_TRANSLATED`.id WHERE ((`cc`.`org_id` = 2) AND ((`SHOULD_BE_TRANSLATED`.`id` = 8) AND ((`L-1-1-1`.`status` != 'obsolete') AND (`SHOULD_BE_TRANSLATED`.`id` = 8)))) UNION SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `SHOULD_ALSO_BE_TRANSLATED` ON `SHOULD_ALSO_BE_TRANSLATED`.servicefamily_id = `L-1`.id JOIN ServiceSubcategory AS `L-1-1-1` ON `L-1-1-1`.service_id = `SHOULD_ALSO_BE_TRANSLATED`.id WHERE ((`SHOULD_ALSO_BE_TRANSLATED`.`id` = 8) AND ((`SHOULD_ALSO_BE_TRANSLATED`.`id` = 8) AND ((`L-1-1-1`.`status` != 'obsolete') AND (`SHOULD_ALSO_BE_TRANSLATED`.`id` = 8))))",
|
||||
],
|
||||
'test 2585 K2' => [
|
||||
'left' => "SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = 2 AND s.status != 'obsolete' UNION SELECT Service WHERE id = 8",
|
||||
'right' => "SELECT ServiceSubcategory AS ssc JOIN Service AS s ON ssc.service_id=s.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 = 2 AND ssc.status != 'obsolete' UNION SELECT ServiceSubcategory AS ssc JOIN Service AS s ON ssc.service_id=s.id WHERE s.id = 8",
|
||||
'parent_att' => 'service_id',
|
||||
'result' => "SELECT `s` FROM Service AS `s` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `s`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `ssc` ON `ssc`.service_id = `s`.id WHERE (((`cc`.`org_id` = 2) AND (`s`.`status` != 'obsolete')) AND ((`cc`.`org_id` = 2) AND (`ssc`.`status` != 'obsolete'))) UNION SELECT `Service` FROM Service AS `Service` JOIN ServiceSubcategory AS `ssc` ON `ssc`.service_id = `Service`.id JOIN lnkCustomerContractToService AS `l11` ON `l11`.service_id = `Service`.id JOIN CustomerContract AS `cc1` ON `l11`.customercontract_id = `cc1`.id WHERE ((`Service`.`id` = 8) AND ((`cc1`.`org_id` = 2) AND (`ssc`.`status` != 'obsolete'))) UNION SELECT `s` FROM Service AS `s` JOIN lnkCustomerContractToService AS `l1` ON `l1`.service_id = `s`.id JOIN CustomerContract AS `cc` ON `l1`.customercontract_id = `cc`.id JOIN ServiceSubcategory AS `ssc` ON `ssc`.service_id = `s`.id WHERE (((`cc`.`org_id` = 2) AND (`s`.`status` != 'obsolete')) AND (`s`.`id` = 8)) UNION SELECT `Service` FROM Service AS `Service` JOIN ServiceSubcategory AS `ssc` ON `ssc`.service_id = `Service`.id WHERE ((`Service`.`id` = 8) AND (`Service`.`id` = 8))",
|
||||
],
|
||||
'Bug 2585 K3' => [
|
||||
'left' => "SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `s` ON `s`.servicefamily_id = `L-1`.id WHERE 1",
|
||||
'right' => "SELECT `L-1-1` FROM Service AS `L-1-1` WHERE 1",
|
||||
'parent_att' => 'servicefamily_id',
|
||||
'result' => "SELECT `L-1` FROM ServiceFamily AS `L-1` JOIN Service AS `s` ON `s`.servicefamily_id = `L-1`.id WHERE 1",
|
||||
],
|
||||
// 'test' => [
|
||||
// 'left' => "",
|
||||
// 'right' => "",
|
||||
// 'parent_att' => '',
|
||||
// 'result' => "",
|
||||
// ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug #2970
|
||||
* @throws \CoreException
|
||||
* @throws \CoreWarning
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function testFilterOnJoin()
|
||||
{
|
||||
$sReq1 = "SELECT `L-1` FROM Organization AS `L-1` WHERE (`L-1`.`id` = 2)";
|
||||
$sReq2 = "SELECT `L-1-1` FROM CustomerContract AS `L-1-1` JOIN Organization AS `SHOULD_BE_TRANSLATED` ON `L-1-1`.org_id = `SHOULD_BE_TRANSLATED`.id WHERE (((`L-1-1`.`status` = 'active') OR (`L-1-1`.`status` = 'standby')) AND (`SHOULD_BE_TRANSLATED`.`id` = 2))";
|
||||
|
||||
$oFilter1 = DBSearch::FromOQL($sReq1);
|
||||
$oFilter2 = DBSearch::FromOQL($sReq2);
|
||||
$aRealiasingMap = array();
|
||||
$oFilter1 = $oFilter1->Join($oFilter2,
|
||||
DBSearch::JOIN_REFERENCED_BY,
|
||||
'org_id',
|
||||
TREE_OPERATOR_EQUALS, $aRealiasingMap);
|
||||
|
||||
$this->debug("\nRealiasing Map");
|
||||
$this->debug($aRealiasingMap);
|
||||
|
||||
$sRes1 = $oFilter1->ToOQL();
|
||||
$this->debug("\nJoined");
|
||||
$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("\nReferencedBy");
|
||||
$this->debug($oForeignFilter->ToOQL());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertFalse(strpos($sRes1, '`SHOULD_BE_TRANSLATED`.'));
|
||||
|
||||
$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("\nFiltered");
|
||||
$this->debug($sRes1);
|
||||
|
||||
$this->assertFalse(strpos($sRes1, '`SHOULD_BE_TRANSLATED`.'));
|
||||
}
|
||||
}
|
||||
88
test/core/DBSearchUpdateRealiasingMapTest.php
Normal file
88
test/core/DBSearchUpdateRealiasingMapTest.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use DBObjectSearch;
|
||||
|
||||
|
||||
/**
|
||||
* Class DBSearchUpdateRealiasingMapTest
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Core
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class DBSearchUpdateRealiasingMapTest extends ItopDataTestCase
|
||||
{
|
||||
const USE_TRANSACTION = false;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider UpdateRealiasingMapProvider
|
||||
* @param $aRealiasingMap
|
||||
* @param $aAliasTranslation
|
||||
* @param $aExpectedRealiasingMap
|
||||
*/
|
||||
public function testUpdateRealiasingMap($aRealiasingMap, $aAliasTranslation, $aExpectedRealiasingMap)
|
||||
{
|
||||
$this->UpdateRealiasingMap($aRealiasingMap, $aAliasTranslation);
|
||||
$this->assertEquals($aExpectedRealiasingMap, $aRealiasingMap);
|
||||
}
|
||||
|
||||
public function UpdateRealiasingMapProvider()
|
||||
{
|
||||
return [
|
||||
'empty' => [
|
||||
'OriginalMap' => null,
|
||||
'AliasTranslation' => [],
|
||||
'ExpectedMap' => null
|
||||
],
|
||||
'Add 1 alias' => [
|
||||
'OriginalMap' => [],
|
||||
'AliasTranslation' => ['a' => ['*' => 'b']],
|
||||
'ExpectedMap' => ['a' => ['b']]
|
||||
],
|
||||
'Add 2 aliases' => [
|
||||
'OriginalMap' => [],
|
||||
'AliasTranslation' => ['a' => ['*' => 'b'], 'c' => ['*' => 'd']],
|
||||
'ExpectedMap' => ['a' => ['b'], 'c' => ['d']]
|
||||
],
|
||||
'Append 1 alias' => [
|
||||
'OriginalMap' => ['a' => ['b']],
|
||||
'AliasTranslation' => ['c' => ['*' => 'd']],
|
||||
'ExpectedMap' => ['a' => ['b'], 'c' => ['d']]
|
||||
],
|
||||
'Merge 1 alias' => [
|
||||
'OriginalMap' => ['a' => ['b']],
|
||||
'AliasTranslation' => ['a' => ['*' => 'd']],
|
||||
'ExpectedMap' => ['a' => ['b', 'd']]
|
||||
],
|
||||
'Merge same alias' => [
|
||||
'OriginalMap' => ['a' => ['b']],
|
||||
'AliasTranslation' => ['a' => ['*' => 'b']],
|
||||
'ExpectedMap' => ['a' => ['b']]
|
||||
],
|
||||
'Transitivity a->b + b->f = a->f' => [
|
||||
'OriginalMap' => ['a' => ['b', 'd'], 'c' => ['e']],
|
||||
'AliasTranslation' => ['b' => ['*' => 'f']],
|
||||
'ExpectedMap' => ['a' => ['f', 'd'], 'c' => ['e']]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function UpdateRealiasingMap(&$aRealiasingMap, $aAliasTranslation)
|
||||
{
|
||||
$class = new \ReflectionClass(DBObjectSearch::class);
|
||||
$method = $class->getMethod('UpdateRealiasingMap');
|
||||
$method->setAccessible(true);
|
||||
$method->invokeArgs(new DBObjectSearch('Organization'), [&$aRealiasingMap, $aAliasTranslation]);
|
||||
}
|
||||
}
|
||||
@@ -179,4 +179,134 @@ class MetaModelTest extends ItopDataTestCase
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dataProvider enumPluginsProvider
|
||||
*
|
||||
* @param $expectedResults
|
||||
* @param $m_aExtensionClassNames
|
||||
* @param $m_aExtensionClasses
|
||||
* @param $interface
|
||||
* @param null $sFilterInstanceOf
|
||||
*/
|
||||
public function testEnumPlugins($expectedInstanciationCalls, $expectedResults, $m_aExtensionClassNames, $m_aExtensionClasses, $interface, $sFilterInstanceOf=null)
|
||||
{
|
||||
$pluginInstanciationManager = new \PluginInstanciationManager();
|
||||
$res = $pluginInstanciationManager->InstantiatePlugins($m_aExtensionClassNames, $interface);
|
||||
|
||||
$mPluginInstanciationManager = $this->createMock(\PluginInstanciationManager::class);
|
||||
$mPluginInstanciationManager->expects($this->exactly($expectedInstanciationCalls))
|
||||
->method('InstantiatePlugins')
|
||||
->willReturn($res);
|
||||
$m_PluginManager = new \PluginManager($m_aExtensionClassNames, $m_aExtensionClasses, $mPluginInstanciationManager);
|
||||
|
||||
//warning: called twice on purpose
|
||||
$m_PluginManager->EnumPlugins($interface, $sFilterInstanceOf);
|
||||
$pluginInstances = $m_PluginManager->EnumPlugins($interface, $sFilterInstanceOf);
|
||||
|
||||
$this->assertCount(sizeof($expectedResults), $pluginInstances);
|
||||
foreach($pluginInstances as $pluginInstance)
|
||||
{
|
||||
if ($sFilterInstanceOf!==null)
|
||||
{
|
||||
$this->assertTrue($pluginInstance instanceof $sFilterInstanceOf);
|
||||
}
|
||||
}
|
||||
$index=0;
|
||||
foreach($expectedResults as $expectedInterface)
|
||||
{
|
||||
$this->assertTrue(is_a($pluginInstances[$index], $expectedInterface));
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
|
||||
public function enumPluginsProvider(){
|
||||
$aInterfaces = [
|
||||
"empty conf" => [ 0, [], [], [], 'Wizzard'],
|
||||
"simple instance retrieval" => [ 1, [Gryffindor::class], [ 'Wizzard' => [ Gryffindor::class]], [], 'Wizzard'],
|
||||
"check instanceof parameter" => [ 1, [Gryffindor::class, Slytherin::class], [ 'Wizzard' => [ Gryffindor::class, Slytherin::class]], [], 'Wizzard'],
|
||||
"try to retrieve a non instanciable object" => [ 1, [Gryffindor::class], [ 'Wizzard' => [ Gryffindor::class, Muggle::class]], [], 'Wizzard', Gryffindor::class ],
|
||||
];
|
||||
return $aInterfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPluginsProvider
|
||||
*
|
||||
* @param $expectedInstanciationCalls
|
||||
* @param $expectedResults
|
||||
* @param $m_aExtensionClassNames
|
||||
* @param $m_aExtensionClasses
|
||||
* @param $interface
|
||||
* @param $className
|
||||
*/
|
||||
public function testGetPlugins($expectedInstanciationCalls, $expectedResults, $m_aExtensionClassNames, $m_aExtensionClasses, $interface, $className)
|
||||
{
|
||||
$pluginInstanciationManager = new \PluginInstanciationManager();
|
||||
$res = $pluginInstanciationManager->InstantiatePlugins($m_aExtensionClassNames, $interface);
|
||||
|
||||
$mPluginInstanciationManager = $this->createMock(\PluginInstanciationManager::class);
|
||||
$mPluginInstanciationManager->expects($this->exactly($expectedInstanciationCalls))
|
||||
->method('InstantiatePlugins')
|
||||
->willReturn($res);
|
||||
$m_PluginManager = new \PluginManager($m_aExtensionClassNames, $m_aExtensionClasses, $mPluginInstanciationManager);
|
||||
|
||||
//warning: called twice on purpose
|
||||
$m_PluginManager->GetPlugins($interface, $className);
|
||||
$pluginInstance = $m_PluginManager->GetPlugins($interface, $className);
|
||||
|
||||
if (sizeof($expectedResults)==0)
|
||||
{
|
||||
$this->assertNull($pluginInstance);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->assertTrue($pluginInstance instanceof $className);
|
||||
$this->assertTrue(is_a($pluginInstance, $expectedResults[0]));
|
||||
}
|
||||
|
||||
public function getPluginsProvider(){
|
||||
$aInterfaces = [
|
||||
"empty conf" => [ 0, [], [], [], 'Wizzard', Gryffindor::class],
|
||||
"simple instance retrieval" => [ 1, [Gryffindor::class], [ 'Wizzard' => [ Gryffindor::class]], [], 'Wizzard', Gryffindor::class],
|
||||
"check instanceof parameter" => [ 1, [Gryffindor::class], [ 'Wizzard' => [ Gryffindor::class, Slytherin::class]], [], 'Wizzard', Gryffindor::class],
|
||||
"try to retrieve a non instanciable object" => [ 1, [Gryffindor::class], [ 'Wizzard' => [ Gryffindor::class, Muggle::class]], [], 'Wizzard', Gryffindor::class ],
|
||||
];
|
||||
return $aInterfaces;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class Wizzard
|
||||
{
|
||||
|
||||
/**
|
||||
* Wizzard constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class Gryffindor extends Wizzard
|
||||
{
|
||||
|
||||
}
|
||||
class Hufflepuff extends Wizzard
|
||||
{
|
||||
|
||||
}
|
||||
class Ravenclaw extends Wizzard
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class Slytherin extends Wizzard
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class Muggle
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,18 @@ use UserRights;
|
||||
*/
|
||||
class UserRightsTest extends ItopDataTestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp(); // TODO: Change the autogenerated stub
|
||||
|
||||
try{
|
||||
\utils::GetConfig()->SetModuleSetting('authent-local', 'password_validation.pattern', '' );
|
||||
self::CreateUser('admin', 1);
|
||||
}
|
||||
catch(\CoreCannotSaveObjectException $e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static $aClasses = array(
|
||||
'FunctionalCI' => array('class' => 'FunctionalCI', 'attcode' => 'name'),
|
||||
|
||||
@@ -9,13 +9,35 @@ use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
class iTopConfigParserTest extends ItopTestCase
|
||||
{
|
||||
private $conf_exists;
|
||||
private $tmpSavePath;
|
||||
private $sConfigPath;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
require_once APPROOT.'/core/iTopConfigParser.php';
|
||||
|
||||
clearstatcache();
|
||||
$this->sConfigPath = utils::GetConfigFilePath();
|
||||
$this->tmpSavePath = tempnam( '/tmp/', 'config-itop');
|
||||
|
||||
$this->conf_exists = is_file($this->sConfigPath);
|
||||
if ($this->conf_exists)
|
||||
{
|
||||
copy($this->sConfigPath, $this->tmpSavePath);
|
||||
}
|
||||
clearstatcache();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
parent::tearDown(); // TODO: Change the autogenerated stub
|
||||
if ($this->conf_exists)
|
||||
{
|
||||
rename($this->tmpSavePath, $this->sConfigPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ParserProvider
|
||||
@@ -185,30 +207,13 @@ CONF;
|
||||
public function testConfigWriteToFile_FromScratchInstallation()
|
||||
{
|
||||
$sConfigPath = utils::GetConfigFilePath();
|
||||
$tmpSavePath = tempnam( '/tmp/', 'config-itop');
|
||||
|
||||
$conf_exists = is_file($sConfigPath);
|
||||
if ($conf_exists)
|
||||
{
|
||||
rename($sConfigPath, $tmpSavePath);
|
||||
}
|
||||
|
||||
$oConfig = new Config($sConfigPath, false);
|
||||
try{
|
||||
clearstatcache();
|
||||
$oConfig->WriteToFile();
|
||||
if ($conf_exists)
|
||||
{
|
||||
rename($tmpSavePath, $sConfigPath);
|
||||
}
|
||||
}catch(\Exception $e)
|
||||
{
|
||||
if ($conf_exists)
|
||||
{
|
||||
rename($tmpSavePath, $sConfigPath);
|
||||
}
|
||||
|
||||
$this->assertTrue(false, "failed writetofile with no initial file");
|
||||
$this->assertTrue(false, "failed writetofile with no initial file: " . $e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
100
test/integration/iTopModulesPhpVersionChecklistTest.php
Normal file
100
test/integration/iTopModulesPhpVersionChecklistTest.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-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
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Integration;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use DOMDocument;
|
||||
use iTopDesignFormat;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*
|
||||
* @covers iTopDesignFormat
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Setup
|
||||
*/
|
||||
class iTopModulesPhpVersionIntegrationTest extends ItopTestCase
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Verify if the datamodel.*.xml files refer to the current itop version
|
||||
* This is an integration test
|
||||
*
|
||||
* @group skipPostBuild
|
||||
*
|
||||
* @dataProvider iTopModulesPhpVersionProvider
|
||||
*/
|
||||
public function testiTopModulesPhpVersion($sExpectedVersion, $sPhpFile)
|
||||
{
|
||||
|
||||
$sModulePath = realpath($sPhpFile);
|
||||
$sModuleFileName = basename($sModulePath);
|
||||
$sModuleName = preg_replace('/[^.]+\.([^.]+)\.php/', '$1', $sModuleFileName);
|
||||
|
||||
$sFileContent = file_get_contents($sPhpFile);
|
||||
|
||||
preg_match(
|
||||
"#'$sModuleName/([^']+)'#",
|
||||
$sFileContent,
|
||||
$matches
|
||||
);
|
||||
|
||||
$this->assertSame($sExpectedVersion, $matches[1], "$sPhpFile file refer does not refer to current itop version ($matches[1] instead of expected $sExpectedVersion)");
|
||||
}
|
||||
|
||||
public function iTopModulesPhpVersionProvider()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
require_once APPROOT.'core/config.class.inc.php';
|
||||
require_once APPROOT.'application/utils.inc.php';
|
||||
|
||||
if (is_dir(APPROOT.'datamodels/2.x'))
|
||||
{
|
||||
$DatamodelsPath = APPROOT.'datamodels/2.x';
|
||||
}
|
||||
elseif (is_dir(APPROOT.'datamodels/1.x'))
|
||||
{
|
||||
$DatamodelsPath = APPROOT.'datamodels/1.x';
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \Exception('Cannot local the datamodels directory');
|
||||
}
|
||||
|
||||
$sPath = $DatamodelsPath.'/*/module.*.php';
|
||||
$aPhpFiles = glob($sPath);
|
||||
|
||||
$sExpectedVersion = \utils::GetItopPatchVersion();
|
||||
|
||||
$aTestCases = array();
|
||||
foreach ($aPhpFiles as $sPhpFile)
|
||||
{
|
||||
$aTestCases[$sPhpFile] = array(
|
||||
'sExpectedVersion' => $sExpectedVersion,
|
||||
'sPhpFile' => $sPhpFile,
|
||||
);
|
||||
}
|
||||
|
||||
return $aTestCases;
|
||||
}
|
||||
|
||||
}
|
||||
93
test/integration/iTopModulesXmlVersionChecklistTest.php
Normal file
93
test/integration/iTopModulesXmlVersionChecklistTest.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-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
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Integration;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use DOMDocument;
|
||||
use iTopDesignFormat;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*
|
||||
* @covers iTopDesignFormat
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Setup
|
||||
*/
|
||||
class iTopModulesXmlVersionIntegrationTest extends ItopTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
require_once APPROOT.'setup/itopdesignformat.class.inc.php';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify if the datamodel.*.xml files refer to the latest version of the design
|
||||
* This is an integration test
|
||||
*
|
||||
* @group skipPostBuild
|
||||
*
|
||||
* @dataProvider DatamodelItopXmlVersionProvider
|
||||
*/
|
||||
public function testDatamodelItopXmlVersion($sXmlFile)
|
||||
{
|
||||
$oOriginalXml = new DOMDocument();
|
||||
$oOriginalXml->load($sXmlFile);
|
||||
|
||||
$oTransformedXml = new DOMDocument();
|
||||
$oTransformedXml->load($sXmlFile);
|
||||
$oFormat = new iTopDesignFormat($oTransformedXml);
|
||||
|
||||
if ($oFormat->Convert())
|
||||
{
|
||||
// Compare the original and new format
|
||||
$sExpectedXmlVersion = ITOP_DESIGN_LATEST_VERSION;
|
||||
$this->assertSame($oTransformedXml->saveXML(), $oOriginalXml->saveXML(), "Datamodel file $sXmlFile not in the latest format ($sExpectedXmlVersion)");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->fail("Failed to convert $sXmlFile into the latest format");
|
||||
}
|
||||
}
|
||||
|
||||
public function DatamodelItopXmlVersionProvider()
|
||||
{
|
||||
static::setUp();
|
||||
|
||||
$sPath = APPROOT.'datamodels/2.x/*/datamodel.*.xml';
|
||||
$aXmlFiles = glob($sPath);
|
||||
|
||||
$aXmlFiles[] = APPROOT.'core/datamodel.core.xml';
|
||||
$aXmlFiles[] = APPROOT.'application/datamodel.application.xml';
|
||||
|
||||
$aTestCases = array();
|
||||
foreach ($aXmlFiles as $sXmlFile)
|
||||
{
|
||||
$aTestCases[$sXmlFile] = array(
|
||||
'sXmlFile' => $sXmlFile,
|
||||
);
|
||||
}
|
||||
|
||||
return $aTestCases;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,6 +59,9 @@
|
||||
<testsuite name="Status">
|
||||
<directory>status</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Integration">
|
||||
<directory>integration</directory>
|
||||
</testsuite>
|
||||
<testsuite name="CoreExtensions">
|
||||
<directory>coreExtensions</directory>
|
||||
</testsuite>
|
||||
|
||||
58
test/releaseChecklist.xml.dist
Normal file
58
test/releaseChecklist.xml.dist
Normal file
@@ -0,0 +1,58 @@
|
||||
<!-- Copyright (c) 2010-2017 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/> -->
|
||||
<!-- -->
|
||||
|
||||
<phpunit bootstrap="unittestautoload.php"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/6.3/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
cacheTokens="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
forceCoversAnnotation="false"
|
||||
mapTestClassNameToCoveredClassName="false"
|
||||
printerClass="\PHPUnit\TextUI\ResultPrinter"
|
||||
processIsolation="true"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
stopOnIncomplete="false"
|
||||
stopOnSkipped="false"
|
||||
stopOnRisky="false"
|
||||
timeoutForSmallTests="1"
|
||||
timeoutForMediumTests="10"
|
||||
timeoutForLargeTests="60"
|
||||
verbose="false">
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="ReleaseChecklist">
|
||||
<directory>releaseChecklist</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<!-- Code coverage white list -->
|
||||
<filter>
|
||||
<whitelist>
|
||||
<file>../core/apc-emulation.php</file>
|
||||
<file>../core/ormlinkset.class.inc.php</file>
|
||||
<file>../datamodels/2.x/itop-tickets/main.itop-tickets.php</file>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
</phpunit>
|
||||
59
test/releaseChecklist/SetupCssIntegrityChecklistTest.php
Normal file
59
test/releaseChecklist/SetupCssIntegrityChecklistTest.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?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/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\ReleaseChecklist;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use DOMDocument;
|
||||
use iTopDesignFormat;
|
||||
|
||||
|
||||
/**
|
||||
* Class iTopDesignFormatChecklistTest
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*
|
||||
* @covers iTopDesignFormat
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Setup
|
||||
*/
|
||||
class SetupCssIntegrityChecklistTest extends ItopTestCase
|
||||
{
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public function testSetupCssIntegrity()
|
||||
{
|
||||
$sSetupCssPath = APPROOT.'css/setup.css';
|
||||
$sSetupCssContent = file_get_contents($sSetupCssPath);
|
||||
$this->assertContains('/* integrityCheck: begin (do not remove/edit) */', $sSetupCssContent);
|
||||
$this->assertContains('/* integrityCheck: end (do not remove/edit) */', $sSetupCssContent);
|
||||
$this->assertGreaterThan(4000, strlen($sSetupCssContent), "Test if the resulting file $sSetupCssPath is long enough, the value is totally arbitrary (at the time of the writing the file is 5660o long");
|
||||
}
|
||||
|
||||
}
|
||||
101
test/releaseChecklist/iTopDesignFormatChecklistTest.php
Normal file
101
test/releaseChecklist/iTopDesignFormatChecklistTest.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\ReleaseChecklist;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use iTopDesignFormat;
|
||||
|
||||
|
||||
/**
|
||||
* Class iTopDesignFormatChecklistTest
|
||||
* Ticket 3053 - Check XML conversion methods
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*
|
||||
* @covers iTopDesignFormat
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Setup
|
||||
*/
|
||||
class TestForITopDesignFormatClass extends ItopTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
require_once APPROOT.'setup/modelfactory.class.inc.php';
|
||||
require_once APPROOT.'setup/itopdesignformat.class.inc.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* release checklist step: make sure we have datamodel conversion functions for new iTop version
|
||||
*/
|
||||
public function testCurrentVersion_DataModelConversionFunctions()
|
||||
{
|
||||
$aDatamodelCurrentVersions = array();
|
||||
$aDataModelFiles = $this->GetDataModelFiles(APPROOT.'/datamodels');
|
||||
|
||||
//retrieve current XML version in datamoldels files
|
||||
foreach ($aDataModelFiles as $sDataModelFile)
|
||||
{
|
||||
if (preg_match('/itop_design .* version="([\d\.]*)"/', file_get_contents($sDataModelFile), $aMatches))
|
||||
{
|
||||
$sVersion = $aMatches[1];
|
||||
if (!array_key_exists($sVersion, $aDatamodelCurrentVersions))
|
||||
{
|
||||
$aDatamodelCurrentVersions[$sVersion] = $sVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//make sure there is only one found
|
||||
$this->assertTrue(is_array($aDatamodelCurrentVersions));
|
||||
$this->assertEquals(1, count($aDatamodelCurrentVersions), "Found too much XML versions: " . json_encode($aDatamodelCurrentVersions));
|
||||
|
||||
//check we have migration function from new version to previous one
|
||||
$sCurrentVersion = array_values($aDatamodelCurrentVersions)[0];
|
||||
$this->assertTrue(array_key_exists($sCurrentVersion, iTopDesignFormat::$aVersions), "Release checklist: missing $sCurrentVersion config in iTopDesignFormat ");
|
||||
$aCurrentVersionInfo = iTopDesignFormat::$aVersions[$sCurrentVersion];
|
||||
$this->assertTrue(is_array($aCurrentVersionInfo), "Release checklist: wrong $sCurrentVersion config in iTopDesignFormat ");
|
||||
$this->assertTrue(array_key_exists('previous', $aCurrentVersionInfo), "Release checklist: missing previous for $sCurrentVersion config in iTopDesignFormat ");
|
||||
$this->TestDefinedFunction($aCurrentVersionInfo, 'go_to_previous', $sCurrentVersion);
|
||||
|
||||
//check we have migration function from N-1 version to new one
|
||||
$sPreviousVersion = $aCurrentVersionInfo['previous'];
|
||||
$this->assertTrue(array_key_exists($sPreviousVersion, iTopDesignFormat::$aVersions), "Release checklist: missing $sPreviousVersion config in iTopDesignFormat ");
|
||||
$aPreviousVersionInfo = iTopDesignFormat::$aVersions[$sPreviousVersion];
|
||||
$this->assertTrue(is_array($aPreviousVersionInfo), "Release checklist: wrong $sPreviousVersion config in iTopDesignFormat ");
|
||||
$this->assertTrue(array_key_exists('previous', $aPreviousVersionInfo), "Release checklist: missing previous for $sPreviousVersion config in iTopDesignFormat ");
|
||||
$this->TestDefinedFunction($aPreviousVersionInfo, 'go_to_previous', $sPreviousVersion);
|
||||
$this->TestDefinedFunction($aPreviousVersionInfo, 'go_to_next', $sPreviousVersion);
|
||||
}
|
||||
|
||||
private function TestDefinedFunction($aCurrentVersionInfo, $sFunctionKey, $sVersion)
|
||||
{
|
||||
$sInfo = json_encode($aCurrentVersionInfo, true);
|
||||
$this->assertTrue(array_key_exists($sFunctionKey, $aCurrentVersionInfo), "Release checklist: missing $sFunctionKey in $sVersion config in iTopDesignFormat: " . $sInfo);
|
||||
echo $aCurrentVersionInfo[$sFunctionKey].'\n';
|
||||
$oReflectionClass = new \ReflectionClass(iTopDesignFormat::class);
|
||||
$this->assertTrue($oReflectionClass->hasMethod($aCurrentVersionInfo[$sFunctionKey]), "Release checklist: wrong go_to_previous function '".$aCurrentVersionInfo[$sFunctionKey]."'' for $sVersion config in iTopDesignFormat " . $sInfo);
|
||||
}
|
||||
|
||||
public function GetDataModelFiles($sFolder)
|
||||
{
|
||||
$aDataModelFiles = array();
|
||||
if (is_dir($sFolder))
|
||||
{
|
||||
foreach (glob($sFolder."/*") as $sPath)
|
||||
{
|
||||
if (is_dir($sPath))
|
||||
{
|
||||
$aDataModelFiles = array_merge($aDataModelFiles, $this->GetDataModelFiles($sPath));
|
||||
}
|
||||
else if (preg_match("/datamodel\..*\.xml/", basename($sPath)))
|
||||
{
|
||||
$aDataModelFiles[] = $sPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aDataModelFiles;
|
||||
}
|
||||
}
|
||||
142
test/releaseChecklist/iTopModuleXmlInstallationChecklistTest.php
Normal file
142
test/releaseChecklist/iTopModuleXmlInstallationChecklistTest.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\ReleaseChecklist;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use DOMDocument;
|
||||
use iTopDesignFormat;
|
||||
|
||||
|
||||
/**
|
||||
* Class iTopDesignFormatChecklistTest
|
||||
* Ticket 3061 - Automatically check the installation.xml consistency
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*
|
||||
* @covers iTopDesignFormat
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Setup
|
||||
*/
|
||||
class iTopModuleXmlInstallationChecklistTest extends ItopTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* make sure installation.xml is provided and respects XML format
|
||||
*/
|
||||
public function testInstallationXmlFormat()
|
||||
{
|
||||
$sInstallationXmlPath = APPROOT . 'datamodels/2.x/installation.xml';
|
||||
$this->assertTrue(is_file($sInstallationXmlPath), "$sInstallationXmlPath does not exist");
|
||||
|
||||
$doc = new \DOMDocument();
|
||||
try{
|
||||
$doc->loadxml(file_get_contents($sInstallationXmlPath));
|
||||
}
|
||||
catch(\Exception $e)
|
||||
{
|
||||
$this->assertFalse(true, "$sInstallationXmlPath is not a valid XML content: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* make sure installation.xml includes all packaged modules
|
||||
*/
|
||||
public function testAllModuleAreIncludedInInstallationXml()
|
||||
{
|
||||
$sInstallationXmlPath = APPROOT.'datamodels/2.x/installation.xml';
|
||||
if (!is_file($sInstallationXmlPath))
|
||||
{
|
||||
$sInstallationXmlPath = APPROOT.'datamodels/1.x/installation.xml';
|
||||
}
|
||||
$this->assertTrue(is_file($sInstallationXmlPath), "$sInstallationXmlPath does not exist");
|
||||
|
||||
$sInstallationXmlContent = file_get_contents($sInstallationXmlPath);
|
||||
preg_match_all("|<module>(.*)</module>|", $sInstallationXmlContent, $aMatches);
|
||||
$aDeclaredModules = [] ;
|
||||
if (!empty($aMatches))
|
||||
{
|
||||
foreach ($aMatches[1] as $sModule)
|
||||
{
|
||||
if (!array_key_exists($sModule, $aDeclaredModules))
|
||||
{
|
||||
$aDeclaredModules[$sModule] = $sModule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertArraySubset($this->GetFilteredModulesFromDatamodels(APPROOT.'/datamodels'), $aDeclaredModules, false, "$sInstallationXmlPath does not refer to all provided modules. Refered modules:\n " . var_export($aDeclaredModules, true));
|
||||
|
||||
$aModulesFromDatamodels = $this->GetAllModules(APPROOT.'/datamodels');
|
||||
$this->assertArraySubset($aDeclaredModules, $aModulesFromDatamodels, false, "Not all modules are contained in $sInstallationXmlPath. Refered modules:\n " . var_export($aModulesFromDatamodels, true));
|
||||
}
|
||||
|
||||
public function GetFilteredModulesFromDatamodels($sFolder)
|
||||
{
|
||||
$aExcludedModules = ['authent-external', 'authent-ldap'];
|
||||
$aModules = array();
|
||||
if (is_dir($sFolder))
|
||||
{
|
||||
foreach (glob($sFolder."/*") as $sPath)
|
||||
{
|
||||
if (is_dir($sPath))
|
||||
{
|
||||
$aModules = array_merge($aModules, $this->GetFilteredModulesFromDatamodels($sPath));
|
||||
}
|
||||
else if (preg_match("/module\..*\.php/", basename($sPath)))
|
||||
{
|
||||
$sModulePhpContent = file_get_contents($sPath);
|
||||
if (strpos($sModulePhpContent, "SetupWebPage::AddModule")!==false
|
||||
&& strpos($sModulePhpContent, "'mandatory' => true")===false)
|
||||
{
|
||||
//filter modules autoselected due to below condition
|
||||
if (strpos($sModulePhpContent, "'mandatory' => false")!==false
|
||||
&& strpos($sModulePhpContent, "'visible' => false")!==false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$sModule = basename(dirname($sPath));
|
||||
if (in_array($sModule, $aExcludedModules))// || $sModule === 'authent-ldap')
|
||||
{
|
||||
//hardcode this condition to make sure test is OK (CI context) + added a ticket to work/investigate why it is failed for these 2 cases (itop dev context)
|
||||
continue;
|
||||
}
|
||||
|
||||
$aModules[$sModule] = $sModule;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aModules;
|
||||
}
|
||||
|
||||
public function GetAllModules($sFolder)
|
||||
{
|
||||
$aModules = array();
|
||||
if (is_dir($sFolder))
|
||||
{
|
||||
foreach (glob($sFolder."/*") as $sPath)
|
||||
{
|
||||
if (is_dir($sPath))
|
||||
{
|
||||
$aModules = array_merge($aModules, $this->GetAllModules($sPath));
|
||||
}
|
||||
else if (preg_match("/module\..*\.php/", basename($sPath)))
|
||||
{
|
||||
$sModulePhpContent = file_get_contents($sPath);
|
||||
if (strpos($sModulePhpContent, "SetupWebPage::AddModule")!==false)
|
||||
{
|
||||
$sModule = basename(dirname($sPath));
|
||||
$aModules[$sModule] = $sModule;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aModules;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<installation>
|
||||
<!-- On manual installs, this file is generated in setup/install-*.xml -->
|
||||
<mode>upgrade</mode>
|
||||
<mode>install</mode>
|
||||
<preinstall>
|
||||
<copies type="array"/>
|
||||
</preinstall>
|
||||
@@ -20,7 +20,7 @@
|
||||
<db_tls_ca></db_tls_ca>
|
||||
<prefix></prefix>
|
||||
</database>
|
||||
<url>http://127.0.0.1/itop/svn/trunk/</url>
|
||||
<url>http://localhost/iTop/</url>
|
||||
<graphviz_path>/usr/bin/dot</graphviz_path>
|
||||
<admin_account>
|
||||
<user>admin</user>
|
||||
79
test/setup_params/setup.xml
Normal file
79
test/setup_params/setup.xml
Normal file
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<installation>
|
||||
<mode>install</mode>
|
||||
<preinstall>
|
||||
<copies type="array"/>
|
||||
<backup>
|
||||
<destination>/var/www/html/iTop/data/backups/manual/setup-2020-07-15_15_52</destination>
|
||||
<configuration_file>/var/www/html/iTop//conf/production/config-itop.php</configuration_file>
|
||||
</backup>
|
||||
</preinstall>
|
||||
<source_dir>datamodels/2.x/</source_dir>
|
||||
<datamodel_version>2.7.1</datamodel_version>
|
||||
<previous_configuration_file></previous_configuration_file>
|
||||
<extensions_dir>extensions</extensions_dir>
|
||||
<target_env>production</target_env>
|
||||
<workspace_dir></workspace_dir>
|
||||
<database>
|
||||
<server>localhost</server>
|
||||
<user>iTop</user>
|
||||
<pwd>**removed**</pwd>
|
||||
<name>toto</name>
|
||||
<db_tls_enabled></db_tls_enabled>
|
||||
<db_tls_ca></db_tls_ca>
|
||||
<prefix></prefix>
|
||||
</database>
|
||||
<url>http://localhost/iTop/</url>
|
||||
<graphviz_path>/usr/bin/dot</graphviz_path>
|
||||
<admin_account>
|
||||
<user>admin</user>
|
||||
<pwd>**removed**</pwd>
|
||||
<language>EN US</language>
|
||||
</admin_account>
|
||||
<language>EN US</language>
|
||||
<selected_modules type="array">
|
||||
<item>authent-cas</item>
|
||||
<item>authent-external</item>
|
||||
<item>authent-ldap</item>
|
||||
<item>authent-local</item>
|
||||
<item>itop-backup</item>
|
||||
<item>itop-config</item>
|
||||
<item>itop-files-information</item>
|
||||
<item>itop-portal-base</item>
|
||||
<item>itop-profiles-itil</item>
|
||||
<item>itop-sla-computation</item>
|
||||
<item>itop-tickets</item>
|
||||
<item>itop-welcome-itil</item>
|
||||
<item>itop-config-mgmt</item>
|
||||
<item>itop-attachments</item>
|
||||
<item>combodo-db-tools</item>
|
||||
<item>itop-core-update</item>
|
||||
<item>itop-hub-connector</item>
|
||||
<item>itop-datacenter-mgmt</item>
|
||||
<item>itop-endusers-devices</item>
|
||||
<item>itop-storage-mgmt</item>
|
||||
<item>itop-virtualization-mgmt</item>
|
||||
<item>itop-bridge-virtualization-storage</item>
|
||||
<item>itop-service-mgmt</item>
|
||||
<item>itop-request-mgmt</item>
|
||||
<item>itop-portal</item>
|
||||
<item>itop-change-mgmt</item>
|
||||
<item>itop-knownerror-mgmt</item>
|
||||
</selected_modules>
|
||||
<selected_extensions type="array">
|
||||
<item>itop-config-mgmt-core</item>
|
||||
<item>itop-config-mgmt-datacenter</item>
|
||||
<item>itop-config-mgmt-end-user</item>
|
||||
<item>itop-config-mgmt-storage</item>
|
||||
<item>itop-config-mgmt-virtualization</item>
|
||||
<item>itop-service-mgmt-enterprise</item>
|
||||
<item>itop-ticket-mgmt-simple-ticket</item>
|
||||
<item>itop-ticket-mgmt-simple-ticket-enhanced-portal</item>
|
||||
<item>itop-change-mgmt-simple</item>
|
||||
<item>itop-kown-error-mgmt</item>
|
||||
</selected_extensions>
|
||||
<sample_data>1</sample_data>
|
||||
<old_addon></old_addon>
|
||||
<options type="array"/>
|
||||
<mysql_bindir></mysql_bindir>
|
||||
</installation>
|
||||
@@ -20,47 +20,47 @@ class StatusTest extends ItopTestCase {
|
||||
*
|
||||
*/
|
||||
public function testStatusWrongUrl() {
|
||||
$sPath = APPROOT . 'status_wrong.php';
|
||||
$sPath = __DIR__ . '/status_wrong.php';
|
||||
|
||||
exec("php $sPath", $aOutput, $iRet);
|
||||
$this->assertNotEquals(0, $iRet, "Problem executing status page: $sPath, $iRet, aOutput:\n" . var_export($aOutput, true));
|
||||
|
||||
$this->assertNotEquals(0, $iRet, "Problem executing status page: $sPath, $iRet");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function testStatusGood() {
|
||||
$sPath = APPROOT . 'status.php';
|
||||
$sPath = __DIR__ . '/status.php';
|
||||
|
||||
exec("php $sPath", $aOutput, $iRet);
|
||||
|
||||
$this->assertEquals(0, $iRet, "Problem executing status page: $sPath, $iRet");
|
||||
$this->assertEquals(0, $iRet, "Problem executing status page: $sPath, $iRet, aOutput:\n" . var_export($aOutput, true));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function testStatusGoodWithJson() {
|
||||
$sPath = APPROOT . 'status.php';
|
||||
$sPath = __DIR__ . '/status.php';
|
||||
|
||||
exec("php $sPath", $aOutput, $iRet);
|
||||
$sAdditionnalInfo = "aOutput:\n" . var_export($aOutput, true);
|
||||
|
||||
//Check response
|
||||
$this->assertNotEmpty($aOutput[0], 'Empty response');
|
||||
$this->assertJson($aOutput[0], 'Not a JSON');
|
||||
$this->assertNotEmpty($aOutput[0], 'Empty response. ' . $sAdditionnalInfo);
|
||||
$this->assertJson($aOutput[0], 'Not a JSON. ' . $sAdditionnalInfo);
|
||||
|
||||
$aResponseDecoded = json_decode($aOutput[0], true);
|
||||
|
||||
//Check status
|
||||
$this->assertArrayHasKey('status', $aResponseDecoded, 'JSON does not have a status\' field');
|
||||
$this->assertEquals('RUNNING', $aResponseDecoded['status'], 'Status is not \'RUNNING\'');
|
||||
$this->assertArrayHasKey('status', $aResponseDecoded, 'JSON does not have a status\' field. ' . $sAdditionnalInfo);
|
||||
$this->assertEquals('RUNNING', $aResponseDecoded['status'], 'Status is not \'RUNNING\'. ' . $sAdditionnalInfo);
|
||||
//Check code
|
||||
$this->assertArrayHasKey('code', $aResponseDecoded, 'JSON does not have a code\' field');
|
||||
$this->assertEquals(0, $aResponseDecoded['code'], 'Code is not 0');
|
||||
$this->assertArrayHasKey('code', $aResponseDecoded, 'JSON does not have a code\' field. ' . $sAdditionnalInfo);
|
||||
$this->assertEquals(0, $aResponseDecoded['code'], 'Code is not 0. ' . $sAdditionnalInfo);
|
||||
//Check message
|
||||
$this->assertArrayHasKey('message', $aResponseDecoded, 'JSON does not have a message\' field');
|
||||
$this->assertEmpty($aResponseDecoded['message'], 'Message is not empty');
|
||||
$this->assertArrayHasKey('message', $aResponseDecoded, 'JSON does not have a message\' field. ' . $sAdditionnalInfo);
|
||||
$this->assertEmpty($aResponseDecoded['message'], 'Message is not empty. ' . $sAdditionnalInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
|
||||
//Include status functions
|
||||
require_once(__DIR__ . '/sources/application/status/status.inc.php');
|
||||
|
||||
require_once(__DIR__.'/../../sources/application/status/status.inc.php');
|
||||
//Do check Status
|
||||
try
|
||||
{
|
||||
@@ -123,87 +123,6 @@ class TestSQLQuery extends TestScenarioOnDB
|
||||
}
|
||||
}
|
||||
|
||||
class TestCSVParser extends TestFunction
|
||||
{
|
||||
static public function GetName() {return 'Check CSV parsing';}
|
||||
static public function GetDescription() {return 'Loads a set of CSV data';}
|
||||
|
||||
public function DoExecute()
|
||||
{
|
||||
$sDataFile = '?field1?;?field2?;?field3?
|
||||
?a?;?b?;?c?
|
||||
a;b;c
|
||||
? a ? ; ? b ? ; ? c ?
|
||||
a ; b ; c
|
||||
??;??;??
|
||||
;;
|
||||
?a"?;?b?;?c?
|
||||
?a1
|
||||
a2?;?b?;?c?
|
||||
?a1,a2?;?b?;?c?
|
||||
?a?;?b?;?c1,",c2
|
||||
,c3?
|
||||
?a?;?b?;?ouf !?
|
||||
Espace sur la fin ; 1234; e@taloc.com ';
|
||||
|
||||
self::DumpVariable($sDataFile);
|
||||
|
||||
$aExpectedResult = array(
|
||||
//array('field1', 'field2', 'field3'),
|
||||
array('a', 'b', 'c'),
|
||||
array('a', 'b', 'c'),
|
||||
array(' a ', ' b ', ' c '),
|
||||
array('a', 'b', 'c'),
|
||||
array('', '', ''),
|
||||
array('', '', ''),
|
||||
array('a"', 'b', 'c'),
|
||||
array("a1\na2", 'b', 'c'),
|
||||
array('a1,a2', 'b', 'c'),
|
||||
array('a', 'b', "c1,\",c2\n,c3"),
|
||||
array('a', 'b', 'ouf !'),
|
||||
array('Espace sur la fin', '1234', 'e@taloc.com'),
|
||||
);
|
||||
|
||||
$oCSVParser = new CSVParser($sDataFile, ';', '?');
|
||||
$aData = $oCSVParser->ToArray(1, null, 0);
|
||||
|
||||
$iIssues = 0;
|
||||
|
||||
echo "<table border=\"1\">\n";
|
||||
foreach ($aData as $iRow => $aRow)
|
||||
{
|
||||
echo "<tr>\n";
|
||||
foreach ($aRow as $iCol => $sCell)
|
||||
{
|
||||
if (empty($sCell))
|
||||
{
|
||||
$sCellValue = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCellValue = htmlentities($sCell, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
if (!isset($aExpectedResult[$iRow][$iCol]))
|
||||
{
|
||||
$iIssues++;
|
||||
$sCellValue = "<span style =\"color: red; background-color: grey;\">$sCellValue</span>";
|
||||
}
|
||||
elseif ($aExpectedResult[$iRow][$iCol] != $sCell)
|
||||
{
|
||||
$iIssues++;
|
||||
$sCellValue = "<span style =\"color: red; background-color: lightgrey;\">$sCellValue</span>, expecting '<span style =\"color: green; background-color: lightgrey;\">".$aExpectedResult[$iRow][$iCol]."</span>'";
|
||||
}
|
||||
|
||||
echo "<td><pre>$sCellValue</pre></td>";
|
||||
}
|
||||
echo "</tr>\n";
|
||||
}
|
||||
echo "</table>\n";
|
||||
return ($iIssues > 0);
|
||||
}
|
||||
}
|
||||
|
||||
class TestGenericItoMyModel extends TestBizModelGeneric
|
||||
{
|
||||
static public function GetName()
|
||||
|
||||
@@ -244,8 +244,14 @@ function CronExec($oP, $aProcesses, $bVerbose)
|
||||
|
||||
foreach ($aProcesses as $oProcess)
|
||||
{
|
||||
|
||||
$sTaskClass = get_class($oProcess);
|
||||
|
||||
// N°3219 for each process will use a specific CMDBChange object with a specific track info
|
||||
// Any BackgroundProcess can overrides this as needed
|
||||
CMDBObject::SetCurrentChange(null);
|
||||
CMDBObject::SetTrackInfo("Background task ($sTaskClass)");
|
||||
CMDBObject::SetTrackOrigin(null);
|
||||
|
||||
if (!array_key_exists($sTaskClass, $aTasks))
|
||||
{
|
||||
// New entry, let's create a new BackgroundTask record, and plan the first execution
|
||||
|
||||
Reference in New Issue
Block a user