Compare commits

...

667 Commits
2.5.2 ... 2.6.1

Author SHA1 Message Date
Vladimir Kunin
b5c4801beb 🌐 Russian translations for 2.6.1 (#70) 2019-03-25 16:29:05 +01:00
Molkobain
3eff8f62b1 Merge branch 'support/2.5' 2019-03-25 15:47:51 +01:00
Eric
b83e5e2b72 N°1618 - Fix custom date format: Fix issue with in-line creation 2019-03-22 15:42:11 +01:00
Pierre Goiffon
cac0da4e3d Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	application/utils.inc.php
#	css/css-variables.scss
#	css/light-grey.css
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/itop-attachments/module.attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2019-03-21 17:39:26 +01:00
Pierre Goiffon
7e064365eb N°1968 Uniqueness : fix only root disabled class was removed from searches
Now all of the following hierarchy is excluded
2019-03-21 17:25:51 +01:00
Pierre Goiffon
4918b9c83a 💡 Add phpdoc for N°1835 new Sanitize param 2019-03-21 12:04:31 +01:00
Pierre Goiffon
ed95f4e05f 📦 Update CSS for 2.6.1 2019-03-19 11:58:48 +01:00
Pierre Goiffon
693fdfdc5b 📦 Update versions for 2.6.1, woops wrong version on previous commit 2019-03-19 11:39:11 +01:00
Pierre Goiffon
01108ca83d 📦 Update versions for 2.6.1 2019-03-19 11:35:42 +01:00
Pierre Goiffon
3d5b7197f6 📦 Update modules versions for 2.6.1 2019-03-19 11:34:01 +01:00
Pierre Goiffon
54c027823b 🌐 Fix dict automatic update 2019-03-19 11:14:39 +01:00
Pierre Goiffon
f63aceeabe Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	datamodels/2.x/authent-external/da.dict.authent-external.php
#	datamodels/2.x/authent-external/de.dict.authent-external.php
#	datamodels/2.x/authent-external/fr.dict.authent-external.php
#	datamodels/2.x/authent-external/hu.dict.authent-external.php
#	datamodels/2.x/authent-external/ja.dict.authent-external.php
#	datamodels/2.x/authent-external/nl.dict.authent-external.php
#	datamodels/2.x/authent-external/pt_br.dict.authent-external.php
#	datamodels/2.x/authent-external/zh_cn.dict.authent-external.php
#	datamodels/2.x/authent-ldap/cs.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/da.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/de.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/fr.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/hu.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/ja.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/nl.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/pt_br.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/ru.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/zh_cn.dict.authent-ldap.php
#	datamodels/2.x/authent-local/da.dict.authent-local.php
#	datamodels/2.x/authent-local/de.dict.authent-local.php
#	datamodels/2.x/authent-local/fr.dict.authent-local.php
#	datamodels/2.x/authent-local/hu.dict.authent-local.php
#	datamodels/2.x/authent-local/ja.dict.authent-local.php
#	datamodels/2.x/authent-local/nl.dict.authent-local.php
#	datamodels/2.x/authent-local/pt_br.dict.authent-local.php
#	datamodels/2.x/authent-local/ru.dict.authent-local.php
#	datamodels/2.x/authent-local/zh_cn.dict.authent-local.php
#	datamodels/2.x/itop-attachments/cs.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/da.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/de.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/en.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/es_cr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/fr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/hu.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/it.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/ja.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/nl.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/pt_br.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/tr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/zh_cn.dict.itop-attachments.php
#	datamodels/2.x/itop-backup/da.dict.itop-backup.php
#	datamodels/2.x/itop-backup/de.dict.itop-backup.php
#	datamodels/2.x/itop-backup/es_cr.dict.itop-backup.php
#	datamodels/2.x/itop-backup/hu.dict.itop-backup.php
#	datamodels/2.x/itop-backup/it.dict.itop-backup.php
#	datamodels/2.x/itop-backup/ja.dict.itop-backup.php
#	datamodels/2.x/itop-backup/nl.dict.itop-backup.php
#	datamodels/2.x/itop-backup/tr.dict.itop-backup.php
#	datamodels/2.x/itop-change-mgmt-itil/da.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/de.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/fr.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/hu.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/it.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/ja.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/nl.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/ru.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/zh_cn.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/da.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/de.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/fr.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/hu.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/it.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/ja.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/nl.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/pt_br.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/ru.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/tr.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/zh_cn.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/cs.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/da.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/de.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/es_cr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/fr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/hu.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/it.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/ja.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/nl.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/pt_br.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/ru.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/tr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/zh_cn.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config/da.dict.itop-config.php
#	datamodels/2.x/itop-config/de.dict.itop-config.php
#	datamodels/2.x/itop-config/es_cr.dict.itop-config.php
#	datamodels/2.x/itop-config/fr.dict.itop-config.php
#	datamodels/2.x/itop-config/hu.dict.itop-config.php
#	datamodels/2.x/itop-config/it.dict.itop-config.php
#	datamodels/2.x/itop-config/ja.dict.itop-config.php
#	datamodels/2.x/itop-config/nl.dict.itop-config.php
#	datamodels/2.x/itop-config/pt_br.dict.itop-config.php
#	datamodels/2.x/itop-config/tr.dict.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/cs.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/da.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/de.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/es_cr.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/fr.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/hu.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/it.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/ja.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/nl.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/pt_br.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/ru.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/tr.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/zh_cn.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/cs.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/da.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/de.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/es_cr.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/fr.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/hu.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/it.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/ja.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/nl.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/pt_br.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/ru.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/tr.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/zh_cn.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-hub-connector/cs.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/da.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/de.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/es_cr.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/fr.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/hu.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/it.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/ja.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/nl.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/pt_br.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/ru.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/tr.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/zh_cn.dict.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/cs.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/da.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/de.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/es_cr.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/hu.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/it.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/ja.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/nl.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/pt_br.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/ru.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/tr.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/zh_cn.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/cs.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/da.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/de.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/hu.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/it.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/ja.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/nl.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/tr.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/zh_cn.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/da.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/de.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/hu.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/it.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/ja.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/nl.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/ru.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/tr.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/zh_cn.dict.itop-portal-base.php
#	datamodels/2.x/itop-problem-mgmt/da.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/de.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/fr.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/hu.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/it.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/ja.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/nl.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/ru.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/zh_cn.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-request-mgmt-itil/cs.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/da.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/de.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/en.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/es_cr.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/fr.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/hu.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/it.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/ja.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/pt_br.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/ru.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/tr.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/zh_cn.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/cs.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/da.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/de.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/fr.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/hu.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/it.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/ja.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/nl.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/ru.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/tr.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/zh_cn.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/cs.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/da.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/de.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/en.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/fr.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/hu.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/it.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/ja.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/nl.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/pt_br.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/ru.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/tr.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/zh_cn.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/cs.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/da.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/de.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/en.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/fr.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/hu.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/it.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/ja.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/nl.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/pt_br.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/ru.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/tr.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/zh_cn.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/cs.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/da.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/de.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/es_cr.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/fr.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/hu.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/it.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/ja.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/nl.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/pt_br.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/ru.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/tr.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/zh_cn.dict.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/cs.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/da.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/de.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/es_cr.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/fr.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/hu.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/it.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/ja.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/nl.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/pt_br.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/ru.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/tr.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/zh_cn.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/cs.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/da.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/de.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/hu.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/it.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/ja.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/nl.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/ru.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/tr.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/zh_cn.dict.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/cs.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/da.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/de.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/es_cr.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/fr.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/hu.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/it.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/ja.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/nl.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/pt_br.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/ru.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/tr.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/zh_cn.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/cs.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/da.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/de.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/en.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/es_cr.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/fr.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/hu.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/it.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/ja.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/nl.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/pt_br.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/ru.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/tr.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/zh_cn.dict.itop-welcome-itil.php
#	dictionaries/cs.dictionary.itop.core.php
#	dictionaries/cs.dictionary.itop.ui.php
#	dictionaries/da.dictionary.itop.core.php
#	dictionaries/da.dictionary.itop.ui.php
#	dictionaries/de.dictionary.itop.core.php
#	dictionaries/de.dictionary.itop.ui.php
#	dictionaries/es_cr.dictionary.itop.core.php
#	dictionaries/es_cr.dictionary.itop.ui.php
#	dictionaries/fr.dictionary.itop.core.php
#	dictionaries/fr.dictionary.itop.ui.php
#	dictionaries/hu.dictionary.itop.core.php
#	dictionaries/hu.dictionary.itop.ui.php
#	dictionaries/it.dictionary.itop.core.php
#	dictionaries/it.dictionary.itop.ui.php
#	dictionaries/ja.dictionary.itop.core.php
#	dictionaries/ja.dictionary.itop.ui.php
#	dictionaries/nl.dictionary.itop.core.php
#	dictionaries/nl.dictionary.itop.ui.php
#	dictionaries/pt_br.dictionary.itop.core.php
#	dictionaries/pt_br.dictionary.itop.ui.php
#	dictionaries/ru.dictionary.itop.core.php
#	dictionaries/ru.dictionary.itop.ui.php
#	dictionaries/tr.dictionary.itop.core.php
#	dictionaries/tr.dictionary.itop.ui.php
#	dictionaries/zh_cn.dictionary.itop.core.php
#	dictionaries/zh_cn.dictionary.itop.ui.php
2019-03-19 10:54:06 +01:00
Pierre Goiffon
243aab1030 N°1968 Uniqueness : do not allow invalid rule overrides definition
On overrides disabled key must has a value
2019-03-18 16:07:36 +01:00
bruno DA SILVA
22dba9ae07 🐛 composer.json dependencies correctness 2019-03-18 15:00:35 +01:00
Pierre Goiffon
02857a86fd Merge remote-tracking branch 'origin/support/2.5' 2019-03-15 17:22:04 +01:00
Stephen Abello
d663d01798 N°1966: Added missing strings to dict files 2019-03-15 17:02:24 +01:00
Stephen Abello
3602163b38 Revert N°1919 and its incorrect translations 2019-03-15 17:00:53 +01:00
Molkobain
83bb3b6d72 🐛 N°1889 Portal: Wrong encoding of special chars like in dashlets (eg. "ö", "&", ...) 2019-03-13 16:56:04 +01:00
Eric
75737b4ffe N° 1837 - Fix Synchro Obsolescence 2019-03-13 11:01:13 +01:00
Molkobain
a5340917a7 🐛 N°1956 Portal: Fix message content in user profile when password edition is disabled 2019-03-13 10:31:04 +01:00
Molkobain
914d19e7e4 🐛 N°2072 Fix missing/empty error message when uploading too large attachment 2019-03-13 09:51:49 +01:00
Pierre Goiffon
eb49dbbdc8 N°1968 uniqueness rules : fix search for classes hierarchy, disallow 'attributes' property overrides 2019-03-12 18:12:01 +01:00
Molkobain
912bab5a43 🐛 N°2091: Portal: Fix regression introduced in 2.5, better error message when user logged out 2019-03-12 17:08:12 +01:00
Pierre Goiffon
94092f445f Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	application/applicationextension.inc.php
#	webservices/rest.php
2019-03-08 14:35:02 +01:00
Eric
856c037bb0 N°2064 - Fix abstract class state list for notification triggers 2019-03-07 15:56:29 +01:00
Eric
388896b963 N°941 - Backup/Windows %, ! or " not allowed in password 2019-03-07 15:26:53 +01:00
Eric
1d8addf675 typo 2019-03-06 18:21:19 +01:00
Eric
c8c3d32b18 Error logs and corresponding eml stored by message and accessible from the console 2019-03-06 17:46:47 +01:00
Stephen Abello
9c71d32964 typo in last merge 2019-03-04 12:01:52 +01:00
Eric
d199d84b27 🌐 french typo fixed 2019-03-04 09:41:09 +01:00
Pierre Goiffon
24aca83de4 Merge remote-tracking branch 'origin/support/2.5' 2019-03-04 09:16:53 +01:00
Stephen Abello
5a0edb5c39 Merge branch 'support/2.5'
# Conflicts:
#	js/jquery.tablesorter.pager.js
2019-03-01 17:06:44 +01:00
Eric
38951fab1a N°1618 - Custom date format: Fix issue with in-line creation 2019-02-28 17:40:49 +01:00
Pierre Goiffon
1224570fa5 Merge remote-tracking branch 'origin/support/2.5' 2019-02-28 08:45:30 +01:00
Eric
733c908e34 N°2043 - Fix: CSV Import reconciliation using ExternalField broken in service management for provider 2019-02-27 16:00:47 +01:00
Eric
377b4b038c N°2041 - Fix: Reset(LinkedSetIndirect) breaks data integrity 2019-02-27 09:57:01 +01:00
Eric
3f7fd6f9f9 N°1954 - Recent change on impact analysis - old change appears on result 2019-02-26 15:36:56 +01:00
Eric
1cb36621a1 N°1963 - Dashlet GroupBy: allow ExternalField selection in UI 2019-02-26 15:31:31 +01:00
Eric
ddd9188eb7 Fix non-existing variable 2019-02-26 15:24:15 +01:00
Eric
02254eac67 N°1966 - Missing dictionaries entries (recover lost translation) 2019-02-25 17:40:08 +01:00
Eric
ebe026b2e9 N°1966 - Missing dictionaries entries (other languages) 2019-02-25 17:20:47 +01:00
Eric
efc7c5b0f4 N°1966 - Missing dictionaries entries 2019-02-25 16:44:51 +01:00
Pierre Goiffon
f68a77450d Merge remote-tracking branch 'origin/support/2.5' 2019-02-25 12:10:30 +01:00
Pierre Goiffon
1f7923beae Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	setup/setup.js
#	setup/wizardsteps.class.inc.php
2019-02-21 17:18:29 +01:00
Eric
a2d34d1779 N°1954 - Fix recent change on impact analysis 2019-02-21 09:41:36 +01:00
Eric
48f15d7781 N°1974 - Fix: Stimuli can be applied through URL even if the access rights are set to deny 2019-02-20 16:11:49 +01:00
Eric
a89bca4626 N°1975 - Fix: Change Menu rights to "Admin only" leads in crash test 2019-02-20 14:57:39 +01:00
Eric
63a36fd0f6 N°2030 - Fix function CopyAttribute for external fields 2019-02-19 14:09:14 +01:00
Eric
7cf7e55454 N°1823 - Fix tags not saved in case of error 2019-02-19 10:16:45 +01:00
Eric
5067c867b8 N°2014 - Fix Object modification refused when a n-n relation is locked by datasynchro 2019-02-18 15:41:06 +01:00
Eric
7bb49893ee N°1884 - Admin Tools Manager no longer has access to 'Schedule Backup' and 'Configuration' menus 2019-02-15 17:34:38 +01:00
Eric
818b4d08da N°1884 - Admin Tools Manager no longer has access to 'Schedule Backup' and 'Configuration' menus 2019-02-15 17:28:55 +01:00
Eric
f438fbd06f N°941 - fix regex 2019-02-15 17:06:50 +01:00
Eric
297a45d477 N°941 - Backup/Windows %, ! or " not allowed in password 2019-02-15 17:03:07 +01:00
Eric
17fe9dfd5f N°1906 - Enhancement Request: Handling a lot of many to many relations in form element (edit mode) 2019-02-15 15:33:22 +01:00
Eric
9d0cbca497 N°941 - Check DB password in configuration when saving 2019-02-14 10:57:36 +01:00
Eric
a613b4b101 Merge branch 'hotfix/N941-2' 2019-02-14 09:48:48 +01:00
Eric
c989e2eda5 N°941 - Check DB password also in configuration 2019-02-14 09:47:22 +01:00
Eric
c6fd381b01 Merge branch 'hotfix/N941' 2019-02-13 17:45:44 +01:00
Eric
a784661025 N°941 - Backup/Windows % not allowed in password 2019-02-13 17:43:32 +01:00
Eric
1f9a638bc1 N°941 - Backup/Windows % not allowed in password 2019-02-13 17:39:04 +01:00
Stephen Abello
43b0747b83 N°1443 : Add table_id used by tables paging 2019-02-13 14:43:25 +01:00
Eric
84767692b0 Merge branch 'hotfix/N2016' 2019-02-13 14:04:25 +01:00
Eric
d484614c0f N°2016 - Fix Issue with "ExecAsyncTask: async_task_retries" 2019-02-13 14:03:22 +01:00
Eric
b403bace6c Merge branch 'hotfix/N2011' 2019-02-13 12:49:25 +01:00
Eric
0b751a9dd6 N°2011 - Fix search auto-completion bug 2019-02-13 12:48:05 +01:00
Eric
8f434cad08 Merge branch 'hotfix/N1963' 2019-02-13 11:01:39 +01:00
Eric
f83292fccc N°1953 - Dashlet GroupBy: allow ExternalField selection in UI 2019-02-13 10:59:40 +01:00
Stephen Abello
90e128f951 N°1148: Fix regression on export 2019-02-11 13:33:07 +01:00
Stephen Abello
8efc372a68 (retrofit from develop) N°1909: PHP 7.3 compatibility 2019-02-11 13:33:07 +01:00
Molkobain
257fb4d036 🐛 N°2019 Fix empty error message on object update 2019-02-08 15:39:01 +01:00
Stephen Abello
97261820f6 N°1919: Fix mistakes in Russian translation integration 2019-02-08 15:08:04 +01:00
Stephen Abello
2f83a2168c N°1919: Update Russian translation 2019-02-07 16:41:53 +01:00
Stephen Abello
c0275eec21 N°1955: Fix typo in German translation (thanks to ITOMIG!) 2019-02-05 10:42:42 +01:00
Pierre Goiffon
2bd7a7b5f8 Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	application/utils.inc.php
2019-01-31 18:43:20 +01:00
Pierre Goiffon
505aad1e89 Merge branch 'support/2.5'
# Conflicts:
#	datamodels/2.x/itop-hub-connector/zh_cn.dict.itop-hub-connector.php
#	lib/tcpdf/CHANGELOG.TXT
#	lib/tcpdf/composer.json
#	lib/tcpdf/include/tcpdf_fonts.php
#	lib/tcpdf/include/tcpdf_images.php
#	lib/tcpdf/include/tcpdf_static.php
#	lib/tcpdf/tcpdf.php
2019-01-28 16:04:19 +01:00
Pierre Goiffon
00c59b7f2c Finish readme_2.5.1 2019-01-10 10:29:18 +01:00
Pierre Goiffon
dbcde14f5e 📝 README : was still pointing to 2.5.0 whereas 2.5.1 was published 2019-01-10 10:28:47 +01:00
Pierre Goiffon
a6f4adfed8 Finish 2.6.0 2019-01-09 17:27:17 +01:00
Pierre Goiffon
effaba42d0 Merge remote-tracking branch 'origin/support/2.5' into release/2.6 2019-01-09 17:24:54 +01:00
Pierre Goiffon
340cacc691 Merge remote-tracking branch 'origin/release/2.6.0-community' into release/2.6
# Conflicts:
#	install.txt
2019-01-09 17:23:43 +01:00
Pierre Goiffon
a85bedd31c Merge remote-tracking branch 'origin/support/2.5' into release/2.6
# Conflicts:
#	install.txt
2019-01-09 10:51:11 +01:00
Pierre Goiffon
23269eab77 📝 Update install.txt for 2.6.x 2019-01-09 10:45:14 +01:00
Pierre Goiffon
27ad2e2169 📝 Update README.md for 2.6.0 community 2019-01-09 10:01:40 +01:00
Pierre Goiffon
5d09841cd5 📝 Update install.txt for 2.6.0 2019-01-09 09:53:20 +01:00
Eric
11ec46c18b N°1408 - Fix overview attribute when not already defined in Organization class 2019-01-08 15:19:52 +01:00
Denis Flaven
2b563d4fc8 Remove debug traces when updating objects
A very verbose debug trace in the error log was happening each time a
CmdbAbstractObject gets created non-interactively.
2019-01-07 16:24:31 +01:00
Molkobain
2773a8bf2f N°1917 Setup: Fix warnings when upgrading an extension with an extension.xml file
(cherry picked from commit 4eb416d407)
2019-01-04 14:13:33 +01:00
Eric
ad36011fc8 N°1716 - "Group by" Dashlet is no more clickable in edition 2019-01-03 17:47:27 +01:00
Eric
b82b095f58 1814 - Search with criteria : always add default criteria 2019-01-03 09:58:54 +01:00
Pierre Goiffon
2f5c810276 Merge remote-tracking branch 'origin/support/2.5' into release/2.6 2018-12-31 17:21:52 +01:00
Pierre Goiffon
d90e094d4d 📝 CONTRIBUTING : add a link to the translation wiki page 2018-12-31 16:55:51 +01:00
Pierre Goiffon
352f8cee10 💡 Config : add ref to loading methods 2018-12-31 11:38:57 +01:00
Pierre Goiffon
9f3332b2f2 💡 PHPDoc for AttributeFlags methods 2018-12-28 16:37:03 +01:00
Pierre Goiffon
2b71621628 Merge remote-tracking branch 'origin/support/2.5' into release/2.6 2018-12-28 11:42:11 +01:00
Pierre Goiffon
5349fc6f4b 📝 CONTRIBUTING : fix some typos, more info in gitflow 2018-12-28 11:15:46 +01:00
Pierre Goiffon
1665b12b86 💡 PHPDoc for \DBObject::DBInsert methods hierarchy (and some more, too O:) ) 2018-12-27 15:07:49 +01:00
purplegrape
1bbed99636 chinsese translation improvement for relase 2.6
chinsese translation improvement for relase 2.6
2018-12-27 10:40:53 +01:00
Pierre Goiffon
bb6bd61c37 👥 Remove duplicate jbostoen (my mistake... see 5338325a73) 2018-12-26 09:53:26 +01:00
Pierre Goiffon
b51b5582a0 Merge remote-tracking branch 'origin/support/2.5' into release/2.6 2018-12-21 10:23:29 +01:00
Eric
27b1b1f8a8 N°1903 - Fix initial value of a MetaEnum attribute is always its default value 2018-12-21 09:59:19 +01:00
Eric
a45e543eac N°1898 - Fix pagination issue for search with accent 2018-12-21 09:36:28 +01:00
Pierre Goiffon
50235bbb04 🌐 Fix typos in EN translations
Report PR #38 modifications, many thanks @jbostoen !
2018-12-20 17:19:32 +01:00
Eric
a2ddb30f78 N°1420 - Autocomplete fix keyboard behavior (compatibility fix) 2018-12-19 17:39:35 +01:00
Eric
75fbb831c9 Fix Setup hardening 2018-12-19 17:10:02 +01:00
Molkobain
3219957eed Internal: Fix regression introduced during XSS protection on tag sets 2018-12-19 11:43:50 +01:00
Eric
44671a5085 Fix typos in configuration description 2018-12-19 09:02:49 +01:00
Purple Grape
bc4873dfe5 🌐 update 2.6 chinese translation and corresponding licences (#45)
* Update zh_cn.dict.authent-external.php

* Update zh_cn.dict.authent-ldap.php

* Update zh_cn.dict.authent-local.php

* Update zh_cn.dict.itop-attachments.php

* Update zh_cn.dict.itop-backup.php

* Update zh_cn.dict.itop-change-mgmt-itil.php

* Update zh_cn.dict.itop-change-mgmt.php

* Update zh_cn.dict.itop-config-mgmt.php

* Update zh_cn.dict.itop-config.php

* Update zh_cn.dict.itop-datacenter-mgmt.php

* Update zh.dict.itop-endusers-devices.php

* Update zh_cn.dict.itop-hub-connector.php

* Update zh_cn.dict.itop-incident-mgmt-itil.php

* Update zh_cn.dict.itop-knownerror-mgmt.php

* Update zh_cn.dict.itop-portal-base.php

* Update zh_cn.dict.itop-portal-base.php

* Update zh_cn.dict.itop-problem-mgmt.php

* Update zh_cn.dict.itop-request-mgmt-itil.php

* Update zh_cn.dict.itop-request-mgmt.php

* Update zh_cn.dict.itop-welcome-itil.php

* Update zh_cn.dict.itop-tickets.php

* Update zh_cn.dict.itop-service-mgmt-provider.php

* Update zh_cn.dict.itop-service-mgmt.php

* Update zh_cn.dictionary.itop.core.php

* Update zh_cn.dictionary.itop.ui.php
2018-12-18 18:11:20 +01:00
Eric
92657951c7 N°1408 - Dashboard Attribute (removed from "Properties" tab in edition) 2018-12-18 16:32:46 +01:00
Eric
8b2c18ab8c N°1420 - Autocomplete fix keyboard behavior 2018-12-18 16:00:29 +01:00
Pierre Goiffon
18999f29c5 🐛 Fix PHP syntax error in itop-chg-mgmt NL dict file 2018-12-18 15:30:23 +01:00
Pierre Goiffon
e8075bf5fd 🌐 Remove useless dict keys (see PR #38) 2018-12-18 15:24:17 +01:00
Pierre Goiffon
5338325a73 👥 Add contributor : jboesten / Jeffrey 2018-12-18 15:12:32 +01:00
Pierre Goiffon
4d3810a10d 🌐 New Dutch translations
Integrated directly from PR #38 as rebase and squash seems very difficult
Many thanks @jbostoen and @Hipska !
2018-12-18 15:10:38 +01:00
Pierre Goiffon
672bc471be N°1885 move back php-gd from mandatory extension to optional 2018-12-18 11:15:56 +01:00
Pierre Goiffon
3089cbc2bc 💡 Add some PhpDoc 2018-12-18 11:15:55 +01:00
Molkobain
ce9416d887 N°1878 Request Template: Fix history updated even if template not updated
When editing the object, the hidden input is not initialized correctly with the values from the form. This is due to a timing issue among async processes. To fix that, we made sure the WizardHelper updates the input value before submit. The real fix would be to better handle the subform building process.
2018-12-18 11:08:54 +01:00
Pierre Goiffon
473d1fb756 Fix 2.5 merge error 2018-12-17 17:12:30 +01:00
Pierre Goiffon
b880ca05a5 Merge remote-tracking branch 'origin/support/2.5' into release/2.6
# Conflicts:
#	composer.json
#	setup/setuputils.class.inc.php
2018-12-17 17:04:39 +01:00
Molkobain
cbc9784d21 N°1882 Internal: Fix regression on organization autocomplete (left menu) that could not be emptied 2018-12-17 16:46:39 +01:00
Molkobain
16cb1bbbbf N°1843 Portal: Fix inline images being displayed too large sometimes in forms 2018-12-17 15:27:15 +01:00
Molkobain
a1c2b809ba 🌐 N°1839 Update translations for TriggerOnThresholdReached 2018-12-17 15:27:15 +01:00
Stephen Abello
c9691457b8 Prepare datamodels' versions for 2.6.0 release 2018-12-17 15:10:37 +01:00
Molkobain
9ef6c78395 N°1637 Portal: Add form mode (create/edit/view) to information passed to iPopupMenuExtension on object details 2018-12-17 09:53:13 +01:00
Molkobain
81f70c4c58 N°1637 Portal: Add CSS/JS hook on object forms (form mode: create/edit/view) 2018-12-17 09:51:47 +01:00
Molkobain
869f590bf4 N°657 Portal: Fix modal not closing when pressing "esc" key 2018-12-14 19:06:14 +01:00
Molkobain
3b6aa7eaf5 N°1879 Additional fix for default print format of dashboards 2018-12-14 17:31:43 +01:00
Molkobain
53ef2b0b5d N°1171 Portal: Add CSS/JS hooks in navigation menu to identify bricks 2018-12-14 17:26:55 +01:00
Molkobain
4dfe6fc817 N°1220 Portal: Fix logo displayed not wide enough when it has a small height 2018-12-14 17:06:37 +01:00
Molkobain
260143050e N°1880 Fix logo url not pointing to the portal home page instead of the 'app_icon_url' parameter's value 2018-12-14 17:01:22 +01:00
Molkobain
6adde38399 N°1880 Fix logo url not pointing to the portal home page instead of the 'app_icon_url' parameter's value 2018-12-14 16:54:51 +01:00
Molkobain
76a40b2f45 N°1879 Change default print format for dashboards to "A4 Landscape" 2018-12-14 16:03:56 +01:00
Denis Flaven
77c2537df7 Zoom-in cursor on zoomable images
(for browsers supporting the zoom-in cursor: i.e. not IE)
2018-12-14 15:12:52 +01:00
Molkobain
6a88f622ad N°1839 Update TriggerOnThresholdReached to select attribute from a set of valid attributes instead of typing it manually 2018-12-14 11:51:14 +01:00
Molkobain
9b5d8b8a01 N°931 Fix bug when max allowed items set to 1 2018-12-14 11:11:59 +01:00
Molkobain
b4448c5bb9 Code cleanup 2018-12-14 10:00:29 +01:00
Denis Flaven
7faf3258f7 Setup hardening. 2018-12-13 18:12:58 +01:00
Denis Flaven
7c0d03ea3b Cosmetics on setup (Licenses prompt)
Limit the height of the list of licences (displaying a scrollbar if
needed) so that the page fits on screen.
2018-12-13 12:12:28 +01:00
Denis Flaven
d1837377a4 Merge branch 'release/2.6' of https://github.com/Combodo/iTop.git into release/2.6 2018-12-13 11:47:41 +01:00
Denis Flaven
1c3a503a82 N°1872 Regression when editing query phrasebook entries. Fixed. 2018-12-13 11:46:39 +01:00
Molkobain
daafa9123c N°1852 Fix loss of inline images and attachments when user has been logged off 2018-12-13 11:35:00 +01:00
Molkobain
5a1b6e43c9 N°1835 Internal: Fix regression introduced by commit 36d47c2 2018-12-13 11:18:25 +01:00
Molkobain
81d502cae8 N°1835 Internal: Fix regression introduced by commit 36d47c2 2018-12-12 20:56:07 +01:00
Molkobain
4a99ed2ad8 N°1835 Internal: Fix regression introduced by commit 36d47c2 2018-12-12 19:51:06 +01:00
Vladimir Kunin
7016724abc Show resolution_date in resolved problem details. 2018-12-12 11:38:07 +01:00
Molkobain
0a8e3f099e N°1864 Portal: Add notification placeholders for portal links in edit or view mode 2018-12-12 10:04:00 +01:00
Pierre Goiffon
45910dc115 Merge remote-tracking branch 'origin/support/2.5' into release/2.6
# Conflicts:
#	datamodels/2.x/itop-config-mgmt/zh.dict.itop-config-mgmt.php
#	dictionaries/zh.dictionary.itop.ui.php
2018-12-11 17:23:34 +01:00
Pierre Goiffon
afd6428411 💡 @var on iApplicationObjectExtension calls 2018-12-11 17:19:52 +01:00
Eric
7eb419507b N°1420 - Perf autocomplete (remove almost duplicated requests) 2018-12-11 16:39:57 +01:00
Pierre Goiffon
b4a6d378ab N°1827 fix freeze on object creation when changing dependant field, if object has hidden TagSet field 2018-12-11 15:54:33 +01:00
Eric
44b7821015 N°1846 - Fix Object Copier: Create Ticket from CI, duplicate links 2018-12-11 15:26:24 +01:00
Pierre Goiffon
52ac819c1f N°1835 wooops fix previous commit 2018-12-11 10:25:48 +01:00
Pierre Goiffon
c4ba1d55ac 🔊 N°1835 log error when transaction_id invalid in UI.php 2018-12-11 10:09:50 +01:00
Molkobain
85acac60c7 💪 Fix execution notice 2018-12-11 09:54:09 +01:00
Molkobain
f54da5f9a6 Internal: Debug trace when InlineImage::FinalizeInlineImages() fails to retrieve transaction ID 2018-12-10 17:44:49 +01:00
Pierre Goiffon
36d47c2274 N°1835 fix transaction_id lost with session
* transaction_id are now stored by default in file instead of session ("transaction_storage" config parameter : default value was 'Session', it is now 'File')
* session_regenerate_id() call can be disabled using "regenerate_session_id_enabled" config parameter
* new 'transaction_id' parameter type to allow dots (with a file storage, transaction_id equals the temp file name and on Windows we're getting *.tmp)
2018-12-10 17:39:07 +01:00
Eric
bd082c0a6e N°917 - Rolled back fields to AttributeText until AttributeQueryAttCodeSet can manage fields order correctly 2018-12-10 15:25:22 +01:00
Stephen Abello
29dd196193 N°1848: Fix timeout regression on impact analysis' tooltip 2018-12-10 10:01:48 +01:00
Pierre Goiffon
80a8f7a888 Update translation files 2018-12-10 08:46:02 +01:00
Pierre Goiffon
7f497fe3be Merge remote-tracking branch 'origin/support/2.5' into release/2.6
# Conflicts:
#	datamodels/2.x/itop-attachments/zh.dict.itop-attachments.php
#	datamodels/2.x/itop-backup/zh.dict.itop-backup.php
#	datamodels/2.x/itop-change-mgmt-itil/zh.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/zh.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/es_cr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/zh.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config/zh.dict.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/zh.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-hub-connector/zh.dict.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/zh.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/zh.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/zh.dict.itop-portal-base.php
#	datamodels/2.x/itop-problem-mgmt/es_cr.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/zh.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-request-mgmt-itil/es_cr.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/zh.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/es_cr.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/zh.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/zh.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/zh.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-tickets/zh.dict.itop-tickets.php
#	datamodels/2.x/itop-welcome-itil/zh.dict.itop-welcome-itil.php
#	dictionaries/zh.dictionary.itop.core.php
#	dictionaries/zh.dictionary.itop.ui.php
2018-12-10 08:38:19 +01:00
Eric
53fe826f9f Autocomplete: Prevent the RETURN key to submit form if no value is set 2018-12-07 16:06:52 +01:00
Molkobain
56b4ecb4ce 🌐 Update spanish translations (thanks to Miguel Turrubiates! ✌) 2018-12-07 14:25:17 +01:00
Pierre Goiffon
d17717e4b5 👥 Add @purplegrape to the contributor's list (PR #24, thank you !) 2018-12-07 11:23:55 +01:00
Molkobain
68da3a4aad Change default attachments (and inline images) lifetime to 1 day instead of 1 hour 2018-12-07 11:16:41 +01:00
Molkobain
855480fd7b PHPDoc 2018-12-07 11:16:41 +01:00
Pierre Goiffon
79200e8f96 🙈 Update gitignore to handle toolkit symlinks 2018-12-07 11:13:51 +01:00
Denis Flaven
540bc3a54b Newsroom finalization & integration tests. 2018-12-06 15:38:29 +01:00
Stephen Abello
35752a8041 N°1837: Internal: fix regression on data synchronized attributes display 2018-12-06 15:05:13 +01:00
Lars Hippler
86dce21849 Some small typo fixes in German translations
Fix typo in German dashlet translations (Group by sum)
Fix typo in German translation (config-saved)
2018-12-06 08:51:42 +01:00
Pierre Goiffon
fca5a625d2 🐛 N°1834 Query Phrasebook : fix fields dictionary key 2018-12-05 16:58:34 +01:00
Eric
2766fad61a Autocomplete: select automatically the first entry with the RETURN key 2018-12-05 12:01:32 +01:00
Pierre Goiffon
c6da1db72b 🔒 N°1802 add auth to check-backup.php 2018-12-05 11:37:42 +01:00
Pierre Goiffon
843c06b007 🚸 improves backup/check-backup
* backup.php now handles calls from datamodels (approot)
* fix check-backup sample cli
* better error on check-backup invalid check_ticket_itop cli parameter
2018-12-05 11:37:42 +01:00
Pierre Goiffon
7cf7628b8f 🔧 N°1802 update backup.params.distrib 2018-12-05 11:18:49 +01:00
Pierre Goiffon
41b096ba76 🔧 N°1802 rename itop_root config file parameter to itop_backup_incident 2018-12-05 10:45:14 +01:00
Eric
2a4bd4b0dc 💚 N°1825 - Fix ormset control when no limit 2018-12-05 10:10:45 +01:00
Eric
f4f5281769 💚 N°1825 - Fix tagset control when no limit 2018-12-05 10:08:40 +01:00
Eric
a4a09cd8c7 N°1825 - Fix TagSet code numeric generate a Fatal Error 2018-12-05 09:39:41 +01:00
Eric
831b18b4d2 Force the number of Tags in ormTagSet 2018-12-05 09:04:30 +01:00
Eric
e4e5f627c4 Display error log if UpdateObjectFromPostedForm fails when modifying an object in console 2018-12-05 09:04:30 +01:00
Denis Flaven
53e532cbaf 🐛 N°1822 fix for the autocomplete on IE.
Support of some accents (western languages) on IE, complete support of
all unicode accents on browsers supporting String.normalize.
2018-12-04 15:07:46 +01:00
Eric
3fa0cbf0fe Merge remote-tracking branch 'origin/support/2.5' into release/2.6 2018-12-04 14:57:56 +01:00
Eric
af8b06dda6 💚 Display error log instead of fatal error in case of Exception when modifying an object in console 2018-12-04 11:33:32 +01:00
Stephen Abello
72e2473444 N°1811: Fix regression in impact analysis: misplaced tooltips on Chrome 2018-12-04 10:58:49 +01:00
Eric
6e31a040b2 Display error log instead of fatal error in case of Exception when modifying an object in console 2018-12-04 10:49:30 +01:00
Pierre Goiffon
6c07688c34 N°1815 TagSet widget : change filter callback to an anonymous function, fix String obj conversion bug 2018-12-04 09:53:45 +01:00
Pierre Goiffon
598c22a285 🏁 N°1815 fix tagset widget parse error in MSIE all versions 2018-12-03 17:31:57 +01:00
Denis Flaven
d392b9c0f6 Adaptation of the Hub URLs for the newsroom. 2018-12-03 17:26:22 +01:00
Eric
387c166985 Fix Remote User synchro 2018-12-03 11:33:15 +01:00
Stephen Abello
9115bc118d 🌐 N°1809: Refactor Triggers' help section and add new triggers description 2018-12-03 11:26:23 +01:00
Eric
aca11ac966 Fix AttributeSet wrong error on number of tags in creation 2018-12-03 11:01:54 +01:00
Eric
c15d626095 Fix AttributeSet history database field size (was too short) 2018-12-03 09:27:56 +01:00
Molkobain
79d00b9fe6 Add CONTRIBUTING.md 2018-12-03 08:31:59 +01:00
Pierre Goiffon
a92e2fd882 📦 Update datamodels versions from 2.5.0 to 2.6.0 2018-11-30 18:25:20 +01:00
Eric
7ed51984c7 N°944 - Fix bad xml when attributes name has been changed and redefinition of label is required and cache is regenerated 2018-11-30 14:28:26 +01:00
Pierre Goiffon
cdf11a3485 GitFlow autoconfig file 2018-11-30 11:39:20 +01:00
Eric
bbefd22950 Version 2.6.0 2018-11-29 09:19:55 +01:00
Eric
e08369122d Merge remote-tracking branch 'origin/support/2.5' into release/2.6
# Conflicts:
#	core/config.class.inc.php
#	css/css-variables.scss
#	css/light-grey.css
2018-11-29 09:10:34 +01:00
Eric
0ce0572c40 N°1783 - Search : fix error when searching for a quote in a text field 2018-11-28 14:52:51 +01:00
Molkobain
9d19556419 Internal: Remove wrongly added default search criterion on FAQ (Introduced in commit 5f7e8c9) 2018-11-28 14:52:51 +01:00
Molkobain
67c0e0eb1c Fix iTop version that stayed to "2.5.0-beta" for the release 2018-11-28 14:52:50 +01:00
Pierre Goiffon
c5d0dd05b9 🔒 N°1802 itop-backup : move iTop root to config file 2018-11-28 14:49:49 +01:00
Molkobain
4e1f877ab4 Internal: Add new Person in the sample data to welcome Alexia a.k.a "Simone de Beauvoir"! 👋 2018-11-28 14:41:58 +01:00
Molkobain
19d4de482d N°1737 Fix tags sanitization in search criteria 2018-11-27 17:43:21 +01:00
Eric
122c3a9542 N°1779 - Fix Excel Export from a search result when object contains AttributeDashboard 2018-11-27 10:11:01 +01:00
Eric
15d9ba9906 N°917 - Add AttributeSubItem to the exclusion list of the trigger attributes 2018-11-27 09:36:50 +01:00
Eric
95a22a1a7e N°1799 - Fix TTO and TTR not recomputed when change on priority 2018-11-27 09:36:50 +01:00
Pierre Goiffon
5309aa225a Merge remote-tracking branch 'origin/support/2.5' into release/2.6
# Conflicts:
#	application/utils.inc.php
#	js/dashboard.js
#	pages/ajax.render.php
2018-11-26 18:28:42 +01:00
Eric
c0cd450c5f Add the user_id to the log_kpi instead of * 2018-11-26 15:26:25 +01:00
Denis
1b4b71cb35 Prevent a catchable fatal error when using memcache(d) as the session handler (regression introduced when fixing n°1778). 2018-11-23 16:56:07 +01:00
Pierre Goiffon
f37f3c4c22 iconv was missing (used in export) 2018-11-23 16:35:46 +01:00
Pierre Goiffon
83b3fb80d5 🎨 N°659 uniqueness constraint : more possibilities to customize check
* 2 methods called per rule instead of only one
* now we're getting the rule id as parameter
2018-11-23 16:35:46 +01:00
Pierre Goiffon
c242f90440 N°659 uniqueness constraint : fix class used in the dict key for the error message
It has to be the rule root class, not the current class
2018-11-23 09:44:42 +01:00
Pierre Goiffon
e4912ee175 🎨 PhpDoc
(cherry picked from commit d3aeb807d433e656df22f166387230b431656f58)
2018-11-22 17:40:37 +01:00
Molkobain
83e1f35f9c N°1792 Fix error message in browser console "Failed to load resource: ERR_INVALID_URL" 2018-11-22 16:59:08 +01:00
Denis Flaven
c42333d085 N°1718 Make attribute image zoomable 2018-11-22 16:06:23 +01:00
Eric
c4c0b0f630 💄 rework of the slider button 2018-11-22 15:24:30 +01:00
Eric
aec3c34eea N°1787 - Fix execution warning 2018-11-22 14:17:45 +01:00
Molkobain
c4ff20b9fb N°1789 Internal: Fix regression introduced in 6a6c069 2018-11-22 10:49:02 +01:00
Molkobain
c7ce35a877 Merge branch 'release/2.6' of https://github.com/Combodo/iTop into release/2.6 2018-11-22 10:36:27 +01:00
Denis Flaven
5ccfa24b27 iTop Newsroom implementation - painfully merged manually... 2018-11-21 19:36:51 +01:00
Molkobain
1c6a6edf5a Merge branch 'release/2.6' of https://github.com/Combodo/iTop into release/2.6 2018-11-21 18:50:29 +01:00
Pierre Goiffon
a659de9c9b Merge remote-tracking branch 'origin/support/2.5' into release/2.6
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/controllers/browsebrickcontroller.class.inc.php
2018-11-21 18:14:13 +01:00
Molkobain
29e28dedf1 Internal: Fix SASS to CSS convert for urls with query string (parameters after the "?") 2018-11-21 17:16:57 +01:00
Molkobain
0acc0b4a74 PHPDoc 2018-11-21 16:09:16 +01:00
Molkobain
c300e46480 N°1779 Fix none draggable columns in exports (Excel, CSV, ...)
- Fix AjaxWebPage load of linked stylesheets
- Remove unnecessary imports of JS/CSS files
2018-11-21 15:55:45 +01:00
Eric
0a3f076335 1784 - Fix Toolkit error on decimals 2018-11-21 15:11:19 +01:00
Eric
12d9551845 N°1706 - Fix error display when object cannot be saved 2018-11-21 14:26:50 +01:00
Vladimir Kunin
0d6c799f9c RU: name_brand iqueness rule and type phone in Model; person is mandatory error in User; all core attributes. 2018-11-21 14:22:46 +01:00
Eric
a395f5b63c N°1583 - No filter applied if only one org defined 2018-11-21 11:25:24 +01:00
Eric
2e46fa50b3 🌐 Update dictionaries with new missing entries 2018-11-21 10:25:58 +01:00
Eric
f9bb2e7a14 💚 N°1583 - user_manager User cannot create a User without Contact (fixed CI) 2018-11-20 18:03:08 +01:00
Eric
57e8b9faaf N°1583 - user_manager User cannot create a User without Contact 2018-11-20 15:43:05 +01:00
chifu1234
c737b83eb5 Update metamodel.class.php (Cherry picked from branch develop 91f1598) 2018-11-20 15:22:59 +01:00
Pierre Goiffon
5c91a1a612 🌐 RU for itop-config-mgmt : put back 2 missing keys for name_brand uniqueness rule in Model class 2018-11-20 11:53:13 +01:00
Pierre Goiffon
0c49641094 N°659 Uniqueness rules : description and error message are now picked in dictionaries entries with the $sClassName/UniquenessRule:$sUniquenessRuleId convention 2018-11-20 11:42:25 +01:00
Molkobain
99e909dae4 N°1702 Change "description" & "error_message" properties from hardcoded string to dictionary entries
New convention is:
- Class:<class>/UniquenessRule:<rule_code> for the error message
- Class:<class>/UniquenessRule:<rule_code>+ for the description of the rule itself
2018-11-19 18:18:35 +01:00
Molkobain
ecf34b47cb Code cleanup 2018-11-19 12:17:35 +01:00
Molkobain
949b4495fc Revert "Code cleanup" from commit ac89b2d 2018-11-19 12:16:22 +01:00
Eric
bd7e0174f3 🎨 Code refactor proposed by jbostoen 2018-11-19 11:31:57 +01:00
Molkobain
ac89b2dc44 Code cleanup 2018-11-19 11:28:38 +01:00
Molkobain
7e3fceb7dc N°1758 Fix read only attributes on bulk apply stimulus 2018-11-16 17:42:21 +01:00
Eric
b28ed08a85 🐎 N°1668 - Perf: avoid object update parse hidden LinkedSet attribute 2018-11-16 17:27:42 +01:00
Stephen Abello
a488c42dca N°721: ResolveFrom now only set attributes mandatory in resolved & closed states that aren't already set 2018-11-16 17:20:11 +01:00
Molkobain
fb233709e0 Search: Fix search submit after closing an empty criterion 2018-11-16 11:37:48 +01:00
Eric
9169cced20 Merge remote-tracking branch 'origin/support/2.5' into release/2.6 2018-11-16 11:33:33 +01:00
Eric
1db9708624 Merge remote-tracking branch 'origin/support/2.5' into release/2.6 2018-11-16 10:36:24 +01:00
Molkobain
52b42d0210 PHPDoc 2018-11-16 09:31:24 +01:00
Kamil Konečný
67f5f09530 Title field XSS vulnerability solution. 2018-11-16 08:59:50 +01:00
Eric
b9e0747825 N°1074 - ExternalField default label is now "key_name->label" 2018-11-15 17:33:42 +01:00
Eric
e44ed248ef N°1659 - Don't use anymore the current organization in the external key search (and n-n) 2018-11-15 17:07:31 +01:00
Stephen Abello
b0c120d7fd N°1728: Display error on setup for unsupported MySQL 8+ versions (MariaDB & Percona not affected) 2018-11-15 15:04:44 +01:00
Molkobain
177b6d1757 N°1750 Portal: Fix broken attachment preview and link on object creation (worked on object edition) 2018-11-15 14:29:52 +01:00
Eric
ff39b7bc51 N°1759 - Fix setup error on abstract class not existing 2018-11-15 10:59:28 +01:00
Eric
7401e88844 N°1408 - Dashboard Attribute (tab order) 2018-11-14 17:56:02 +01:00
Eric
b1e1e29254 N°244 - Object Copier: copy attachments 2018-11-14 17:20:40 +01:00
Eric
ffac3250af N°1408 - Dashboard Attribute (removed from "Properties" tab) 2018-11-14 17:20:40 +01:00
Eric
e9fb885d34 N°1408 - Dashboard Attribute (dictionary) 2018-11-14 17:20:40 +01:00
Eric
b87037d187 code cleanup 2018-11-14 17:20:40 +01:00
Pierre Goiffon
a97680fb04 N°931 fix TagSet regression (not displayed correctly in the console)
introduced in f9b2fda092
2018-11-14 15:37:08 +01:00
Stephen Abello
798e526010 N°1758: Fix regression on bulk transition due to filter format changes 2018-11-14 15:25:27 +01:00
Stephen Abello
d344478b48 N°1408: Fix AttributeDashboard for portal 2018-11-14 12:11:42 +01:00
Stephen Abello
b898a09c4c N°1728: Display error on setup for unsupported MySQL 8+ versions (MariaDB & Percona not affected) 2018-11-14 10:13:08 +01:00
Pierre Goiffon
17589e060a Merge branch 'support/2.5' into release/2.6 2018-11-14 10:04:29 +01:00
Molkobain
7867cc1dca N° 688 Portal: Fix enum attributes sort order in ManageBrick (eg. Ongoing requests). Now sorted by code like in the admin. console 2018-11-13 19:31:46 +01:00
Molkobain
114b18d7c8 Portal: Add HTML hooks in object forms to know object's class and ID (useful for CSS /JS hacks) 2018-11-13 16:01:19 +01:00
Eric
5baff6257b N°1754 - Linkset and CheckToWrite: Loss of links on error during creation 2018-11-13 15:37:10 +01:00
Pierre Goiffon
8264d21520 Add Nould to contributors list (PR #20) 2018-11-13 14:48:14 +01:00
Kamil Konečný
8bd10a2d11 Session id regeneration on login. 2018-11-13 14:28:08 +01:00
jbostoen
cce88f09b0 Versoin 20181113-1219
* additional strings
2018-11-13 13:40:57 +01:00
Lars Hippler
26475ad10f Add missing German translations related to 4072ed0 2018-11-13 13:40:01 +01:00
Molkobain
c1a81e4f0d N°944 Fix wrong XML generated when attribute name has been changed and redefinition of label is required 2018-11-13 11:48:27 +01:00
Vladimir Kunin
4a5cb23730 Add Russian translations for TagSetFieldData fields 2018-11-13 11:26:39 +01:00
Eric
f0e5128fb5 N°1740 - Class Query invalid Datamodel - Attention: Setup mandatory when applying this (core datamodel migration) 2018-11-13 11:18:10 +01:00
Eric
63b08b0e70 Merge remote-tracking branch 'origin/support/2.5' into release/2.6
# Conflicts:
#	sources/application/search/searchform.class.inc.php
2018-11-13 11:11:15 +01:00
Pierre Goiffon
4072ed09ac N°931 TagSet : add missing translations for TagSetFieldData fields 2018-11-13 10:55:46 +01:00
Molkobain
70137808f0 PHPDoc 2018-11-12 21:36:03 +01:00
Stephen Abello
36ade3b15c N°1727: Fix non disappearing tooltip for mandatory HTML field 2018-11-12 17:42:56 +01:00
Vladimir Kunin
6b5f32611d Russian lang for 2.6-beta 2018-11-12 11:36:43 +01:00
Pierre Goiffon
43bfe06882 Merge branch 'support/2.5' into release/2.6 2018-11-09 17:39:17 +01:00
Eric
29df8f8f92 N°1737 - Tags HTML sanitization for display of labels 2018-11-09 10:09:42 +01:00
Eric
405b1b8e6f N°917 - Trigger on object update: limit the fields proposed to the ones that can be updated 2018-11-08 14:36:15 +01:00
Eric
b050210737 N°1374 - Fix TTR deadline wrong value when re-assigning overdue tickets outside open hours 2018-11-08 08:55:48 +01:00
Eric
ed02e3522e code cleanup 2018-11-08 08:55:48 +01:00
Eric
a327b5fb5e Allow an alternative SQL generation from OQL (same as 2.3.0) 2018-11-08 08:55:48 +01:00
Pierre Goiffon
f9b2fda092 N°931 fix link for TagSet in portal lists
- they were linking to a console search
- now will filter the list using the label
2018-11-07 18:06:59 +01:00
Molkobain
0d082bf378 N°1487 Search: Removing an empty criterion doesn't refresh the results anymore 2018-11-07 14:44:37 +01:00
steffunky
d8cef95d56 N°1639: Add white-space style to CKEditor whitelist 2018-11-07 12:15:10 +01:00
steffunky
2258e8c652 N°1650: Add vertical-align style to CKEditor whitelist 2018-11-07 12:14:47 +01:00
Molkobain
5e19d338b9 N°1537 Fix "Demandeur" not updated on Change objects in french version only.
caller_id and requestor_id had the same labels which was misleading in transitions.
2018-11-07 11:36:11 +01:00
Molkobain
14ecfc4ce8 Add Jeffrey Bostoen, larhip & chifu1234 to the contributors list! Many thanks to them 🙌 2018-11-07 11:10:59 +01:00
steffunky
170d84d635 N°1061: Remove bold on "auto_select" lines in setup "ready to install" step 2018-11-07 10:08:55 +01:00
Lars Hippler
a962f6535e Add German translations regarding release 2.6
especially AttributeTag, AttributeOverview and uniqueness rules translations
2018-11-07 09:30:20 +01:00
Pierre Goiffon
a4055c4a74 Change comments to use same tickets ref syntax 2018-11-06 17:36:14 +01:00
Molkobain
9ed550ee7f N°562 Notifications: Add support for placeholders in HTML hyperlinks (regular href, mailto, ...) 2018-11-06 17:35:17 +01:00
steffunky
00bd7583f7 N°1053: Set the first day of the week on date/time picker to a language based setting
* Update moment.js
* Add locales to moment.js
* Align console & portal on the same moment.js library
2018-11-06 11:06:07 +01:00
Eric
e4619fbc0f N°1724 - Fix external key search error when an UNION is used as a filter in the datamodel 2018-11-06 10:01:36 +01:00
Eric
518d94f5f3 Fix audit bug (DBUnionSearch::AddConditionForInOperatorUsingParam() does not exist) when category is a UNION and rule flag 'Valid objects?' is 'true' and there are invalid entries in the result of the audit. 2018-11-06 08:49:35 +01:00
Eric
389a1791d7 N°828 - GetSelectFilter issue with DBUnionSearch 2018-11-06 08:49:35 +01:00
Thomas Casteleyn
54eb41b66f Fix typo Incident Impact (report PR#10)
(cherry picked from commit 676b83519a)
2018-11-05 17:00:20 +01:00
Pierre Goiffon
ede2673ba5 Fix uniqueness PHPUnit tests - 2nd commit (forgot to change annotation) 2018-11-05 16:02:15 +01:00
Pierre Goiffon
dd50c99ec4 Fix uniqueness PHPUnit tests 2018-11-05 15:52:35 +01:00
Pierre Goiffon
357555e507 AttributeImage : add css classes to be able to style 2018-11-05 11:54:35 +01:00
Pierre Goiffon
1f6a51b31f Some refactoring in AttributeImage URL generation 2018-11-05 11:54:35 +01:00
Molkobain
6d4cf71197 N°567 Enhance WikiText URLs synthax (usage of IDs and labels supported)
[[<objClass>:<objName|objId>|<label>]]
 <label> is optional

 Examples:
 - [[Server:db1.tnut.com]]
 - [[Server:123]]
 - [[Server:db1.tnut.com|Production server]]
 - [[Server:123|Production server]]
2018-11-05 11:29:06 +01:00
steffunky
87f898d29d Fix a typo in Dutch translations 2018-11-05 11:23:38 +01:00
jbostoen
d5576522ae Version 20181102-1455
* quick first translation
2018-11-05 11:03:52 +01:00
Molkobain
6addd9acec Update dictionaries for all languages to iTop 2.6 2018-11-02 11:52:33 +01:00
Molkobain
9b5942bf4e Remove duplicate entries in english dictionaries 2018-11-02 11:40:05 +01:00
Molkobain
9faa4e4cbd Update english dictionaries' file comment 2018-11-02 11:02:34 +01:00
Molkobain
8b80bd71e7 N°1714 Tags: Add default search criteria 2018-11-02 09:42:54 +01:00
Molkobain
8989206461 (Cherry pick 5f7e8c9 from branch support/2.5) N°1715 Search: Add default criteria for FAQ, FAQCategory & KnownError classes. 2018-11-02 09:04:02 +01:00
Molkobain
89a21c8e44 Code cleanup
Remove ending "?>" from dictionary files before updating all languages.
2018-11-01 15:35:49 +01:00
Molkobain
a7ec6e5ca0 Internal: Remove unused dictionary file. 2018-11-01 15:35:49 +01:00
steffunky
a08675352a N°1687: Fix typos in German translation (thanks to ITOMIG) 2018-10-31 12:04:13 +01:00
steffunky
7158b1f787 N°1504: Add a translation for the top left "Page" string on PDF export 2018-10-31 10:21:43 +01:00
Eric
3925ad252f Fixed execution warning in search (not visible in production) 2018-10-31 09:00:15 +01:00
Pierre Goiffon
f3fd47a792 N°659 PHPUnit to test uniqueness rule validity check... and debug tested method (woops ! unit test will save the world ;) ) 2018-10-30 18:01:35 +01:00
steffunky
af92f58265 N°1704: Upgrade portal datetime picker widget & moment.js for jQuery 3 compatibility 2018-10-30 15:51:19 +01:00
Pierre Goiffon
81b5f18579 N°1707 fix mandatory TagSet were not highlighted in the console 2018-10-30 15:06:47 +01:00
Molkobain
184c0cb84b Fix visual glitch in icon select widget 2018-10-30 10:53:50 +01:00
Eric
c892862822 N°1202 - Fix History on Decimal. No diff when modify only trailing 0s. 2018-10-30 09:13:52 +01:00
Pierre Goiffon
b61d1feec4 Update README after 2.6.0-beta release 2018-10-29 16:49:22 +01:00
Molkobain
a73ff16642 Merge pull request #7 from Super-Visions/feature/dutch-dict
Another update for Dutch dictionary

(cherry picked from commit 0e2c8ff220)
2018-10-29 12:11:32 +01:00
Pierre Goiffon
7763983ef4 N°1408 fix Organization.overview EN and FR dict keys 2018-10-24 14:58:24 +02:00
Pierre Goiffon
35f7fe98ae update versions 2018-10-24 10:35:11 +02:00
Pierre Goiffon
533acbde8d Update README 2018-10-24 10:35:00 +02:00
Pierre Goiffon
d72da422de PHPDoc 2018-10-24 10:35:00 +02:00
Pierre Goiffon
891608f812 N°1408 Organization.overview dashboard is now editable 2018-10-24 10:00:52 +02:00
Pierre Goiffon
e66da81a20 N°1408 new AttributeDashboard : Organization.overview 2018-10-23 18:37:39 +02:00
Eric
cae0cd00c2 DBSearch: Fix serialization rework (missing internal parameters) 2018-10-23 17:23:43 +02:00
Eric
32fc87023c AttributeDashboard support Dashlet Badge in XML files (fix the <class> collision) 2018-10-23 16:14:30 +02:00
Molkobain
0021482c1b Internal jQuery 3 migration fix 2018-10-23 15:43:31 +02:00
Molkobain
276427e3df PHPDoc 2018-10-23 15:43:31 +02:00
Eric
7b49bde3ad Dashlet "Header with statistics" query parameter changed to a text area 2018-10-23 15:15:35 +02:00
Eric
a94d940bd3 DBSearch: Fix serialization rework in bulk modify 2018-10-23 15:08:40 +02:00
Eric
313c3c6fc3 Code cleanup 2018-10-23 15:08:40 +02:00
Eric
de86f71c90 Fix error on Tag Admin screen when no TagSet attribute is defined 2018-10-23 15:08:40 +02:00
Eric
098b0531d8 Remove the generation of an additional class TagFieldData for AttributeDashboard 2018-10-23 15:08:40 +02:00
Pierre Goiffon
34552214bf N°1703 The dates are no longer reset when inserting a Ticket
Could cause problems when importing using REST API or using form prefill
2018-10-23 14:17:03 +02:00
Eric
d61a8ba160 DBSearch: Fix serialization rework 2018-10-22 18:59:38 +02:00
Pierre Goiffon
84ebca58ac N°931 TagSet : FAQ.domains is now displayes in the portal (FAQ list and details form) 2018-10-22 18:39:31 +02:00
Pierre Goiffon
def4c54d26 N°659 Console & Portal now we have same object save error messages and sessionmessages
* create new CoreCannotSaveException
* throw this exception in DBInsertNoReload/DBUpdate if CheckToWrite fails
* console : change UI.php code to catch this exception instead of calling CheckToWrite itself (was called twice :(( )
* portal : specific catch for the new exception
* portal : get and displays session messages
2018-10-22 17:11:08 +02:00
Pierre Goiffon
027b0fcff7 N°659 use header message to display uniqueness errors on object creation / modification 2018-10-22 17:11:08 +02:00
Pierre Goiffon
fc0cb44a84 Reformat iTopWebPage 2018-10-22 17:11:08 +02:00
Pierre Goiffon
574d72b0e7 N°659 Add uniqueness checks
* modify compiler to save the new rules
* add check on object save (\DBObject::DoCheckUniqueness)
* default model : add uniqueness rules on Brand, Model, Person
2018-10-22 17:11:07 +02:00
Pierre Goiffon
cd5e1afb2b DBSearch: Fix serialization rework, use htmlentities 2018-10-22 15:51:28 +02:00
Eric
0298d6bc19 DBSearch: Fix serialization rework 2018-10-22 15:37:32 +02:00
Eric
60d2fd4f7e N°1408 - Fix specific dashboard display without context 2018-10-22 12:26:56 +02:00
Thomas Casteleyn
5faa0ffe08 Update Dutch dictionary for attachments (#6)
(Thanks to @Hipska !)
2018-10-20 00:20:31 +02:00
Eric
705ce02580 Setup steps duration 2018-10-19 17:18:39 +02:00
Eric
78c674a989 DBSearch serialization rework 2018-10-19 16:02:07 +02:00
Eric
84d9be3293 Strengthen the SQL creation from OQL 2018-10-19 15:58:50 +02:00
Thomas Casteleyn
30c622052e Fix integer validation in dashlet forms
Mandatory integer fields were valid even when empty. (Thanks @Hipska for the fix!)
2018-10-19 15:06:56 +02:00
Eric
ada56da63e Fix multiple TagSet creation in the same object 2018-10-19 11:16:34 +02:00
Pierre Goiffon
21c5638e1e Merge branch 'support/2.5' into develop
# Conflicts:
#	application/applicationextension.inc.php
#	application/dashlet.class.inc.php
#	application/itopwebpage.class.inc.php
#	application/loginwebpage.class.inc.php
#	application/menunode.class.inc.php
#	application/nicewebpage.class.inc.php
#	application/query.class.inc.php
#	application/utils.inc.php
#	application/webpage.class.inc.php
#	application/wizardhelper.class.inc.php
#	core/action.class.inc.php
#	core/attributedef.class.inc.php
#	core/cmdbchangeop.class.inc.php
#	core/cmdbobject.class.inc.php
#	core/cmdbsource.class.inc.php
#	core/config.class.inc.php
#	core/dbobject.class.php
#	core/dbobjectsearch.class.php
#	core/dbobjectset.class.php
#	core/kpi.class.inc.php
#	core/modelreflection.class.inc.php
#	core/modulehandler.class.inc.php
#	core/oql/expression.class.inc.php
#	core/oql/oql-lexer.plex
#	core/oql/oql-parser.y
#	core/oql/oqlquery.class.inc.php
#	core/ormpassword.class.inc.php
#	core/sqlobjectquery.class.inc.php
#	core/sqlquery.class.inc.php
#	core/sqlunionquery.class.inc.php
#	core/trigger.class.inc.php
#	core/userrights.class.inc.php
#	css/light-grey.scss
#	datamodels/2.x/itop-backup/status.php
#	datamodels/2.x/itop-config/config.php
#	datamodels/2.x/itop-full-itil/datamodel.itop-full-itil.xml
#	datamodels/2.x/itop-knownerror-mgmt/en.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/portal/src/controllers/abstractcontroller.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/controllers/brickcontroller.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/controllers/browsebrickcontroller.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/controllers/createbrickcontroller.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/controllers/defaultcontroller.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/controllers/managebrickcontroller.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/controllers/objectcontroller.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/controllers/userprofilebrickcontroller.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/routers/abstractrouter.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/routers/browsebrickrouter.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/routers/createbrickrouter.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/routers/defaultrouter.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/routers/managebrickrouter.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/routers/objectrouter.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/routers/userprofilebrickrouter.class.inc.php
#	datamodels/2.x/itop-portal/datamodel.itop-portal.xml
#	datamodels/2.x/itop-tickets/en.dict.itop-tickets.php
#	dictionaries/da.dictionary.itop.core.php
#	dictionaries/da.dictionary.itop.ui.php
#	dictionaries/en.dictionary.itop.core.php
#	dictionaries/en.dictionary.itop.ui.php
#	dictionaries/fr.dictionary.itop.core.php
#	dictionaries/hu.dictionary.itop.core.php
#	dictionaries/it.dictionary.itop.core.php
#	dictionaries/nl.dictionary.itop.core.php
#	dictionaries/pt_br.dictionary.itop.core.php
#	dictionaries/tr.dictionary.itop.core.php
#	dictionaries/zh.dictionary.itop.core.php
#	js/forms-json-utils.js
#	js/jquery-migrate-1.4.1.min.js
#	js/jquery.layout.js
#	js/jquery.layout.min.js
#	js/jquery.tablesorter.pager.js
#	js/linkswidget.js
#	js/searchformforeignkeys.js
#	js/utils.js
#	js/wizardhelper.js
#	pages/schema.php
#	readme.txt
#	setup/ajax.dataloader.php
#	setup/xmldataloader.class.inc.php
#	sources/renderer/bootstrap/fieldrenderer/bssubformfieldrenderer.class.inc.php
#	sources/renderer/console/consoleformrenderer.class.inc.php
#	sources/renderer/console/fieldrenderer/consoleselectobjectfieldrenderer.class.inc.php
#	sources/renderer/console/fieldrenderer/consolesimplefieldrenderer.class.inc.php
#	sources/renderer/console/fieldrenderer/consolesubformfieldrenderer.class.inc.php
#	test/ItopDataTestCase.php
#	test/core/ormLinkSetTest.php
#	webservices/backoffice.dataloader.php
#	webservices/rest.php
2018-10-18 14:34:22 +02:00
bruno DA SILVA
02da84b4cf jenkins conf in .jenkins
- clearer dead ends
2018-10-16 10:57:17 +02:00
Eric
ebb5ede613 N°1408 - Asynchronous load of dashboard tab 2018-10-15 16:37:19 +02:00
Eric
db4b8b2f43 N°1408 - Dashboard selector 2018-10-15 16:37:18 +02:00
Eric
c17f7caa29 N°1408 - Dashboard selector 2018-10-15 16:37:18 +02:00
Eric
d7df975971 cleanup code 2018-10-15 16:37:18 +02:00
Eric
f3f70d6296 N°1408 - Dashboard Printer friendly page + user edit rights 2018-10-15 16:37:18 +02:00
Eric
256d4e2cb3 N°1408 - Revert dashboard field in datamodel 2018-10-15 16:37:18 +02:00
Eric
fb31c9006a N°1408 - Fix import dashboard 2018-10-15 16:37:18 +02:00
Eric
bf000e6a89 N°1408 - DBSearch Serialize refactor (json format) 2018-10-15 16:37:17 +02:00
Eric
94b4f10277 N°1408 - Fix Object print page for attributes dashboard 2018-10-15 16:37:17 +02:00
Eric
15671f6dd4 N°1408 - Use dashboards only in read-only mode 2018-10-15 16:37:17 +02:00
Eric
2460d2112f N°1408 - Auto-refresh dashboards 2018-10-15 16:37:17 +02:00
Eric
709badd0f7 N°1408 - Support contextual parameters in attributes dashboard 2018-10-15 16:37:17 +02:00
Eric
99153d02ee N°1408 - Support contextual parameters in attributes dashboard 2018-10-15 16:37:17 +02:00
Eric
4347b2c5ff N°1408 - Support contextual parameters in attributes dashboard 2018-10-15 16:37:17 +02:00
Eric
c13b6ea13a N°1408 - AttributeDashboard first implementation 2018-10-15 16:37:17 +02:00
Eric
6e5d4834f1 N°1408 - Move of the dashboard menu into the dashboard 2018-10-15 16:37:16 +02:00
Eric
9f489d8a59 cleanup code 2018-10-15 16:37:16 +02:00
Eric
c9be1a8e71 cleanup code 2018-10-15 16:37:16 +02:00
Pierre Goiffon
c1e2f35c96 N°931 TagSet : fix remove/add of a partial value in bulk edit 2018-10-15 11:45:29 +02:00
Pierre Goiffon
462148a12f N°931 TagSet POC : fix markup for preview 2018-10-15 11:35:59 +02:00
Pierre Goiffon
7e4edc6e3a Forms mandatory check : switch to first tab with error
(cherry picked from commit 80e6801db3e7770997e705d087e57b682e8df092)
2018-10-15 09:45:08 +02:00
bruno DA SILVA
452e7cce01 jenkins: default iTop isntance conf 2018-10-12 17:18:49 +02:00
bruno DA SILVA
cb7b5dfffb jenkins: default iTop isntance conf 2018-10-12 17:11:27 +02:00
steffunky
0a34fb7a7a N°1590: Advanced search: fixed a bug when selecting foreign keys would not add items (selectionMode is mandatory) 2018-10-12 11:06:31 +02:00
Pierre Goiffon
460337954b CSS : remove duplicate declaration
(cherry picked from commit 7f99322da8896fa5120077381dc1a7fc9ff1f828)
2018-10-12 10:47:59 +02:00
steffunky
74a8c3f5bd N°1555: fixed a case where AttributeExternal field was pointing anything else than an ExternalKey and its label wouldn't show up on search criteria, remove duplicate catch 2018-10-09 18:27:36 +02:00
steffunky
3347d32f2a N°1555: fixed a case where AttributeExternal field was pointing anything else than an ExternalKey and its label wouldn't show up on search criteria 2018-10-09 18:25:48 +02:00
Pierre Goiffon
4c2be6b2c5 N°931 AttributeTagSet widget js wrapper : partial values can now be added with just a click 2018-10-09 09:04:21 +02:00
Pierre Goiffon
860bb6d615 N°931 AttributeTagSet widget js wrapper : fix remaining console.debug call, and fix indentations 2018-10-08 17:56:34 +02:00
Pierre Goiffon
950ffcde2b N°931 remove remaining references to Ticket.tagfield
(it was the old AttributeTagSet test, now we have FAQ.domains)
2018-10-08 15:45:50 +02:00
Pierre Goiffon
0580c71749 N°931 fix requirements for AttributeTagSet PhpUnit tests
* itop-kown-error-mgmt module
* creates a FAQCategory if needed
2018-10-08 11:58:45 +02:00
Pierre Goiffon
c66b0bea41 N°931 fix PhpUnit tests for TagSets (default datamodel doesn't have anymore Ticket.tagfield, but FAQ.domains) 2018-10-05 17:55:40 +02:00
Pierre Goiffon
7281bd4a1a N°931 default datamodel : remove Ticket.tagfield and add AttributeTagSet FAQ.domains 2018-10-05 17:37:23 +02:00
Pierre Goiffon
c9d73fc0c8 N°931 fix TagSet not saved on object creation 2018-10-05 17:12:58 +02:00
Pierre Goiffon
8720ac2b08 N°931 Fix import TagSet failing with multiple code values (using CLI with no_localize=1) 2018-10-05 09:18:23 +02:00
Pierre Goiffon
33e8b6a64c Switch back files from CrLf to Lf 2018-10-04 17:43:03 +02:00
Pierre Goiffon
d7cf339ab5 N°931 TagSet : adding widget JQuery plugin (Selectize.js) to the licences 2018-10-04 15:30:01 +02:00
Pierre Goiffon
714fada9f0 N°931 Fix TagSet export : with format=spreadsheet codes where always returned (now uses codes or labels according to what was set) 2018-10-04 15:00:33 +02:00
Pierre Goiffon
d36fd6a47d N°931 export localize option label : change FR value 2018-10-04 14:57:12 +02:00
Stephen
c0d83e96d8 Retrofit 2.5.1 fixes into develop (11308dc7, 1e0d654, 7fddd6ac, 12e9e453, cbe749af, 10b7fa60, c5f3598f, 396fc2cd) 2018-10-04 11:12:16 +02:00
Pierre Goiffon
3da6251cc7 N°931 change export localize option label
as attcodes, enum and tagset are concerned then the label don't contains anymore the field types impacted
also, we rephrased it to remove the negation and be clearer about the effect
2018-10-04 09:31:49 +02:00
Pierre Goiffon
1bf39aa092 N°931 fix XML export (node was always empty) 2018-10-03 10:53:34 +02:00
Pierre Goiffon
3387d8fdd2 Fix XLSX export failing on PHP 7.1 on systems without "/tmp" path
Since PHP 7.1 tempnam() throws a notice if the dir parameter can't be used : bug #69489 in http://php.net/ChangeLog-7.php#7.1.0
2018-10-02 17:20:12 +02:00
Pierre Goiffon
9f926fccd8 N°931 TagSet admin updates :
* when modifying tag, code field is read only if tag is used by at least one object
* "tag usage" tab : show only parent class
2018-10-02 16:36:49 +02:00
Pierre Goiffon
e9a77c798e Fix Contact data sample (reordering and remove duplicate id) 2018-10-02 15:01:30 +02:00
Pierre Goiffon
29691a09fe .idea : use Combodo presets for code style and inspections 2018-10-02 09:11:56 +02:00
Eric
cb73c2a3f9 N°931: Unit tests for mysql stop words 2018-10-02 08:56:13 +02:00
Pierre Goiffon
713a41909b N°931 TagSet history : CSS and scss were out of sync 2018-10-01 19:02:39 +02:00
Steffunky
9972e253d5 enable jQuery migrate warning only in dev. environment 2018-10-01 16:17:41 +02:00
Eric
55309be9a1 N°917: AttributeQueryAttCodeSet support in XML definition 2018-10-01 15:52:36 +02:00
Eric
fd75f3af28 cleanup (fix error) 2018-10-01 15:36:47 +02:00
Eric
af7e5eb03e N°917: Display attcodes instead of labels in triggers and allow search on AttributeSet 2018-10-01 15:23:29 +02:00
Eric
b97ce7a25f N°917: Fix Ajax calls on objects containing AttributeSet 2018-10-01 15:22:31 +02:00
Molkobain
89d617c152 N°931 Better tooltips. 2018-10-01 12:30:16 +02:00
Molkobain
4595c565cc N°931 Portal: Start working on UI. 2018-10-01 09:27:07 +02:00
Eric
8f55d4054a N°917: Fix Search criterion when TagSet = 'tag1 tag2...' 2018-09-28 17:33:05 +02:00
Pierre Goiffon
2e255b10a7 Finish b931 2018-09-28 16:42:49 +02:00
Molkobain
cf9c1b52ed N°931 Portal integration due to AttributeSet refactoring. UI still to be done. 2018-09-28 16:34:52 +02:00
Eric
d3b3c44cbd N°917: Fix unit tests 2018-09-28 16:32:15 +02:00
Molkobain
edb25b6dca N°931 Internal: Move AttributeTagSet class. 2018-09-28 15:49:00 +02:00
Molkobain
4bf748641d N°931 UI: Display a regular tooltip instead of a qtip when item has no description. 2018-09-28 15:47:22 +02:00
Eric
16a6b70708 N°917: Fix unit tests 2018-09-28 15:36:13 +02:00
Eric
40355cb2d0 N°917: Fix refresh edit screen, avoid storing bad values in db 2018-09-28 12:08:35 +02:00
Eric
b52aaaadf2 N°917: Remove AttributeSet search 2018-09-28 12:08:34 +02:00
Pierre Goiffon
f55c0aa1c9 N°917: Fix TagSet search 2018-09-28 12:08:34 +02:00
Molkobain
333c51b0f9 N°931 Fix XSS in console. 2018-09-28 12:08:14 +02:00
Molkobain
46dee2919e Taxons: Fix item's remove box position. 2018-09-28 11:17:20 +02:00
Pierre Goiffon
799618dee7 N°931 AttributeSet widget : add charset parameter to htmlentities 2018-09-28 10:33:17 +02:00
Pierre Goiffon
96aaa0d8e1 N°931 TagSet in history : display "added" in bold and "removed" as striked 2018-09-28 10:33:17 +02:00
Molkobain
5556e3efec Taxons: Refactor of CSS for the console. 2018-09-28 09:40:46 +02:00
Pierre Goiffon
d8b2d4435a N°931 AttributeSet widget : keep initial removed values 2018-09-28 09:26:23 +02:00
Pierre Goiffon
a4315b4789 N°931 AttributeSet widget : better fix for crash when having a ' in the label
use htmlentities for input hidden value instead of a textarea display:none
2018-09-28 09:02:35 +02:00
Pierre Goiffon
3e8dd61607 N°931 AttributeSet widget : fix crash when having a ' in the label 2018-09-27 18:31:58 +02:00
Pierre Goiffon
1d28a67d21 N°931 TagSet widget enabling / disabling in bulk edit
- rename ToogleField -> ToggleField
- for the set input, use the same ID as other fields (used by ToggleField method)
- disable the set widget if needed on opening
- binds the update event to enable / disable at will
2018-09-27 17:29:36 +02:00
Eric
3a551e9cec N°917: Restrict "Attribute...AttCodeSet" to existing attributes on database write 2018-09-27 17:14:26 +02:00
Eric
cf6693a534 N°931: Fix tag css class 2018-09-27 16:47:14 +02:00
Eric
8c5c275952 N°931: Fix compiler (AttributeSet is abstract) 2018-09-27 16:47:14 +02:00
Molkobain
5fa4b4cb88 Taxons: Rework of CSS & markup in the console (portal still to come). 2018-09-27 16:45:04 +02:00
Eric
58525f247e N°917: AttributeDef CSS class 2018-09-27 15:57:08 +02:00
Eric
d78f19525e N°917: AttributeDef CSS class 2018-09-27 15:40:00 +02:00
Eric
5559c8205f Merge branch 'feature/AttributeSet' into feature/b931
# Conflicts:
#	core/attributedef.class.inc.php
2018-09-27 15:29:27 +02:00
Eric
2a6a97526d N°917: AttributeDef CSS class 2018-09-27 15:25:51 +02:00
bruno DA SILVA
1adea8f014 jenkins: slack notif on build fixed 2018-09-27 14:40:17 +02:00
Eric
29177ec86b N°917: New AttributeQueryAttCodeSet and enhanced Query phrase book 2018-09-27 14:37:50 +02:00
Pierre Goiffon
741e88e1f0 N°931 AttributeSet widget : little naming fixes 2018-09-27 12:05:45 +02:00
Pierre Goiffon
66bddc5730 N°931 AttributeSet CSS : fix remove box disappearing with long tag labels 2018-09-27 12:05:17 +02:00
Pierre Goiffon
6eaa7c0530 N°931 AttributeSet more generic CSS classes :
* more generic names
* use same classes in both view and edit
* in the scss file, 3 sections : attribute-set edition (with other field types styles), generic Selectize overridings, and styles for attribute-set items visualisation
2018-09-27 11:47:28 +02:00
Eric
b257fae03e N°917: Fix Unit tests 2018-09-27 11:44:28 +02:00
Eric
2d24324dea N°917: Fix AttributeClassState 2018-09-27 11:36:41 +02:00
Eric
ec597f697a N°917: Adapt to generic widget for set 2018-09-27 11:04:20 +02:00
Pierre Goiffon
3bed62a473 N°931 AttributeSet POC : now testing both widget for edition and CSS for viewing 2018-09-27 10:50:12 +02:00
Eric
b5f7227ecd N°917: Fix AttributeClassState 2018-09-27 10:49:33 +02:00
Eric
615adf8281 N°917: Trigger on delete 2018-09-27 10:49:32 +02:00
Eric
cb7bb3242a N°917: New AttributeClassState for triggers on state (entering or leaving a state) 2018-09-27 10:49:30 +02:00
Eric
4b08eea998 N°931: Fix Label protection for tags 2018-09-27 10:49:29 +02:00
Eric
01551942b3 N°917: Fix Bulk modify for AttCodeSet 2018-09-27 09:11:03 +02:00
Eric
720d334053 N°917: AttributeClassAttCodeSet created 2018-09-27 09:11:02 +02:00
Molkobain
9397d1ac2e Taxons: Add support in the portal (UI still to be done). 2018-09-26 17:13:55 +02:00
Pierre Goiffon
1530bb89fe N°931 TagSet widget and its POC are now more generic (to be used in all AttributeSet hierarchy) 2018-09-26 14:26:26 +02:00
Thomas Casteleyn
f09683949d Add Dutch dictionary for incident management (#3)
Thanks to @Hipska!
2018-09-25 17:29:05 +02:00
Pierre Goiffon
e1f96974bb Merge branch 'develop' into feature/b931 2018-09-25 14:52:59 +02:00
Pierre Goiffon
1fc261937f Add toolkit to gitignore 2018-09-25 14:52:38 +02:00
Eric
a94211d3d3 Merge remote-tracking branch 'origin/develop' into feature/b931
# Conflicts:
#	.idea/inspectionProfiles/Combodo.xml
#	application/cmdbabstract.class.inc.php
#	core/attributedef.class.inc.php
#	css/light-grey.css
#	css/light-grey.scss
2018-09-25 12:03:44 +02:00
Eric
534f6b6a54 Merge branch 'feature/b917' into develop 2018-09-25 11:55:11 +02:00
Eric
901764bf70 N°917 - Add new trigger on object update 2018-09-25 11:29:15 +02:00
Eric
e67d6e8a80 N°917 - Add new trigger on object update 2018-09-25 10:38:01 +02:00
Molkobain
16476f736a Taxons: Add specific CSS class to tags so they can be easily stylized. 2018-09-24 16:38:11 +02:00
Eric
e9ecd89cda Code cleanup 2018-09-24 13:57:45 +02:00
Eric
55036511ab longer lines for PHP code format 2018-09-24 11:48:15 +02:00
Molkobain
3cf5d31f5d Start working on tags integration in the end-users portal. 2018-09-21 17:25:45 +02:00
Molkobain
84ae36cf1a Merge branch 'feature/b931' of https://github.com/Combodo/iTop into feature/b931 2018-09-21 17:24:56 +02:00
Molkobain
00515ac14a Refactor GetJSONForWidget helper (moved from cmdbAbstract to AttributeTagSet) 2018-09-21 17:24:34 +02:00
Eric
245612d0eb N°931: Tags dictionary and validation pattern 2018-09-21 16:56:53 +02:00
Eric
ae6e3b4f17 N°931: Tags Template format 2018-09-21 16:00:18 +02:00
Eric
12a3f1c36c N°931: Fix integrity controls (reserved word) 2018-09-21 15:59:54 +02:00
Eric
1af1e92b60 N°931: Fix error reporting for data synchro 2018-09-20 17:22:46 +02:00
Eric
a1ad7a5def N°931: Fix Search when screen is refreshed (number of selected criteria for tags) 2018-09-20 15:57:37 +02:00
Eric
398d1aa820 N°931: Limit the number of tags in the widget 2018-09-20 15:15:25 +02:00
Molkobain
38278f3432 Taxons: UI WIP. 2018-09-20 14:41:56 +02:00
Molkobain
baf4662ed8 Taxons: UI WIP. 2018-09-20 10:53:07 +02:00
Eric
cf07c9f1a2 N°931: Fix search when referencing a non existing tag 2018-09-20 10:31:02 +02:00
Eric
446bd0ad1f N°931: Rename Tag columns in database 2018-09-20 09:58:35 +02:00
Eric
3d5e46faac N°931: Fix Search when screen is refreshed 2018-09-19 17:21:05 +02:00
bruno DA SILVA
b8fba8997a Merge branch 'feature/jenkinsfile' into develop 2018-09-19 16:33:05 +02:00
Eric
a07c81d0d9 N°931: TagSetData table name 2018-09-19 16:18:09 +02:00
Eric
0252d091ca N°931: TagSetData table name 2018-09-19 16:07:10 +02:00
Molkobain
18d9ada58d N°1636 Fix concurrent lock not released on failed transition (in the console) 2018-09-19 15:56:50 +02:00
Molkobain
055d2cc62c Taxons: UI WIP. 2018-09-19 11:00:33 +02:00
Eric
e15c5c58d8 N°931: HTML 2018-09-19 10:19:16 +02:00
Eric
b734aca4d0 N°931: Fix History 2018-09-19 09:52:06 +02:00
Eric
e495b36dfb N°931: Add link to read-only HTML representation of tags 2018-09-18 17:34:03 +02:00
Eric
cdcfe03d67 N°931: Tag max number and max length per class and field (defined in xml) 2018-09-18 15:33:00 +02:00
Eric
2c6dc20046 N°931: "Empty" message added to the tag usage tab 2018-09-18 14:57:56 +02:00
Eric
a8c3b7ac2e N°931: Reformat 2018-09-18 14:16:30 +02:00
Eric
98572c6efb N°931: Tags admin menu (typo) 2018-09-18 13:57:46 +02:00
Eric
70840c53aa N°931: TagSetFieldData Fix bad name extract 2018-09-18 12:17:27 +02:00
Eric
9fbd27f3a8 N°931: Import/Export separator defined in configuration 2018-09-18 12:04:08 +02:00
Eric
c3c1897258 N°931: Tags data class display name 2018-09-18 11:38:34 +02:00
Eric
5403219746 N°931: Tags admin menu 2018-09-18 10:28:39 +02:00
Molkobain
fb08903a8a UI: Improve selected rows background color in tables. 2018-09-17 17:09:12 +02:00
Molkobain
416005654e Merge branch 'develop' of https://github.com/Combodo/iTop into develop 2018-09-17 17:07:52 +02:00
Eric
1debf77ab4 N°931: DBSearch AddCondition updated for tags 2018-09-17 12:46:46 +02:00
Eric
f1a8bb08da N°931: Fix integrity controls (tag code length) 2018-09-17 12:03:17 +02:00
Eric
7edff12bbf Merge remote-tracking branch 'origin/support/2.5' into develop
# Conflicts:
#	application/startup.inc.php
2018-09-17 11:47:47 +02:00
Molkobain
34d601f49c UI: Make caselog entries' header font a bit more tinier in enhanced portal. 2018-09-16 21:39:09 +02:00
Pierre Goiffon
ec38473d88 N°931 TagSet widget : disable/enable methods for mass edit (need to be plugged, but where ?!??) 2018-09-14 17:57:45 +02:00
Pierre Goiffon
875c77ae65 N°931 TagSet widget : fix empty widget on creation form
Now displays a placeholder
The + icon can't be used as is cause it's inserted with ::after, and without tags it can't be right aligned
2018-09-14 17:43:38 +02:00
Eric
75e24f9f79 Changed table selection background color 2018-09-14 17:26:06 +02:00
Eric
16a4662129 warnings reworked 2018-09-14 17:08:25 +02:00
Eric
1eda1eb9de N°931: cleanup code 2018-09-14 17:08:05 +02:00
Eric
d981fd35ef N°931: TagSet Fix CSV import 2018-09-14 14:55:41 +02:00
Pierre Goiffon
6f1dc44932 N°931 TagSet widget : fix added / removed not updating correctly
* move integrity checks from refresh method to add/remove callbacks
* use onInitialize callback for partial values instead of render
2018-09-14 14:50:24 +02:00
Pierre Goiffon
95b929dd27 N°931 TagSet widget POC HTML page 2018-09-13 18:15:58 +02:00
Eric
0c75b98f48 N°931: TagSet Fix Bulk update 2018-09-13 17:49:25 +02:00
Eric
ecc5a0bf8a N°931: TagSet Fix 'New...' object form 2018-09-13 17:06:58 +02:00
Pierre Goiffon
d9315bec84 N°931 integrate TagSet widget 2018-09-13 15:57:32 +02:00
Eric
d1ee7f4353 N°931: TagSet search (manage undefined values) unit tests 2018-09-12 18:17:17 +02:00
Eric
979095337b code cleanup 2018-09-12 15:35:18 +02:00
Eric
34b528b1f4 N°931: TagSet search unit tests 2018-09-12 15:34:37 +02:00
Eric
5ea056a3fc code cleanup 2018-09-12 15:14:27 +02:00
Eric
2ba31244c2 N°931: TagSet search form integration 2018-09-12 15:03:35 +02:00
Molkobain
3ea16694b4 Fix dictionary entry on linkedset tab that was adding a 's' on object class. (eg. "Add IP Addresss..." now becomes "Add IP Address objects...") 2018-09-12 11:02:02 +02:00
Molkobain
35e872310d Update dictionaries for ActionEmail class. (Missing uppercase on first letters) 2018-09-12 09:58:04 +02:00
Pierre Goiffon
b86b24d444 N°931 JS/CSS files for TagSet widget 2018-09-12 09:03:04 +02:00
Molkobain
45e1a6ffd6 PHP Documentation 2018-09-11 21:45:19 +02:00
Pierre Goiffon
c87f001956 Little modifications on PHPStorm code style & inspections 2018-09-11 18:44:44 +02:00
Eric
1fc3b3a4ed N°931: Integrity controls + unit tests 2018-09-11 17:27:27 +02:00
Eric
c706a2d77c N°931: Integrity controls 2018-09-11 12:40:46 +02:00
Eric
8577fc6701 N°931: Integrity controls 2018-09-11 12:22:50 +02:00
Eric
ead3067d49 Merge remote-tracking branch 'origin/support/2.5' into develop 2018-09-11 09:59:47 +02:00
Eric
4772b8c5bb PHPStorm config 2018-09-11 09:54:56 +02:00
Eric
6817cfbeea N°931: Integrity controls 2018-09-07 17:50:40 +02:00
bruno DA SILVA
f851238eab Merge branch 'develop' into feature/jenkinsfile 2018-09-07 15:11:14 +02:00
bruno DA SILVA
79157c465a [WIP] jenkinsfile integration 2018-09-07 14:52:22 +02:00
Pierre Goiffon
64438f6b6d idea : change XML indent 2018-09-06 18:35:51 +02:00
Pierre Goiffon
f2b914a4c5 N°931 change CSS for displaying TagSet datas 2018-09-06 18:34:23 +02:00
Pierre Goiffon
da71004898 N°931 update TagSet DataModel declaration parameters 2018-09-06 17:10:40 +02:00
Eric
e9019b294a Merge branch 'develop' into feature/jenkinsfile 2018-09-06 15:51:31 +02:00
Eric
e2c3ea22e4 N°931: Start bulk modify implementation 2018-09-06 15:42:10 +02:00
Eric
c7f3f20229 New Unit tests 2018-09-06 14:42:00 +02:00
bruno DA SILVA
46dc024335 Merge branch 'develop' into feature/jenkinsfile 2018-09-06 14:31:59 +02:00
Eric
34c68cfef0 Fix regression introduced in bugfix 1554 and detected by unit tests 2018-09-06 14:20:42 +02:00
Molkobain
d6a564a38b Add Thomas Casteleyn to the contributors list! :)
(I forgot him on the initial commit)
2018-09-06 11:32:38 +02:00
Pierre Goiffon
31c69088ca New contributor : Dennis Lanister (PR #1) 2018-09-06 10:14:24 +02:00
Dennis Lassiter
49bb8fd515 Fixed bug that caused memory_limit=-1 to lead to 'not enough memory' … (#1)
* Fixed bug that caused memory_limit=-1 to lead to 'not enough memory' error
* Added Unit Test to Memory Limit Check
2018-09-06 10:04:28 +02:00
Eric
b8f8a0d455 N°1585: Fix ajax request uri too long on auto-complete 2018-09-05 15:10:11 +02:00
Molkobain
374e34a8b8 Merge branch 'develop' of https://github.com/Combodo/iTop into develop 2018-09-05 15:07:34 +02:00
Molkobain
7ef1d72314 Add a "Contributors" in the READMME.md to thank all the contributors! :) 2018-09-05 15:06:56 +02:00
bruno DA SILVA
5666a3d74c Merge branch 'develop' into feature/jenkinsfile 2018-09-05 10:12:13 +02:00
bruno DA SILVA
8004b411da gitignore
- add the test's  vendor dir
2018-09-05 10:11:09 +02:00
bruno DA SILVA
e473c46dc3 [WIP] jenkinsfile integration 2018-09-05 08:08:19 +02:00
bruno DA SILVA
148309245b [WIP] jenkinsfile integration 2018-09-05 08:00:07 +02:00
bruno DA SILVA
5dc39fe068 [WIP] jenkinsfile integration 2018-09-04 18:10:39 +02:00
bruno DA SILVA
47c7a3c5e3 [WIP] jenkinsfile integration 2018-09-04 18:05:19 +02:00
bruno DA SILVA
85b7e86e58 [WIP] jenkinsfile integration 2018-09-04 18:03:56 +02:00
Pierre Goiffon
f871581997 Merge branch 'develop' into feature/b931
# Conflicts:
#	application/applicationextension.inc.php
#	core/attributedef.class.inc.php
#	core/cmdbchangeop.class.inc.php
#	core/cmdbobject.class.inc.php
#	core/dbobjectsearch.class.php
#	core/oql/expression.class.inc.php
#	core/oql/oql-lexer.plex
#	core/oql/oql-parser.y
#	core/oql/oqlquery.class.inc.php
#	css/light-grey.scss
#	datamodels/2.x/itop-full-itil/datamodel.itop-full-itil.xml
#	datamodels/2.x/itop-portal/datamodel.itop-portal.xml
#	datamodels/2.x/itop-tickets/en.dict.itop-tickets.php
#	dictionaries/en.dictionary.itop.core.php
#	dictionaries/fr.dictionary.itop.core.php
#	setup/xmldataloader.class.inc.php
#	test/ItopDataTestCase.php
2018-09-04 18:02:51 +02:00
bruno DA SILVA
3fca465f1d [WIP] jenkinsfile integration 2018-09-04 18:02:12 +02:00
bruno DA SILVA
7955dd86f4 [WIP] jenkinsfile integration 2018-09-04 18:00:34 +02:00
Pierre Goiffon
40a4e6d7b0 Fix files using CrLf, convert them to Lf to have the whole repo using Lf
Warn your git config (core.autocrlf = input or true)
2018-09-04 17:59:51 +02:00
bruno DA SILVA
bef4ac83a4 [WIP] jenkinsfile integration 2018-09-04 17:58:20 +02:00
bruno DA SILVA
bde83fc705 [WIP] jenkinsfile integration 2018-09-04 17:53:18 +02:00
Pierre Goiffon
cad18bee73 SetupUtils : update comments to match current branch version 2018-09-04 12:02:16 +02:00
Eric
98e5eaa4e0 N°931: Add Delta management in ormTagSet (with unit tests) 2018-09-04 11:06:53 +02:00
Pierre Goiffon
3129e94363 Merge branch 'N°1620' into develop
# Conflicts:
#	sources/application/search/searchform.class.inc.php
2018-09-04 09:54:12 +02:00
bruno DA SILVA
7ac0e50bd9 phpstorm's line separator in edited files 2018-09-03 17:36:37 +02:00
Eric
7d502fae23 N°931: TagSet : OQL Parse/Normalize unit tests 2018-09-03 15:18:19 +02:00
Pierre Goiffon
894eeef140 ignore : add extensions 2018-09-03 15:10:51 +02:00
bruno DA SILVA
7991069282 .gitignore completion
- cache
 - logs
 - composer's vendor dir
2018-09-03 12:13:57 +02:00
Eric
39f3972a24 N°931: TagSet : OQL Parsing unit tests 2018-08-31 18:02:22 +02:00
Pierre Goiffon
fc0e5ecd3b Ignore : add other shared files in index 2018-08-31 17:46:12 +02:00
Pierre Goiffon
8897d9f82b ignore : add other shared idea files in index 2018-08-31 17:36:52 +02:00
Eric
1329b5f684 N°931: TagSet : Set automatically the class and attcode from tag class name 2018-08-31 16:59:02 +02:00
Eric
e58bc738d0 N°931: TagSet : Parsing OQL 2018-08-31 15:58:55 +02:00
Denis Flaven
af63579f31 Rework of the readme... 2018-08-30 16:35:56 +02:00
Denis Flaven
55fad3a9ec Added .project files to ignore. 2018-08-30 15:32:41 +02:00
Eric
3250e0a1e6 N°931: TagSet - Support for removed tags in history 2018-08-30 12:42:56 +02:00
bruno DA SILVA
8df287f45e updated readme 2018-08-30 12:25:20 +02:00
Eric Espié
f458a77449 N°962: TagSet - Attribute definition (continued)
SVN:b931[6032]
2018-08-30 11:08:47 +02:00
Eric Espié
1953c05b33 N°962: TagSet - add label support
SVN:b931[6031]
2018-08-30 11:08:47 +02:00
Eric Espié
a03c553000 N°962: TagSet attribute definition dev (continued)
SVN:b931[6030]
2018-08-30 11:08:47 +02:00
Eric Espié
ecdc4076d9 N°962: TagSet Edition in string mode
SVN:b931[6029]
2018-08-30 11:08:47 +02:00
Eric Espié
cd2ea3793e N°962: TagSet Fix Attribute definition
SVN:b931[6028]
2018-08-30 11:08:47 +02:00
Pierre Goiffon
d40ffd944f Revert "@@@@ OQL parsing"
This reverts commit 0beafc1e9a7c4a629a458aec669aa5e11b27db49.

SVN:b931[6027]
2018-08-30 11:08:47 +02:00
Pierre Goiffon
0ee4b52baa N°931 rights on TagSetFieldData on ConfigurationManager profile
SVN:b931[6026]
2018-08-30 11:08:46 +02:00
Pierre Goiffon
5e7db7a27e @@@@ OQL parsing
SVN:b931[6025]
2018-08-30 11:08:46 +02:00
Eric Espié
9631021f84 N°962: TagSet Attribute definition and values (with unit tests on values)
SVN:b931[6024]
2018-08-30 11:08:46 +02:00
Pierre Goiffon
157193c831 N°931 Change default XML version and remove done TODOs
SVN:b931[6022]
2018-08-30 11:08:46 +02:00
Pierre Goiffon
1415f12506 composer.json : add missing ext-dom (iTopDesignFormat)
SVN:b931[6021]
2018-08-30 11:08:46 +02:00
Pierre Goiffon
4df497a768 N°931 Increment iTop XML version (1.5 -> 1.6)
SVN:b931[6020]
2018-08-30 11:08:46 +02:00
Eric Espié
9a13eb0f90 warnings cleanup, search mode switched to BOOLEAN
SVN:b931[6019]
2018-08-30 11:08:46 +02:00
Pierre Goiffon
eaa49bce05 N°931 woops forgot to rename TagSetFielData file name
SVN:b931[6018]
2018-08-30 11:08:46 +02:00
Pierre Goiffon
96334d859a Add shared idea file 2018-08-30 11:05:27 +02:00
Pierre Goiffon
13855b1501 Add .gitignore and README 2018-08-30 11:04:23 +02:00
Eric Espié
277b24e2f0 warnings cleanup, search mode switched to BOOLEAN
SVN:trunk[6019]
2018-08-24 12:09:55 +00:00
Pierre Goiffon
abb2a2a6e2 N°931 new MatchExpression, allow to query the DB
SVN:trunk[6016]
2018-08-23 15:06:41 +00:00
Pierre Goiffon
961f5fd387 N°931 new TagField iTop class generated for each AttributeTag field
SVN:trunk[6015]
2018-08-23 15:06:17 +00:00
Pierre Goiffon
bdb62de81c N°931 new AttributeTag, handle field creation & update
SVN:trunk[6014]
2018-08-23 15:05:36 +00:00
Pierre Goiffon
422a850792 AttributeDefinition : fix lots of warnings
SVN:trunk[6013]
2018-08-23 14:55:33 +00:00
Eric Espié
1dfcc77098 N°1554: Search form - hide unknown external keys from the search criteria.
SVN:trunk[6012]
2018-08-23 12:32:37 +00:00
Eric Espié
13097d7e0e Fix excel export when reconciliation key list is containing empty keys.
Fix the compiler to avoid generating a reconciliation array not empty when
the tag is present in the xml definition of the class but no key is defined.

SVN:trunk[6011]
2018-08-23 08:22:44 +00:00
Eric Espié
ea9b7eddde N°1595 - Setup : Blocking error on backup failure
SVN:trunk[6010]
2018-08-22 10:16:35 +00:00
Guillaume Lajarige
07056613e5 N°1611 Fix "UTF-8 Characters Malformed" error due to wrong file encoding.
SVN:trunk[6009]
2018-08-21 10:29:46 +00:00
Pierre Goiffon
4d45793ec7 PHPDoc (schema)
SVN:trunk[6008]
2018-08-17 12:04:57 +00:00
Pierre Goiffon
4718133ab6 PHPDoc
SVN:trunk[6006]
2018-08-16 13:17:56 +00:00
Pierre Goiffon
ba518fa975 PHPDoc
SVN:trunk[6005]
2018-08-16 12:36:24 +00:00
Pierre Goiffon
0311e78690 KPI : fix PHP notice var might be undefined
SVN:trunk[6004]
2018-08-16 12:36:11 +00:00
Stephen Abello
9db47428db N°1559: Fixed external attributes selection on export form
SVN:trunk[6003]
2018-08-14 13:19:36 +00:00
Vincent Dumas
84dc3c2093 German Translation: typos (UserRequest #18704)
SVN:trunk[6001]
2018-08-08 09:18:25 +00:00
Stephen Abello
7d37b06555 Form prefill: add possibility to change attributes flag on the fly
SVN:trunk[6000]
2018-08-06 14:33:47 +00:00
Pierre Goiffon
6e6a89fb8c Selectable lines in tables : add an exclusion rule for click on padding/margin/border
SVN:trunk[5999]
2018-08-06 10:12:48 +00:00
Stephen Abello
135d9c5e55 Support any php default password hash algorithm change
SVN:trunk[5998]
2018-08-03 14:52:05 +00:00
Stephen Abello
dd46048ea6 Use a better algorithm to hash new passwords
SVN:trunk[5997]
2018-08-03 12:49:20 +00:00
Stephen Abello
8fe38b03f6 Added replacement for mcrypt removal in PHP 7.2, added stronger encryption options
SVN:trunk[5996]
2018-08-03 08:43:36 +00:00
Pierre Goiffon
0adadeb280 Remove old code that was here for old PHP version compatibility
SVN:trunk[5995]
2018-08-03 07:39:50 +00:00
Pierre Goiffon
775a5122c9 Selectable lines in tables can now be checked clicking anywhere in the line (previously this could only be done clicking on the checkbox)
SVN:trunk[5994]
2018-08-02 14:36:15 +00:00
Stephen Abello
5099060be2 N°1203 : Correctly use 'from' field for test email notifications
SVN:trunk[5993]
2018-08-02 08:34:58 +00:00
Pierre Goiffon
201c93e20a N°1582 Fix audit when a current organization is set and there is an audit rule with valid=true
Was generating an OQL missing query argument error

SVN:trunk[5991]
2018-08-01 09:12:00 +00:00
Stephen Abello
c852d91a72 Updated tcpdf to v6.2.17 for better compatibility with different PHP versions
SVN:trunk[5990]
2018-07-31 13:29:18 +00:00
Pierre Goiffon
a3c6e62bd5 PHP 7.2 compatibility: fix another count(null)
SVN:trunk[5988]
2018-07-30 10:30:16 +00:00
Pierre Goiffon
8cd18fe190 Setup : log in log/setup.log on PHP error (register_shutdown_function callback)
SVN:trunk[5987]
2018-07-26 14:54:45 +00:00
Guillaume Lajarige
5e1452f9b9 N°1580 Portal: Default image of image attributes not correctly displayed in object forms.
SVN:trunk[5986]
2018-07-26 14:22:15 +00:00
Guillaume Lajarige
b76de65886 Internal: Fix CSS typo.
SVN:trunk[5985]
2018-07-25 15:11:21 +00:00
Guillaume Lajarige
48b3bd8bf3 N°1579 Portal: Fix wrong pictogram placement on email & tel attributes in the ManageBrick.
SVN:trunk[5984]
2018-07-25 15:02:29 +00:00
Guillaume Lajarige
ab1715edec N°1576 Portal: Security hardening.
SVN:trunk[5983]
2018-07-25 14:48:11 +00:00
Guillaume Lajarige
3589783ee1 PHPDoc.
SVN:trunk[5982]
2018-07-25 13:09:35 +00:00
Guillaume Lajarige
af43e22f03 N°1578 Fix "Run Query" page hotkeys behavior in some configurations due to a wrong url.
SVN:trunk[5981]
2018-07-25 13:06:38 +00:00
Eric Espié
956f6403b8 Display error message for end users instead of blank screen
SVN:trunk[5980]
2018-07-25 08:02:28 +00:00
Guillaume Lajarige
82ed1853fa Code cleanup.
SVN:trunk[5977]
2018-07-25 07:57:07 +00:00
Guillaume Lajarige
eb0403945b PHPDoc
SVN:trunk[5976]
2018-07-25 07:55:39 +00:00
Pierre Goiffon
3126af94ac N°931 change MySQL dependency from 5.5.3 to 5.6 (to have fulltext on InnoDB)
SVN:trunk[5974]
2018-07-25 07:51:47 +00:00
Guillaume Lajarige
e71ad536a9 Code cleanup
SVN:trunk[5973]
2018-07-25 07:23:52 +00:00
Guillaume Lajarige
d0322b471d PHPDoc
SVN:trunk[5972]
2018-07-24 16:03:15 +00:00
Guillaume Lajarige
a6af11e644 Code cleanup and PHPDoc.
SVN:trunk[5971]
2018-07-24 15:50:52 +00:00
Guillaume Lajarige
1809180d41 Internal: Portal: Fix router API that was never returning the right route.
SVN:trunk[5970]
2018-07-24 15:46:23 +00:00
Pierre Goiffon
9612b1b9e9 Fix non existing parameter passed to function
SVN:trunk[5969]
2018-07-24 15:36:26 +00:00
Guillaume Lajarige
526d4c4d15 N°1575 Portal: Security hardening.
SVN:trunk[5968]
2018-07-24 14:26:51 +00:00
Pierre Goiffon
3f4c824fd5 N°1572 composer.json : add PHP ext dependencies
SVN:trunk[5967]
2018-07-24 12:30:07 +00:00
Pierre Goiffon
9c25feb67c N°1572 Add composer.json for PHP language level
SVN:trunk[5965]
2018-07-24 12:17:05 +00:00
Stephen Abello
e31faf81a9 jQuery 3: patched layout library
SVN:trunk[5964]
2018-07-23 09:47:53 +00:00
Pierre Goiffon
6eb13c24aa JQuery 3 : oops use the REAL min file this time
SVN:trunk[5963]
2018-07-23 09:12:50 +00:00
Pierre Goiffon
dd20017b0a JQuery 3 : change JQuery min file
SVN:trunk[5962]
2018-07-23 09:09:49 +00:00
Stephen Abello
6630051ef3 Updated jQuery to v3.3.1
SVN:trunk[5961]
2018-07-23 06:58:30 +00:00
Vincent Dumas
a66d91c507 Fix duplicated label in French: Ticket.caller_id = Change.requestor_id by renaming the second
SVN:trunk[5960]
2018-07-20 13:08:30 +00:00
Vincent Dumas
2f34f0458b Fix XML bug: Duplicate id=team_id in state new.
SVN:trunk[5959]
2018-07-20 12:49:23 +00:00
Guillaume Lajarige
076abc8ae7 N°1568 Portal: Fix async error messages catching when submitting a form was redirecting to a modal that crashed (eg. Transition on object that failed to load)
SVN:trunk[5958]
2018-07-19 16:54:11 +00:00
Eric Espié
5134e57109 Search: better translation of search criteria in "natural language" for ISNULL function
SVN:trunk[5957]
2018-07-19 08:45:39 +00:00
Guillaume Lajarige
3d1ccf2028 N°1566 Fix security message in the browser console ("Unsafe attempt to load URL data:image/svg+xml;utf8")
SVN:trunk[5952]
2018-07-18 14:40:12 +00:00
Eric Espié
60b980fbff N°1556 - Search: Fix removing last criterion on a 'or' line resulted in 'OR 1'.
The empty OR condition is now removed completely from the screen and the criterion list.

SVN:trunk[5951]
2018-07-18 14:08:56 +00:00
Eric Espié
df20f1b5ab N°1553 - Search: Fix operator on indexed attributes. It was previously always forced to '=', now it's only defaulted to '='
SVN:trunk[5950]
2018-07-18 13:13:02 +00:00
Eric Espié
27144f07b1 N°1561 - Fix auto-complete error when the friendlyname depends on other classes
SVN:trunk[5948]
2018-07-18 12:30:40 +00:00
Guillaume Lajarige
8135fdb9d2 Update spanish translations (Thanks to Miguel Turrubiates!)
SVN:trunk[5947]
2018-07-18 07:51:17 +00:00
Pierre Goiffon
985ad18048 REST service PHPDoc & code cleanup
SVN:trunk[5946]
2018-07-18 07:40:10 +00:00
Eric Espié
768ed2666d N°1555 - Search: ExternalField label not displayed.
The search is not restricted for external fields anymore.

SVN:trunk[5945]
2018-07-17 13:17:22 +00:00
Eric Espié
c289a53ed3 Advanced search: Fix an error when using search form from an union.
SVN:trunk[5940]
2018-07-17 12:04:53 +00:00
Eric Espié
5c206718c8 N°1551 - Search: selected org & default criteria
Display the default search criteria also when an org is selected.
The org restriction criterion is read only.

SVN:trunk[5939]
2018-07-17 12:01:29 +00:00
Eric Espié
d7f18e879a Automatic Tests: new ormLinkSet test
SVN:trunk[5938]
2018-07-13 12:34:45 +00:00
Eric Espié
4881a2c274 Better KPI navigation
SVN:trunk[5937]
2018-07-13 09:42:52 +00:00
Eric Espié
5eb5fa05bc cleanup code
SVN:trunk[5936]
2018-07-13 08:34:47 +00:00
Eric Espié
c7e0f36d7c N°1546 - Ticket JSON Export PHP Notice for pre-2.0 tickets
SVN:trunk[5935]
2018-07-13 07:47:13 +00:00
Pierre Goiffon
54d54ca78e Fix setup for PHP 5.5 (cannot use expression as default field value)
SVN:trunk[5933]
2018-07-12 07:45:13 +00:00
Eric Espié
4cd591f81c DBObject->GetOriginal() hardening (now support attributes not set: for example sla_tto_passed for UserRequest until it is closed)
SVN:trunk[5932]
2018-07-11 13:40:38 +00:00
Denis Flaven
d21d732545 Do not check if the organizations are allowed if there is no user logged in (use case: automatic synchro of users at connection time)
SVN:trunk[5930]
2018-07-05 12:54:58 +00:00
Pierre Goiffon
ea4854d239 fix comment typo
SVN:trunk[5929]
2018-07-02 07:48:28 +00:00
Guillaume Lajarige
7ca95b9413 Code cleanup: Warning suppression & PHPDoc.
SVN:trunk[5928]
2018-06-29 17:14:13 +00:00
Guillaume Lajarige
00d0d383f8 Code cleanup: Warning suppression & PHPDoc.
SVN:trunk[5927]
2018-06-29 16:05:11 +00:00
Bruno Da Silva
401a8cdd77 Lab "performance"
- move some heavy introspection into cached part of MetaModel aka iterate over get_declared_classes() in order to check is_subclass_of($sPHPClass, 'ModuleHandlerAPI')
 - make use of an interface in order to rely on existing code

SVN:trunk[5926]
2018-06-29 13:00:18 +00:00
Guillaume Lajarige
b2384855fd Code cleanup: Warning suppression.
SVN:trunk[5925]
2018-06-28 15:33:29 +00:00
Guillaume Lajarige
602c087c29 N°1528 Portal: Add support for SCSS files through the PortalUIExtension API (only CSS were supported)
SVN:trunk[5924]
2018-06-28 14:51:31 +00:00
Guillaume Lajarige
0d96ed5436 Starting dev on iTop 2.6!
SVN:trunk[5923]
2018-06-28 08:27:15 +00:00
Pierre Goiffon
97fa3ac3b3 Fix portal DE translation (thanks again Lars Hippler / Itomig !)
SVN:trunk[5919]
2018-06-28 07:15:39 +00:00
725 changed files with 43716 additions and 21884 deletions

9
.gitflow Normal file
View File

@@ -0,0 +1,9 @@
[gitflow "branch"]
master = master
develop = develop
[gitflow "prefix"]
feature = feature/
release = release/
hotfix = hotfix/
versiontag =
support = support/

1
.gitignore vendored
View File

@@ -121,3 +121,4 @@ local.properties
.cache-main
.scala_dependencies
.worksheet

View File

@@ -1,5 +1,11 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<option name="RIGHT_MARGIN" value="320" />
<HTMLCodeStyleSettings>
<option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="html,body,thead,tbody,tfoot,script,style" />
<option name="HTML_DO_NOT_ALIGN_CHILDREN_OF_MIN_LINES" value="4" />
</HTMLCodeStyleSettings>
<PHPCodeStyleSettings>
<option name="CONCAT_SPACES" value="false" />
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
@@ -20,6 +26,7 @@
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="PHP">
<option name="RIGHT_MARGIN" value="320" />
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
<option name="BRACE_STYLE" value="2" />
<option name="ELSE_ON_NEW_LINE" value="true" />
@@ -41,5 +48,10 @@
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

2
.idea/encodings.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

44
.idea/inspectionProfiles/Combodo.xml generated Normal file
View File

@@ -0,0 +1,44 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Combodo" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MysqlParsingInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpIncludeInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpMethodParametersCountMismatchInspection" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="PhpTooManyParametersInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="7" />
</inspection_tool>
<inspection_tool class="PhpUndefinedClassInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="DONT_REPORT_MULTI_RESOLVE" value="true" />
</inspection_tool>
<inspection_tool class="PhpUndefinedMethodInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpUnhandledExceptionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpUnusedLocalVariableInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="DONT_REPORT_INSIDE_LIST" value="true" />
</inspection_tool>
<inspection_tool class="PhpUnusedParameterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="DONT_REPORT_ABSTRACT_CLASS" value="true" />
</inspection_tool>
<inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlCheckUsingColumnsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDerivedTableAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNullComparisonInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlPostgresqlSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSideEffectsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlStorageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnusedVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@@ -1,154 +1,19 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="BladeControlDirectives" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckEmptyScriptTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckImageSize" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckNodeTest" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckTagEmptyBody" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckValidXmlInScriptTagBody" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptArgumentsOutsideFunction" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptFunctionSignatures" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptInfiniteLoop" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptLiteralNotFunction" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptSillyAssignment" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptSwitchStatementWithNoDefaultBranch" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptUnusedLocalSymbols" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ComposeFileStructure" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssFloatPxLength" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidAtRule" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidCharsetRule" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidElement" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidFunction" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidHtmlTagReference" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidImport" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidMediaFeature" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidPropertyValue" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidPseudoSelector" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssMissingComma" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssNegativeValue" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssNoGenericFontName" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssOptimizeSimilarProperties" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CssOverwrittenProperties" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssRedundantUnit" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssUnitlessNumber" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssUnknownProperty" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myCustomPropertiesEnabled" value="false" />
<option name="myIgnoreVendorSpecificProperties" value="false" />
<option name="myCustomPropertiesList">
<value>
<list size="0" />
</value>
</option>
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpIncludeInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpMethodParametersCountMismatchInspection" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="PhpTooManyParametersInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="7" />
</inspection_tool>
<inspection_tool class="CssUnknownTarget" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnresolvedClass" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnresolvedCustomProperty" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnresolvedCustomPropertySet" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CucumberExamplesColon" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CucumberMissedExamples" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CucumberTableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CucumberUndefinedStep" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DockerFileArgumentCount" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="DuplicateKeyInSection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DuplicateSectionInFile" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EmptyEventHandler" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="FileHeaderInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="GherkinBrokenTableInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="GherkinMisplacedBackground" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="HamlNestedTagContent" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HardwiredNamespacePrefix" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlDeprecatedTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlExtraClosingTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlFormInputWithoutLabel" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlMissingClosingTag" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="HtmlPresentationalElement" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownAnchorTarget" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownAttribute" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myValues">
<value>
<list size="0" />
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
<inspection_tool class="PhpUndefinedClassInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="DONT_REPORT_MULTI_RESOLVE" value="true" />
</inspection_tool>
<inspection_tool class="HtmlUnknownBooleanAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownTag" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myValues">
<value>
<list size="6">
<item index="0" class="java.lang.String" itemvalue="nobr" />
<item index="1" class="java.lang.String" itemvalue="noembed" />
<item index="2" class="java.lang.String" itemvalue="comment" />
<item index="3" class="java.lang.String" itemvalue="noscript" />
<item index="4" class="java.lang.String" itemvalue="embed" />
<item index="5" class="java.lang.String" itemvalue="script" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
<inspection_tool class="PhpUnhandledExceptionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpUnusedParameterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="DONT_REPORT_ABSTRACT_CLASS" value="true" />
</inspection_tool>
<inspection_tool class="HtmlUnknownTarget" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ImplicitTypeConversion" enabled="false" level="WARNING" enabled_by_default="false">
<option name="BITS" value="1720" />
<option name="FLAG_EXPLICIT_CONVERSION" value="true" />
<option name="IGNORE_NODESET_TO_BOOLEAN_VIA_STRING" value="true" />
</inspection_tool>
<inspection_tool class="IndexZeroUsage" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSLastCommaInArrayLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSLastCommaInObjectLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnresolvedFunction" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnresolvedVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="LossyEncoding" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MissingSinceTagDocInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="MysqlParsingInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NonAsciiCharacters" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpDocMissingReturnTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpDocMissingThrowsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpIncludeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpRedundantClosingTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedClassConstantInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedClassInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedConstantInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedFieldInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedFunctionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedGotoLabelInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedNamespaceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUnusedParameterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RedundantTypeConversion" enabled="false" level="WARNING" enabled_by_default="false">
<option name="CHECK_ANY" value="false" />
</inspection_tool>
<inspection_tool class="RequiredAttributes" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myAdditionalRequiredHtmlAttributes" value="" />
</inspection_tool>
<inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlCheckUsingColumnsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDerivedTableAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNullComparisonInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlPostgresqlSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlStorageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnusedVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TaskProblemsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptPreferShortImport" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlDefaultAttributeValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlUnboundNsPrefix" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlUnusedNamespaceDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XsltUnusedDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XsltVariableShadowing" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="projectProfile" value="Combodo" />
<option name="PROJECT_PROFILE" value="Combodo" />
<version value="1.0" />
</settings>
</component>

105
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,105 @@
# Contributing to iTop
You want to contribute to iTop? Many thanks to you! 🎉 👍
Here are some guidelines that will help us integrate your work!
## Contributions
### Subjects
You are welcome to create pull requests on any of those subjects:
* 🐛 `:bug:` bug fix
* 🔒 `:lock:` security
* 🌐 `:globe_with_meridians:` translation / i18n / l10n
If you want to implement a **new feature**, please [create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) for review.
If you ever want to begin implementation, do so in a fork, and add a link to the corresponding commits in the ticket.
### License
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file),
your code must comply with this license.
If you want to use another license, you may [create an extension][wiki new ext].
[license.txt]: https://github.com/Combodo/iTop/blob/develop/license.txt
[wiki new ext]: https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Astart#by_writing_your_own_extension
## Branch model
TL;DR:
> **create a fork from iTop main repository,
> create a branch based on either release branch if present, or develop otherwise**
We are using the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branch model. That means we have in our repo those
main branches:
- develop: ongoing development version
- release/\*: if present, that means we are working on a beta version
- master: previous stable version
For example, if no beta version is currently ongoing we could have:
- develop containing future 2.8.0 version
- master containing 2.7.x maintenance version
In this example, when 2.8.0-beta is shipped that will become:
- develop: future 2.9.0 version
- release/2.8: 2.8.0-beta
- master: 2.7.x maintenance version
And when 2.8.0 final will be out:
- develop: future 2.9.0 version
- master: 2.8.x maintenance version
- support/2.7 : 2.7.x maintenance version
## Coding
### PHP styleguide
Please follow [our guidelines](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Acoding_standards).
### 🌐 Translations
A [dedicated page](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Atranslation) is available in the official wiki.
### Tests
Please create tests that covers as much as possible the code you're submitting.
Our tests are located in the `test/` directory, containing a PHPUnit config file : `phpunit.xml.dist`.
### Git Commit Messages
* Describe the functional change instead of the technical modifications
* Use the present tense ("Add feature" not "Added feature")
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
* Limit the first line to 72 characters or less
* Please start the commit message with an applicable emoji code (following the [Gitmoji guide](https://gitmoji.carloscuesta.me/)). For example :
* 🌐 `:globe_with_meridians:` for translations
* 🎨 `:art:` when improving the format/structure of the code
* ⚡️ `:zap:` when improving performance
* 🐛 `:bug:` when fixing a bug
* 🔥 `:fire:` when removing code or files
* 💚 `:green_heart:` when fixing the CI build
*`:white_check_mark:` when adding tests
* 🔒 `:lock:` when dealing with security
* ⬆️ `:arrow_up:` when upgrading dependencies
* ⬇️ `:arrow_down:` when downgrading dependencies
* ♻️ `:recycle:` code refactoring
* 💄 `:lipstick:` Updating the UI and style files.
## Pull request
When your code is working, please:
* stash as much as possible your commits,
* rebase your branch on our repo last commit,
* create a pull request.
Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).

140
README.md Normal file
View File

@@ -0,0 +1,140 @@
<p align="center"><a href="https://www.combodo.com/itop-193" target="_blank">
<img src="https://www.combodo.com/logos/logo-itop.svg">
</a></p>
# iTop - ITSM & CMDB
iTop stands for *IT Operations Portal*.
It is a complete open source, ITIL, web based service management tool including a fully customizable CMDB, a helpdesk system and a document management tool.
iTop also offers mass import tools and web services to integrate with your IT
## Features
- Fully configurable [Configuration Management (CMDB)][10]
- [HelpDesk][11] and Incident Management
- [Service and Contract Management][12]
- [Change][13] Management
- Configurable [SLA][14] Management
- Graphical [impact analysis][15]
- [CSV import][16] tool for any data
- Consistency [audit][17] to check data quality
- [Data synchronization][18] (for data federation)
## Resources
- [iTop Forums][1]: for support request
- [iTop Tickets][2]: for feature requests and bug reports
- [Releases download][3]
- [iTop documentation][4] for iTop and official extensions
- [iTop extensions][5] for discovering and installing extensions
## Releases
### Version 2.6
- [Changes since the previous version][58]
- [New features][59]
- [Migration notes][60]
- [Download iTop 2.6.0][61]
### Version 2.5
- [Changes since the previous version][54]
- [New features][55]
- [Migration notes][56]
- [Download iTop 2.5.1][57]
### Version 2.4
- [Changes since the previous version][50]
- [New features][51]
- [Migration notes][52]
- [Download iTop 2.4.1][53]
# About Us
iTop development is sponsored, led and supported by [Combodo][0].
# Contributors
We would like to give a special thank you to the people from the community who contributed to this project, including:
- Alves, David
- Beck, Pedro
- Bilger, Jean-François
- Bostoen, Jeffrey
- Cardoso, Anderson
- Cassaro, Bruno
- Casteleyn, Thomas
- Castro, Randall Badilla
- Colantoni, Maria Laura
- Dvořák, Lukáš
- Goethals, Stefan
- Gumble, David
- Hippler, Lars
- Khamit, Shamil
- Konečný, Kamil
- Kunin, Vladimir
- Lassiter, Dennis
- Lucas, Jonathan
- Malik, Remie
- Rosenke, Stephan
- Seki, Shoji
- Shilov, Vladimir
- Tulio, Marco
- Turrubiates, Miguel
#### Aliases
- chifu1234
- cprobst
- Karkoff1212
- larhip
- Laura
- Purple Grape
- Schlobinux
- theBigOne
- ulmerspatz
#### Companies
- Hardis
- ITOMIG
[0]: https://www.combodo.com
[1]: https://sourceforge.net/p/itop/discussion/
[2]: https://sourceforge.net/p/itop/tickets/
[3]: https://sourceforge.net/projects/itop/files/itop/
[4]: https://www.itophub.io/wiki
[5]: https://store.itophub.io/en_US/
[10]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#configuration_management_cmdb
[11]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#ticketing
[12]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#service_management
[13]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#change_management
[14]: https://www.itophub.io/wiki/page?id=2_5_0%3Aimplementation%3Astart#service_level_agreements_and_targets
[15]: https://www.itophub.io/wiki/page?id=2_5_0%3Auser%3Aactions#relations
[16]: https://www.itophub.io/wiki/page?id=2_5_0%3Auser%3Abulk_modify#uploading_data
[17]: https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Aaudit
[18]: https://www.itophub.io/wiki/page?id=2_5_0%3Aadvancedtopics%3Adata_synchro_overview
[50]: https://www.itophub.io/wiki/page?id=2_4_0:release:change_log
[51]: https://www.itophub.io/wiki/page?id=2_4_0:release:2_4_whats_new
[52]: https://www.itophub.io/wiki/page?id=2_4_0:install:230_to_240_migration_notes
[53]: https://sourceforge.net/projects/itop/files/itop/2.4.1
[54]: https://www.itophub.io/wiki/page?id=2_5_0:release:change_log
[55]: https://www.itophub.io/wiki/page?id=2_5_0:release:2_5_whats_new
[56]: https://www.itophub.io/wiki/page?id=2_5_0:install:240_to_250_migration_notes
[57]: https://sourceforge.net/projects/itop/files/itop/2.5.1
[58]: https://www.itophub.io/wiki/page?id=2_6_0:release:change_log
[59]: https://www.itophub.io/wiki/page?id=2_6_0:release:2_6_whats_new
[60]: https://www.itophub.io/wiki/page?id=2_6_0:install:250_to_260_migration_notes
[61]: https://sourceforge.net/projects/itop/files/itop/2.6.0

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010-2018 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* Class ApplicationContext
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -31,6 +31,12 @@ require_once(APPROOT."/application/utils.inc.php");
*/
interface iDBObjectURLMaker
{
/**
* @param string $sClass
* @param string $iId
*
* @return string
*/
public static function MakeObjectURL($sClass, $iId);
}
@@ -39,6 +45,13 @@ interface iDBObjectURLMaker
*/
class iTopStandardURLMaker implements iDBObjectURLMaker
{
/**
* @param string $sClass
* @param string $iId
*
* @return string
* @throws \Exception
*/
public static function MakeObjectURL($sClass, $iId)
{
$sPage = DBObject::ComputeStandardUIPage($sClass);
@@ -53,6 +66,13 @@ class iTopStandardURLMaker implements iDBObjectURLMaker
*/
class PortalURLMaker implements iDBObjectURLMaker
{
/**
* @param string $sClass
* @param string $iId
*
* @return string
* @throws \Exception
*/
public static function MakeObjectURL($sClass, $iId)
{
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
@@ -74,10 +94,20 @@ class PortalURLMaker implements iDBObjectURLMaker
*/
class ApplicationContext
{
public static $m_sUrlMakerClass = null;
protected static $m_aPluginProperties = null;
protected static $aDefaultValues; // Cache shared among all instances
protected $aNames;
protected $aValues;
protected static $aDefaultValues; // Cache shared among all instances
/**
* ApplicationContext constructor.
*
* @param bool $bReadContext
*
* @throws \Exception
*/
public function __construct($bReadContext = true)
{
$this->aNames = array(
@@ -89,11 +119,13 @@ class ApplicationContext
}
}
/**
* Read the context directly in the PHP parameters (either POST or GET)
* return nothing
*/
/**
* Read the context directly in the PHP parameters (either POST or GET)
* return nothing
*
* @throws \Exception
*/
protected function ReadContext()
{
if (!isset(self::$aDefaultValues))
@@ -110,20 +142,26 @@ class ApplicationContext
}
// Hmm, there must be a better (more generic) way to handle the case below:
// When there is only one possible (allowed) organization, the context must be
// fixed to this org
// fixed to this org unless there is only one organization in the system then
// no filter is applied
if ($sName == 'org_id')
{
if (MetaModel::IsValidClass('Organization'))
{
$oSearchFilter = new DBObjectSearch('Organization');
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->CountWithLimit(2);
if ($iCount == 1)
if ($iCount > 1)
{
// Only one possible value for org_id, set it in the context
$oOrg = $oSet->Fetch();
self::$aDefaultValues[$sName] = $oOrg->GetKey();
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->CountWithLimit(2);
if ($iCount == 1)
{
// Only one possible value for org_id, set it in the context
$oOrg = $oSet->Fetch();
self::$aDefaultValues[$sName] = $oOrg->GetKey();
}
}
}
}
@@ -131,12 +169,15 @@ class ApplicationContext
}
$this->aValues = self::$aDefaultValues;
}
/**
* Returns the current value for the given parameter
* @param string $sParamName Name of the parameter to read
* @return mixed The value for this parameter
*/
/**
* Returns the current value for the given parameter
*
* @param string $sParamName Name of the parameter to read
* @param string $defaultValue
*
* @return mixed The value for this parameter
*/
public function GetCurrentValue($sParamName, $defaultValue = '')
{
if (isset($this->aValues[$sParamName]))
@@ -148,7 +189,7 @@ class ApplicationContext
/**
* Returns the context as string with the format name1=value1&name2=value2....
* return string The context as a string to be appended to an href property
* @return string The context as a string to be appended to an href property
*/
public function GetForLink()
{
@@ -162,7 +203,7 @@ class ApplicationContext
/**
* 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
* @return string The context as a sequence of <input type="hidden" /> tags
*/
public function GetForForm()
{
@@ -176,7 +217,7 @@ class ApplicationContext
/**
* Returns the context as a hash array 'parameter_name' => value
* return array The context information
* @return array The context information
*/
public function GetAsHash()
{
@@ -199,8 +240,7 @@ class ApplicationContext
/**
* Removes the specified parameter from the context, for example when the same parameter
* is already a search parameter
* @param string $sParamName Name of the parameter to remove
* @return none
* @param string $sParamName Name of the parameter to remove
*/
public function Reset($sParamName)
{
@@ -212,6 +252,11 @@ class ApplicationContext
/**
* Initializes the given object with the default values provided by the context
*
* @param \DBObject $oObj
*
* @throws \Exception
* @throws \CoreUnexpectedValue
*/
public function InitObjectFromContext(DBObject &$oObj)
{
@@ -238,13 +283,11 @@ class ApplicationContext
}
}
}
static $m_sUrlMakerClass = null;
/**
* Set the current application url provider
* @param sClass string Class implementing iDBObjectURLMaker
* @return void
* @param string $sClass Class implementing iDBObjectURLMaker
* @return string
*/
public static function SetUrlMakerClass($sClass = 'iTopStandardURLMaker')
{
@@ -278,7 +321,14 @@ class ApplicationContext
/**
* Get the current application url provider
*
* @param string $sObjClass
* @param string $sObjKey
* @param null $sUrlMakerClass
* @param bool $bWithNavigationContext
*
* @return string the name of the class
* @throws \Exception
*/
public static function MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass = null, $bWithNavigationContext = true)
{
@@ -306,8 +356,6 @@ class ApplicationContext
}
}
protected static $m_aPluginProperties = null;
/**
* Load plugin properties for the current session
* @return void
@@ -326,9 +374,9 @@ class ApplicationContext
/**
* Set plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @param sProperty string Name of the property
* @param value scalar Value (numeric or string)
* @param string $sPluginClass Class implementing any plugin interface
* @param string $sProperty Name of the property
* @param mixed $value Value (numeric or string)
* @return void
*/
public static function SetPluginProperty($sPluginClass, $sProperty, $value)
@@ -341,7 +389,7 @@ class ApplicationContext
/**
* Get plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @param string $sPluginClass Class implementing any plugin interface
* @return array of sProperty=>value pairs
*/
public static function GetPluginProperties($sPluginClass)
@@ -359,4 +407,3 @@ class ApplicationContext
}
}
?>

View File

@@ -16,6 +16,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
require_once(APPROOT.'application/newsroomprovider.class.inc.php');
/**
* Management of application plugins
*
@@ -122,7 +124,8 @@ interface iApplicationUIExtension
* Sorry, the verb has been reserved. You must implement it, but it is not called as of now.
*
* @param DBObject $oObject The object being displayed
* @return type desc
*
* @return string[] desc
*/
public function EnumUsedAttributes($oObject); // Not yet implemented
@@ -441,8 +444,9 @@ abstract class ApplicationPopupMenuItem
/**
* Returns the components to create a popup menu item in HTML
* @return Hash A hash array: array('label' => , 'url' => , 'target' => , 'onclick' => )
* @ignore
*
* @return array A hash array: array('label' => , 'url' => , 'target' => , 'onclick' => )
* @ignore
*/
abstract public function GetMenuItem();
@@ -759,11 +763,15 @@ interface iRestServiceProvider
* @return array An array of hash 'verb' => verb, 'description' => description
*/
public function ListOperations($sVersion);
/**
* Enumerate services delivered by this class
*
* @param string $sVersion The version (e.g. 1.0) supported by the services
* @param string $sVerb
* @param array $aParams
*
* @return RestResult The standardized result structure (at least a message)
* @throws Exception in case of internal failure.
*/
public function ExecOperation($sVersion, $sVerb, $aParams);
}
@@ -870,7 +878,7 @@ class RestUtils
* @param StdClass $oData Structured input data. Must contain the entry defined by sParamName.
* @param string $sParamName Name of the parameter to fetch from the input data
*
* @return string
* @return mixed parameter value if present
* @throws Exception If the parameter is missing
* @api
*/
@@ -893,7 +901,8 @@ class RestUtils
* @param StdClass $oData Structured input data.
* @param string $sParamName Name of the parameter to fetch from the input data
* @param mixed $default Default value if the parameter is not found in the input data
* @return void
*
* @return mixed
* @throws Exception
* @api
*/
@@ -937,7 +946,8 @@ class RestUtils
* @param string $sClass Name of the class
* @param StdClass $oData Structured input data.
* @param string $sParamName Name of the parameter to fetch from the input data
* @return An array of class => list of attributes (see RestResultWithObjects::AddObject that uses it)
*
* @return array of class => list of attributes (see RestResultWithObjects::AddObject that uses it)
* @throws Exception
* @api
*/
@@ -1120,7 +1130,6 @@ class RestUtils
{
// OQL
$oSearch = DBObjectSearch::FromOQL($key);
$oObjectSet = new DBObjectSet($oSearch);
}
else
{
@@ -1169,6 +1178,14 @@ class RestUtils
}
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
}
elseif ($oAttDef instanceof AttributeTagSet)
{
if (!is_array($value))
{
throw new Exception("A tag set must be defined by an array of tag codes");
}
$value = $oAttDef->FromJSONToValue($value);
}
else
{
$value = $oAttDef->FromJSONToValue($value);

File diff suppressed because it is too large Load Diff

View File

@@ -21,6 +21,7 @@ require_once(APPROOT.'application/dashlet.class.inc.php');
require_once(APPROOT.'core/modelreflection.class.inc.php');
/**
*
* A user editable dashboard page
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
@@ -49,6 +50,11 @@ abstract class Dashboard
$this->sId = $sId;
}
/**
* @param $sXml
*
* @throws \Exception
*/
public function FromXml($sXml)
{
$this->aCells = array(); // reset the content of the dashboard
@@ -100,10 +106,10 @@ abstract class Dashboard
$oCellsList = $oCellsNode->getElementsByTagName('cell');
$aCellOrder = array();
$iCellRank = 0;
/** @var \DOMElement $oCellNode */
foreach($oCellsList as $oCellNode)
{
$aDashletList = array();
$oCellRank = $oCellNode->getElementsByTagName('rank')->item(0);
$oCellRank = $oCellNode->getElementsByTagName('rank')->item(0);
if ($oCellRank)
{
$iCellRank = (float)$oCellRank->textContent;
@@ -113,6 +119,7 @@ abstract class Dashboard
$oDashletList = $oDashletsNode->getElementsByTagName('dashlet');
$iRank = 0;
$aDashletOrder = array();
/** @var \DOMElement $oDomNode */
foreach($oDashletList as $oDomNode)
{
$oRank = $oDomNode->getElementsByTagName('rank')->item(0);
@@ -145,6 +152,11 @@ abstract class Dashboard
}
}
/**
* @param \DOMElement $oDomNode
*
* @return mixed
*/
protected function InitDashletFromDOMNode($oDomNode)
{
$sId = $oDomNode->getAttribute('id');
@@ -152,7 +164,8 @@ abstract class Dashboard
// Test if dashlet can be instanciated, otherwise (uninstalled, broken, ...) we display a placeholder
$sClass = static::GetDashletClassFromType($sDashletType);
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
/** @var \Dashlet $oNewDashlet */
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
$oNewDashlet->SetDashletType($sDashletType);
$oNewDashlet->FromDOMNode($oDomNode);
@@ -163,8 +176,17 @@ abstract class Dashboard
{
return ($aItem1['rank'] > $aItem2['rank']) ? +1 : -1;
}
/**
* Error handler to turn XML loading warnings into exceptions
*
* @param $errno
* @param $errstr
* @param $errfile
* @param $errline
*
* @return bool
* @throws \DOMException
*/
public static function ErrorHandler($errno, $errstr, $errfile, $errline)
{
@@ -194,8 +216,12 @@ abstract class Dashboard
return $sXml;
}
/**
* @param \DOMElement $oDefinition
*/
public function ToDOMNode($oDefinition)
{
/** @var \DOMDocument $oDoc */
$oDoc = $oDefinition->ownerDocument;
$oNode = $oDoc->createElement('layout', $this->sLayoutClass);
@@ -227,6 +253,7 @@ abstract class Dashboard
$iDashletRank = 0;
$oDashletsNode = $oDoc->createElement('dashlets');
$oCellNode->appendChild($oDashletsNode);
/** @var \Dashlet $oDashlet */
foreach ($aCell as $oDashlet)
{
$oNode = $oDoc->createElement('dashlet');
@@ -256,6 +283,7 @@ abstract class Dashboard
{
$sDashletClass = $aDashletParams['dashlet_class'];
$sId = $aDashletParams['dashlet_id'];
/** @var \Dashlet $oNewDashlet */
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
if (isset($aDashletParams['dashlet_type']))
{
@@ -318,31 +346,28 @@ abstract class Dashboard
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec);
}
/**
* @param \Dashlet $oDashlet
*/
public function AddDashlet($oDashlet)
{
$sId = $this->GetNewDashletId();
$oDashlet->SetId($sId);
$this->aCells[] = array($oDashlet);
}
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$oPage->add('<h1>'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</h1>');
$oLayout = new $this->sLayoutClass;
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
if (!$bEditMode)
{
$oPage->add_linked_script('../js/dashlet.js');
$oPage->add_linked_script('../js/dashboard.js');
}
}
public function RenderProperties($oPage)
/**
* @param \WebPage $oPage *
* @param array $aExtraParams
*
* @throws \ReflectionException
*/
public function RenderProperties($oPage, $aExtraParams = array())
{
// menu to pick a layout and edit other properties of the dashboard
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:Properties').'</div>');
$sUrl = utils::GetAbsoluteUrlAppRoot();
$oPage->add('<div style="text-align:center">'.Dict::S('UI:DashboardEdit:Layout').'</div>');
$oPage->add('<div id="select_layout" style="text-align:center">');
foreach( get_declared_classes() as $sLayoutClass)
@@ -360,13 +385,13 @@ abstract class Dashboard
}
}
$oPage->add('</div>');
$oForm = new DesignerForm();
$oField = new DesignerHiddenField('dashboard_id', '', $this->sId);
$oForm->AddField($oField);
$oField = new DesignerLongTextField('dashboard_title', Dict::S('UI:DashboardEdit:DashboardTitle'), $this->sTitle);
$oField = new DesignerTextField('dashboard_title', Dict::S('UI:DashboardEdit:DashboardTitle'), $this->sTitle);
$oForm->AddField($oField);
$oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload);
@@ -377,8 +402,8 @@ abstract class Dashboard
$oForm->AddField($oField);
$this->SetFormParams($oForm);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$this->SetFormParams($oForm, $aExtraParams);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$oPage->add('</div>');
@@ -422,8 +447,28 @@ abstract class Dashboard
EOF
);
}
public function RenderDashletsSelection($oPage)
/**
* @param \iTopWebPage $oPage
* @param bool $bEditMode
* @param array $aExtraParams
* @param bool $bCanEdit
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
{
$oPage->add('<div class="dashboard-title-line"><div class="dashboard-title">'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</div></div>');
$oLayout = new $this->sLayoutClass;
/** @var \DashboardLayoutMultiCol $oLayout */
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
if (!$bEditMode)
{
$oPage->add_linked_script('../js/dashlet.js');
$oPage->add_linked_script('../js/dashboard.js');
}
}
public function RenderDashletsSelection(WebPage $oPage)
{
// Toolbox/palette to drag and drop dashlets
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:Dashlets').'</div>');
@@ -441,7 +486,7 @@ EOF
$oPage->add_ready_script("$('.dashlet_icon').draggable({helper: 'clone', appendTo: 'body', zIndex: 10000, revert:'invalid'});");
}
public function RenderDashletsProperties($oPage)
public function RenderDashletsProperties(WebPage $oPage, $aExtraParams = array())
{
// Toolbox/palette to edit the properties of each dashlet
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:DashletProperties').'</div>');
@@ -449,15 +494,15 @@ EOF
$oPage->add('<div id="dashlet_properties" style="text-align:center">');
foreach($this->aCells as $aCell)
{
/** @var \Dashlet $oDashlet */
foreach($aCell as $oDashlet)
{
$sId = $oDashlet->GetID();
$sClass = get_class($oDashlet);
if ($oDashlet->IsVisible())
{
$oPage->add('<div class="dashlet_properties" id="dashlet_properties_'.$sId.'" style="display:none">');
$oForm = $oDashlet->GetForm();
$this->SetFormParams($oForm);
$this->SetFormParams($oForm, $aExtraParams);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$oPage->add('</div>');
}
@@ -472,6 +517,7 @@ EOF
* Return an array of dashlets available for selection.
*
* @return array
* @throws \ReflectionException
*/
protected function GetAvailableDashlets()
{
@@ -505,6 +551,7 @@ EOF
$iNewId = 0;
foreach($this->aCells as $aDashlets)
{
/** @var \Dashlet $oDashlet */
foreach($aDashlets as $oDashlet)
{
$iNewId = max($iNewId, (int)$oDashlet->GetID());
@@ -512,8 +559,14 @@ EOF
}
return $iNewId + 1;
}
abstract protected function SetFormParams($oForm);
/**
* @param $oForm
* @param array $aExtraParams
*
* @return mixed
*/
abstract protected function SetFormParams($oForm, $aExtraParams = array());
public static function GetDashletClassFromType($sType, $oFactory = null)
{
@@ -523,11 +576,22 @@ EOF
}
return 'DashletUnknown';
}
/**
* @return mixed
*/
public function GetId()
{
return $this->sId;
}
}
class RuntimeDashboard extends Dashboard
{
protected $bCustomized;
private $sDefinitionFile = '';
private $sReloadURL = null;
public function __construct($sId)
{
@@ -540,10 +604,17 @@ class RuntimeDashboard extends Dashboard
{
$this->bCustomized = $bCustomized;
}
protected function SetFormParams($oForm)
/**
* @param \DesignerForm $oForm
*
* @param array $aExtraParams
*
* @throws \Exception
*/
protected function SetFormParams($oForm, $aExtraParams = array())
{
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property'));
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property', 'extra_params' => $aExtraParams));
}
public function Save()
@@ -587,41 +658,265 @@ class RuntimeDashboard extends Dashboard
utils::PopArchiveMode();
}
}
public function RenderEditionTools($oPage)
/**
* @param string $sDashboardFile file name relative to the current module folder
* @param string $sDashBoardId code of the dashboard either menu_id or <class>__<attcode>
*
* @return null|RuntimeDashboard
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public static function GetDashboard($sDashboardFile, $sDashBoardId)
{
$bCustomized = false;
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false))
{
// Search for an eventual user defined dashboard
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oUDSearch->AddCondition('menu_code', $sDashBoardId, '=');
$oUDSet = new DBObjectSet($oUDSearch);
if ($oUDSet->Count() > 0)
{
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
$sDashboardDefinition = $oUserDashboard->Get('contents');
$bCustomized = true;
}
else
{
$sDashboardDefinition = @file_get_contents($sDashboardFile);
}
}
else
{
$sDashboardDefinition = @file_get_contents($sDashboardFile);
}
if ($sDashboardDefinition !== false)
{
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
$oDashboard->SetDefinitionFile($sDashboardFile);
}
else
{
$oDashboard = null;
}
return $oDashboard;
}
/**
* @param \iTopWebPage $oPage
* @param bool $bEditMode
* @param array $aExtraParams (class and id of the current object
*
* @throws \Exception
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
{
if (!isset($aExtraParams['query_params']) && isset($aExtraParams['this->class']))
{
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
$aRenderParams = array('query_params' => $oObj->ToArgsForQuery());
}
else
{
$aRenderParams = $aExtraParams;
}
parent::Render($oPage, $bEditMode, $aRenderParams);
if (isset($aExtraParams['query_params']['this->object()']))
{
/** @var \DBObject $oObj */
$oObj = $aExtraParams['query_params']['this->object()'];
$aAjaxParams = array('this->class' => get_class($oObj), 'this->id' => $oObj->GetKey());
}
else
{
$aAjaxParams = $aExtraParams;
}
if (!$bEditMode && !$oPage->IsPrintableVersion())
{
$sId = $this->GetId();
$sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $sId);
if ($this->GetAutoReload())
{
$sFile = addslashes($this->GetDefinitionFile());
$sExtraParams = json_encode($aAjaxParams);
$iReloadInterval = 1000 * $this->GetAutoReloadInterval();
$sReloadURL = $this->GetReloadURL();
$oPage->add_script(
<<<EOF
if (typeof(AutoReloadDashboardId$sDivId) !== 'undefined')
{
clearInterval(AutoReloadDashboardId$sDivId);
delete AutoReloadDashboardId$sDivId;
}
AutoReloadDashboardId$sDivId = setInterval("ReloadDashboard$sDivId();", $iReloadInterval);
function ReloadDashboard$sDivId()
{
// Do not reload when a dialog box is active
if (!($('.ui-dialog:visible').length > 0) && $('.dashboard_contents#$sDivId').is(':visible'))
{
$('.dashboard_contents#$sDivId').block();
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
{ operation: 'reload_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, reload_url: '$sReloadURL'},
function(data){
$('.dashboard_contents#$sDivId').html(data);
$('.dashboard_contents#$sDivId').unblock();
}
);
}
}
EOF
);
}
else
{
$oPage->add_script(
<<<EOF
if (typeof(AutoReloadDashboardId$sDivId) !== 'undefined')
{
clearInterval(AutoReloadDashboardId$sDivId);
delete AutoReloadDashboardId$sDivId;
}
EOF
);
}
if ($bCanEdit)
{
$this->RenderSelector($oPage, $aAjaxParams);
$this->RenderEditionTools($oPage, $aAjaxParams);
}
}
}
/**
* @param \iTopWebPage $oPage
* @param array $aAjaxParams
*/
protected function RenderSelector($oPage, $aAjaxParams = array())
{
$sId = $this->GetId();
$sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $sId);
$sExtraParams = json_encode($aAjaxParams);
$sSelectorHtml = '<div class="dashboard-selector">';
if ($this->HasCustomDashboard())
{
$bStandardSelected = appUserPreferences::GetPref('display_original_dashboard_'.$sId, false);
$sStandard = Dict::S('UI:Toggle:StandardDashboard');
$sSelectorHtml .= '<div class="selector-label">'.$sStandard.'</div>';
$sSelectorHtml .= '<label class="switch"><input type="checkbox" onchange="ToggleDashboardSelector'.$sDivId.'();" '.($bStandardSelected ? '' : 'checked').'><span class="slider round"></span></label></input></label>';
$sCustom = Dict::S('UI:Toggle:CustomDashboard');
$sSelectorHtml .= '<div class="selector-label">'.$sCustom.'</div>';
}
$sSelectorHtml .= '</div>';
$sSelectorHtml = addslashes($sSelectorHtml);
$sFile = addslashes($this->GetDefinitionFile());
$sReloadURL = $this->GetReloadURL();
$oPage->add_ready_script(
<<<EOF
$('.dashboard-title').after('$sSelectorHtml');
EOF
);
$oPage->add_script(
<<<EOF
function ToggleDashboardSelector$sDivId()
{
$('.dashboard_contents#$sDivId').block();
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
{ operation: 'toggle_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, reload_url: '$sReloadURL' },
function(data) {
$('.dashboard_contents#$sDivId').html(data);
$('.dashboard_contents#$sDivId').unblock();
}
);
}
EOF
);
}
protected function HasCustomDashboard()
{
try
{
// Search for an eventual user defined dashboard
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oUDSearch->AddCondition('menu_code', $this->GetId(), '=');
$oUDSet = new DBObjectSet($oUDSearch);
return ($oUDSet->Count() > 0);
}
catch (Exception $e)
{
return false;
}
}
/**
* @param \WebPage $oPage
* @param array $aExtraParams
*
* @throws \Exception
*/
protected function RenderEditionTools(WebPage $oPage, $aExtraParams)
{
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/pencil-menu.png\"><ul>";
$sEditMenu = "<div id=\"DashboardMenu\"><ul><li><img src=\"../images/pencil-menu.png\"><ul>";
$aActions = array();
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}')");
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
$sFile = addslashes($this->sDefinitionFile);
$sJSExtraParams = json_encode($aExtraParams);
$bCanEdit = true;
if ($this->HasCustomDashboard())
{
$bCanEdit = !appUserPreferences::GetPref('display_original_dashboard_'.$this->GetId(), false);
}
if ($bCanEdit)
{
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}', '$sFile', $sJSExtraParams)");
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
}
if ($this->bCustomized)
{
$oRevert = new JSPopupMenuItem('UI:Dashboard:RevertConfirm', Dict::S('UI:Dashboard:Revert'),
"if (confirm('".addslashes(Dict::S('UI:Dashboard:RevertConfirm'))."')) return RevertDashboard('{$this->sId}'); else return false");
"if (confirm('".addslashes(Dict::S('UI:Dashboard:RevertConfirm'))."')) return RevertDashboard('{$this->sId}', $sJSExtraParams); else return false");
$aActions[$oRevert->GetUID()] = $oRevert->GetMenuItem();
}
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_DASHBOARD_ACTIONS, $this, $aActions);
$sEditMenu .= $oPage->RenderPopupMenuItems($aActions);
$sEditMenu = addslashes($sEditMenu);
//$sEditBtn = addslashes('<div style="display: inline-block; height: 55px; width:200px;vertical-align:center;line-height:60px;text-align:left;"><button onclick="EditDashboard(\''.$this->sId.'\');">Edit This Page</button></div>');
$sReloadURL = $this->GetReloadURL();
$oPage->add_ready_script(
<<<EOF
$('#logOffBtn').parent().before('$sEditMenu');
$('.dashboard-title').after('$sEditMenu');
$('#DashboardMenu>ul').popupmenu();
EOF
);
$oPage->add_script(
<<<EOF
function EditDashboard(sId)
function EditDashboard(sId, sDashboardFile, aExtraParams)
{
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'dashboard_editor', id: sId},
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'dashboard_editor', id: sId, file: sDashboardFile, extra_params: aExtraParams, reload_url: '$sReloadURL'},
function(data)
{
$('body').append(data);
@@ -629,12 +924,12 @@ function EditDashboard(sId)
);
return false;
}
function RevertDashboard(sId)
function RevertDashboard(sId, aExtraParams)
{
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'revert_dashboard', dashboard_id: sId},
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'revert_dashboard', dashboard_id: sId, extra_params: aExtraParams, reload_url: '$sReloadURL'},
function(data)
{
$('body').append(data);
location.reload();
}
);
return false;
@@ -643,9 +938,14 @@ EOF
);
}
public function RenderProperties($oPage)
/**
* @param \WebPage $oPage
*
* @throws \ReflectionException
*/
public function RenderProperties($oPage, $aExtraParams = array())
{
parent::RenderProperties($oPage);
parent::RenderProperties($oPage, $aExtraParams);
$oPage->add_ready_script(
<<<EOF
@@ -676,16 +976,36 @@ EOF
}
public function RenderEditor($oPage)
/**
* @param \iTopWebPage $oPage
*
* @param array $aExtraParams
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \ReflectionException
* @throws \Exception
*/
public function RenderEditor($oPage, $aExtraParams = array())
{
if (isset($aExtraParams['this->class']))
{
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
$aRenderParams = array('query_params' => $oObj->ToArgsForQuery());
}
else
{
$aRenderParams = $aExtraParams;
}
$sJSExtraParams = json_encode($aExtraParams);
$oPage->add('<div id="dashboard_editor">');
$oPage->add('<div class="ui-layout-center">');
$this->Render($oPage, true);
$this->Render($oPage, true, $aRenderParams);
$oPage->add('</div>');
$oPage->add('<div class="ui-layout-east">');
$this->RenderProperties($oPage);
$this->RenderProperties($oPage, $aExtraParams);
$this->RenderDashletsSelection($oPage);
$this->RenderDashletsProperties($oPage);
$this->RenderDashletsProperties($oPage, $aExtraParams);
$oPage->add('</div>');
$oPage->add('<div id="event_bus"/>'); // For exchanging messages between the panes, same as in the designer
$oPage->add('</div>');
@@ -699,7 +1019,9 @@ EOF
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
$sAutoReloadSec = (string) $this->iAutoReloadSec;
$sTitle = addslashes($this->sTitle);
$sFile = addslashes($this->GetDefinitionFile());
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
$sReloadURL = $this->GetReloadURL();
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
@@ -729,7 +1051,7 @@ $('#dashboard_editor').dialog({
}
}
window.bLeavingOnUserAction = true;
oDashboard.save();
oDashboard.save($(this));
} },
{ text: "$sCancelButtonLabel", click: function() {
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
@@ -751,8 +1073,8 @@ $('#dashboard_editor').dialog({
$('#dashboard_editor .ui-layout-center').runtimedashboard({
dashboard_id: '$sId', layout_class: '$sLayoutClass', title: '$sTitle',
auto_reload: $sAutoReload, auto_reload_sec: $sAutoReloadSec,
submit_to: '$sUrl', submit_parameters: {operation: 'save_dashboard'},
render_to: '$sUrl', render_parameters: {operation: 'render_dashboard'},
submit_to: '$sUrl', submit_parameters: {operation: 'save_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
render_to: '$sUrl', render_parameters: {operation: 'render_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
new_dashlet_parameters: {operation: 'new_dashlet'}
});
@@ -835,7 +1157,8 @@ EOF
$sParentId = $aParentMenu['parent'];
$aParentMenu = $aParentMenus[$sParentId];
}
$oParentMenu = $aParentMenu['node'];
/** @var \MenuNode $oParentMenu */
$oParentMenu = $aParentMenu['node'];
if ($oMenu->IsEnabled() && $oParentMenu->IsEnabled())
{
$sMenuLabel = $oMenu->GetTitle();
@@ -890,6 +1213,7 @@ EOF
{
$oSubForm = new DesignerForm();
$oMetaModel = new ModelReflectionRuntime();
/** @var \Dashlet $oDashlet */
$oDashlet = new $sDashletClass($oMetaModel, 0);
$oDashlet->GetPropertiesFieldsFromOQL($oSubForm, $sOQL);
@@ -900,7 +1224,11 @@ EOF
return $oForm;
}
/**
* @param \WebPage $oPage
* @param $sOQL
*/
public static function GetDashletCreationDlgFromOQL($oPage, $sOQL)
{
$oPage->add('<div id="dashlet_creation_dlg">');
@@ -947,4 +1275,30 @@ $('#dashlet_creation_dlg').dialog({
EOF
);
}
/**
* @return string
*/
public function GetDefinitionFile()
{
return $this->sDefinitionFile;
}
/**
* @param string $sDefinitionFile
*/
public function SetDefinitionFile($sDefinitionFile)
{
$this->sDefinitionFile = $sDefinitionFile;
}
public function GetReloadURL()
{
return $this->sReloadURL;
}
public function SetReloadURL($sReloadURL)
{
$this->sReloadURL = $sReloadURL;
}
}

View File

@@ -59,6 +59,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$bNoVisibleFound = true;
while($idx < count($aKeys) && $bNoVisibleFound)
{
/** @var \Dashlet $oDashlet */
$oDashlet = $aDashlets[$aKeys[$idx]];
if ($oDashlet->IsVisible())
{
@@ -98,7 +99,13 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
return $aCells;
}
/**
* @param \WebPage $oPage
* @param $aCells
* @param bool $bEditMode
* @param array $aExtraParams
*/
public function Render($oPage, $aCells, $bEditMode = false, $aExtraParams = array())
{
// Trim the list of cells to remove the invisible/empty ones at the end of the array
@@ -122,6 +129,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$aDashlets = $aCells[$iCellIdx];
if (count($aDashlets) > 0)
{
/** @var \Dashlet $oDashlet */
foreach($aDashlets as $oDashlet)
{
if ($oDashlet->IsVisible())

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.5">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
<portals>
<portal id="legacy_portal" _delta="define">
<url>portal/index.php</url>

View File

@@ -756,6 +756,10 @@ class DataTableSettings implements Serializable
{
foreach($this->aClassAliases as $sAlias => $sClass)
{
if (!isset($this->aColumns[$sAlias]))
{
continue;
}
foreach($this->aColumns[$sAlias] as $sAttCode => $aData)
{
// Remove non-existent columns
@@ -771,7 +775,7 @@ class DataTableSettings implements Serializable
$aTempData = array();
foreach($aList as $sAttCode => $oAttDef)
{
if ( (!array_key_exists($sAttCode, $this->aColumns[$sAlias])) && (!$oAttDef instanceof AttributeLinkSet))
if ( (!array_key_exists($sAttCode, $this->aColumns[$sAlias])) && (!($oAttDef instanceof AttributeLinkedSet || $oAttDef instanceof AttributeDashboard)))
{
$aFieldData = $this->GetFieldData($sAlias, $sAttCode, $oAttDef, false /* bChecked */, 'none');
if ($aFieldData) $aTempData[$aFieldData['label']] = $aFieldData;

View File

@@ -220,9 +220,26 @@ class DisplayBlock
$aExtraParams['currentId'] = $sId;
$sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them
if (isset($aExtraParams['query_params']))
{
$aQueryParams = $aExtraParams['query_params'];
}
else
{
if (isset($aExtraParams['this->id']) && isset($aExtraParams['this->class']))
{
$sClass = $aExtraParams['this->class'];
$iKey = $aExtraParams['this->id'];
$oObj = MetaModel::GetObject($sClass, $iKey);
$aQueryParams = array('this->object()' => $oObj);
}
else
{
$aQueryParams = array();
}
}
$sFilter = $this->m_oFilter->serialize(); // Used either for asynchronous or auto_reload
$sFilter = addslashes($this->m_oFilter->serialize(false, $aQueryParams)); // Used either for asynchronous or auto_reload
if (!$this->m_bAsynchronous)
{
// render now
@@ -314,20 +331,31 @@ class DisplayBlock
* @throws MySQLException
* @throws Exception
*/
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
public function GetRenderContent(WebPage $oPage, $aExtraParams, $sId)
{
$sHtml = '';
// Add the extra params into the filter if they make sense for such a filter
$bDoSearch = utils::ReadParam('dosearch', false);
$aQueryParams = array();
if (isset($aExtraParams['query_params']))
{
$aQueryParams = $aExtraParams['query_params'];
}
else
{
if (isset($aExtraParams['this->id']) && isset($aExtraParams['this->class']))
{
$sClass = $aExtraParams['this->class'];
$iKey = $aExtraParams['this->id'];
$oObj = MetaModel::GetObject($sClass, $iKey);
$aQueryParams = array('this->object()' => $oObj);
}
}
if ($this->m_oSet == null)
{
$aQueryParams = array();
if (isset($aExtraParams['query_params']))
{
$aQueryParams = $aExtraParams['query_params'];
}
// In case of search, the context filtering is done by the search itself
if (($this->m_sStyle != 'links') && ($this->m_sStyle != 'search'))
if (($this->m_sStyle != 'links') && ($this->m_sStyle != 'search') && ($this->m_sStyle != 'list_search'))
{
$oAppContext = new ApplicationContext();
$sClass = $this->m_oFilter->GetClass();
@@ -457,7 +485,15 @@ class DisplayBlock
$oSubsetSearch = $this->m_oFilter->DeepClone();
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($aValues[$iRow]));
$oSubsetSearch->AddConditionExpression($oCondition);
$sFilter = urlencode($oSubsetSearch->serialize());
if (isset($aExtraParams['query_params']))
{
$aQueryParams = $aExtraParams['query_params'];
}
else
{
$aQueryParams = array();
}
$sFilter = rawurlencode($oSubsetSearch->serialize(false, $aQueryParams));
$aData[] = array ('group' => $aLabels[$iRow],
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>"); // TO DO: add the context information
@@ -580,6 +616,7 @@ class DisplayBlock
}
break;
case 'list_search':
case 'list':
$aClasses = $this->m_oSet->GetSelectedClasses();
$aAuthorizedClasses = array();
@@ -715,7 +752,7 @@ class DisplayBlock
$sClass = $this->m_oFilter->GetClass();
$oAppContext = new ApplicationContext();
$bContextFilter = isset($aExtraParams['context_filter']) ? isset($aExtraParams['context_filter']) != 0 : false;
if ($bContextFilter)
if ($bContextFilter && is_null($this->m_oSet))
{
foreach($oAppContext->GetNames() as $sFilterCode)
{
@@ -734,7 +771,7 @@ class DisplayBlock
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
}
$iCount = $this->m_oSet->Count();
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($this->m_oFilter->serialize());
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize());
$sHtml .= '<p><a class="actions" href="'.$sHyperlink.'">';
// Note: border set to 0 due to various browser interpretations (IE9 adding a 2px border)
$sHtml .= MetaModel::GetClassIcon($sClass, true, 'float;left;margin-right:10px;border:0;');
@@ -823,7 +860,7 @@ class DisplayBlock
}
$sHyperlink = utils::GetAbsoluteUrlAppRoot()
.'pages/UI.php?operation=search&'.$oAppContext->GetForLink()
.'&filter='.urlencode($oSingleGroupByValueFilter->serialize());
.'&filter='.rawurlencode($oSingleGroupByValueFilter->serialize());
$aCounts[$sStateValue] = "<a href=\"$sHyperlink\">{$aCounts[$sStateValue]}</a>";
}
}
@@ -832,7 +869,7 @@ class DisplayBlock
$sHtml .= '<tr><td>'.implode('</td><td>', $aCounts).'</td></tr></table></div>';
// Title & summary
$iCount = $this->m_oSet->Count();
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($this->m_oFilter->serialize());
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize());
$sHtml .= '<h1>'.Dict::S(str_replace('_', ':', $sTitle)).'</h1>';
$sHtml .= '<a class="summary" href="'.$sHyperlink.'">'.Dict::Format(str_replace('_', ':', $sLabel), $iCount).'</a>';
$sHtml .= '<div style="clear:both;"></div>';
@@ -843,7 +880,7 @@ class DisplayBlock
$sCsvFile = strtolower($this->m_oFilter->GetClass()).'.csv';
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?expression='.urlencode($this->m_oFilter->ToOQL(true)).'&format=csv&filename='.urlencode($sCsvFile);
$sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($this->m_oFilter->serialize()).'&format=csv';
$sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize()).'&format=csv';
// Pass the parameters via POST, since expression may be very long
$aParamsToPost = array(
'expression' => $this->m_oFilter->ToOQL(true),
@@ -913,10 +950,10 @@ class DisplayBlock
$sChartType = isset($aExtraParams['chart_type']) ? $aExtraParams['chart_type'] : 'pie';
$sTitle = isset($aExtraParams['chart_title']) ? '<h1 style="text-align:center">'.htmlentities(Dict::S($aExtraParams['chart_title']), ENT_QUOTES, 'UTF-8').'</h1>' : '';
$sHtml = "$sTitle<div style=\"height:200px;width:100%\" id=\"my_chart_$sId{$iChartCounter}\"><div style=\"height:200px;line-height:200px;vertical-align:center;text-align:center;width:100%\"><img src=\"../images/indicator.gif\"></div></div>\n";
$sHtml = "$sTitle<div style=\"height:200px;width:100%\" class=\"dashboard_chart\" id=\"my_chart_$sId{$iChartCounter}\"><div style=\"height:200px;line-height:200px;vertical-align:center;text-align:center;width:100%\"><img src=\"../images/indicator.gif\"></div></div>\n";
$sGroupBy = isset($aExtraParams['group_by']) ? $aExtraParams['group_by'] : '';
$sGroupByExpr = isset($aExtraParams['group_by_expr']) ? '&params[group_by_expr]='.$aExtraParams['group_by_expr'] : '';
$sFilter = $this->m_oFilter->serialize();
$sFilter = $this->m_oFilter->serialize(false, $aQueryParams);
$oContext = new ApplicationContext();
$sContextParam = $oContext->GetForLink();
$sAggregationFunction = isset($aExtraParams['aggregation_function']) ? $aExtraParams['aggregation_function'] : '';
@@ -927,11 +964,11 @@ class DisplayBlock
if (isset($aExtraParams['group_by_label']))
{
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$sId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$sId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam);
}
else
{
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[chart_type]=$sChartType&params[currentId]=$sId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[chart_type]=$sChartType&params[currentId]=$sId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam);
}
$oPage->add_ready_script(
@@ -971,7 +1008,7 @@ EOF
$oSubsetSearch = $this->m_oFilter->DeepClone();
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
$oSubsetSearch->AddConditionExpression($oCondition);
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".urlencode($oSubsetSearch->serialize()).'&'.$sContextParam;
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".rawurlencode($oSubsetSearch->serialize()).'&'.$sContextParam;
}
$sJSURLs = json_encode($aURLs);
}
@@ -989,6 +1026,7 @@ EOF
$sJson = json_encode($aValues);
$oPage->add_ready_script(
<<<EOF
var chart = c3.generate({
bindto: d3.select('#my_chart_$sId'),
data: {
@@ -1036,6 +1074,12 @@ var chart = c3.generate({
}
}
});
if (typeof(charts) === "undefined")
{
charts = [];
}
charts.push(chart);
EOF
);
break;
@@ -1074,6 +1118,12 @@ var chart = c3.generate({
}
}
});
if (typeof(charts) === "undefined")
{
charts = [];
}
charts.push(chart);
EOF
);
break;
@@ -1122,7 +1172,7 @@ EOF
}
if (($bAutoReload) && ($this->m_sStyle != 'search')) // Search form do NOT auto-reload
{
$sFilter = $this->m_oFilter->serialize(); // Used either for asynchronous or auto_reload
$sFilter = addslashes(str_replace('"', "'", $this->m_oFilter->serialize())); // Used either for asynchronous or auto_reload
$sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them
$oPage->add_script('if (typeof window.oAutoReloadBlock == "undefined") {
@@ -1131,7 +1181,7 @@ EOF
if (typeof window.oAutoReloadBlock[\''.$sId.'\'] != "undefined") {
clearInterval(window.oAutoReloadBlock[\''.$sId.'\']);
}
window.oAutoReloadBlock[\''.$sId.'\'] = setInterval("ReloadBlock(\''.$sId.'\', \''.$this->m_sStyle.'\', \''.$sFilter.'\', \"'.$sExtraParams.'\")", '.$iReloadInterval.');');
window.oAutoReloadBlock[\''.$sId.'\'] = setInterval("ReloadBlock(\''.$sId.'\', \''.$this->m_sStyle.'\', \"'.$sFilter.'\", \"'.$sExtraParams.'\")", '.$iReloadInterval.');');
}
return $sHtml;
@@ -1391,7 +1441,7 @@ class HistoryBlock extends DisplayBlock
default:
if ($bTruncated)
{
$sFilter = $this->m_oFilter->serialize();
$sFilter = htmlentities($this->m_oFilter->serialize(), ENT_QUOTES, 'UTF-8');
$sHtml .= '<div id="history_container"><p>';
$sHtml .= Dict::Format('UI:TruncatedResults', $this->iLimitCount, $oSet->Count());
$sHtml .= ' ';

View File

@@ -378,9 +378,9 @@ $('#$sDialogId').dialog({
}
}
} },
{ text: "$sCancelButtonLabel", click: function() { KillAllMenus(); $(this).dialog( "close" ); $(this).remove(); } },
{ text: "$sCancelButtonLabel", click: function() { $(this).dialog( "close" ); $(this).remove(); } },
],
close: function() { KillAllMenus(); $(this).remove(); }
close: function() { $(this).remove(); }
});
var oForm = $('#$sDialogId form');
var sFormId = oForm.attr('id');
@@ -682,6 +682,7 @@ class DesignerFormField
protected $sLabel;
protected $sCode;
protected $defaultValue;
/** @var \DesignerForm $oForm */
protected $oForm;
protected $bMandatory;
protected $bReadOnly;
@@ -707,8 +708,11 @@ class DesignerFormField
{
return $this->sCode;
}
public function SetForm($oForm)
/**
* @param \DesignerForm $oForm
*/
public function SetForm(\DesignerForm $oForm)
{
$this->oForm = $oForm;
}

File diff suppressed because it is too large Load Diff

View File

@@ -50,6 +50,9 @@ class LoginWebPage extends NiceWebPage
self::$sHandlerClass = $sClass;
}
/**
* @return \LoginWebPage
*/
public static function NewLoginWebPage()
{
return new self::$sHandlerClass;

View File

@@ -84,14 +84,11 @@ class ApplicationMenu
{
// Build menus from module handlers
//
foreach(get_declared_classes() as $sPHPClass)
{
if (is_subclass_of($sPHPClass, 'ModuleHandlerAPI'))
{
$aCallSpec = array($sPHPClass, 'OnMenuCreation');
call_user_func($aCallSpec);
}
}
/** @var \ModuleHandlerApiInterface $oPHPClass */
foreach(MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass)
{
$oPHPClass::OnMenuCreation();
}
// Build menus from the menus themselves (e.g. the ShortcutContainerMenuNode will do that)
//
@@ -125,9 +122,11 @@ class ApplicationMenu
}
/**
* Check wether a menu Id is enabled or not
* Check whether a menu Id is enabled or not
*
* @param $sMenuId
* @throws DictExceptionMissingString
*
* @throws \Exception
*/
static public function CheckMenuIdEnabled($sMenuId)
{
@@ -167,7 +166,9 @@ class ApplicationMenu
}
else
{
$sParentId = self::$aMenusIndex[$iParentIndex]['node']->GetMenuId();
/** @var \MenuNode $oNode */
$oNode = self::$aMenusIndex[$iParentIndex]['node'];
$sParentId = $oNode->GetMenuId();
self::$aMenusIndex[$iParentIndex]['children'][] = array ('rank' => $fRank, 'index' => $index);
}
@@ -181,7 +182,9 @@ class ApplicationMenu
else
{
// the menu already exists, let's combine the conditions that make it visible
self::$aMenusIndex[$index]['node']->AddCondition($oMenuNode);
/** @var \MenuNode $oNode */
$oNode = self::$aMenusIndex[$index]['node'];
$oNode->AddCondition($oMenuNode);
}
return $index;
@@ -198,7 +201,7 @@ class ApplicationMenu
/**
* Entry point to display the whole menu into the web page, used by iTopWebPage
* @param $oPage
* @param \iTopWebPage $oPage
* @param $aExtraParams
* @throws DictExceptionMissingString
*/
@@ -208,6 +211,7 @@ class ApplicationMenu
// Sort the root menu based on the rank
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
$iAccordion = 0;
$iActiveAccordion = $iAccordion;
$iActiveMenu = self::GetMenuIndexById(self::GetActiveNodeId());
foreach(self::$aRootMenus as $aMenu)
{
@@ -221,16 +225,18 @@ class ApplicationMenu
$oPage->AddToMenu('</ul>');
if ($bActive)
{
$oPage->add_ready_script(
<<<EOF
// Accordion Menu
$("#accordion").css({display:'block'}).accordion({ header: "h3", navigation: true, heightStyle: "content", collapsible: true, active: $iAccordion, icons: false, animate:true }); // collapsible will be enabled once the item will be selected
EOF
);
$iActiveAccordion = $iAccordion;
}
$oPage->AddToMenu('</div>');
$iAccordion++;
}
$oPage->add_ready_script(
<<<EOF
// Accordion Menu
$("#accordion").css({display:'block'}).accordion({ header: "h3", heightStyle: "content", collapsible: true, active: $iActiveAccordion, icons: false, animate: true }); // collapsible will be enabled once the item will be selected
EOF
);
}
/**
@@ -264,7 +270,7 @@ EOF
/**
* Handles the display of the sub-menus (called recursively if necessary)
* @param WebPage $oPage
* @param \iTopWebPage $oPage
* @param array $aMenus
* @param array $aExtraParams
* @param int $iActiveMenu
@@ -356,6 +362,7 @@ EOF
static public function GetMenuIndexById($sTitle)
{
$index = -1;
/** @var MenuNode[] $aMenu */
foreach(self::$aMenusIndex as $aMenu)
{
if ($aMenu['node']->GetMenuId() == $sTitle)
@@ -837,7 +844,7 @@ class OQLMenuNode extends MenuNode
/**
* Set some extra parameters to be passed to the display block to fine tune its appearence
* @param Hash $aParams paramCode => value. See DisplayBlock::GetDisplay for the meaning of the parameters
* @param array $aParams paramCode => value. See DisplayBlock::GetDisplay for the meaning of the parameters
*/
public function SetParameters($aParams)
{
@@ -950,7 +957,7 @@ class SearchMenuNode extends MenuNode
}
/**
* @param WebPage $oPage
* @param \iTopWebPage $oPage
* @param array $aExtraParams
* @return mixed|void
* @throws DictExceptionMissingString
@@ -1054,7 +1061,9 @@ class NewObjectMenuNode extends MenuNode
/**
* @param string[] $aExtraParams
*
* @return string
* @throws \Exception
*/
public function GetHyperlink($aExtraParams)
{
@@ -1143,37 +1152,11 @@ class DashboardMenuNode extends MenuNode
*/
public function GetDashboard()
{
$sDashboardDefinition = @file_get_contents($this->sDashboardFile);
if ($sDashboardDefinition !== false)
{
$bCustomized = false;
// Search for an eventual user defined dashboard, overloading the existing one
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oUDSearch->AddCondition('menu_code', $this->sMenuId, '=');
$oUDSet = new DBObjectSet($oUDSearch);
if ($oUDSet->Count() > 0)
{
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
$sDashboardDefinition = $oUserDashboard->Get('contents');
$bCustomized = true;
}
$oDashboard = new RuntimeDashboard($this->sMenuId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
}
else
{
$oDashboard = null;
}
return $oDashboard;
return RuntimeDashboard::GetDashboard($this->sDashboardFile, $this->sMenuId);
}
/**
* @param WebPage $oPage
* @param \iTopWebPage $oPage
* @param string[] $aExtraParams
* @throws CoreException
* @throws Exception
@@ -1186,38 +1169,9 @@ class DashboardMenuNode extends MenuNode
{
$sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $this->sMenuId);
$oPage->add('<div class="dashboard_contents" id="'.$sDivId.'">');
$oDashboard->SetReloadURL($this->GetHyperlink($aExtraParams));
$oDashboard->Render($oPage, false, $aExtraParams);
$oPage->add('</div>');
$oDashboard->RenderEditionTools($oPage);
if ($oDashboard->GetAutoReload())
{
$sId = $this->sMenuId;
$sExtraParams = json_encode($aExtraParams);
$iReloadInterval = 1000 * $oDashboard->GetAutoReloadInterval();
$oPage->add_script(
<<<EOF
setInterval("ReloadDashboard('$sDivId');", $iReloadInterval);
function ReloadDashboard(sDivId)
{
var oExtraParams = $sExtraParams;
// Do not reload when a dialog box is active
if (!($('.ui-dialog:visible').length > 0))
{
$('.dashboard_contents#'+sDivId).block();
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
{ operation: 'reload_dashboard', dashboard_id: '$sId', extra_params: oExtraParams},
function(data){
$('.dashboard_contents#'+sDivId).html(data);
$('.dashboard_contents#'+sDivId).unblock();
}
);
}
}
EOF
);
}
$bEdit = utils::ReadParam('edit', false);
if ($bEdit)

View File

@@ -0,0 +1,159 @@
<?php
// Copyright (C) 2010-2015 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/>
/**
* A provider for messages to be displayed in the iTop Newsroom
*/
interface iNewsroomProvider
{
/**
* Inject the current configuration in the provider
* @param Config $oConfig
* @return void
*/
public function SetConfig(Config $oConfig);
/**
* Tells if this provider is enabled for the given user
* @param User $oUser The user for who to check if the provider is applicable.
* return bool
*/
public function IsApplicable(User $oUser = null);
/**
* The human readable (localized) label for this provider
* @return string
*/
public function GetLabel();
/**
* The URL to query (from the browser, using jsonp) to fetch all unread messages
* @return string
*/
public function GetFetchURL();
/**
* The URL to navigate to in order to display all messages
* @return string
*/
public function GetViewAllURL();
/**
* The URL to query(from the browser, using jsonp) to mark all unread messages as read
* @return string
*/
public function GetMarkAllAsReadURL();
/**
* Return the URL to configure the preferences for this provider or null is there is nothing to configure
* @return string|null
*/
public function GetPreferencesUrl();
/**
* Return an array key => value to be replaced in URL of the messages
* Example: '%itop_root%' => utils::GetAbsoluteUrlAppRoot();
* @return string[]
*/
public function GetPlaceholders();
/**
* The duration between to refreshes of the cache (in seconds)
* @return int
*/
public function GetTTL();
}
/**
* Basic implementation of a Newsroom provider, to be overloaded by your own provider implementation
*
*/
abstract class NewsroomProviderBase implements iNewsroomProvider
{
/**
* The current configuration parameters
* @var Config
*/
protected $oConfig;
public function __construct()
{
$this->oConfig = null;
}
/**
* {@inheritDoc}
* @see iNewsroomProvider::SetConfig()
*/
public function SetConfig(Config $oConfig)
{
$this->oConfig = $oConfig;
}
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetPreferencesUrl()
*/
public function GetPreferencesUrl()
{
return null; // No preferences
}
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetLabel()
*/
public abstract function GetLabel();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetFetchURL()
*/
public abstract function GetFetchURL();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetMarkAllURL()
*/
public abstract function GetMarkAllAsReadURL();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetViewAllURL()
*/
public abstract function GetViewAllURL();
public function IsApplicable(User $oUser = null)
{
return false;
}
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetPlaceholders()
*/
public function GetPlaceholders()
{
return array(); // By default, empty set of placeholders
}
public function GetTTL()
{
return 10*60; // Refresh every 10 minutes
}
}

View File

@@ -37,8 +37,15 @@ class NiceWebPage extends WebPage
{
parent::__construct($s_title, $bPrintable);
$this->m_aReadyScripts = array();
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-1.12.4.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-1.4.1.min.js'); // Needed since many other plugins still rely on oldies like $.browser
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-3.3.1.min.js');
if(utils::IsDevelopmentEnvironment()) // Needed since many other plugins still rely on oldies like $.browser
{
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-3.0.1.dev.js');
}
else
{
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-3.0.1.prod.min.js');
}
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/ui-lightness/jquery-ui-1.11.4.custom.css');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui-1.11.4.custom.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/utils.js');
@@ -47,6 +54,7 @@ class NiceWebPage extends WebPage
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.tablesorter.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.tablesorter.pager.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.tablehover.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/table-selectable-lines.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/field_sorter.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/datatable.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.positionBy.js');
@@ -61,6 +69,7 @@ class NiceWebPage extends WebPage
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_field.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_numeric.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_enum.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_tag_set.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_key.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_hierarchical_key.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_abstract.js');

View File

@@ -35,7 +35,7 @@ class iTopPDF extends TCPDF
// Display the page number (right aligned)
// Warning: the 'R'ight alignment does not work when using placeholders like $this->getAliasNumPage() or $this->getAliasNbPages()
$this->MultiCell($iPageNumberWidth, 15, 'Page '.$this->page, 0, 'R', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
$this->MultiCell($iPageNumberWidth, 15, Dict::Format('Core:BulkExport:PDF:PageNumber' ,$this->page), 0, 'R', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
// Branding logo
$sBrandingIcon = APPROOT.'images/itop-logo.png';

View File

@@ -807,7 +807,7 @@ EOF
*/
public function DoUpdateObjectFromPostedForm(DBObject $oObj, $aAttList = null)
{
$sTransactionId = utils::ReadPostedParam('transaction_id', '');
$sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id');
if (!utils::IsTransactionValid($sTransactionId))
{
throw new TransactionException();

View File

@@ -47,13 +47,12 @@ abstract class Query extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'fields')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'fields')); // Criteria of the std search form
MetaModel::Init_SetZListItems('standard_search', array('name', 'description')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
@@ -78,6 +77,9 @@ class QueryOQL extends Query
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array("allowed_values"=>null, "sql"=>"oql", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Rolled back to AttributeText until AttributeQueryAttCodeSet can manage fields order correctly
//MetaModel::Init_AddAttribute(new AttributeQueryAttCodeSet("fields", array("allowed_values"=>null,"max_items" => 1000, "query_field" => "oql", "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array('oql'))));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'oql', 'fields')); // Attributes to be displayed for the complete details
@@ -136,6 +138,41 @@ class QueryOQL extends Query
}
return $aFieldsMap;
}
// Rolled back until 'fields' can be properly managed by AttributeQueryAttCodeSet
//
// public function ComputeValues()
// {
// parent::ComputeValues();
//
// // Remove unwanted attribute codes
// $aChanges = $this->ListChanges();
// if (isset($aChanges['fields']))
// {
// $oAttDef = MetaModel::GetAttributeDef(get_class($this), 'fields');
// $aArgs = array('this' => $this);
// $aAllowedValues = $oAttDef->GetAllowedValues($aArgs);
//
// /** @var \ormSet $oValue */
// $oValue = $this->Get('fields');
// $aValues = $oValue->GetValues();
// $bChanged = false;
// foreach($aValues as $key => $sValue)
// {
// if (!isset($aAllowedValues[$sValue]))
// {
// unset($aValues[$key]);
// $bChanged = true;
// }
// }
// if ($bChanged)
// {
// $oValue->SetValues($aValues);
// $this->Set('fields', $oValue);
// }
// }
// }
}
?>

View File

@@ -24,6 +24,26 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
// This storage is freed on error (case of allowed memory exhausted)
$sReservedMemory = str_repeat('*', 1024 * 1024);
register_shutdown_function(function()
{
global $sReservedMemory;
$sReservedMemory = null;
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
{
if (strpos($err['message'], 'Allowed memory size of') !== false)
{
$sLimit = ini_get('memory_limit');
echo "<p>iTop: Allowed memory size of $sLimit exhausted, contact your administrator to increase memory_limit in php.ini</p>\n";
}
else
{
echo "<p>iTop: An error occurred, check server error log for more information.</p>\n";
}
}
});
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/core/contexttag.class.inc.php');

View File

@@ -119,7 +119,7 @@ class privUITransactionSession
// Strictly speaking, the two lines below should be grouped together
// by a critical section
// sem_acquire($rSemIdentified);
$id = str_replace(array('.', ' '), '', microtime()); //1 + count($_SESSION['transactions']);
$id = static::GetUserPrefix() . str_replace(array('.', ' '), '', microtime()); //1 + count($_SESSION['transactions']);
$_SESSION['transactions'][$id] = true;
// sem_release($rSemIdentified);
@@ -174,6 +174,17 @@ class privUITransactionSession
// sem_release($rSemIdentified);
}
}
/**
* Returns a string to prefix transaction ID with info from the current user.
*
* @return string
*/
protected static function GetUserPrefix()
{
$sPrefix = 'u'.UserRights::GetUserId();
return $sPrefix.'-';
}
}
/**
@@ -206,7 +217,7 @@ class privUITransactionFile
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
}
self::CleanupOldTransactions();
$id = basename(tempnam(APPROOT.'data/transactions', self::GetUserPrefix()));
$id = basename(tempnam(APPROOT.'data/transactions', static::GetUserPrefix()));
self::Info('GetNewTransactionId: Created transaction: '.$id);
return (string)$id;
@@ -310,6 +321,11 @@ class privUITransactionFile
return $aResult;
}
/**
* Returns a prefix based on the user login instead of its ID for a better usage in tempnam()
*
* @inheritdoc
*/
protected static function GetUserPrefix()
{
$sPrefix = substr(UserRights::GetUser(), 0, 10);

View File

@@ -332,7 +332,8 @@ EOF
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
$aArgs = array('this' => $oCurrObject);
/** @var \DBObject $oCurrObject */
$aArgs = $oCurrObject->ToArgsForQuery();
$aParams = array('query_params' => $aArgs);
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aArgs);
$oFilter = $oSet->GetFilter();
@@ -402,7 +403,7 @@ EOF
// Current extkey value, so we can display event if it is not available anymore (eg. archived).
$iCurrentExtKeyId = (is_null($oObj)) ? 0 : $oObj->Get($this->sAttCode);
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId)));
$oBlock = new DisplayBlock($oFilter, 'list_search', false, array('query_params' => array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId)));
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode)); // Don't display the 'Actions' menu on the results
}

View File

@@ -101,7 +101,7 @@ class UIHTMLEditorWidget
$oPage->add_ready_script(
<<<EOF
$('#$iId').bind('update', function(evt){
BlockField('cke_$iId', $('#$iId').attr('disabled'));
BlockField('cke_$iId', $('#$iId').prop('disabled'));
//Delayed execution - ckeditor must be properly initialized before setting readonly
var retryCount = 0;
var oMe = $('#$iId');

View File

@@ -73,17 +73,17 @@ class UIPasswordWidget
$oPage->add_ready_script("$('#{$this->iId}_confirm').bind('keyup change', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate
$oPage->add_ready_script("$('#{$this->iId}').bind('update', function(evt, sFormId)
{
if ($(this).attr('disabled'))
if ($(this).prop('disabled'))
{
$('#{$this->iId}_confirm').attr('disabled', 'disabled');
$('#{$this->iId}_changed').attr('disabled', 'disabled');
$('#{$this->iId}_reset').attr('disabled', 'disabled');
$('#{$this->iId}_confirm').prop('disabled', true);
$('#{$this->iId}_changed').prop('disabled', true);
$('#{$this->iId}_reset').prop('disabled', true);
}
else
{
$('#{$this->iId}_confirm').removeAttr('disabled');
$('#{$this->iId}_changed').removeAttr('disabled');
$('#{$this->iId}_reset').removeAttr('disabled');
$('#{$this->iId}_confirm').prop('disabled', false);
$('#{$this->iId}_changed').prop('disabled', false);
$('#{$this->iId}_reset').prop('disabled', false);
}
}
);"); // Bind to a custom event: update to handle enabling/disabling

View File

@@ -287,74 +287,79 @@ class utils
*/
protected static function Sanitize_Internal($value, $sSanitizationFilter)
{
switch($sSanitizationFilter)
switch ($sSanitizationFilter)
{
case 'integer':
$retValue = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
break;
$retValue = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
break;
case 'class':
$retValue = $value;
if (!MetaModel::IsValidClass($value))
{
$retValue = false;
}
break;
$retValue = $value;
if (!MetaModel::IsValidClass($value))
{
$retValue = false;
}
break;
case 'string':
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
break;
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
break;
case 'context_param':
case 'parameter':
case 'field_name':
if (is_array($value))
{
$retValue = array();
foreach($value as $key => $val)
if (is_array($value))
{
$retValue[$key] = self::Sanitize_Internal($val, $sSanitizationFilter); // recursively check arrays
if ($retValue[$key] === false)
$retValue = array();
foreach ($value as $key => $val)
{
$retValue = false;
break;
$retValue[$key] = self::Sanitize_Internal($val, $sSanitizationFilter); // recursively check arrays
if ($retValue[$key] === false)
{
$retValue = false;
break;
}
}
}
}
else
{
switch($sSanitizationFilter)
else
{
case 'transaction_id':
// same as parameter type but keep the dot character
// see N°1835 : when using file transaction_id on Windows you get *.tmp tokens
// it must be included at the regexp beginning otherwise you'll get an invalid character error
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
break;
switch ($sSanitizationFilter)
{
case 'transaction_id':
// same as parameter type but keep the dot character
// see N°1835 : when using file transaction_id on Windows you get *.tmp tokens
// it must be included at the regexp beginning otherwise you'll get an invalid character error
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
break;
case 'parameter':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>'/^([ A-Za-z0-9_=-]|%3D|%2B|%2F)*$/'))); // the '=', '%3D, '%2B', '%2F' characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
break;
case 'field_name':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>'/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
break;
case 'context_param':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>'/^[ A-Za-z0-9_=%:+-]*$/')));
break;
case 'parameter':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[ A-Za-z0-9_=-]*$/'))); // the '=', '%3D, '%2B', '%2F'
// characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
break;
case 'field_name':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
break;
case 'context_param':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
break;
}
}
}
break;
break;
default:
case 'raw_data':
$retValue = $value;
$retValue = $value;
// Do nothing
}
return $retValue;
return $retValue;
}
/**
@@ -536,7 +541,7 @@ class utils
/**
* Returns a unique tmp id for the current upload based on the transaction system (db).
*
* Build as session_id() . '_' . static::GetNewTransactionId()
* Build as static::GetNewTransactionId()
*
* @return string
*/
@@ -546,7 +551,7 @@ class utils
{
$sTransactionId = static::GetNewTransactionId();
}
return session_id() . '_' . $sTransactionId;
return $sTransactionId;
}
public static function ReadFromFile($sFileName)
@@ -581,6 +586,18 @@ class utils
}
return $iReturn;
}
/**
* Checks if the memory limit is at least what is required
*
* @param int $memoryLimit set limit in bytes
* @param int $requiredLimit required limit in bytes
* @return bool
*/
public static function IsMemoryLimitOk($memoryLimit, $requiredLimit)
{
return ($memoryLimit >= $requiredLimit) || ($memoryLimit == -1);
}
/**
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
@@ -725,7 +742,11 @@ class utils
}
/**
* @return string The absolute URL to the application, including host and path
* Returns the absolute URL to the application root path
*
* @return string The absolute URL to the application root, without the first slash
*
* @throws \Exception
*/
static public function GetAbsoluteUrlAppRoot()
{
@@ -754,7 +775,15 @@ class utils
return $sUrl;
}
static public function GetDefaultUrlAppRoot()
/**
* Builds an root url from the server's variables.
* For most usages, when an root url is needed, use utils::GetAbsoluteUrlAppRoot() instead as uses this only as a fallback when the app_root_url conf parameter is not defined.
*
* @return string
*
* @throws \Exception
*/
static public function GetDefaultUrlAppRoot()
{
// Build an absolute URL to this page on this server/port
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
@@ -823,7 +852,7 @@ class utils
/**
* Helper to handle the variety of HTTP servers
* See #286 (fixed in [896]), and #634 (this fix)
* See 286 (fixed in [896]), and 634 (this fix)
*
* Though the official specs says 'a non empty string', some servers like IIS do set it to 'off' !
* nginx set it to an empty string
@@ -1076,12 +1105,7 @@ class utils
// $param is a DBObject
$oObj = $param;
$sOQL = "SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey();
$oFilter = DBObjectSearch::FromOQL($sOQL);
$sFilter = $oFilter->serialize();
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage(get_class($oObj));
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/tabularfieldsselector.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.dragtable.js');
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/dragtable.css');
@@ -1101,21 +1125,29 @@ class utils
break;
case iPopupMenuExtension::MENU_DASHBOARD_ACTIONS:
// $param is a Dashboard
$oAppContext = new ApplicationContext();
$aParams = $oAppContext->GetAsHash();
$sMenuId = ApplicationMenu::GetActiveNodeId();
$sDlgTitle = addslashes(Dict::S('UI:ImportDashboardTitle'));
$sDlgText = addslashes(Dict::S('UI:ImportDashboardText'));
$sCloseBtn = addslashes(Dict::S('UI:Button:Cancel'));
// $param is a Dashboard
/** @var \RuntimeDashboard $oDashboard */
$oDashboard = $param;
$sDashboardId = $oDashboard->GetId();
$sDashboardFile = $oDashboard->GetDefinitionFile();
$sDlgTitle = addslashes(Dict::S('UI:ImportDashboardTitle'));
$sDlgText = addslashes(Dict::S('UI:ImportDashboardText'));
$sCloseBtn = addslashes(Dict::S('UI:Button:Cancel'));
$sDashboardFileJS = addslashes($sDashboardFile);
$sDashboardFileURL = urlencode($sDashboardFile);
$sUploadDashboardTransactId = utils::GetNewTransactionId();
$aResult = array(
new SeparatorPopupMenuItem(),
new URLPopupMenuItem('UI:ExportDashboard', Dict::S('UI:ExportDashBoard'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=export_dashboard&id='.$sMenuId),
new JSPopupMenuItem('UI:ImportDashboard', Dict::S('UI:ImportDashBoard'),
"UploadDashboard({dashboard_id: '$sMenuId', title: '$sDlgTitle', text: '$sDlgText', close_btn: '$sCloseBtn', transaction: '$sUploadDashboardTransactId' })"),
);
break;
$aResult = array(
new SeparatorPopupMenuItem(),
new URLPopupMenuItem('UI:ExportDashboard', Dict::S('UI:ExportDashBoard'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=export_dashboard&id='.$sDashboardId.'&file='.$sDashboardFileURL),
new JSPopupMenuItem('UI:ImportDashboard', Dict::S('UI:ImportDashBoard'), "UploadDashboard({dashboard_id: '$sDashboardId', file: '$sDashboardFileJS', title: '$sDlgTitle', text: '$sDlgText', close_btn: '$sCloseBtn', transaction: '$sUploadDashboardTransactId' })"),
);
if ($oDashboard->GetReloadURL())
{
$aResult[] = new SeparatorPopupMenuItem();
$aResult[] = new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $oDashboard->GetReloadURL().'&printable=1', '_blank');
}
break;
default:
// Unknown type of menu, do nothing
@@ -1482,7 +1514,7 @@ class utils
static public function GetCSSFromSASS($sSassRelPath, $aImportPaths = null)
{
// Avoiding compilation if file is already a css file.
if (preg_match('/\.css$/', $sSassRelPath))
if (preg_match('/\.css(\?.*)?$/', $sSassRelPath))
{
return $sSassRelPath;
}
@@ -1990,4 +2022,14 @@ class utils
$aHugeClasses = MetaModel::GetConfig()->Get('high_cardinality_classes');
return in_array($sClass, $aHugeClasses);
}
/**
* Check if iTop is in a development environment (VCS vs build number)
*
* @return bool
*/
public static function IsDevelopmentEnvironment()
{
return ITOP_REVISION === 'svn';
}
}

View File

@@ -311,7 +311,10 @@ class WebPage implements Page
}
/**
* Add a script (as an include, i.e. link) to the header of the page
* Add a script (as an include, i.e. link) to the header of the page.<br>
* Handles duplicates : calling twice with the same script will add the script only once
*
* @param string $s_linked_script
*/
public function add_linked_script($s_linked_script)
{

View File

@@ -154,7 +154,7 @@ class WizardHelper
}
else
{
// May happen for security reasons (portal, see ticket #1074)
// May happen for security reasons (portal, see ticket 1074)
$oObj->Set($sAttCode, $value);
}
}
@@ -174,6 +174,30 @@ class WizardHelper
}
$oObj->Set($sAttCode, $value);
}
else if ($oAttDef instanceof AttributeTagSet) // AttributeDate is derived from AttributeDateTime
{
if (is_null($value))
{
// happens if field is hidden (see N°1827)
$value = array();
}
else
{
$value = json_decode($value, true);
}
$oTagSet = new ormTagSet(get_class($oObj), $sAttCode, $oAttDef->GetMaxItems());
$oTagSet->SetValues($value['orig_value']);
$oTagSet->ApplyDelta($value);
$oObj->Set($sAttCode, $oTagSet);
}
else if ($oAttDef instanceof AttributeSet) // AttributeDate is derived from AttributeDateTime
{
$value = json_decode($value, true);
$oTagSet = new ormSet(get_class($oObj), $sAttCode, $oAttDef->GetMaxItems());
$oTagSet->SetValues($value['orig_value']);
$oTagSet->ApplyDelta($value);
$oObj->Set($sAttCode, $oTagSet);
}
else
{
$oObj->Set($sAttCode, $value);
@@ -205,11 +229,10 @@ class WizardHelper
// this as to be handled as an array of objects
// thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...]
// NOT YET IMPLEMENTED !!
$sLinkedClass = $oAttDef->GetLinkedClass();
$oSet = $value;
$aData = array();
$aFields = $this->GetLinkedWizardStructure($oAttDef);
while($oSet->fetch())
while($oLinkedObj = $oSet->fetch())
{
foreach($aFields as $sLinkedAttCode)
{

View File

@@ -40,7 +40,7 @@ Class XLSXWriter
protected function tempFilename()
{
$filename = tempnam("/tmp", "xlsx_writer_");
$filename = tempnam(SetupUtils::GettmpDir(), 'xlsx_writer_');
$this->temp_files[] = $filename;
return $filename;
}

View File

@@ -1,16 +1,22 @@
{
"require": {
"php": ">=5.6.0",
"ext-mysql": "*",
"ext-ldap": "*",
"ext-mcrypt": "*",
"ext-cli": "*",
"ext-soap": "*",
"ext-json": "*",
"ext-zip": "*",
"ext-mysqli": "*",
"ext-dom": "*",
"ext-iconv": "*",
"ext-gd": "*"
},
"suggest": {
"ext-libsodium": "Required to use the AttributeEncryptedString.",
"ext-openssl": "Can be used as a polyfill if libsodium is not installed",
"ext-mcrypt": "Can be used as a polyfill if either libsodium and openssl are not installed (libsodium and openssl are more secure)",
"ext-ldap": "Required to use LDAP as an identity provider",
"ext-posix": "Not required by the core, but some extensions uses it.",
"ext-imap": "Required by the extension \"Mail to ticket automation\""
},
"config": {
"platform": {
"php": "5.6.0"

View File

@@ -223,7 +223,14 @@ class ActionEmail extends ActionNotification
return implode(', ', $aRecipients);
}
/**
* @param \Trigger $oTrigger
* @param array $aContextArgs
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
*/
public function DoExecute($oTrigger, $aContextArgs)
{
if (MetaModel::IsLogEnabledNotification())
@@ -292,6 +299,14 @@ class ActionEmail extends ActionNotification
}
/**
* @param \Trigger $oTrigger
* @param array $aContextArgs
* @param \EventNotification $oLog
*
* @return string
* @throws \CoreException
*/
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
{
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();

View File

@@ -150,7 +150,7 @@ abstract class AsyncTask extends DBObject
public function GetRetryDelay($iErrorCode = null)
{
$iRetryDelay = 600;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
@@ -162,7 +162,7 @@ abstract class AsyncTask extends DBObject
public function GetMaxRetries($iErrorCode = null)
{
$iMaxRetries = 0;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
@@ -381,6 +381,6 @@ class AsyncSendEmail extends AsyncTask
case EMAIL_SEND_ERROR:
return "Failed: ".implode(', ', $aIssues);
}
return '';
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -29,6 +29,7 @@ MetaModel::IncludeModule('core/action.class.inc.php');
MetaModel::IncludeModule('core/trigger.class.inc.php');
MetaModel::IncludeModule('core/bulkexport.class.inc.php');
MetaModel::IncludeModule('core/ownershiplock.class.inc.php');
MetaModel::IncludeModule('core/tagsetfield.class.inc.php');
MetaModel::IncludeModule('synchro/synchrodatasource.class.inc.php');
MetaModel::IncludeModule('core/backgroundtask.class.inc.php');
MetaModel::IncludeModule('core/inlineimage.class.inc.php');

View File

@@ -242,6 +242,64 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
return $sResult;
}
}
/**
* Record the modification of a tag set attribute
*
* @package iTopORM
*/
class CMDBChangeOpSetAttributeTagSet extends CMDBChangeOpSetAttribute
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_tagset",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeText("oldvalue", array("allowed_values"=>null, "sql"=>"oldvalue", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("newvalue", array("allowed_values"=>null, "sql"=>"newvalue", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for a list
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
$sResult = '';
$sTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
$sAttCode = $this->Get('attcode');
$oTargetSearch = new DBObjectSearch($sTargetObjectClass);
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($sTargetObjectClass, $sAttCode, UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) return ''; // Protects against renamed attributes...
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$sNewValue = $this->Get('newvalue');
$sOldValue = $this->Get('oldvalue');
$sResult = $oAttDef->DescribeChangeAsHTML($sOldValue, $sNewValue);
}
return $sResult;
}
}
/**
* Record the modification of an URL
*

View File

@@ -188,6 +188,16 @@ abstract class CMDBObject extends DBObject
self::$m_oCurrChange->DBInsert();
}
/**
* @inheritdoc
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MySQLException
* @throws \OQLException
*/
protected function RecordObjCreation()
{
// Delete any existing change tracking about the current object (IDs can be reused due to InnoDb bug; see TRAC #886)
@@ -206,6 +216,7 @@ abstract class CMDBObject extends DBObject
MetaModel::PurgeData($oFilter);
parent::RecordObjCreation();
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpCreate");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
@@ -232,9 +243,17 @@ abstract class CMDBObject extends DBObject
}
/**
* @param $sAttCode
* @param string $sAttCode
* @param $original Original value
* @param $value Current value
*
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MySQLException
* @throws \OQLException
*/
protected function RecordAttChange($sAttCode, $original, $value)
{
@@ -409,7 +428,19 @@ abstract class CMDBObject extends DBObject
$oMyChangeOp->Set("newvalue", $value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
else
elseif ($oAttDef instanceOf AttributeSet)
{
// Tag Set
//
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeTagSet");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("oldvalue", implode(' ', $original->GetValues()));
$oMyChangeOp->Set("newvalue", implode(' ', $value->GetValues()));
$iId = $oMyChangeOp->DBInsertNoReload();
}
else
{
// Scalars
//
@@ -426,6 +457,14 @@ abstract class CMDBObject extends DBObject
/**
* @param array $aValues
* @param array $aOrigValues
*
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MySQLException
* @throws \OQLException
*/
protected function RecordAttChanges(array $aValues, array $aOrigValues)
{
@@ -551,6 +590,12 @@ abstract class CMDBObject extends DBObject
$this->DBUpdate();
}
/**
* @param null $oDeletionPlan
*
* @return \DeletionPlan|null
* @throws \DeleteException
*/
public function DBDelete(&$oDeletionPlan = null)
{
return $this->DBDeleteTracked_Internal($oDeletionPlan);
@@ -563,6 +608,12 @@ abstract class CMDBObject extends DBObject
$this->DBDeleteTracked_Internal($oDeletionPlan);
}
/**
* @param null $oDeletionPlan
*
* @return \DeletionPlan|null
* @throws \DeleteException
*/
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
{
$prevkey = $this->GetKey();

View File

@@ -108,18 +108,22 @@ class MySQLHasGoneAwayException extends MySQLException
*/
class CMDBSource
{
const ENUM_DB_VENDOR_MYSQL = 'MySQL';
const ENUM_DB_VENDOR_MARIADB = 'MariaDB';
const ENUM_DB_VENDOR_PERCONA = 'Percona';
protected static $m_sDBHost;
protected static $m_sDBUser;
protected static $m_sDBPwd;
protected static $m_sDBName;
/**
* @var boolean
* @since 2.5 #1260 MySQL TLS first implementation
* @since 2.5 1260 MySQL TLS first implementation
*/
protected static $m_bDBTlsEnabled;
/**
* @var string
* @since 2.5 #1260 MySQL TLS first implementation
* @since 2.5 1260 MySQL TLS first implementation
*/
protected static $m_sDBTlsCA;
@@ -133,7 +137,7 @@ class CMDBSource
* use expression as value)
*
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-column.html
* @since 2.5 #1001 switch to utf8mb4
* @since 2.5 1001 switch to utf8mb4
*/
public static function GetSqlStringColumnDefinition()
{
@@ -406,6 +410,27 @@ class CMDBSource
return $aVersions[0];
}
/**
* Get the DB vendor between MySQL and its main forks
* @return string
*/
static public function GetDBVendor()
{
$sDBVendor = static::ENUM_DB_VENDOR_MYSQL;
$sVersionComment = static::GetServerVariable('version') . ' - ' . static::GetServerVariable('version_comment');
if(preg_match('/mariadb/i', $sVersionComment) === 1)
{
$sDBVendor = static::ENUM_DB_VENDOR_MARIADB;
}
else if(preg_match('/percona/i', $sVersionComment) === 1)
{
$sDBVendor = static::ENUM_DB_VENDOR_PERCONA;
}
return $sDBVendor;
}
/**
* @param string $sSource
*
@@ -455,10 +480,15 @@ class CMDBSource
public static function DropTable($sTable)
{
$res = self::Query("DROP TABLE `$sTable`");
self::_TablesInfoCacheReset(true); // reset the table info cache!
self::_TablesInfoCacheReset(); // reset the table info cache!
return $res;
}
public static function CacheReset($sTable)
{
self::_TablesInfoCacheReset($sTable);
}
/**
* @return \mysqli
*/
@@ -870,6 +900,20 @@ class CMDBSource
return ($aFieldData["Type"]);
}
private static function IsNumericType($aFieldData)
{
$aNumericTypes = array('tinyint(', 'decimal(', 'int(' );
$sType = strtolower($aFieldData["Type"]);
foreach ($aNumericTypes as $sNumericType)
{
if (strpos($sType, $sNumericType) === 0)
{
return true;
}
}
return false;
}
/**
* @param string $sTable
* @param string $sField
@@ -908,8 +952,15 @@ class CMDBSource
}
else
{
$default = $aFieldData["Default"] + 0; // Coerce to a numeric variable
$sRet .= ' DEFAULT '.self::Quote($default);
if (self::IsNumericType($aFieldData))
{
$sRet .= ' DEFAULT '.$aFieldData["Default"];
}
else
{
$default = $aFieldData["Default"] + 0; // Coerce to a numeric variable
$sRet .= ' DEFAULT '.self::Quote($default);
}
}
}
elseif (is_string($aFieldData["Default"]) == 'string')
@@ -967,9 +1018,16 @@ class CMDBSource
// Cache the information about existing tables, and their fields
private static $m_aTablesInfo = array();
private static function _TablesInfoCacheReset()
private static function _TablesInfoCacheReset($sTableName = null)
{
self::$m_aTablesInfo = array();
if (is_null($sTableName))
{
self::$m_aTablesInfo = array();
}
else
{
self::$m_aTablesInfo[strtolower($sTableName)] = null;
}
}
/**
@@ -1042,7 +1100,7 @@ class CMDBSource
* @return string query to upgrade table charset and collation if needed, null if not
* @throws \MySQLException
*
* @since 2.5 #1001 switch to utf8mb4
* @since 2.5 1001 switch to utf8mb4
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-table.html
*/
public static function DBCheckTableCharsetAndCollation($sTableName)
@@ -1192,7 +1250,7 @@ class CMDBSource
* @return string query to upgrade database charset and collation if needed, null if not
* @throws \MySQLException
*
* @since 2.5 #1001 switch to utf8mb4
* @since 2.5 1001 switch to utf8mb4
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-database.html
*/
public static function DBCheckCharsetAndCollation()

View File

@@ -19,7 +19,7 @@
define('ITOP_APPLICATION', 'iTop');
define('ITOP_APPLICATION_SHORT', 'iTop');
define('ITOP_VERSION', '2.5.0');
define('ITOP_VERSION', '2.6.0');
define('ITOP_REVISION', 'svn');
define('ITOP_BUILD_DATE', '$WCNOW$');
@@ -37,6 +37,7 @@ define('ACCESS_READONLY', 0);
require_once('coreexception.class.inc.php');
require_once('attributedef.class.inc.php'); // For the defines
require_once('simplecrypt.class.inc.php');
class ConfigException extends CoreException
{
@@ -64,13 +65,16 @@ define('DEFAULT_FAST_RELOAD_INTERVAL', 1 * 60);
define('DEFAULT_SECURE_CONNECTION_REQUIRED', false);
define('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|basic|external');
define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random value, later...
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
/**
* Config
* configuration data (this class cannot not be localized, because it is responsible for loading the dictionaries)
*
* @package iTopORM
*
* @see \MetaModel::GetConfig() to get the config, if the metamodel was already loaded
* @see utils::GetConfig() to load config from the current env, if metamodel is not loaded
*/
class Config
{
@@ -93,7 +97,7 @@ class Config
protected $m_aSettings = array(
'app_env_label' => array(
'type' => 'string',
'description' => 'Label displayed to describe the current application environnment, defaults to the environment name (e.g. "production")',
'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
'default' => '',
'value' => '',
'source_of_value' => '',
@@ -166,7 +170,7 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'db_character_set' => array( // @deprecated to remove in 2.7 ? #1001 utf8mb4 switch
'db_character_set' => array( // @deprecated to remove in 2.7 ? 1001 utf8mb4 switch
'type' => 'string',
'description' => 'Deprecated since iTop 2.5 : now using utf8mb4',
'default' => 'DEPRECATED_2.5',
@@ -174,7 +178,7 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'db_collation' => array( // @deprecated to remove in 2.7 ? #1001 utf8mb4 switch
'db_collation' => array( // @deprecated to remove in 2.7 ? 1001 utf8mb4 switch
'type' => 'string',
'description' => 'Deprecated since iTop 2.5 : now using utf8mb4_unicode_ci',
'default' => 'DEPRECATED_2.5',
@@ -200,7 +204,7 @@ class Config
),
'skip_strong_security' => array(
'type' => 'bool',
'description' => 'Disable strong security - TEMPORY: this flag should be removed when we are more confident in the recent change in security',
'description' => 'Disable strong security - TEMPORARY: this flag should be removed when we are more confident in the recent change in security',
'default' => true,
'value' => true,
'source_of_value' => '',
@@ -216,7 +220,7 @@ class Config
),
'query_indentation_enabled' => array(
'type' => 'bool',
'description' => 'For developpers: format the SQL queries for human analysis',
'description' => 'For developers: format the SQL queries for human analysis',
'default' => false,
'value' => false,
'source_of_value' => '',
@@ -224,7 +228,7 @@ class Config
),
'disable_mandatory_ext_keys' => array(
'type' => 'bool',
'description' => 'For developpers: allow every external keys to be undefined',
'description' => 'For developers: allow every external keys to be undefined',
'default' => false,
'value' => false,
'source_of_value' => '',
@@ -407,6 +411,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'tag_set_item_separator' => array(
'type' => 'string',
'description' => 'Tag set from string: tag label separator',
'default' => '|',
'value' => '|',
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'cron_max_execution_time' => array(
'type' => 'integer',
'description' => 'Duration (seconds) of the page cron.php, must be shorter than php setting max_execution_time and shorter than the web server response timeout',
@@ -441,7 +453,7 @@ class Config
),
'email_transport' => array(
'type' => 'string',
'description' => 'Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocole)',
'description' => 'Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocol)',
'default' => "PHPMail",
'value' => "PHPMail",
'source_of_value' => '',
@@ -537,7 +549,7 @@ class Config
),
'timezone' => array(
'type' => 'string',
'description' => 'Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitely configured in PHP',
'description' => 'Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitly configured in PHP',
// examples... not used (nor 'description')
'examples' => array(
'America/Sao_Paulo',
@@ -935,7 +947,7 @@ class Config
),
'tracking_level_linked_set_default' => array(
'type' => 'integer',
'description' => '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)',
'description' => 'Default tracking level if not explicitly 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' => LINKSET_TRACKING_LIST,
'value' => LINKSET_TRACKING_LIST,
'source_of_value' => '',
@@ -943,7 +955,7 @@ class Config
),
'tracking_level_linked_set_indirect_default' => array(
'type' => 'integer',
'description' => 'Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSetIndirect',
'description' => 'Default tracking level if not explicitly set at the attribute level, for AttributeLinkedSetIndirect',
'default' => LINKSET_TRACKING_ALL,
'value' => LINKSET_TRACKING_ALL,
'source_of_value' => '',
@@ -984,7 +996,7 @@ class Config
'transaction_storage' => array(
'type' => 'string',
'description' => 'The type of mechanism to use for storing the unique identifiers for transactions (Session|File).',
'default' => 'Session',
'default' => 'File',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
@@ -1056,7 +1068,7 @@ class Config
'draft_attachments_lifetime' => array(
'type' => 'integer',
'description' => 'Lifetime (in seconds) of drafts\' attachments and inline images: after this duration, the garbage collector will delete them.',
'default' => 3600,
'default' => 86400,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
@@ -1117,6 +1129,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'optimize_requests_for_join_count' => array(
'type' => 'bool',
'description' => 'Optimize request joins to minimize the count (default is true, try to set it to false in case of performance issues)',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'high_cardinality_classes' => array(
'type' => 'array',
'description' => 'List of classes with high cardinality (Force manual submit of search)',
@@ -1125,6 +1145,22 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'newsroom_enabled' => array(
'type' => 'bool',
'description' => 'Whether or not the whole newsroom is enabled',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'regenerate_session_id_enabled' => array(
'type' => 'bool',
'description' => 'If true then session id will be regenerated on each login, to prevent session fixation.',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
);
public function IsProperty($sPropCode)
@@ -1184,6 +1220,11 @@ class Config
}
/**
* @param string $sPropCode
*
* @return mixed
*/
public function Get($sPropCode)
{
return $this->m_aSettings[$sPropCode]['value'];
@@ -1246,13 +1287,29 @@ class Config
*/
protected $m_sEncryptionKey;
/**
* @var string Encryption key used for all attributes of type "encrypted string". Can be set to a random value
* unless you want to import a database from another iTop instance, in which case you must use
* the same encryption key in order to properly decode the encrypted fields
*/
protected $m_sEncryptionLibrary;
/**
* @var array Additional character sets to be supported by the interactive CSV import
* 'iconv_code' => 'display name'
*/
protected $m_aCharsets;
public function __construct($sConfigFile = null, $bLoadConfig = true)
/**
* Config constructor.
*
* @param string|null $sConfigFile
* @param bool $bLoadConfig
*
* @throws \ConfigException
* @throws \CoreException
*/
public function __construct($sConfigFile = null, $bLoadConfig = true)
{
$this->m_sFile = $sConfigFile;
if (is_null($sConfigFile))
@@ -1282,10 +1339,14 @@ class Config
$this->m_sDefaultLanguage = 'EN US';
$this->m_sAllowedLoginTypes = DEFAULT_ALLOWED_LOGIN_TYPES;
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
$this->m_sEncryptionKey = DEFAULT_ENCRYPTION_KEY;
$this->m_aCharsets = array();
$this->m_bQueryCacheEnabled = DEFAULT_QUERY_CACHE_ENABLED;
//define default encryption params according to php install
$aEncryptParams = SimpleCrypt::GetNewDefaultParams();
$this->m_sEncryptionLibrary = isset($aEncryptParams['lib']) ? $aEncryptParams['lib'] : DEFAULT_ENCRYPTION_LIB;
$this->m_sEncryptionKey= isset($aEncryptParams['key']) ? $aEncryptParams['key'] : DEFAULT_ENCRYPTION_KEY;
$this->m_aModuleSettings = array();
if ($bLoadConfig)
@@ -1310,7 +1371,13 @@ class Config
*/
}
protected function CheckFile($sPurpose, $sFileName)
/**
* @param string $sPurpose
* @param string $sFileName
*
* @throws \ConfigException
*/
protected function CheckFile($sPurpose, $sFileName)
{
if (!file_exists($sFileName))
{
@@ -1425,7 +1492,8 @@ class Config
$this->m_sDefaultLanguage = isset($MySettings['default_language']) ? trim($MySettings['default_language']) : 'EN US';
$this->m_sAllowedLoginTypes = isset($MySettings['allowed_login_types']) ? trim($MySettings['allowed_login_types']) : DEFAULT_ALLOWED_LOGIN_TYPES;
$this->m_sExtAuthVariable = isset($MySettings['ext_auth_variable']) ? trim($MySettings['ext_auth_variable']) : DEFAULT_EXT_AUTH_VARIABLE;
$this->m_sEncryptionKey = isset($MySettings['encryption_key']) ? trim($MySettings['encryption_key']) : DEFAULT_ENCRYPTION_KEY;
$this->m_sEncryptionKey = isset($MySettings['encryption_key']) ? trim($MySettings['encryption_key']) : $this->m_sEncryptionKey;
$this->m_sEncryptionLibrary = isset($MySettings['encryption_library']) ? trim($MySettings['encryption_library']) : $this->m_sEncryptionLibrary;
$this->m_aCharsets = isset($MySettings['csv_import_charsets']) ? $MySettings['csv_import_charsets'] : array();
}
@@ -1446,7 +1514,14 @@ class Config
return $this->GetModuleParameter($sModule, $sProperty, $defaultvalue);
}
public function GetModuleParameter($sModule, $sProperty, $defaultvalue = null)
/**
* @param string $sModule
* @param string $sProperty
* @param mixed|null $defaultvalue
*
* @return mixed|null
*/
public function GetModuleParameter($sModule, $sProperty, $defaultvalue = null)
{
$ret = $defaultvalue;
if (class_exists('ModulesXMLParameters'))
@@ -1478,6 +1553,7 @@ class Config
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6
* @see Config::Get() as a replacement
*/
@@ -1488,6 +1564,7 @@ class Config
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6
* @see Config::Get() as a replacement
*/
@@ -1498,6 +1575,7 @@ class Config
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6
* @see Config::Get() as a replacement
*/
@@ -1508,7 +1586,8 @@ class Config
/**
* @return string
* @deprecated 2.5 will be removed in 2.6 #1001 utf8mb4 switch
*
* @deprecated 2.5 will be removed in 2.6 N°1001 utf8mb4 switch
* @see Config::DEFAULT_CHARACTER_SET
*/
public function GetDBCharacterSet()
@@ -1518,7 +1597,8 @@ class Config
/**
* @return string
* @deprecated 2.5 will be removed in 2.6 #1001 utf8mb4 switch
*
* @deprecated 2.5 will be removed in 2.6 N°1001 utf8mb4 switch
* @see Config::DEFAULT_COLLATION
*/
public function GetDBCollation()
@@ -1528,6 +1608,7 @@ class Config
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6
* @see Config::Get() as a replacement
*/
@@ -1538,6 +1619,7 @@ class Config
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6
* @see Config::Get() as a replacement
*/
@@ -1611,6 +1693,11 @@ class Config
return $this->m_sEncryptionKey;
}
public function GetEncryptionLibrary()
{
return $this->m_sEncryptionLibrary;
}
public function GetAllowedLoginTypes()
{
return explode('|', $this->m_sAllowedLoginTypes);
@@ -1739,6 +1826,7 @@ class Config
$aSettings['allowed_login_types'] = $this->m_sAllowedLoginTypes;
$aSettings['ext_auth_variable'] = $this->m_sExtAuthVariable;
$aSettings['encryption_key'] = $this->m_sEncryptionKey;
$aSettings['encryption_library'] = $this->m_sEncryptionLibrary;
$aSettings['csv_import_charsets'] = $this->m_aCharsets;
foreach ($this->m_aModuleSettings as $sModule => $aProperties)
@@ -1756,14 +1844,16 @@ class Config
return $aSettings;
}
/**
* Write the configuration to a file (php format) that can be reloaded later
* By default write to the same file that was specified when constructing the object
/**
* Write the configuration to a file (php format) that can be reloaded later
* By default write to the same file that was specified when constructing the object
*
* @param string $sFileName string Name of the file to write to (emtpy to write to the same file)
*
* @return boolean True otherwise throws an Exception
*
* @param $sFileName string Name of the file to write to (emtpy to write to the same file)
*
* @return boolean True otherwise throws an Exception
*/
* @throws \ConfigException
*/
public function WriteToFile($sFileName = '')
{
if (empty($sFileName))
@@ -1825,6 +1915,7 @@ class Config
'allowed_login_types' => $this->m_sAllowedLoginTypes,
'ext_auth_variable' => $this->m_sExtAuthVariable,
'encryption_key' => $this->m_sEncryptionKey,
'encryption_library' => $this->m_sEncryptionLibrary,
'csv_import_charsets' => $this->m_aCharsets,
);
foreach ($aOtherValues as $sKey => $value)
@@ -1910,9 +2001,16 @@ class Config
}
}
/**
* Helper function to initialize a configuration from the page arguments
*/
/**
* Helper function to initialize a configuration from the page arguments
*
* @param array $aParamValues
* @param string|null $sModulesDir
* @param bool $bPreserveModuleSettings
*
* @throws \Exception
* @throws \CoreException
*/
public function UpdateFromParams($aParamValues, $sModulesDir = null, $bPreserveModuleSettings = false)
{
if (isset($aParamValues['application_path']))
@@ -2042,9 +2140,13 @@ class Config
}
}
/**
* Helper: for an array of string, change the prefix when found
*/
/**
* Helper: for an array of string, change the prefix when found
*
* @param array $aStrings
* @param string $sSearchPrefix
* @param string $sNewPrefix
*/
protected static function ChangePrefix(&$aStrings, $sSearchPrefix, $sNewPrefix)
{
foreach ($aStrings as &$sFile)
@@ -2056,10 +2158,13 @@ class Config
}
}
/**
* Obsolete: kept only for backward compatibility of the Toolkit
* Quick and dirty way to clone a config file into another environment
*/
/**
* Obsolete: kept only for backward compatibility of the Toolkit
* Quick and dirty way to clone a config file into another environment
*
* @param string $sSourceEnv
* @param string $sTargetEnv
*/
public function ChangeModulesPath($sSourceEnv, $sTargetEnv)
{
// Now does nothing since the includes are built into the environment itself
@@ -2096,5 +2201,3 @@ class Config
}
}
?>

View File

@@ -107,6 +107,82 @@ class CoreException extends Exception
}
}
/**
* Class CoreCannotSaveObjectException
*
* Specialized exception to raise if {@link DBObject::CheckToWrite()} fails, which allow easy data retrieval
*
* @see \DBObject::DBInsertNoReload()
* @see \DBObject::DBUpdate()
*
* @since 2.6 N°659 uniqueness constraint
*/
class CoreCannotSaveObjectException extends CoreException
{
/** @var string[] */
private $aIssues;
/** @var int */
private $iObjectId;
/** @var string */
private $sObjectClass;
/**
* CoreCannotSaveObjectException constructor.
*
* @param array $aContextData containing at least those keys : issues, id, class
*/
public function __construct($aContextData)
{
$this->aIssues = $aContextData['issues'];
$this->iObjectId = $aContextData['id'];
$this->sObjectClass = $aContextData['class'];
$sIssues = implode(', ', $this->aIssues);
parent::__construct($sIssues, $aContextData);
}
/**
* @return string
*/
public function getHtmlMessage()
{
$sTitle = Dict::S('UI:Error:SaveFailed');
$sContent = "<span><strong>{$sTitle}</strong></span>";
if (count($this->aIssues) == 1)
{
$sIssue = reset($this->aIssues);
$sContent .= " <span>{$sIssue}</span>";
}
else
{
$sContent .= '<ul>';
foreach ($this->aIssues as $sError)
{
$sContent .= "<li>$sError</li>";
}
$sContent .= '</ul>';
}
return $sContent;
}
public function getIssues()
{
return $this->aIssues;
}
public function getObjectId()
{
return $this->iObjectId;
}
public function getObjectClass()
{
return $this->sObjectClass;
}
}
class CoreWarning extends CoreException
{
}

View File

@@ -266,7 +266,7 @@ EOF
if ($this->aStatusInfo['charset'] != 'UTF-8')
{
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
// and thus to convert field by field and not the whole row or file at once (see ticket #991)
// and thus to convert field by field and not the whole row or file at once (see ticket 991)
$aData[$idx] = @iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $aData[$idx]);
}
}
@@ -325,7 +325,7 @@ EOF
if ($this->aStatusInfo['charset'] != 'UTF-8')
{
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
// and thus to convert field by field and not the whole row or file at once (see ticket #991)
// and thus to convert field by field and not the whole row or file at once (see ticket 991)
$aData[] = @iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $sField);
}
else

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.5">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
<user_rights>
<profiles>
<profile id="1024" _delta="define">

File diff suppressed because it is too large Load Diff

View File

@@ -480,6 +480,11 @@ class DBObjectSearch extends DBSearch
$oNewCondition = Expression::FromOQL($sOQLCondition);
break;
case 'MATCHES':
$oRightExpr = new ScalarExpression($value);
$oNewCondition = new MatchExpression($oField, $oRightExpr);
break;
case 'Contains':
case 'Begins with':
case 'Finishes with':
@@ -1299,6 +1304,13 @@ class DBObjectSearch extends DBSearch
$oRight = $this->OQLExpressionToCondition($sQuery, $oExpression->GetRightExpr(), $aClassAliases);
return new BinaryExpression($oLeft, $sOperator, $oRight);
}
elseif ($oExpression instanceof MatchOqlExpression)
{
$oLeft = $this->OQLExpressionToCondition($sQuery, $oExpression->GetLeftExpr(), $aClassAliases);
$oRight = $this->OQLExpressionToCondition($sQuery, $oExpression->GetRightExpr(), $aClassAliases);
return new MatchExpression($oLeft, $oRight);
}
elseif ($oExpression instanceof FieldOqlExpression)
{
$sClassAlias = $oExpression->GetParent();
@@ -1925,46 +1937,86 @@ class DBObjectSearch extends DBSearch
}
}
// First query built from the root, adding all tables including the leaf
// Before N.1065 we were joining from the leaf first, but this wasn't a good choice :
// most of the time (obsolescence, friendlyname, ...) we want to get a root attribute !
//
$oSelectBase = null;
$aClassHierarchy = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, true);
$bIsClassStandaloneClass = (count($aClassHierarchy) == 1);
foreach($aClassHierarchy as $sSomeClass)
$bRootFirst = MetaModel::GetConfig()->Get('optimize_requests_for_join_count');
if ($bRootFirst)
{
if (!MetaModel::HasTable($sSomeClass))
// First query built from the root, adding all tables including the leaf
// Before N.1065 we were joining from the leaf first, but this wasn't a good choice :
// most of the time (obsolescence, friendlyname, ...) we want to get a root attribute !
//
$oSelectBase = null;
$aClassHierarchy = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, true);
$bIsClassStandaloneClass = (count($aClassHierarchy) == 1);
foreach($aClassHierarchy as $sSomeClass)
{
continue;
}
self::DbgTrace("Adding join from root to leaf: $sSomeClass... let's call MakeSQLObjectQuerySingleTable()");
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sSomeClass, $aExtKeys, $aValues);
if (is_null($oSelectBase))
{
$oSelectBase = $oSelectParentTable;
if (!$bIsClassStandaloneClass && (MetaModel::IsRootClass($sSomeClass)))
if (!MetaModel::HasTable($sSomeClass))
{
// As we're linking to root class first, we're adding a where clause on the finalClass attribute :
// COALESCE($sRootClassFinalClass IN ('$sExpectedClasses'), 1)
// If we don't, the child classes can be removed in the query optimisation phase, including the leaf that was queried
// So we still need to filter records to only those corresponding to the child classes !
// The coalesce is mandatory if we have a polymorphic query (left join)
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
$sFinalClassSqlColumnName = MetaModel::DBGetClassField($sSomeClass);
$oClassExpr = new FieldExpression($sFinalClassSqlColumnName, $oSelectBase->GetTableAlias());
$oInExpression = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
$oTrueExpression = new TrueExpression();
$aCoalesceAttr = array($oInExpression, $oTrueExpression);
$oFinalClassRestriction = new FunctionExpression("COALESCE", $aCoalesceAttr);
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
continue;
}
self::DbgTrace("Adding join from root to leaf: $sSomeClass... let's call MakeSQLObjectQuerySingleTable()");
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sSomeClass, $aExtKeys, $aValues);
if (is_null($oSelectBase))
{
$oSelectBase = $oSelectParentTable;
if (!$bIsClassStandaloneClass && (MetaModel::IsRootClass($sSomeClass)))
{
// As we're linking to root class first, we're adding a where clause on the finalClass attribute :
// COALESCE($sRootClassFinalClass IN ('$sExpectedClasses'), 1)
// If we don't, the child classes can be removed in the query optimisation phase, including the leaf that was queried
// So we still need to filter records to only those corresponding to the child classes !
// The coalesce is mandatory if we have a polymorphic query (left join)
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
$sFinalClassSqlColumnName = MetaModel::DBGetClassField($sSomeClass);
$oClassExpr = new FieldExpression($sFinalClassSqlColumnName, $oSelectBase->GetTableAlias());
$oInExpression = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
$oTrueExpression = new TrueExpression();
$aCoalesceAttr = array($oInExpression, $oTrueExpression);
$oFinalClassRestriction = new FunctionExpression("COALESCE", $aCoalesceAttr);
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
}
}
else
{
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sSomeClass));
}
}
}
else
{
// First query built upon on the leaf (ie current) class
//
self::DbgTrace("Main (=leaf) class, call MakeSQLObjectQuerySingleTable()");
if (MetaModel::HasTable($sClass))
{
$oSelectBase = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sClass, $aExtKeys, $aValues);
}
else
{
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sSomeClass));
$oSelectBase = null;
// As the join will not filter on the expected classes, we have to specify it explicitely
$sExpectedClasses = implode("', '", MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
$oFinalClassRestriction = Expression::FromOQL("`$sClassAlias`.finalclass IN ('$sExpectedClasses')");
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
}
// Then we join the queries of the eventual parent classes (compound model)
foreach(MetaModel::EnumParentClasses($sClass) as $sParentClass)
{
if (!MetaModel::HasTable($sParentClass)) continue;
self::DbgTrace("Parent class: $sParentClass... let's call MakeSQLObjectQuerySingleTable()");
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sParentClass, $aExtKeys, $aValues);
if (is_null($oSelectBase))
{
$oSelectBase = $oSelectParentTable;
}
else
{
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sParentClass));
}
}
}

View File

@@ -38,7 +38,7 @@ class DBObjectSet implements iDBObjectSetIterator
*/
protected $m_aAddedIds; // Ids of objects added (discrete lists)
/**
* @var hash array of (row => array of (classalias) => object/null) storing the objects added "in memory"
* @var array array of (row => array of (classalias) => object/null) storing the objects added "in memory"
*/
protected $m_aAddedObjects;
/**
@@ -118,12 +118,19 @@ class DBObjectSet implements iDBObjectSetIterator
}
}
/**
* @return string
*
* @throws \Exception
* @throws \CoreException
* @throws \MissingQueryArgument
*/
public function __toString()
{
$sRet = '';
$this->Rewind();
$sRet .= "Set (".$this->m_oFilter->ToOQL().")<br/>\n";
$sRet .= "Query: <pre style=\"font-size: smaller; display:inline;\">".$this->m_oFilter->MakeSelectQuery().")</pre>\n";
$sRet .= "Query: <pre style=\"font-size: smaller; display:inline;\">".$this->m_oFilter->MakeSelectQuery().")</pre>\n";
$sRet .= $this->Count()." records<br/>\n";
if ($this->Count() > 0)
@@ -171,13 +178,16 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_oFilter->GetShowObsoleteData();
}
/**
* Specify the subset of attributes to load (for each class of objects) before performing the SQL query for retrieving the rows from the DB
*
* @param array $aAttToLoad Format: alias => array of attribute_codes
*
* @return void
*/
/**
* Specify the subset of attributes to load (for each class of objects) before performing the SQL query for retrieving the rows from the DB
*
* @param array $aAttToLoad Format: alias => array of attribute_codes
*
* @return void
*
* @throws \Exception
* @throws \CoreException
*/
public function OptimizeColumnLoad($aAttToLoad)
{
if (is_null($aAttToLoad))
@@ -249,13 +259,15 @@ class DBObjectSet implements iDBObjectSetIterator
}
}
/**
* Create a set (in-memory) containing just the given object
*
* @param DBobject $oObject
*
* @return DBObjectSet The singleton set
*/
/**
* Create a set (in-memory) containing just the given object
*
* @param \DBobject $oObject
*
* @return \DBObjectSet The singleton set
*
* @throws \Exception
*/
static public function FromObject($oObject)
{
$oRetSet = self::FromScratch(get_class($oObject));
@@ -263,13 +275,15 @@ class DBObjectSet implements iDBObjectSetIterator
return $oRetSet;
}
/**
* Create an empty set (in-memory), for the given class (and its subclasses) of objects
*
* @param string $sClass The class (or an ancestor) for the objects to be added in this set
*
* @return DBObjectSet The empty set
*/
/**
* Create an empty set (in-memory), for the given class (and its subclasses) of objects
*
* @param string $sClass The class (or an ancestor) for the objects to be added in this set
*
* @return \DBObjectSet The empty set
*
* @throws \Exception
*/
static public function FromScratch($sClass)
{
$oFilter = new DBObjectSearch($sClass);
@@ -278,34 +292,38 @@ class DBObjectSet implements iDBObjectSetIterator
$oRetSet->m_bLoaded = true; // no DB load
$oRetSet->m_iNumTotalDBRows = 0; // Nothing from the DB
return $oRetSet;
}
}
/**
* Create a set (in-memory) with just one column (i.e. one object per row) and filled with the given array of objects
*
* @param string $sClass The class of the objects (must be a common ancestor to all objects in the set)
* @param array $aObjects The list of objects to add into the set
*
* @return DBObjectSet
*/
/**
* Create a set (in-memory) with just one column (i.e. one object per row) and filled with the given array of objects
*
* @param string $sClass The class of the objects (must be a common ancestor to all objects in the set)
* @param array $aObjects The list of objects to add into the set
*
* @return \DBObjectSet
*
* @throws \Exception
*/
static public function FromArray($sClass, $aObjects)
{
$oRetSet = self::FromScratch($sClass);
$oRetSet->AddObjectArray($aObjects, $sClass);
return $oRetSet;
}
}
/**
* Create a set in-memory with several classes of objects per row (with one alias per "column")
*
* Limitation:
* The filter/OQL query representing such a set can not be rebuilt (only the first column will be taken into account)
*
* @param hash $aClasses Format: array of (alias => class)
* @param hash $aObjects Format: array of (array of (classalias => object))
*
* @return DBObjectSet
*/
/**
* Create a set in-memory with several classes of objects per row (with one alias per "column")
*
* Limitation:
* The filter/OQL query representing such a set can not be rebuilt (only the first column will be taken into account)
*
* @param array $aClasses Format: array of (alias => class)
* @param array $aObjects Format: array of (array of (classalias => object))
*
* @return \DBObjectSet
*
* @throws \Exception
*/
static public function FromArrayAssoc($aClasses, $aObjects)
{
// In a perfect world, we should create a complete tree of DBObjectSearch,
@@ -324,9 +342,19 @@ class DBObjectSet implements iDBObjectSetIterator
$oRetSet->AddObjectExtended($aObjectsByClassAlias);
}
return $oRetSet;
}
}
static public function FromLinkSet($oObject, $sLinkSetAttCode, $sExtKeyToRemote)
/**
* @param $oObject
* @param string $sLinkSetAttCode
* @param string $sExtKeyToRemote
*
* @return \DBObjectSet
*
* @throws \Exception
* @throws \ArchivedObjectException
* @throws \CoreException
*/static public function FromLinkSet($oObject, $sLinkSetAttCode, $sExtKeyToRemote)
{
$oLinkAttCode = MetaModel::GetAttributeDef(get_class($oObject), $sLinkSetAttCode);
$oExtKeyAttDef = MetaModel::GetAttributeDef($oLinkAttCode->GetLinkedClass(), $sExtKeyToRemote);
@@ -346,7 +374,13 @@ class DBObjectSet implements iDBObjectSetIterator
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
*
* @param bool $bWithId
*
* @return array
*
* @throws \Exception
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function ToArray($bWithId = true)
{
@@ -364,8 +398,16 @@ class DBObjectSet implements iDBObjectSetIterator
}
}
return $aRet;
}
}
/**
* @return array
*
* @throws \Exception
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function ToArrayOfValues()
{
if (!$this->m_bLoaded) $this->Load();
@@ -421,7 +463,11 @@ class DBObjectSet implements iDBObjectSetIterator
*
* @param string $sAttCode
* @param bool $bWithId
*
* @return array
*
* @throws \Exception
* @throws \CoreException
*/
public function GetColumnAsArray($sAttCode, $bWithId = true)
{
@@ -441,14 +487,16 @@ class DBObjectSet implements iDBObjectSetIterator
return $aRet;
}
/**
* Retrieve the DBSearch corresponding to the objects present in this set
*
* Limitation:
* This method will NOT work for sets with several columns (i.e. several objects per row)
*
* @return DBObjectSearch
*/
/**
* Retrieve the DBSearch corresponding to the objects present in this set
*
* Limitation:
* This method will NOT work for sets with several columns (i.e. several objects per row)
*
* @return \DBObjectSearch
*
* @throws \CoreException
*/
public function GetFilter()
{
// Make sure that we carry on the parameters of the set with the filter
@@ -495,18 +543,20 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* The list of all classes (one per column) which are part of this set
*
* @return hash Format: alias => class
* @return array Format: alias => class
*/
public function GetSelectedClasses()
{
return $this->m_oFilter->GetSelectedClasses();
}
/**
* The root class (i.e. highest ancestor in the MeaModel class hierarchy) for the first column on this set
*
* @return string The root class for the objects in the first column of the set
*/
/**
* The root class (i.e. highest ancestor in the MeaModel class hierarchy) for the first column on this set
*
* @return string The root class for the objects in the first column of the set
*
* @throws \CoreException
*/
public function GetRootClass()
{
return MetaModel::GetRootClass($this->GetClass());
@@ -515,7 +565,7 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* The arguments used for building this set
*
* @return hash Format: parameter_name => value
* @return array Format: parameter_name => value
*/
public function GetArgs()
{
@@ -533,11 +583,13 @@ class DBObjectSet implements iDBObjectSetIterator
$this->m_iLimitStart = $iLimitStart;
}
/**
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
*
* @param hash $aOrderBy Format: [alias.]attcode => boolean (true = ascending, false = descending)
*/
/**
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
*
* @param array $aOrderBy Format: [alias.]attcode => boolean (true = ascending, false = descending)
*
* @throws \MySQLException
*/
public function SetOrderBy($aOrderBy)
{
if ($this->m_aOrderBy != $aOrderBy)
@@ -551,11 +603,14 @@ class DBObjectSet implements iDBObjectSetIterator
}
}
/**
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
*
* @param hash $aAliases Format: alias => boolean (true = ascending, false = descending). If omitted, then it defaults to all the selected classes
*/
/**
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
*
* @param array $aAliases Format: alias => boolean (true = ascending, false = descending). If omitted, then it defaults to all the selected classes
*
* @throws \CoreException
* @throws \MySQLException
*/
public function SetOrderByClasses($aAliases = null)
{
if ($aAliases === null)
@@ -599,13 +654,15 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_iLimitStart;
}
/**
* Get the sort order used for loading this set from the database
*
* Limitation: the sort order has no effect on objects added in-memory
*
* @return array Format: field_code => boolean (true = ascending, false = descending)
*/
/**
* Get the sort order used for loading this set from the database
*
* Limitation: the sort order has no effect on objects added in-memory
*
* @return array Format: field_code => boolean (true = ascending, false = descending)
*
* @throws \CoreException
*/
public function GetRealSortOrder()
{
if (!$this->m_bSort)
@@ -625,9 +682,12 @@ class DBObjectSet implements iDBObjectSetIterator
}
}
/**
* Loads the set from the database. Actually performs the SQL query to retrieve the records from the DB.
*/
/**
* Loads the set from the database. Actually performs the SQL query to retrieve the records from the DB.
*
* @throws \Exception
* @throws \MySQLException
*/
public function Load()
{
if ($this->m_bLoaded) return;
@@ -682,11 +742,14 @@ class DBObjectSet implements iDBObjectSetIterator
$this->m_iNumLoadedDBRows = $this->m_oSQLResult->num_rows;
}
/**
* @param string[] $aAttToLoad
*
* @return string SQL query
*/
/**
* @param string[] $aAttToLoad
*
* @return string SQL query
*
* @throws \CoreException
* @throws \MissingQueryArgument
*/
private function _makeSelectQuery($aAttToLoad)
{
if ($this->m_iLimitCount > 0)
@@ -703,15 +766,20 @@ class DBObjectSet implements iDBObjectSetIterator
return $sSQL;
}
/**
* The total number of rows in this set. Independently of the SetLimit used for loading the set and taking into
* account the rows added in-memory.
*
* May actually perform the SQL query SELECT COUNT... if the set was not previously loaded, or loaded with a
* SetLimit
*
* @return int The total number of rows for this set.
*/
/**
* The total number of rows in this set. Independently of the SetLimit used for loading the set and taking into
* account the rows added in-memory.
*
* May actually perform the SQL query SELECT COUNT... if the set was not previously loaded, or loaded with a
* SetLimit
*
* @return int The total number of rows for this set.
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public function Count()
{
if (is_null($this->m_iNumTotalDBRows))
@@ -732,6 +800,7 @@ class DBObjectSet implements iDBObjectSetIterator
* @param $iLimit
*
* @return bool
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
@@ -746,8 +815,8 @@ class DBObjectSet implements iDBObjectSetIterator
if ($resQuery)
{
$aRow = CMDBSource::FetchArray($resQuery);
CMDBSource::FreeResult($resQuery);
$iCount = intval($aRow['COUNT']);
CMDBSource::FreeResult($resQuery);
}
else
{
@@ -766,6 +835,7 @@ class DBObjectSet implements iDBObjectSetIterator
* @param $iLimit
*
* @return int
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
@@ -806,12 +876,17 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_iNumLoadedDBRows + count($this->m_aAddedObjects);
}
/**
* Fetch the object (with the given class alias) at the current position in the set and move the cursor to the next position.
*
* @param string $sRequestedClassAlias The class alias to fetch (if there are several objects/classes per row)
* @return DBObject The fetched object or null when at the end
*/
/**
* Fetch the object (with the given class alias) at the current position in the set and move the cursor to the next position.
*
* @param string $sRequestedClassAlias The class alias to fetch (if there are several objects/classes per row)
*
* @return \DBObject The fetched object or null when at the end
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function Fetch($sRequestedClassAlias = '')
{
if (!$this->m_bLoaded) $this->Load();
@@ -855,11 +930,15 @@ class DBObjectSet implements iDBObjectSetIterator
return $oRetObj;
}
/**
* Fetch the whole row of objects (if several classes have been specified in the query) and move the cursor to the next position
*
* @return hash A hash with the format 'classAlias' => $oObj representing the current row of the set. Returns null when at the end.
*/
/**
* Fetch the whole row of objects (if several classes have been specified in the query) and move the cursor to the next position
*
* @return array An associative with the format 'classAlias' => $oObj representing the current row of the set. Returns null when at the end.
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function FetchAssoc()
{
if (!$this->m_bLoaded) $this->Load();
@@ -902,6 +981,8 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* Position the cursor (for iterating in the set) to the first position (equivalent to Seek(0))
*
* @throws \Exception
*/
public function Rewind()
{
@@ -911,11 +992,18 @@ class DBObjectSet implements iDBObjectSetIterator
}
}
/**
* Position the cursor (for iterating in the set) to the given position
*
* @param int $iRow
*/
/**
* Position the cursor (for iterating in the set) to the given position
*
* @param int $iRow
*
* @return int|mixed
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public function Seek($iRow)
{
if (!$this->m_bLoaded) $this->Load();
@@ -928,15 +1016,17 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_iCurrRow;
}
/**
* Add an object to the current set (in-memory only, nothing is written to the database)
*
* Limitation:
* Sets with several objects per row are NOT supported
*
* @param DBObject $oObject The object to add
* @param string $sClassAlias The alias for the class of the object
*/
/**
* Add an object to the current set (in-memory only, nothing is written to the database)
*
* Limitation:
* Sets with several objects per row are NOT supported
*
* @param \DBObject $oObject The object to add
* @param string $sClassAlias The alias for the class of the object
*
* @throws \MySQLException
*/
public function AddObject($oObject, $sClassAlias = '')
{
if (!$this->m_bLoaded) $this->Load();
@@ -954,16 +1044,18 @@ class DBObjectSet implements iDBObjectSetIterator
}
}
/**
* Add a hash containig objects into the current set.
*
* The expected format for the hash is: $aObjectArray[$idx][$sClassAlias] => $oObject
* Limitation:
* The aliases MUST match the ones used in the current set
* Only the ID of the objects associated to the first alias (column) is remembered.. in case we have to rebuild a filter
*
* @param hash $aObjectArray
*/
/**
* Add a hash containig objects into the current set.
*
* The expected format for the hash is: $aObjectArray[$idx][$sClassAlias] => $oObject
* Limitation:
* The aliases MUST match the ones used in the current set
* Only the ID of the objects associated to the first alias (column) is remembered.. in case we have to rebuild a filter
*
* @param array $aObjectArray
*
* @throws \MySQLException
*/
protected function AddObjectExtended($aObjectArray)
{
if (!$this->m_bLoaded) $this->Load();
@@ -983,15 +1075,17 @@ class DBObjectSet implements iDBObjectSetIterator
}
}
/**
* Add an array of objects into the current set
*
* Limitation:
* Sets with several classes per row are not supported (use AddObjectExtended instead)
*
* @param array $aObjects The array of objects to add
* @param string $sClassAlias The Alias of the class for the added objects
*/
/**
* Add an array of objects into the current set
*
* Limitation:
* Sets with several classes per row are not supported (use AddObjectExtended instead)
*
* @param array $aObjects The array of objects to add
* @param string $sClassAlias The Alias of the class for the added objects
*
* @throws \MySQLException
*/
public function AddObjectArray($aObjects, $sClassAlias = '')
{
if (!$this->m_bLoaded) $this->Load();
@@ -1009,8 +1103,9 @@ class DBObjectSet implements iDBObjectSetIterator
* Limitation:
* The added objects are not checked for duplicates (i.e. one cann add several times the same object, or add an object already present in the set).
*
* @param DBObjectSet $oObjectSet The set to append
* @throws CoreException
* @param \DBObjectSet $oObjectSet The set to append
*
* @throws \CoreException
*/
public function Append(DBObjectSet $oObjectSet)
{
@@ -1027,17 +1122,24 @@ class DBObjectSet implements iDBObjectSetIterator
}
}
/**
* Create a set containing the objects present in both the current set and another specified set
*
* Limitations:
* Will NOT work if only a subset of the sets was loaded with SetLimit.
* Works only with sets made of objects loaded from the database since the comparison is based on the objects identifiers
*
* @param DBObjectSet $oObjectSet The set to intersect with. The current position inside the set will be lost (= at the end)
* @throws CoreException
* @return DBObjectSet A new set of objects, containing the objects present in both sets (based on their identifier)
*/
/**
* Create a set containing the objects present in both the current set and another specified set
*
* Limitations:
* Will NOT work if only a subset of the sets was loaded with SetLimit.
* Works only with sets made of objects loaded from the database since the comparison is based on the objects identifiers
*
* @param \DBObjectSet $oObjectSet The set to intersect with. The current position inside the set will be lost (= at the end)
*
* @return \DBObjectSet A new set of objects, containing the objects present in both sets (based on their identifier)
*
* @throws \Exception
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public function CreateIntersect(DBObjectSet $oObjectSet)
{
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
@@ -1069,16 +1171,19 @@ class DBObjectSet implements iDBObjectSetIterator
return $oNewSet;
}
/**
* Compare two sets of objects to determine if their content is identical or not.
*
* Limitation:
* Works only for sets of 1 column (i.e. one class of object selected)
*
* @param DBObjectSet $oObjectSet
* @param array $aExcludeColumns The list of columns to exclude frop the comparison
* @return boolean True if the sets are identical, false otherwise
*/
/**
* Compare two sets of objects to determine if their content is identical or not.
*
* Limitation:
* Works only for sets of 1 column (i.e. one class of object selected)
*
* @param \DBObjectSet $oObjectSet
* @param array $aExcludeColumns The list of columns to exclude frop the comparison
*
* @return boolean True if the sets are identical, false otherwise
*
* @throws \CoreException
*/
public function HasSameContents(DBObjectSet $oObjectSet, $aExcludeColumns = array())
{
$oComparator = new DBObjectSetComparator($this, $oObjectSet, $aExcludeColumns);
@@ -1092,10 +1197,12 @@ class DBObjectSet implements iDBObjectSetIterator
* The objects inside the set must be written in the database since the comparison is based on their identifiers
* Sets with several objects per row are NOT supported
*
* @param DBObjectSet $oObjectSet
* @throws CoreException
* @param \DBObjectSet $oObjectSet
*
* @return DBObjectSet The "delta" set.
* @return \DBObjectSet The "delta" set.
*
* @throws \Exception
* @throws \CoreException
*/
public function CreateDelta(DBObjectSet $oObjectSet)
{
@@ -1128,9 +1235,11 @@ class DBObjectSet implements iDBObjectSetIterator
return $oNewSet;
}
/**
* Will be deprecated soon - use MetaModel::GetRelatedObjectsDown/Up instead to take redundancy into account
*/
/**
* Will be deprecated soon - use MetaModel::GetRelatedObjectsDown/Up instead to take redundancy into account
*
* @throws \Exception
*/
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99)
{
$aRelatedObjs = array();
@@ -1154,16 +1263,21 @@ class DBObjectSet implements iDBObjectSetIterator
return $aRelatedObjs;
}
/**
* Compute the "RelatedObjects" (forward or "down" direction) for the set
* for the specified relation
*
* @param string $sRelCode The code of the relation to use for the computation
* @param int $iMaxDepth Maximum recursion depth
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
*
* @return RelationGraph The graph of all the related objects
*/
/**
* Compute the "RelatedObjects" (forward or "down" direction) for the set
* for the specified relation
*
* @param string $sRelCode The code of the relation to use for the computation
* @param int $iMaxDepth Maximum recursion depth
* @param bool $bEnableRedundancy
*
* @return \RelationGraph The graph of all the related objects
*
* @throws \Exception
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function GetRelatedObjectsDown($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
{
$oGraph = new RelationGraph();
@@ -1175,17 +1289,22 @@ class DBObjectSet implements iDBObjectSetIterator
$oGraph->ComputeRelatedObjectsDown($sRelCode, $iMaxDepth, $bEnableRedundancy);
return $oGraph;
}
/**
* Compute the "RelatedObjects" (reverse or "up" direction) for the set
* for the specified relation
*
* @param string $sRelCode The code of the relation to use for the computation
* @param int $iMaxDepth Maximum recursion depth
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
*
* @return RelationGraph The graph of all the related objects
*/
/**
* Compute the "RelatedObjects" (reverse or "up" direction) for the set
* for the specified relation
*
* @param string $sRelCode The code of the relation to use for the computation
* @param int $iMaxDepth Maximum recursion depth
* @param bool $bEnableRedundancy
*
* @return \RelationGraph The graph of all the related objects
*
* @throws \Exception
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function GetRelatedObjectsUp($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
{
$oGraph = new RelationGraph();
@@ -1197,14 +1316,21 @@ class DBObjectSet implements iDBObjectSetIterator
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
return $oGraph;
}
/**
* Builds an object that contains the values that are common to all the objects
* in the set. If for a given attribute, objects in the set have various values
* then the resulting object will contain null for this value.
* @param $aValues Hash Output: the distribution of the values, in the set, for each attribute
* @return DBObject The object with the common values
*/
/**
* Builds an object that contains the values that are common to all the objects
* in the set. If for a given attribute, objects in the set have various values
* then the resulting object will contain null for this value.
*
* @param array $aValues Hash Output: the distribution of the values, in the set, for each attribute
*
* @return \DBObject The object with the common values
*
* @throws \Exception
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function ComputeCommonObject(&$aValues)
{
$sClass = $this->GetClass();
@@ -1288,7 +1414,7 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* List the constant fields (and their value) in the given query
* @return Hash [Alias][AttCode] => value
* @return array [Alias][AttCode] => value
*/
public function ListConstantFields()
{
@@ -1378,10 +1504,12 @@ class DBObjectSetComparator
$this->oSet1 = $oSet1;
$this->oSet2 = $oSet2;
}
/**
* Builds the lists of fingerprints and initializes internal structures, if it was not already done
*/
/**
* Builds the lists of fingerprints and initializes internal structures, if it was not already done
*
* @throws \CoreException
*/
protected function ComputeFingerprints()
{
if ($this->aFingerprints1 === null)
@@ -1426,11 +1554,13 @@ class DBObjectSetComparator
}
}
}
/**
* Tells if the sets are equivalent or not. Returns as soon as the first difference is found.
* @return boolean true if the set have an equivalent content, false otherwise
*/
/**
* Tells if the sets are equivalent or not. Returns as soon as the first difference is found.
* @return boolean true if the set have an equivalent content, false otherwise
*
* @throws \CoreException
*/
public function SetsAreEquivalent()
{
if (($this->oSet1 === null) && ($this->oSet2 === null))
@@ -1469,13 +1599,16 @@ class DBObjectSetComparator
return true;
}
/**
* Get the list of differences between the two sets. In ordeer to write back into the database only the minimum changes
* THE FIRST SET MUST BE THE ONE LOADED FROM THE DATABASE
* Returns a hash: 'added' => DBObject(s), 'removed' => DBObject(s), 'modified' => DBObjects(s)
* @return Ambigous <int:DBObject: , unknown>
*/
/**
* Get the list of differences between the two sets. In ordeer to write back into the database only the minimum changes
* THE FIRST SET MUST BE THE ONE LOADED FROM THE DATABASE
* Returns a hash: 'added' => DBObject(s), 'removed' => DBObject(s), 'modified' => DBObjects(s)
* @return array
*
* @throws \Exception
* @throws \CoreException
*/
public function GetDifferences()
{
$aResult = array('added' => array(), 'removed' => array(), 'modified' => array());
@@ -1524,13 +1657,19 @@ class DBObjectSetComparator
}
return $aResult;
}
/**
* Helpr to clone (in memory) an object and to apply to it the values taken from a second object
* @param DBObject $oObjToClone
* @param DBObject $oObjWithValues
* @return DBObject The modified clone
*/
/**
* Helpr to clone (in memory) an object and to apply to it the values taken from a second object
*
* @param \DBObject $oObjToClone
* @param \DBObject $oObjWithValues
*
* @return \DBObject The modified clone
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
*/
protected function CopyFrom($oObjToClone, $oObjWithValues)
{
$oObj = MetaModel::GetObject(get_class($oObjToClone), $oObjToClone->GetKey());

View File

@@ -235,29 +235,96 @@ abstract class DBSearch
abstract public function GetInternalParams();
abstract public function GetQueryParams($bExcludeMagicParams = true);
abstract public function ListConstantFields();
/**
* Turn the parameters (:xxx) into scalar values in order to easily
* serialize a search
*
* @param array $aArgs
*
* @return string
*/
abstract public function ApplyParameters($aArgs);
public function serialize($bDevelopParams = false, $aContextParams = null)
public function serialize($bDevelopParams = false, $aContextParams = array())
{
$aQueryParams = $this->GetQueryParams();
$aContextParams = array_merge($this->GetInternalParams(), $aContextParams);
foreach($aQueryParams as $sParam => $sValue)
{
if (isset($aContextParams[$sParam]))
{
$aQueryParams[$sParam] = $aContextParams[$sParam];
}
elseif (($iPos = strpos($sParam, '->')) !== false)
{
$sParamName = substr($sParam, 0, $iPos);
if (isset($aContextParams[$sParamName.'->object()']))
{
$sAttCode = substr($sParam, $iPos + 2);
/** @var \DBObject $oObj */
$oObj = $aContextParams[$sParamName.'->object()'];
if ($oObj->IsModified())
{
if ($sAttCode == 'id')
{
$aQueryParams[$sParam] = $oObj->GetKey();
}
else
{
$aQueryParams[$sParam] = $oObj->Get($sAttCode);
}
}
else
{
unset($aQueryParams[$sParam]);
// For database objects, serialize only class, key
$aQueryParams[$sParamName.'->id'] = $oObj->GetKey();
$aQueryParams[$sParamName.'->class'] = get_class($oObj);
}
}
}
}
$sOql = $this->ToOql($bDevelopParams, $aContextParams);
return rawurlencode(base64_encode(serialize(array($sOql, $this->GetInternalParams(), $this->m_aModifierProperties))));
return json_encode(array($sOql, $aQueryParams, $this->m_aModifierProperties));
}
/**
* @param string $sValue Serialized OQL query
*
* @return \DBSearch
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \OQLException
*/
static public function unserialize($sValue)
{
$aData = unserialize(base64_decode(rawurldecode($sValue)));
$aData = json_decode($sValue, true);
if (is_null($aData))
{
throw new CoreException("Invalid filter parameter");
}
$sOql = $aData[0];
$aParams = $aData[1];
$aExtraParams = array();
foreach($aParams as $sParam => $sValue)
{
if (($iPos = strpos($sParam, '->class')) !== false)
{
$sParamName = substr($sParam, 0, $iPos);
if (isset($aParams[$sParamName.'->id']))
{
$sClass = $aParams[$sParamName.'->class'];
$iKey = $aParams[$sParamName.'->id'];
$oObj = MetaModel::GetObject($sClass, $iKey);
$aExtraParams[$sParamName.'->object()'] = $oObj;
}
}
}
$aParams = array_merge($aExtraParams, $aParams);
// We've tried to use gzcompress/gzuncompress, but for some specific queries
// it was not working at all (See Trac #193)
// gzuncompress was issuing a warning "data error" and the return object was null
@@ -335,6 +402,7 @@ abstract class DBSearch
}
}
/** @var DBObjectSearch | null $oResultFilter */
if (!isset($oResultFilter))
{
$oKPI = new ExecutionKPI();
@@ -373,11 +441,20 @@ abstract class DBSearch
return $oResultFilter;
}
// Alternative to object mapping: the data are transfered directly into an array
// This is 10 times faster than creating a set of objects, and makes sense when optimization is required
/**
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
*/
* Alternative to object mapping: the data are transfered directly into an array
* This is 10 times faster than creating a set of objects, and makes sense when optimization is required
*
* @param array $aColumns
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @param array $aArgs
*
* @return array|void
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public function ToDataArray($aColumns = array(), $aOrderBy = array(), $aArgs = array())
{
$sSQL = $this->MakeSelectQuery($aOrderBy, $aArgs);

View File

@@ -116,6 +116,22 @@ class Dict
self::$m_iErrorMode = $iErrorMode;
}
/**
* Check if a dictionary entry exists or not
* @param $sStringCode
*
* @return bool
*/
public static function Exists($sStringCode)
{
$sImpossibleString = 'aVlHYKEI3TZuDV5o0pghv7fvhYNYuzYkTk7WL0Zoqw8rggE7aq';
if (static::S($sStringCode, $sImpossibleString) === $sImpossibleString)
{
return false;
}
return true;
}
/**
* Returns a localised string from the dictonary
*
@@ -197,7 +213,7 @@ class Dict
/**
* Initialize a the entries for a given language (replaces the former Add() method)
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
* @param hash $aEntries Hash array of dictionnary entries
* @param array $aEntries Hash array of dictionnary entries
*/
public static function SetEntries($sLanguageCode, $aEntries)
{

View File

@@ -188,11 +188,16 @@ EOF
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else if ($value instanceOf ormDocument)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else if ($value instanceOf ormDocument)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else if ($value instanceOf ormTagSet)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);

View File

@@ -154,7 +154,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
/**
* @var array
* @see https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Arich_text_limitations
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
*/
protected static $aTagsWhiteList = array(
'html' => array(),
@@ -211,7 +211,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
/**
* @var array
* @see https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Arich_text_limitations
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
*/
protected static $aStylesWhiteList = array(
'background-color',
@@ -230,7 +230,9 @@ class HTMLDOMSanitizer extends HTMLSanitizer
'margin',
'padding',
'text-align',
'vertical-align',
'width',
'white-space',
);
public function __construct()

View File

@@ -166,7 +166,7 @@ class InlineImage extends DBObject
*/
public static function FinalizeInlineImages(DBObject $oObject)
{
$iTransactionId = utils::ReadParam('transaction_id', null);
$iTransactionId = utils::ReadParam('transaction_id', null, false, 'transaction_id');
if (!is_null($iTransactionId))
{
// Attach new (temporary) inline images
@@ -183,6 +183,19 @@ class InlineImage extends DBObject
$oInlineImage->DBUpdate();
}
}
// For tracing issues with Inline Images... but beware not all updates are interactive, so this trace happens when creating objects non-interactively (REST, Synchro...)
// else
// {
// IssueLog::Error('InlineImage: Error during FinalizeInlineImages(), no transaction ID for object '.get_class($oObject).'#'.$oObject->GetKey().'.');
//
// IssueLog::Error('|- Call stack:');
// $oException = new Exception();
// $sStackTrace = $oException->getTraceAsString();
// IssueLog::Error($sStackTrace);
//
// IssueLog::Error('|- POST vars:');
// IssueLog::Error(print_r($_POST, true));
// }
}
/**
@@ -299,8 +312,12 @@ EOF
/**
* Check if an the given mimeType is an image that can be processed by the system
*
* @param string $sMimeType
* @return boolean
*
* @return boolean always false if php-gd not installed
* otherwise true if file is one of those type : image/gif, image/jpeg, image/png
* @uses php-gd extension
*/
public static function IsImage($sMimeType)
{
@@ -446,9 +463,11 @@ EOF
* Get the fragment of javascript needed to complete the initialization of
* CKEditor when creating/modifying an object
*
* @param DBObject $oObject The object being edited
* @param string $sTempId The concatenation of session_id().'_'.$iTransactionId.
* @param \DBObject $oObject The object being edited
* @param string $sTempId Generated through utils::GetUploadTempId($iTransactionId)
*
* @return string The JS fragment to insert in "on document ready"
* @throws \Exception
*/
public static function EnableCKEditorImageUpload(DBObject $oObject, $sTempId)
{
@@ -541,49 +560,67 @@ class InlineImageGC implements iBackgroundProcess
{
public function GetPeriodicity()
{
return 3600; // Runs every hour
return 1; // Runs every 8 hours
}
/**
* @param int $iTimeLimit
*
* @return string
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DeleteException
* @throws \MySQLException
* @throws \OQLException
*/
public function Process($iTimeLimit)
{
$sDateLimit = date(AttributeDateTime::GetSQLFormat(), time()); // Every temporary InlineImage/Attachment expired will be deleted
$iProcessed = 0;
$sOQL = "SELECT InlineImage WHERE (item_id = 0) AND (expire < '$sDateLimit')";
while (time() < $iTimeLimit)
$aResults = array();
$aClasses = array('InlineImage', 'Attachment');
foreach($aClasses as $sClass)
{
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('expire' => true) /* order by*/, array(), null, 1 /* limit count */);
$oSet->OptimizeColumnLoad(array());
$oResult = $oSet->Fetch();
if (is_null($oResult))
$iProcessed = 0;
if(class_exists($sClass))
{
// Nothing to be done
break;
$iProcessed = $this->DeleteExpiredDocuments($sClass, $iTimeLimit, $sDateLimit);
}
$iProcessed++;
$oResult->DBDelete();
$aResults[] = "$iProcessed old temporary $sClass(s)";
}
$iProcessed2 = 0;
if (class_exists('Attachment'))
{
$sOQL = "SELECT Attachment WHERE (item_id = 0) AND (expire < '$sDateLimit')";
while (time() < $iTimeLimit)
{
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('expire' => true) /* order by*/, array(), null, 1 /* limit count */);
$oSet->OptimizeColumnLoad(array());
$oResult = $oSet->Fetch();
if (is_null($oResult))
{
// Nothing to be done
break;
}
$iProcessed2++;
$oResult->DBDelete();
}
}
return "Cleaned $iProcessed old temporary InlineImage(s) and $iProcessed2 old temporary Attachment(s).";
return "Cleaned ".implode(' and ', $aResults).".";
}
/**
* @param string $sClass
* @param int $iTimeLimit
* @param string $sDateLimit
*
* @return int
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DeleteException
* @throws \MySQLException
* @throws \OQLException
*/
protected function DeleteExpiredDocuments($sClass, $iTimeLimit, $sDateLimit)
{
$iProcessed = 0;
$sOQL = "SELECT $sClass WHERE (item_id = 0) AND (expire < '$sDateLimit')";
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('expire' => true) /* order by*/, array(), null,
1 /* limit count */);
$oSet->OptimizeColumnLoad(array());
while ((time() < $iTimeLimit) && ($oResult = $oSet->Fetch()))
{
/** @var \ormDocument $oDocument */
$oDocument = $oResult->Get('contents');
IssueLog::Info($sClass.' GC: Removed temp. file '.$oDocument->GetFileName().' on "'.$oResult->Get('item_class').'" #'.$oResult->Get('item_id').' as it has expired.');
$oResult->DBDelete();
$iProcessed++;
}
return $iProcessed;
}
}

View File

@@ -116,13 +116,14 @@ class ExecutionKPI
foreach (self::$m_aExecData as $aOpStats)
{
$sOperation = $aOpStats['op'];
$sBegin = $sEnd = $sDuration = $sMemBegin = $sMemEnd = $sMemPeak = '?';
$sBegin = round($aOpStats['time_begin'], 3);
$sEnd = round($aOpStats['time_end'], 3);
$fDuration = $aOpStats['time_end'] - $aOpStats['time_begin'];
$sDuration = round($fDuration, 3);
$sMemBegin = 'n/a';
$sMemEnd = 'n/a';
$sMemPeak = 'n/a';
if (isset($aOpStats['mem_begin']))
{
$sMemBegin = self::MemStr($aOpStats['mem_begin']);
@@ -197,12 +198,13 @@ class ExecutionKPI
self::Report("</div>");
self::Report("<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>");
// Report operation details
foreach (self::$m_aStats as $sOperation => $aOpStats)
{
$sOperationHtml = '<a name="'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
self::Report("<h4>$sOperationHtml</h4>");
self::Report("<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>");
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
self::Report("<thead>");
self::Report(" <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>");
@@ -251,7 +253,9 @@ class ExecutionKPI
self::Report("</tr>");
}
self::Report("</table>");
self::Report("<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>");
}
self::Report('<a name="end-'.md5($sExecId).'">&nbsp;</a>');
}

View File

@@ -144,7 +144,7 @@ abstract class MetaModel
// $aBacktrace[1] is the info we want
if (!empty($sExpectedFunctionName))
{
assert('$aBacktrace[2]["function"] == $sExpectedFunctionName');
assert($aBacktrace[2]['function'] == $sExpectedFunctionName);
}
if ($bRecordSourceFile)
{
@@ -320,8 +320,7 @@ abstract class MetaModel
final static public function GetName($sClass)
{
self::_check_subclass($sClass);
$sStringCode = 'Class:'.$sClass;
return Dict::S($sStringCode, str_replace('_', ' ', $sClass));
return $sClass::GetClassName($sClass);
}
/**
@@ -411,8 +410,7 @@ abstract class MetaModel
final static public function GetClassDescription($sClass)
{
self::_check_subclass($sClass);
$sStringCode = 'Class:'.$sClass.'+';
return Dict::S($sStringCode, '');
return $sClass::GetClassDescription($sClass);
}
/**
@@ -528,6 +526,170 @@ abstract class MetaModel
return $oRet;
}
/**
* @param string $sClass
* @param bool $bClassDefinitionOnly if true then will only return properties defined in the specified class on not the properties
* from its parent classes
*
* @return array rule id as key, rule properties as value
* @throws \CoreException
* @since 2.6 N°659 uniqueness constraint
* @see #SetUniquenessRuleRootClass that fixes a specific 'root_class' property to know which class is root per rule
*/
final public static function GetUniquenessRules($sClass, $bClassDefinitionOnly = false)
{
if (!isset(self::$m_aClassParams[$sClass]))
{
return array();
}
$aCurrentUniquenessRules = array();
if (array_key_exists('uniqueness_rules', self::$m_aClassParams[$sClass]))
{
$aCurrentUniquenessRules = self::$m_aClassParams[$sClass]['uniqueness_rules'];
}
if ($bClassDefinitionOnly)
{
return $aCurrentUniquenessRules;
}
$sParentClass = self::GetParentClass($sClass);
if ($sParentClass)
{
$aParentUniquenessRules = self::GetUniquenessRules($sParentClass);
foreach ($aParentUniquenessRules as $sUniquenessRuleId => $aParentUniquenessRuleProperties)
{
$bCopyDisabledKey = true;
$bCurrentDisabledValue = null;
if (array_key_exists($sUniquenessRuleId, $aCurrentUniquenessRules))
{
if (self::IsUniquenessRuleContainingOnlyDisabledKey($aCurrentUniquenessRules[$sUniquenessRuleId]))
{
$bCopyDisabledKey = false;
}
else
{
continue;
}
}
$aMergedUniquenessProperties = $aParentUniquenessRuleProperties;
if (!$bCopyDisabledKey)
{
$aMergedUniquenessProperties['disabled'] = $aCurrentUniquenessRules[$sUniquenessRuleId]['disabled'];
}
$aCurrentUniquenessRules[$sUniquenessRuleId] = $aMergedUniquenessProperties;
}
}
return $aCurrentUniquenessRules;
}
/**
* @param string $sRootClass
* @param string $sRuleId
*
* @throws \CoreException
* @since 2.6.1 N°1918 (sous les pavés, la plage) initialize in 'root_class' property the class that has the first
* definition of the rule in the hierarchy
*/
final private static function SetUniquenessRuleRootClass($sRootClass, $sRuleId)
{
foreach (self::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_ALL) as $sClass)
{
self::$m_aClassParams[$sClass]['uniqueness_rules'][$sRuleId]['root_class'] = $sClass;
}
}
/**
* @param string $sRuleId
* @param string $sLeafClassName
*
* @return string name of the class, null if not present
*/
final public static function GetRootClassForUniquenessRule($sRuleId, $sLeafClassName)
{
$sFirstClassWithRuleId = null;
if (isset(self::$m_aClassParams[$sLeafClassName]['uniqueness_rules'][$sRuleId]))
{
$sFirstClassWithRuleId = $sLeafClassName;
}
$sParentClass = self::GetParentClass($sLeafClassName);
if ($sParentClass)
{
$sParentClassWithRuleId = self::GetRootClassForUniquenessRule($sRuleId, $sParentClass);
if (!is_null($sParentClassWithRuleId))
{
$sFirstClassWithRuleId = $sParentClassWithRuleId;
}
}
return $sFirstClassWithRuleId;
}
/**
* @param string $sRootClass
* @param string $sRuleId
*
* @return string[] child classes with the rule disabled, and that are concrete classes
*
* @throws \CoreException
* @since 2.6.1 N°1968 (soyez réalistes, demandez l'impossible)
*/
final public static function GetChildClassesWithDisabledUniquenessRule($sRootClass, $sRuleId)
{
$aClassesWithDisabledRule = array();
foreach (self::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_EXCLUDETOP) as $sChildClass)
{
if (array_key_exists($sChildClass, $aClassesWithDisabledRule))
{
continue;
}
if (!array_key_exists('uniqueness_rules', self::$m_aClassParams[$sChildClass]))
{
continue;
}
if (!array_key_exists($sRuleId, self::$m_aClassParams[$sChildClass]['uniqueness_rules']))
{
continue;
}
if (self::$m_aClassParams[$sChildClass]['uniqueness_rules'][$sRuleId]['disabled'] === true)
{
$aDisabledClassChildren = self::EnumChildClasses($sChildClass, ENUM_CHILD_CLASSES_ALL);
foreach ($aDisabledClassChildren as $sDisabledClassChild)
{
if (!self::IsAbstract($sDisabledClassChild))
{
$aClassesWithDisabledRule[] = $sDisabledClassChild;
}
}
}
}
return $aClassesWithDisabledRule;
}
/**
* @param array $aRuleProperties
*
* @return bool
* @since 2.6 N°659 uniqueness constraint
*/
private static function IsUniquenessRuleContainingOnlyDisabledKey($aRuleProperties)
{
$aNonNullRuleProperties = array_filter($aRuleProperties, function ($v) {
return (!is_null($v));
});
return ((count($aNonNullRuleProperties) == 1) && (array_key_exists('disabled', $aNonNullRuleProperties)));
}
/**
* @param string $sClass
*
@@ -1024,7 +1186,7 @@ abstract class MetaModel
/**
* array of ("classname" => array of attributes)
*
* @var array
* @var \AttributeDefinition[]
*/
private static $m_aAttribDefs = array();
/**
@@ -2599,7 +2761,7 @@ abstract class MetaModel
// Build the list of available extensions
//
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension');
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider');
foreach($aInterfaces as $sInterface)
{
self::$m_aExtensionClasses[$sInterface] = array();
@@ -2682,6 +2844,30 @@ abstract class MetaModel
$oClassInit->OnAfterClassInitialization($sPHPClass);
}
}
$aCurrentClassUniquenessRules = MetaModel::GetUniquenessRules($sPHPClass, true);
if (!empty($aCurrentClassUniquenessRules))
{
$aClassFields = self::GetAttributesList($sPHPClass);
foreach ($aCurrentClassUniquenessRules as $sUniquenessRuleId => $aUniquenessRuleProperties)
{
$bIsRuleOverride = self::HasSameUniquenessRuleInParent($sPHPClass, $sUniquenessRuleId);
try
{
self::CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bIsRuleOverride, $aClassFields);
}
catch (CoreUnexpectedValue $e)
{
throw new Exception("Invalid uniqueness rule declaration : class={$sPHPClass}, rule=$sUniquenessRuleId, reason={$e->getMessage()}");
}
if (!$bIsRuleOverride)
{
self::SetUniquenessRuleRootClass($sPHPClass, $sUniquenessRuleId);
}
}
}
}
catch (ReflectionException $e)
{
@@ -2950,6 +3136,107 @@ abstract class MetaModel
}
}
/**
* @param string $sClassName
* @param string $sUniquenessRuleId
*
* @return bool true if one of the parent class (recursive) has the same rule defined
* @throws \CoreException
*/
private static function HasSameUniquenessRuleInParent($sClassName, $sUniquenessRuleId)
{
$sParentClass = self::GetParentClass($sClassName);
if (empty($sParentClass))
{
return false;
}
$aParentClassUniquenessRules = self::GetUniquenessRules($sParentClass);
if (array_key_exists($sUniquenessRuleId, $aParentClassUniquenessRules))
{
return true;
}
return self::HasSameUniquenessRuleInParent($sParentClass, $sUniquenessRuleId);
}
/**
* @param array $aUniquenessRuleProperties
* @param bool $bRuleOverride if false then control an original declaration validity,
* otherwise an override validity (can only have the 'disabled' key)
* @param string[] $aExistingClassFields if non empty, will check that all fields declared in the rules exists in the class
*
* @throws \CoreUnexpectedValue if the rule is invalid
*
* @since 2.6 N°659 uniqueness constraint
* @since 2.6.1 N°1968 (joli mois de mai...) disallow overrides of 'attributes' properties
*/
public static function CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bRuleOverride = true, $aExistingClassFields = array())
{
$MANDATORY_ATTRIBUTES = array('attributes');
$UNIQUENESS_MANDATORY_KEYS_NB = count($MANDATORY_ATTRIBUTES);
$bHasMissingMandatoryKey = true;
$iMissingMandatoryKeysNb = $UNIQUENESS_MANDATORY_KEYS_NB;
/** @var boolean $bHasNonDisabledKeys true if rule contains at least one key that is not 'disabled' */
$bHasNonDisabledKeys = false;
$bDisabledKeyValue = null;
foreach ($aUniquenessRuleProperties as $sUniquenessRuleKey => $aUniquenessRuleProperty)
{
if ($sUniquenessRuleKey === 'disabled')
{
$bDisabledKeyValue = $aUniquenessRuleProperty;
if (!is_null($aUniquenessRuleProperty))
{
continue;
}
}
if (is_null($aUniquenessRuleProperty))
{
continue;
}
$bHasNonDisabledKeys = true;
if (in_array($sUniquenessRuleKey, $MANDATORY_ATTRIBUTES, true)) {
$iMissingMandatoryKeysNb--;
}
if ($sUniquenessRuleKey === 'attributes')
{
if (!empty($aExistingClassFields))
{
foreach ($aUniquenessRuleProperties[$sUniquenessRuleKey] as $sRuleAttribute)
{
if (!in_array($sRuleAttribute, $aExistingClassFields, true))
{
throw new CoreUnexpectedValue("Uniqueness rule : non existing field '$sRuleAttribute'");
}
}
}
}
}
if ($iMissingMandatoryKeysNb === 0)
{
$bHasMissingMandatoryKey = false;
}
if ($bRuleOverride && $bHasNonDisabledKeys)
{
throw new CoreUnexpectedValue('Uniqueness rule : only the \'disabled\' key can be overridden');
}
if ($bRuleOverride && is_null($bDisabledKeyValue))
{
throw new CoreUnexpectedValue('Uniqueness rule : when overriding a rule, value must be set for the \'disabled\' key');
}
if (!$bRuleOverride && $bHasMissingMandatoryKey)
{
throw new CoreUnexpectedValue('Uniqueness rule : missing mandatory property');
}
}
/**
* To be overriden, must be called for any object class (optimization)
*/
@@ -3600,7 +3887,7 @@ abstract class MetaModel
/**
* @param string $sClass
* @param int $iOption
* @param int $iOption one of ENUM_CHILD_CLASSES_EXCLUDETOP, ENUM_CHILD_CLASSES_ALL
*
* @return array
* @throws \CoreException
@@ -4505,7 +4792,7 @@ abstract class MetaModel
}
}
// Check unicity of the SQL columns
// Check SQL columns uniqueness
//
if (self::HasTable($sClass))
{
@@ -4971,6 +5258,7 @@ abstract class MetaModel
$aCreateTableItems = array(); // array of <table> => array of <create definition>
$aAlterTableMetaData = array();
$aAlterTableItems = array(); // array of <table> => <alter specification>
$aPostTableAlteration = array(); // array of <table> => post alteration queries
foreach(self::GetClasses() as $sClass)
{
@@ -5065,6 +5353,7 @@ abstract class MetaModel
$aTableInfo['Fields'][$sField]['used'] = true;
$bIndexNeeded = $oAttDef->RequiresIndex();
$bFullTextIndexNeeded = false;
if (!$bIndexNeeded)
{
// Add an index on the columns of the friendlyname
@@ -5073,6 +5362,13 @@ abstract class MetaModel
$bIndexNeeded = true;
}
}
else
{
if ($oAttDef->RequiresFullTextIndex())
{
$bFullTextIndexNeeded = true;
}
}
$sFieldDefinition = "`$sField` $sDBFieldSpec";
if (!CMDBSource::IsField($sTable, $sField))
@@ -5092,21 +5388,37 @@ abstract class MetaModel
if ($bIndexNeeded)
{
$aTableInfo['Indexes'][$sField]['used'] = true;
$aColumns = array($sField);
$aLength = self::DBGetIndexesLength($sClass, $aColumns, $aTableInfo);
$sIndexName = $sField;
$sColumns = '`'.$sField.'`';
if (!is_null($aLength[0]))
if ($bFullTextIndexNeeded)
{
$sColumns .= ' ('.$aLength[0].')';
}
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD INDEX `$sField` ($sColumns)";
if ($bTableToCreate)
{
$aCreateTableItems[$sTable][] = "INDEX `$sField` ($sColumns)";
$sIndexType = 'FULLTEXT INDEX';
}
else
{
$aAlterTableItems[$sTable][] = "ADD INDEX `$sField` ($sColumns)";
$sIndexType = 'INDEX';
$aColumns = array($sField);
$aLength = self::DBGetIndexesLength($sClass, $aColumns, $aTableInfo);
if (!is_null($aLength[0]))
{
$sColumns .= ' ('.$aLength[0].')';
}
}
$sSugFix = "ALTER TABLE `$sTable` ADD $sIndexType `$sIndexName` ($sColumns)";
$aSugFix[$sClass][$sAttCode][] = $sSugFix;
if ($bFullTextIndexNeeded)
{
// MySQL does not support multi fulltext index creation in a single query (mysql_errno = 1795)
$aPostTableAlteration[$sTable][] = $sSugFix;
}
elseif ($bTableToCreate)
{
$aCreateTableItems[$sTable][] = "$sIndexType `$sIndexName` ($sColumns)";
}
else
{
$aAlterTableItems[$sTable][] = "ADD $sIndexType `$sIndexName` ($sColumns)";
}
}
@@ -5119,12 +5431,24 @@ abstract class MetaModel
$sAlterTableItemsAfterChange = '';
if ($bIndexNeeded)
{
$aColumns = array($sField);
$aLength = self::DBGetIndexesLength($sClass, $aColumns, $aTableInfo);
$aTableInfo['Indexes'][$sField]['used'] = true;
if ($bFullTextIndexNeeded)
{
$sIndexType = 'FULLTEXT INDEX';
$aColumns = null;
$aLength = null;
}
else
{
$sIndexType = 'INDEX';
$aColumns = array($sField);
$aLength = self::DBGetIndexesLength($sClass, $aColumns, $aTableInfo);
}
if (!CMDBSource::HasIndex($sTable, $sField, $aColumns, $aLength))
{
$sIndexName = $sField;
$sColumns = '`'.$sField.'`';
if (!is_null($aLength[0]))
{
@@ -5134,16 +5458,11 @@ abstract class MetaModel
$aErrors[$sClass][$sAttCode][] = "Foreign key '$sField' in table '$sTable' should have an index";
if (CMDBSource::HasIndex($sTable, $sField))
{
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` DROP INDEX `$sField`";
$sSugFixAfterChange = "ALTER TABLE `$sTable` ADD INDEX `$sField` ($sColumns)";
$aAlterTableItems[$sTable][] = "DROP INDEX `$sField`";
$sAlterTableItemsAfterChange = "ADD INDEX `$sField` ($sColumns)";
}
else
{
$sSugFixAfterChange = "ALTER TABLE `$sTable` ADD INDEX `$sField` ($sColumns)";
$sAlterTableItemsAfterChange = "ADD INDEX `$sField` ($sColumns)";
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` DROP INDEX `$sIndexName`";
$aAlterTableItems[$sTable][] = "DROP INDEX `$sIndexName`";
}
$sSugFixAfterChange = "ALTER TABLE `$sTable` ADD $sIndexType `$sIndexName` ($sColumns)";
$sAlterTableItemsAfterChange = "ADD $sIndexType `$sIndexName` ($sColumns)";
}
}
@@ -5167,7 +5486,15 @@ abstract class MetaModel
if (!empty($sSugFixAfterChange))
{
$aSugFix[$sClass][$sAttCode][] = $sSugFixAfterChange;
$aAlterTableItems[$sTable][] = $sAlterTableItemsAfterChange;
if ($bFullTextIndexNeeded)
{
// MySQL does not support multi fulltext index creation in a single query (mysql_errno = 1795)
$aPostTableAlteration[$sTable][] = $sSugFixAfterChange;
}
else
{
$aAlterTableItems[$sTable][] = $sAlterTableItemsAfterChange;
}
}
}
}
@@ -5291,6 +5618,10 @@ abstract class MetaModel
$sChangeList = implode(', ', $aChangeList);
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
}
foreach($aPostTableAlteration as $sTable => $aChangeList)
{
$aCondensedQueries = array_merge($aCondensedQueries, $aChangeList);
}
return array($aErrors, $aSugFix, $aCondensedQueries);
}
@@ -5858,20 +6189,10 @@ abstract class MetaModel
CMDBSource::SelectDB(self::$m_sDBName);
foreach(get_declared_classes() as $sPHPClass)
{
if (is_subclass_of($sPHPClass, 'ModuleHandlerAPI'))
{
$aCallSpec = array($sPHPClass, 'OnMetaModelStarted');
call_user_func_array($aCallSpec, array());
}
}
// if (false)
// {
// echo "Debug<br/>\n";
// self::static_var_dump();
// }
foreach(MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass)
{
$oPHPClass::OnMetaModelStarted();
}
ExpressionCache::Warmup();
}
@@ -6326,7 +6647,7 @@ abstract class MetaModel
*
* @param string $sClass
* @param int $iKey id value of the object to retrieve
* @param bool $bMustBeFound
* @param bool $bMustBeFound see throws ArchivedObjectException
* @param bool $bAllowAllData if true then no rights filtering
* @param null $aModifierProperties
*
@@ -6393,12 +6714,14 @@ abstract class MetaModel
}
catch(Exception $e)
{
// We need to pop the pushed archived mode before the exception is thrown, otherwise the application stays in ArchiveMode true which has caused hazardious behavior!
// Note: When switching to PHP 5.6, we can use a finally block instead of duplicating this line.
utils::PopArchiveMode();
// In the finally block we will pop the pushed archived mode
// otherwise the application stays in ArchiveMode true which has caused hazardious behavior!
throw $e;
}
utils::PopArchiveMode();
finally
{
utils::PopArchiveMode();
}
if (empty($aRow))
{

View File

@@ -56,6 +56,11 @@ abstract class ModelReflection
abstract public function GetFiltersList($sClass);
abstract public function IsValidFilterCode($sClass, $sFilterCode);
/**
* @param string $sOQL
*
* @return \DBObjectSearch
*/
abstract public function GetQuery($sOQL);
abstract public function DictString($sStringCode, $sDefault = null, $bUserLanguageOnly = false);
@@ -75,6 +80,13 @@ abstract class ModelReflection
return vsprintf($sLocalizedFormat, $aArguments);
}
/**
* @param $sCode
* @param string $sLabel
* @param string $defaultValue
*
* @return \RunTimeIconSelectionField
*/
abstract public function GetIconSelectionField($sCode, $sLabel = '', $defaultValue = '');
abstract public function GetRootClass($sClass);

View File

@@ -25,7 +25,7 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class ModuleHandlerAPI
abstract class ModuleHandlerAPI implements ModuleHandlerApiInterface
{
public static function OnMetaModelStarted()
{
@@ -34,5 +34,19 @@ abstract class ModuleHandlerAPI
public static function OnMenuCreation()
{
}
public function __construct()
{
}
}
?>
interface ModuleHandlerApiInterface
{
public static function OnMetaModelStarted();
public static function OnMenuCreation();
public function __construct(); //empty params is required in order to be instantiable by MetaModel::InitClasses()
}

View File

@@ -42,7 +42,7 @@
* @copyright 2006 Gregory Beaver
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once 'PEAR/Exception.php';
//require_once 'PEAR/Exception.php';
/**
* @package PHP_LexerGenerator
* @author Gregory Beaver <cellog@php.net>
@@ -51,5 +51,5 @@ require_once 'PEAR/Exception.php';
* @version @package_version@
* @since File available since Release 0.1.0
*/
class PHP_LexerGenerator_Exception extends PEAR_Exception {}
class PHP_LexerGenerator_Exception extends Exception {}
?>

View File

@@ -22,6 +22,9 @@ class MissingQueryArgument extends CoreException
}
/**
* @method Check($oModelReflection, array $aAliases, $sSourceQuery)
*/
abstract class Expression
{
/**
@@ -47,13 +50,30 @@ abstract class Expression
/**
* recursive rendering
*
* @deprecated use RenderExpression
*
* @param array $aArgs used as input by default, or used as output if bRetrofitParams set to True
* @param bool $bRetrofitParams
*
* @return array|string
* @throws \MissingQueryArgument
*/
abstract public function Render(&$aArgs = null, $bRetrofitParams = false);
public function Render(&$aArgs = null, $bRetrofitParams = false)
{
return $this->RenderExpression(false, $aArgs, $bRetrofitParams);
}
/**
* recursive rendering
*
* @param bool $bForSQL generates code for OQL if false, for SQL otherwise
* @param array $aArgs used as input by default, or used as output if bRetrofitParams set to True
* @param bool $bRetrofitParams
*
* @return array|string
* @throws \MissingQueryArgument
*/
abstract public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false);
/**
* @param DBObjectSearch $oSearch
@@ -66,7 +86,7 @@ abstract class Expression
*/
public function Display($oSearch, &$aArgs = null, $oAttDef = null, &$aCtx = array())
{
return $this->Render($aArgs);
return $this->RenderExpression(false, $aArgs);
}
public function GetAttDef($aClasses = array())
@@ -104,7 +124,7 @@ abstract class Expression
public function serialize()
{
return base64_encode($this->Render());
return base64_encode($this->RenderExpression(false));
}
/**
@@ -172,7 +192,7 @@ abstract class Expression
* @param string sValue The value returned by the query, for this expression
* @param string sDefault The default value if no relevant label could be computed
*
* @return The label
* @return string label
*/
public function MakeValueLabel($oFilter, $sValue, $sDefault)
{
@@ -183,7 +203,7 @@ abstract class Expression
{
return array(
'widget' => AttributeDefinition::SEARCH_WIDGET_TYPE_RAW,
'oql' => $this->Render($aArgs, $bRetrofitParams),
'oql' => $this->RenderExpression(false, $aArgs, $bRetrofitParams),
'label' => $this->Display($oSearch, $aArgs, $oAttDef),
'source' => get_class($this),
);
@@ -229,7 +249,7 @@ class SQLExpression extends Expression
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
public function RenderExpression($bForSql = false, &$aArgs = null, $bRetrofitParams = false)
{
return $this->m_sSQL;
}
@@ -285,7 +305,30 @@ class BinaryExpression extends Expression
protected $m_oRightExpr;
protected $m_sOperator;
/**
* @param \Expression $oLeftExpr
* @param string $sOperator
* @param \Expression $oRightExpr
*
* @throws \CoreException
*/
public function __construct($oLeftExpr, $sOperator, $oRightExpr)
{
$this->ValidateConstructorParams($oLeftExpr, $sOperator, $oRightExpr);
$this->m_oLeftExpr = $oLeftExpr;
$this->m_oRightExpr = $oRightExpr;
$this->m_sOperator = $sOperator;
}
/**
* @param $oLeftExpr
* @param $sOperator
* @param $oRightExpr
*
* @throws \CoreException if one of the parameter is invalid
*/
protected function ValidateConstructorParams($oLeftExpr, $sOperator, $oRightExpr)
{
if (!is_object($oLeftExpr))
{
@@ -303,13 +346,11 @@ class BinaryExpression extends Expression
{
throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr)));
}
if ( (($sOperator == "IN") || ($sOperator == "NOT IN")) && !$oRightExpr instanceof ListExpression)
if ((($sOperator == "IN") || ($sOperator == "NOT IN")) && !($oRightExpr instanceof ListExpression))
{
throw new CoreException("Expecting a List Expression object on the right hand for operator $sOperator", array('found_class' => get_class($oRightExpr)));
throw new CoreException("Expecting a List Expression object on the right hand for operator $sOperator",
array('found_class' => get_class($oRightExpr)));
}
$this->m_oLeftExpr = $oLeftExpr;
$this->m_oRightExpr = $oRightExpr;
$this->m_sOperator = $sOperator;
}
public function IsTrue()
@@ -341,12 +382,11 @@ class BinaryExpression extends Expression
return $this->m_sOperator;
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
{
$sOperator = $this->GetOperator();
$sLeft = $this->GetLeftExpr()->Render($aArgs, $bRetrofitParams);
$sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams);
$sLeft = $this->GetLeftExpr()->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
$sRight = $this->GetRightExpr()->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
return "($sLeft $sOperator $sRight)";
}
@@ -538,6 +578,7 @@ class BinaryExpression extends Expression
* @param null $oAttDef
*
* @return array
* @throws \MissingQueryArgument
*/
public function GetCriterion($oSearch, &$aArgs = null, $bRetrofitParams = false, $oAttDef = null)
{
@@ -605,7 +646,7 @@ class BinaryExpression extends Expression
$aCriteria = self::MergeCriteria($aCriteriaLeft, $aCriteriaRight, $this->GetOperator());
}
$aCriteria['oql'] = $this->Render($aArgs, $bRetrofitParams);
$aCriteria['oql'] = $this->RenderExpression(false, $aArgs, $bRetrofitParams);
$aCriteria['label'] = $this->Display($oSearch, $aArgs, $oAttDef);
if (isset($aCriteriaLeft['ref']) && isset($aCriteriaRight['ref']) && ($aCriteriaLeft['ref'] != $aCriteriaRight['ref']))
@@ -636,6 +677,10 @@ class BinaryExpression extends Expression
}
}
}
if (isset($aCriteriaLeft['widget']) && isset($aCriteriaRight['widget']) && ($aCriteriaLeft['widget'] == AttributeDefinition::SEARCH_WIDGET_TYPE_TAG_SET) && ($aCriteriaRight['widget'] == AttributeDefinition::SEARCH_WIDGET_TYPE_TAG_SET))
{
$aCriteriaOverride['operator'] = 'MATCHES';
}
}
return array_merge($aCriteriaLeft, $aCriteriaRight, $aCriteriaOverride);
@@ -643,6 +688,58 @@ class BinaryExpression extends Expression
}
/**
* @since 2.6 N°931 tag fields
*/
class MatchExpression extends BinaryExpression
{
/** @var \FieldExpression */
protected $m_oLeftExpr;
/** @var \ScalarExpression */
protected $m_oRightExpr;
/**
* MatchExpression constructor.
*
* @param \FieldExpression $oLeftExpr
* @param \ScalarExpression $oRightExpr
*
* @throws \CoreException
*/
public function __construct(FieldExpression $oLeftExpr, ScalarExpression $oRightExpr)
{
parent::__construct($oLeftExpr, 'MATCHES', $oRightExpr);
}
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
{
$sLeft = $this->GetLeftExpr()->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
$sRight = $this->GetRightExpr()->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
if ($bForSQL)
{
$sRet = "MATCH ($sLeft) AGAINST ($sRight IN BOOLEAN MODE)";
}
else
{
$sRet = "$sLeft MATCHES $sRight";
}
return $sRet;
}
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
{
/** @var \FieldExpression $oLeft */
$oLeft = $this->GetLeftExpr()->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
/** @var \ScalarExpression $oRight */
$oRight = $this->GetRightExpr()->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
return new static($oLeft, $oRight);
}
}
class UnaryExpression extends Expression
{
protected $m_value;
@@ -664,7 +761,7 @@ class UnaryExpression extends Expression
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
{
return CMDBSource::Quote($this->m_value);
}
@@ -772,11 +869,11 @@ class ScalarExpression extends UnaryExpression
return $aCtx['date_display']->MakeValueLabel($oSearch, $this->m_value, $this->m_value);
}
return $this->Render($aArgs);
return $this->RenderExpression(false, $aArgs);
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
{
if (is_null($this->m_value))
{
@@ -796,23 +893,23 @@ class ScalarExpression extends UnaryExpression
public function GetCriterion($oSearch, &$aArgs = null, $bRetrofitParams = false, $oAttDef = null)
{
$aCriteria = array();
$aCriterion = array();
switch ((string)($this->m_value))
{
case '%Y-%m-%d':
$aCriteria['unit'] = 'DAY';
$aCriterion['unit'] = 'DAY';
break;
case '%Y-%m':
$aCriteria['unit'] = 'MONTH';
$aCriterion['unit'] = 'MONTH';
break;
case '%w':
$aCriteria['unit'] = 'WEEKDAY';
$aCriterion['unit'] = 'WEEKDAY';
break;
case '%H':
$aCriteria['unit'] = 'HOUR';
$aCriterion['unit'] = 'HOUR';
break;
default:
$aValue = array('value' => $this->GetValue());
$aValue = array();
if (!is_null($oAttDef))
{
switch (true)
@@ -822,7 +919,7 @@ class ScalarExpression extends UnaryExpression
{
$oFinalAttDef = $oAttDef->GetFinalAttDef();
if($oFinalAttDef instanceof AttributeExternalKey)
{
{
if ($this->GetValue() !== 0)
{
/** @var AttributeExternalKey $oFinalAttDef */
@@ -849,42 +946,79 @@ class ScalarExpression extends UnaryExpression
IssueLog::Error($e->getMessage());
}
break;
case ($oAttDef instanceof AttributeTagSet):
try
{
if (!empty($this->GetValue()))
{
$aValues = array();
$oValue = $this->GetValue();
if (is_string($oValue))
{
$oValue = $oAttDef->GetExistingTagsFromString($oValue, true);
}
/** @var \ormTagSet $oValue */
$aTags = $oValue->GetTags();
foreach($aTags as $oTag)
{
$aValue['label'] = $oTag->Get('label');
$aValue['value'] = $oTag->Get('code');
$aValues[] = $aValue;
}
$aCriterion['values'] = $aValues;
}
else
{
$aCriterion['has_undefined'] = true;
}
} catch (Exception $e)
{
IssueLog::Error($e->getMessage());
}
break;
case $oAttDef->IsExternalKey():
try
{
if ($this->GetValue() == 0)
{
$aValue['label'] = Dict::S('UI:UndefinedObject');
}
else
if ($this->GetValue() != 0)
{
/** @var AttributeExternalKey $oAttDef */
$sTarget = $oAttDef->GetTargetClass();
$oObj = MetaModel::GetObject($sTarget, $this->GetValue(), true, true);
$aValue['label'] = $oObj->Get("friendlyname");
$aValue['value'] = $this->GetValue();
$aCriterion['values'] = array($aValue);
}
}
catch (Exception $e)
else
{
$aValue['label'] = Dict::S('Enum:Undefined');
$aValue['value'] = $this->GetValue();
$aCriterion['values'] = array($aValue);
}
} catch (Exception $e)
{
IssueLog::Error($e->getMessage());
// This object cannot be seen... ignore
}
break;
default:
try
{
$aValue['label'] = $oAttDef->GetAsPlainText($this->GetValue());
$aValue['value'] = $this->GetValue();
$aCriterion['values'] = array($aValue);
} catch (Exception $e)
{
$aValue['label'] = $this->GetValue();
$aValue['value'] = $this->GetValue();
$aCriterion['values'] = array($aValue);
}
break;
}
}
$aCriteria['values'] = array($aValue);
break;
}
$aCriteria['oql'] = $this->Render($aArgs, $bRetrofitParams);
return $aCriteria;
$aCriterion['oql'] = $this->RenderExpression(false, $aArgs, $bRetrofitParams);
return $aCriterion;
}
}
@@ -991,7 +1125,7 @@ class FieldExpression extends UnaryExpression
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
{
if (empty($this->m_sParent))
{
@@ -1097,7 +1231,7 @@ class FieldExpression extends UnaryExpression
* @param string sValue The value returned by the query, for this expression
* @param string sDefault The default value if no relevant label could be computed
*
* @return The label
* @return string label
* @throws \CoreException
*/
public function MakeValueLabel($oFilter, $sValue, $sDefault)
@@ -1337,19 +1471,18 @@ class VariableExpression extends UnaryExpression
return $oAttDef->GetAsPlainText($sValue);
}
return $this->Render($aArgs);
return $this->RenderExpression(false, $aArgs);
}
// recursive rendering
/**
* @param null $aArgs
* @param bool $bForSQL
* @param array $aArgs
* @param bool $bRetrofitParams
*
* @return array|string
* @throws \MissingQueryArgument
*/
public function Render(&$aArgs = null, $bRetrofitParams = false)
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
{
if (is_null($aArgs))
{
@@ -1467,12 +1600,12 @@ class ListExpression extends Expression
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
$aRes[] = $oExpr->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
}
return '('.implode(', ', $aRes).')';
}
@@ -1488,12 +1621,11 @@ class ListExpression extends Expression
public function ApplyParameters($aArgs)
{
$aRes = array();
foreach ($this->m_aExpressions as $idx => $oExpr)
{
if ($oExpr instanceof VariableExpression)
{
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar();
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar($aArgs);
}
else
{
@@ -1550,7 +1682,6 @@ class ListExpression extends Expression
public function RenameParam($sOldName, $sNewName)
{
$aRes = array();
foreach ($this->m_aExpressions as $key => $oExpr)
{
$this->m_aExpressions[$key] = $oExpr->RenameParam($sOldName, $sNewName);
@@ -1559,7 +1690,6 @@ class ListExpression extends Expression
public function RenameAlias($sOldName, $sNewName)
{
$aRes = array();
foreach ($this->m_aExpressions as $key => $oExpr)
{
$oExpr->RenameAlias($sOldName, $sNewName);
@@ -1623,12 +1753,12 @@ class FunctionExpression extends Expression
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
{
$aRes = array();
foreach ($this->m_aArgs as $iPos => $oExpr)
{
$aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
$aRes[] = $oExpr->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
}
return $this->m_sVerb.'('.implode(', ', $aRes).')';
}
@@ -1644,7 +1774,6 @@ class FunctionExpression extends Expression
public function ApplyParameters($aArgs)
{
$aRes = array();
foreach ($this->m_aArgs as $idx => $oExpr)
{
if ($oExpr instanceof VariableExpression)
@@ -1738,7 +1867,7 @@ class FunctionExpression extends Expression
* @param string sValue The value returned by the query, for this expression
* @param string sDefault The default value if no relevant label could be computed
*
* @return The label
* @return string label
*/
public function MakeValueLabel($oFilter, $sValue, $sDefault)
{
@@ -1814,6 +1943,7 @@ class FunctionExpression extends Expression
$sVerb = '';
switch ($this->m_sVerb)
{
case 'ISNULL':
case 'NOW':
$sVerb = $this->VerbToNaturalLanguage();
break;
@@ -1827,7 +1957,7 @@ class FunctionExpression extends Expression
$aCtx['date_display'] = $this;
break;
default:
return $this->Render($aArgs);
return $this->RenderExpression(false, $aArgs);
}
foreach($this->m_aArgs as $oExpression)
@@ -1844,7 +1974,7 @@ class FunctionExpression extends Expression
{
$sOperation .= $sVerb;
}
return $sOperation;
return '('.$sOperation.')';
}
private function VerbToNaturalLanguage()
@@ -1864,7 +1994,7 @@ class FunctionExpression extends Expression
$aCriteria = array_merge($oExpression->GetCriterion($oSearch, $aArgs, $bRetrofitParams, $oAttDef), $aCriteria);
}
$aCriteria['has_undefined'] = true;
$aCriteria['oql'] = $this->Render($aArgs, $bRetrofitParams);
$aCriteria['oql'] = $this->RenderExpression(false, $aArgs, $bRetrofitParams);
break;
case 'NOW':
@@ -1892,6 +2022,9 @@ class FunctionExpression extends Expression
}
}
/**
* @see https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
*/
class IntervalExpression extends Expression
{
protected $m_oValue; // expression
@@ -1920,9 +2053,9 @@ class IntervalExpression extends Expression
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
{
return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit;
return 'INTERVAL '.$this->m_oValue->RenderExpression($bForSQL, $aArgs, $bRetrofitParams).' '.$this->m_sUnit;
}
public function Browse(Closure $callback)
@@ -1987,7 +2120,7 @@ class IntervalExpression extends Expression
public function Display($oSearch, &$aArgs = null, $oAttDef = null, &$aCtx = array())
{
return $this->m_oValue->Render($aArgs).' '.Dict::S('Expression:Unit:Long:'.$this->m_sUnit, $this->m_sUnit);
return $this->m_oValue->RenderExpression(false, $aArgs).' '.Dict::S('Expression:Unit:Long:'.$this->m_sUnit, $this->m_sUnit);
}
}
@@ -2012,12 +2145,12 @@ class CharConcatExpression extends Expression
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$sCol = $oExpr->Render($aArgs, $bRetrofitParams);
$sCol = $oExpr->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
// Concat will be globally NULL if one single argument is null !
$aRes[] = "COALESCE($sCol, '')";
}
@@ -2035,12 +2168,11 @@ class CharConcatExpression extends Expression
public function ApplyParameters($aArgs)
{
$aRes = array();
foreach ($this->m_aExpressions as $idx => $oExpr)
{
if ($oExpr instanceof VariableExpression)
{
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar();
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar($aArgs);
}
else
{
@@ -2124,12 +2256,12 @@ class CharConcatWSExpression extends CharConcatExpression
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false)
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$sCol = $oExpr->Render($aArgs, $bRetrofitParams);
$sCol = $oExpr->RenderExpression($bForSQL, $aArgs, $bRetrofitParams);
// Concat will be globally NULL if one single argument is null !
$aRes[] = "COALESCE($sCol, '')";
}

View File

@@ -140,6 +140,7 @@ class OQLLexerRaw
'/\GNOT LIKE/ ',
'/\GIN/ ',
'/\GNOT IN/ ',
'/\GMATCHES/ ',
'/\GINTERVAL/ ',
'/\GIF/ ',
'/\GELT/ ',
@@ -441,204 +442,209 @@ class OQLLexerRaw
function yy_r1_33($yy_subpatterns)
{
$this->token = OQLParser::INTERVAL;
$this->token = OQLParser::MATCHES;
}
function yy_r1_34($yy_subpatterns)
{
$this->token = OQLParser::F_IF;
$this->token = OQLParser::INTERVAL;
}
function yy_r1_35($yy_subpatterns)
{
$this->token = OQLParser::F_ELT;
$this->token = OQLParser::F_IF;
}
function yy_r1_36($yy_subpatterns)
{
$this->token = OQLParser::F_COALESCE;
$this->token = OQLParser::F_ELT;
}
function yy_r1_37($yy_subpatterns)
{
$this->token = OQLParser::F_ISNULL;
$this->token = OQLParser::F_COALESCE;
}
function yy_r1_38($yy_subpatterns)
{
$this->token = OQLParser::F_CONCAT;
$this->token = OQLParser::F_ISNULL;
}
function yy_r1_39($yy_subpatterns)
{
$this->token = OQLParser::F_SUBSTR;
$this->token = OQLParser::F_CONCAT;
}
function yy_r1_40($yy_subpatterns)
{
$this->token = OQLParser::F_TRIM;
$this->token = OQLParser::F_SUBSTR;
}
function yy_r1_41($yy_subpatterns)
{
$this->token = OQLParser::F_DATE;
$this->token = OQLParser::F_TRIM;
}
function yy_r1_42($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_FORMAT;
$this->token = OQLParser::F_DATE;
}
function yy_r1_43($yy_subpatterns)
{
$this->token = OQLParser::F_CURRENT_DATE;
$this->token = OQLParser::F_DATE_FORMAT;
}
function yy_r1_44($yy_subpatterns)
{
$this->token = OQLParser::F_NOW;
$this->token = OQLParser::F_CURRENT_DATE;
}
function yy_r1_45($yy_subpatterns)
{
$this->token = OQLParser::F_TIME;
$this->token = OQLParser::F_NOW;
}
function yy_r1_46($yy_subpatterns)
{
$this->token = OQLParser::F_TO_DAYS;
$this->token = OQLParser::F_TIME;
}
function yy_r1_47($yy_subpatterns)
{
$this->token = OQLParser::F_FROM_DAYS;
$this->token = OQLParser::F_TO_DAYS;
}
function yy_r1_48($yy_subpatterns)
{
$this->token = OQLParser::F_YEAR;
$this->token = OQLParser::F_FROM_DAYS;
}
function yy_r1_49($yy_subpatterns)
{
$this->token = OQLParser::F_MONTH;
$this->token = OQLParser::F_YEAR;
}
function yy_r1_50($yy_subpatterns)
{
$this->token = OQLParser::F_DAY;
$this->token = OQLParser::F_MONTH;
}
function yy_r1_51($yy_subpatterns)
{
$this->token = OQLParser::F_HOUR;
$this->token = OQLParser::F_DAY;
}
function yy_r1_52($yy_subpatterns)
{
$this->token = OQLParser::F_MINUTE;
$this->token = OQLParser::F_HOUR;
}
function yy_r1_53($yy_subpatterns)
{
$this->token = OQLParser::F_SECOND;
$this->token = OQLParser::F_MINUTE;
}
function yy_r1_54($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_ADD;
$this->token = OQLParser::F_SECOND;
}
function yy_r1_55($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_SUB;
$this->token = OQLParser::F_DATE_ADD;
}
function yy_r1_56($yy_subpatterns)
{
$this->token = OQLParser::F_ROUND;
$this->token = OQLParser::F_DATE_SUB;
}
function yy_r1_57($yy_subpatterns)
{
$this->token = OQLParser::F_FLOOR;
$this->token = OQLParser::F_ROUND;
}
function yy_r1_58($yy_subpatterns)
{
$this->token = OQLParser::F_INET_ATON;
$this->token = OQLParser::F_FLOOR;
}
function yy_r1_59($yy_subpatterns)
{
$this->token = OQLParser::F_INET_NTOA;
$this->token = OQLParser::F_INET_ATON;
}
function yy_r1_60($yy_subpatterns)
{
$this->token = OQLParser::BELOW;
$this->token = OQLParser::F_INET_NTOA;
}
function yy_r1_61($yy_subpatterns)
{
$this->token = OQLParser::BELOW_STRICT;
$this->token = OQLParser::BELOW;
}
function yy_r1_62($yy_subpatterns)
{
$this->token = OQLParser::NOT_BELOW;
$this->token = OQLParser::BELOW_STRICT;
}
function yy_r1_63($yy_subpatterns)
{
$this->token = OQLParser::NOT_BELOW_STRICT;
$this->token = OQLParser::NOT_BELOW;
}
function yy_r1_64($yy_subpatterns)
{
$this->token = OQLParser::ABOVE;
$this->token = OQLParser::NOT_BELOW_STRICT;
}
function yy_r1_65($yy_subpatterns)
{
$this->token = OQLParser::ABOVE_STRICT;
$this->token = OQLParser::ABOVE;
}
function yy_r1_66($yy_subpatterns)
{
$this->token = OQLParser::NOT_ABOVE;
$this->token = OQLParser::ABOVE_STRICT;
}
function yy_r1_67($yy_subpatterns)
{
$this->token = OQLParser::NOT_ABOVE_STRICT;
$this->token = OQLParser::NOT_ABOVE;
}
function yy_r1_68($yy_subpatterns)
{
$this->token = OQLParser::HEXVAL;
$this->token = OQLParser::NOT_ABOVE_STRICT;
}
function yy_r1_69($yy_subpatterns)
{
$this->token = OQLParser::NUMVAL;
$this->token = OQLParser::HEXVAL;
}
function yy_r1_70($yy_subpatterns)
{
$this->token = OQLParser::STRVAL;
$this->token = OQLParser::NUMVAL;
}
function yy_r1_71($yy_subpatterns)
{
$this->token = OQLParser::NAME;
$this->token = OQLParser::STRVAL;
}
function yy_r1_72($yy_subpatterns)
{
$this->token = OQLParser::VARNAME;
$this->token = OQLParser::NAME;
}
function yy_r1_73($yy_subpatterns)
{
$this->token = OQLParser::VARNAME;
}
function yy_r1_74($yy_subpatterns)
{
$this->token = OQLParser::DOT;
@@ -666,25 +672,25 @@ class OQLLexer extends OQLLexerRaw
function yylex()
{
try
{
return parent::yylex();
}
catch (Exception $e)
{
$sMessage = $e->getMessage();
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
{
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
{
$iLine = $aMatches[1];
$sUnexpected = $aMatches[2];
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
}
}
// Default: forward the exception
throw $e;
try
{
return parent::yylex();
}
catch (Exception $e)
{
$sMessage = $e->getMessage();
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
{
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
{
$iLine = $aMatches[1];
$sUnexpected = $aMatches[2];
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
}
}
// Default: forward the exception
throw $e;
}
}
}

View File

@@ -139,6 +139,7 @@ f_round = "ROUND"
f_floor = "FLOOR"
f_inet_aton = "INET_ATON"
f_inet_ntoa = "INET_NTOA"
matches = "MATCHES"
below = "BELOW"
below_strict = "BELOW STRICT"
not_below = "NOT BELOW"
@@ -273,6 +274,9 @@ in {
not_in {
$this->token = OQLParser::NOT_IN;
}
matches {
$this->token = OQLParser::MATCHES;
}
interval {
$this->token = OQLParser::INTERVAL;
}
@@ -419,25 +423,25 @@ class OQLLexer extends OQLLexerRaw
function yylex()
{
try
{
return parent::yylex();
}
catch (Exception $e)
{
$sMessage = $e->getMessage();
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
{
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
{
$iLine = $aMatches[1];
$sUnexpected = $aMatches[2];
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
}
}
// Default: forward the exception
throw $e;
try
{
return parent::yylex();
}
catch (Exception $e)
{
$sMessage = $e->getMessage();
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
{
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
{
$iLine = $aMatches[1];
$sUnexpected = $aMatches[2];
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
}
}
// Default: forward the exception
throw $e;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@ Example:
%left TIMES DIVIDE MOD.
%right EXP NOT.
TODO : solve the 2 remaining shift-reduce conflicts (JOIN)
later : solve the 2 remaining shift-reduce conflicts (JOIN)
*/
@@ -108,7 +108,16 @@ expression_prio1(A) ::= expression_basic(X). { A = X; }
expression_prio1(A) ::= expression_prio1(X) operator1(Y) expression_basic(Z). { A = new BinaryOqlExpression(X, Y, Z); }
expression_prio2(A) ::= expression_prio1(X). { A = X; }
expression_prio2(A) ::= expression_prio2(X) operator2(Y) expression_prio1(Z). { A = new BinaryOqlExpression(X, Y, Z); }
expression_prio2(A) ::= expression_prio2(X) operator2(Y) expression_prio1(Z).{
if (Y == 'MATCHES')
{
A = new MatchOqlExpression(X, Z);
}
else
{
A = new BinaryOqlExpression(X, Y, Z);
}
}
expression_prio3(A) ::= expression_prio2(X). { A = X; }
expression_prio3(A) ::= expression_prio3(X) operator3(Y) expression_prio2(Z). { A = new BinaryOqlExpression(X, Y, Z); }
@@ -180,18 +189,22 @@ str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));}
operator1(A) ::= num_operator1(X). {A=X;}
operator1(A) ::= bitwise_operator1(X). {A=X;}
operator2(A) ::= num_operator2(X). {A=X;}
operator2(A) ::= str_operator(X). {A=X;}
operator2(A) ::= REGEXP(X). {A=X;}
operator2(A) ::= EQ(X). {A=X;}
operator2(A) ::= NOT_EQ(X). {A=X;}
operator3(A) ::= LOG_AND(X). {A=X;}
operator3(A) ::= bitwise_operator3(X). {A=X;}
operator4(A) ::= LOG_OR(X). {A=X;}
operator4(A) ::= bitwise_operator4(X). {A=X;}
num_operator1(A) ::= MATH_DIV(X). {A=X;}
num_operator1(A) ::= MATH_MULT(X). {A=X;}
num_operator2(A) ::= MATH_PLUS(X). {A=X;}
num_operator2(A) ::= MATH_MINUS(X). {A=X;}
num_operator2(A) ::= GT(X). {A=X;}
@@ -201,10 +214,13 @@ num_operator2(A) ::= LE(X). {A=X;}
str_operator(A) ::= LIKE(X). {A=X;}
str_operator(A) ::= NOT_LIKE(X). {A=X;}
str_operator(A) ::= MATCHES(X). {A=X;}
bitwise_operator1(A) ::= BITWISE_LEFT_SHIFT(X). {A=X;}
bitwise_operator1(A) ::= BITWISE_RIGHT_SHIFT(X). {A=X;}
bitwise_operator3(A) ::= BITWISE_AND(X). {A=X;}
bitwise_operator4(A) ::= BITWISE_OR(X). {A=X;}
bitwise_operator4(A) ::= BITWISE_XOR(X). {A=X;}

View File

@@ -84,7 +84,7 @@ class OqlInterpreter
}
/**
* @return OqlQuery
* @return \OqlObjectQuery|\OqlUnionQuery
* @throws \OQLException
*/
public function ParseQuery()

View File

@@ -160,6 +160,26 @@ class BinaryOqlExpression extends BinaryExpression implements CheckableExpressio
}
}
class MatchOqlExpression extends MatchExpression implements CheckableExpression
{
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
{
$this->m_oLeftExpr->Check($oModelReflection, $aAliases, $sSourceQuery);
$this->m_oRightExpr->Check($oModelReflection, $aAliases, $sSourceQuery);
// Only field MATCHES scalar is allowed
if (!$this->m_oLeftExpr instanceof FieldExpression)
{
throw new OqlNormalizeException('Only "field MATCHES string" syntax is allowed', $sSourceQuery, new OqlName($this->m_oLeftExpr->RenderExpression(true), 0));
}
// Only field MATCHES scalar is allowed
if (!$this->m_oRightExpr instanceof ScalarExpression)
{
throw new OqlNormalizeException('Only "field MATCHES string" syntax is allowed', $sSourceQuery, new OqlName($this->m_oRightExpr->RenderExpression(true), 0));
}
}
}
class ScalarOqlExpression extends ScalarExpression implements CheckableExpression
{
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)

View File

@@ -1 +1 @@
2015-08-31
2018-08-31

View File

@@ -34,7 +34,7 @@ class ormCaseLog {
/**
* Initializes the log with the first (initial) entry
* @param $sLog string The text of the whole case log
* @param $aIndex hash The case log index
* @param $aIndex array The case log index
*/
public function __construct($sLog = '', $aIndex = array())
{
@@ -159,6 +159,7 @@ class ormCaseLog {
$aEntries[] = array(
'date' => '',
'user_login' => '',
'user_id' => 0,
'message' => $sTextEntry,
'message_html' => utils::TextToHtml($sTextEntry),
);
@@ -295,7 +296,6 @@ class ormCaseLog {
*/
public function GetAsSimpleHtml($aTransfoHandler = null)
{
$sStyleCaseLogHeader = '';
$sStyleCaseLogEntry = '';
$sHtml = '<ul class="case_log_simple_html">';
@@ -513,7 +513,6 @@ class ormCaseLog {
public function AddLogEntry($sText, $sOnBehalfOf = '')
{
$sText = HTMLSanitizer::Sanitize($sText);
$bMergeEntries = false;
$sDate = date(AttributeDateTime::GetInternalFormat());
if ($sOnBehalfOf == '')
{
@@ -699,7 +698,6 @@ class ormCaseLog {
{
$iPos = 0;
$index = count($this->m_aIndex) - 1;
$aIndex = $this->m_aIndex;
while($index > $iIndex)
{
$iPos += $this->m_aIndex[$index]['separator_length'];

View File

@@ -42,6 +42,7 @@ class ormPassword
public function __construct($sHash = '', $sSalt = '')
{
$this->m_sHashed = $sHash;
//only used for <= 2.5 hashed password
$this->m_sSalt = $sSalt;
}
@@ -50,8 +51,7 @@ class ormPassword
*/
public function SetPassword($sClearTextPassword)
{
$this->m_sSalt = SimpleCrypt::GetNewSalt();
$this->m_sHashed = $this->ComputeHash($sClearTextPassword);
$this->m_sHashed = password_hash($sClearTextPassword, PASSWORD_DEFAULT);
}
/**
@@ -95,10 +95,19 @@ class ormPassword
public function CheckPassword($sClearTextPassword)
{
$bResult = false;
$sHashedPwd = $this->ComputeHash($sClearTextPassword);
if ($this->m_sHashed == $sHashedPwd)
$aInfo = password_get_info($this->m_sHashed);
switch ($aInfo["algo"])
{
$bResult = true;
case 0:
//unknown, assume it's a legacy password
$sHashedPwd = $this->ComputeHash($sClearTextPassword);
if ($this->m_sHashed == $sHashedPwd)
{
$bResult = true;
}
break;
default:
$bResult = password_verify($sClearTextPassword, $this->m_sHashed);
}
return $bResult;
}

380
core/ormset.class.inc.php Normal file
View File

@@ -0,0 +1,380 @@
<?php
/**
* Copyright (c) 2010-2018 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/>
*
*/
/**
* Created by PhpStorm.
* Date: 24/08/2018
* Time: 14:35
*/
class ormSet
{
protected $sClass; // class of the field
protected $sAttCode; // attcode of the field
protected $aOriginalObjects = null;
/**
* Object from the original set, minus the removed objects
*/
protected $aPreserved = array();
/**
* New items
*/
protected $aAdded = array();
/**
* Removed items
*/
protected $aRemoved = array();
/**
* Modified items (mass edit)
*/
protected $aModified = array();
/**
* @var int Max number of tags in collection
*/
protected $iLimit;
/**
* __toString magical function overload.
*/
public function __toString()
{
$aValue = $this->GetValues();
if (!empty($aValue))
{
return implode(', ', $aValue);
}
else
{
return ' ';
}
}
/**
* ormSet constructor.
*
* @param string $sClass
* @param string $sAttCode
* @param int $iLimit
*
* @throws \Exception
*/
public function __construct($sClass, $sAttCode, $iLimit = 12)
{
$this->sAttCode = $sAttCode;
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if (!$oAttDef instanceof AttributeSet)
{
throw new Exception("ormSet: field {$sClass}:{$sAttCode} is not a set");
}
$this->sClass = $sClass;
$this->iLimit = $iLimit;
}
/**
* @return string
*/
public function GetClass()
{
return $this->sClass;
}
/**
* @return string
*/
public function GetAttCode()
{
return $this->sAttCode;
}
/**
*
* @param array $aItems
*
* @throws \CoreException
* @throws \CoreUnexpectedValue when a code is invalid
*/
public function SetValues($aItems)
{
if (!is_array($aItems))
{
throw new CoreUnexpectedValue("Wrong value {$aItems} for {$this->sClass}:{$this->sAttCode}");
}
$aValues = array();
$iCount = 0;
$bError = false;
foreach($aItems as $oItem)
{
$iCount++;
if (($this->iLimit != 0) && ($iCount > $this->iLimit))
{
$bError = true;
continue;
}
$aValues[] = $oItem;
}
$this->aPreserved = &$aValues;
$this->aRemoved = array();
$this->aAdded = array();
$this->aModified = array();
$this->aOriginalObjects = $aValues;
if ($bError)
{
throw new CoreException("Maximum number of items ({$this->iLimit}) reached for {$this->sClass}:{$this->sAttCode}");
}
}
public function Count()
{
return count($this->aPreserved) + count($this->aAdded) - count($this->aRemoved);
}
/**
* @return array of codes
*/
public function GetValues()
{
$aValues = array_merge($this->aPreserved, $this->aAdded);
return $aValues;
}
/**
* @return array of tag labels indexed by code for only the added tags
*/
private function GetAdded()
{
return $this->aAdded;
}
/**
* @return array of tag labels indexed by code for only the removed tags
*/
private function GetRemoved()
{
return $this->aRemoved;
}
/** Get the delta with another ItemSet
*
* $aDelta['added] = array of tag codes for only the added tags
* $aDelta['removed'] = array of tag codes for only the removed tags
*
* @param \ormSet $oOtherSet
*
* @return array
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \Exception
*/
public function GetDelta(ormSet $oOtherSet)
{
$oSet = new ormSet($this->sClass, $this->sAttCode, $this->iLimit);
// Set the initial value
$aOrigItems = $this->GetValues();
$oSet->SetValues($aOrigItems);
// now remove everything
foreach($aOrigItems as $oItem)
{
$oSet->Remove($oItem);
}
// now add the tags of the other ItemSet
foreach($oOtherSet->GetValues() as $oItem)
{
$oSet->Add($oItem);
}
$aDelta = array();
$aDelta['added'] = $oSet->GetAdded();
$aDelta['removed'] = $oSet->GetRemoved();
return $aDelta;
}
/**
* @return string[] list of codes for partial entries
*/
public function GetModified()
{
return $this->aModified;
}
/**
* Apply a delta to the current ItemSet
* $aDelta['added] = array of added items
* $aDelta['removed'] = array of removed items
*
* @param $aDelta
*
* @throws \CoreException
*/
public function ApplyDelta($aDelta)
{
if (isset($aDelta['removed']))
{
foreach($aDelta['removed'] as $oItem)
{
$this->Remove($oItem);
}
}
if (isset($aDelta['added']))
{
foreach($aDelta['added'] as $oItem)
{
$this->Add($oItem);
}
}
// Reset the object
$this->SetValues($this->GetValues());
}
/**
* @param string $oItem
*
* @throws \CoreException
*/
public function Add($oItem)
{
if (($this->iLimit != 0) && ($this->Count() > $this->iLimit))
{
throw new CoreException("Maximum number of items ({$this->iLimit}) reached for {$this->sClass}:{$this->sAttCode}");
}
if ($this->IsItemInList($this->aPreserved, $oItem) || $this->IsItemInList($this->aAdded, $oItem))
{
// nothing to do, already existing tag
return;
}
// if removed and added again
if (($this->RemoveItemFromList($this->aRemoved, $oItem)) !== false)
{
// put it back into preserved
$this->aPreserved[] = $oItem;
// no need to add it to aModified : was already done when calling RemoveItem method
}
else
{
$this->aAdded[] = $oItem;
$this->aModified[] = $oItem;
}
}
/**
* @param $oItem
*/
public function Remove($oItem)
{
if ($this->IsItemInList($this->aRemoved, $oItem))
{
// nothing to do, already removed tag
return;
}
if ($this->RemoveItemFromList($this->aAdded, $oItem) !== false)
{
$this->aModified[] = $oItem;
return; // if present in added, can't be in preserved !
}
if ($this->RemoveItemFromList($this->aPreserved, $oItem) !== false)
{
$this->aModified[] = $oItem;
$this->aRemoved[] = $oItem;
}
}
private function IsItemInList($aItemList, $oItem)
{
return in_array($oItem, $aItemList);
}
/**
* @param \DBObject[] $aItemList
* @param $oItem
*
* @return bool|\DBObject false if not found, else the removed element
*/
private function RemoveItemFromList(&$aItemList, $oItem)
{
if (!($this->IsItemInList($aItemList, $oItem)))
{
return false;
}
foreach ($aItemList as $index => $value)
{
if ($value === $oItem)
{
unset($aItemList[$index]);
return $oItem;
}
}
return false;
}
/**
* Populates the added and removed arrays for bulk edit
*
* @param string[] $aItems
*
* @throws \CoreException
*/
public function GenerateDiffFromArray($aItems)
{
foreach($this->GetValues() as $oCurrentItem)
{
if (!in_array($oCurrentItem, $aItems))
{
$this->Remove($oCurrentItem);
}
}
foreach($aItems as $oNewItem)
{
$this->Add($oNewItem);
}
}
/**
* Compare Item Set
*
* @param \ormSet $other
*
* @return bool true if same tag set
*/
public function Equals(ormSet $other)
{
return implode(', ', $this->GetValue()) === implode(', ', $other->GetValue());
}
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2010-2018 Combodo SARL
//
// This file is part of iTop.
//
@@ -85,8 +85,12 @@ class ormStopWatch
/**
* Get the working elapsed time since the start of the stop watch
* even if it is currently running
* @param oAttDef AttributeDefinition Attribute hosting the stop watch
* @param oObject Hosting object (used for query parameters)
*
* @param AttributeDefinition oAttDef Attribute hosting the stop watch
* @param Object oObject Hosting object (used for query parameters)
*
* @return int|mixed
* @throws \CoreException
*/
public function GetElapsedTime($oAttDef, $oObject)
{
@@ -260,6 +264,16 @@ class ormStopWatch
return $iRet;
}
/**
* @param $oObject
* @param $oAttDef
* @param $iPercent
* @param $iStartTime
* @param $iDurationSec
*
* @return mixed
* @throws \CoreException
*/
protected function ComputeDeadline($oObject, $oAttDef, $iPercent, $iStartTime, $iDurationSec)
{
$sWorkingTimeComputer = $oAttDef->Get('working_time_computing');
@@ -267,11 +281,6 @@ class ormStopWatch
{
$sWorkingTimeComputer = class_exists('SLAComputation') ? 'SLAComputation' : 'DefaultWorkingTimeComputer';
}
$aCallSpec = array($sWorkingTimeComputer, '__construct');
if (!is_callable($aCallSpec))
{
//throw new CoreException("Pas de constructeur pour $sWorkingTimeComputer!");
}
$oComputer = new $sWorkingTimeComputer();
$aCallSpec = array($oComputer, 'GetDeadline');
if (!is_callable($aCallSpec))
@@ -285,6 +294,15 @@ class ormStopWatch
return $iRet;
}
/**
* @param $oObject
* @param $oAttDef
* @param $iStartTime
* @param $iEndTime
*
* @return mixed
* @throws \CoreException
*/
protected function ComputeDuration($oObject, $oAttDef, $iStartTime, $iEndTime)
{
$sWorkingTimeComputer = $oAttDef->Get('working_time_computing');
@@ -384,7 +402,20 @@ class ormStopWatch
$sAttCode = $oAttDef->GetCode();
WorkingTimeRecorder::Start($oObject, $iComputationRefTime, "ormStopWatch-Deadline-$iPercent-$sAttCode", 'Core:ExplainWTC:StopWatch-Deadline', array("Class:$sClass/Attribute:$sAttCode", $iPercent));
}
$aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $iPercent, $this->iLastStart, $iThresholdDuration - $this->iTimeSpent);
$iRemaining = $iThresholdDuration - $this->iTimeSpent;
if ($iRemaining < 0)
{
if (class_exists('WorkingTimeRecorder'))
{
$sClass = get_class($oObject);
$sKey = $oObject->GetKey();
$sAttCode = $oAttDef->GetCode();
$sDate = date('Y-m-d H:i:s', $aThresholdData['deadline']);
WorkingTimeRecorder::Log(WorkingTimeRecorder::TRACE_INFO, "$sClass($sKey) ormStopWatch-Deadline-$iPercent-$sAttCode ($sDate) already reached, not changed.");
}
continue;
}
$aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $iPercent, $this->iLastStart, $iRemaining);
// OR $aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $iPercent, $this->iStarted, $iThresholdDuration);
if (class_exists('WorkingTimeRecorder'))
@@ -398,7 +429,7 @@ class ormStopWatch
$aThresholdData['triggered'] = false;
$aThresholdData['overrun'] = null;
}
else
// else
{
// The new threshold is in the past
// Note: the overrun can be wrong, but the correct algorithm to compute
@@ -454,7 +485,10 @@ class ormStopWatch
$aThresholdData['overrun'] = $iOverrun;
}
}
$aThresholdData['deadline'] = null;
if ($aThresholdData['overrun'] == 0)
{
$aThresholdData['deadline'] = null;
}
}
$this->iLastStart = null;
@@ -558,7 +592,7 @@ class CheckStopWatchThresholds implements iBackgroundProcess
CMDBObject::SetTrackInfo("Automatic - threshold triggered");
$oMyChange = CMDBObject::GetCurrentChange();
$oObj->DBUpdateTracked($oMyChange, true /*skip security*/);
$oObj->DBUpdateTracked($oMyChange);
}
// Activate any existing trigger

View File

@@ -0,0 +1,578 @@
<?php
/**
* Copyright (c) 2010-2018 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/>
*
*/
/**
* Created by PhpStorm.
* Date: 24/08/2018
* Time: 14:35
*/
final class ormTagSet extends ormSet
{
private $m_bDisplayPartial = false;
/**
* ormTagSet constructor.
*
* @param string $sClass
* @param string $sAttCode
* @param int $iLimit
*
* @throws \Exception
*/
public function __construct($sClass, $sAttCode, $iLimit = 12)
{
parent::__construct($sClass, $sAttCode, $iLimit);
}
/**
*
* @param array $aTagCodes
*
* @throws \CoreException
* @throws \CoreUnexpectedValue when a code is invalid
*/
public function SetValues($aTagCodes)
{
if (is_null($aTagCodes))
{
$aTagCodes = array();
}
if (!is_array($aTagCodes))
{
throw new CoreUnexpectedValue("Wrong value {$aTagCodes} for {$this->sClass}:{$this->sAttCode}");
}
$oTags = array();
$iCount = 0;
$bError = false;
foreach($aTagCodes as $sTagCode)
{
$iCount++;
if (($this->iLimit != 0) && ($iCount > $this->iLimit))
{
$bError = true;
continue;
}
$oTag = $this->GetTagFromCode($sTagCode);
$oTags[$sTagCode] = $oTag;
}
$this->aPreserved = &$oTags;
$this->aRemoved = array();
$this->aAdded = array();
$this->aModified = array();
$this->aOriginalObjects = $oTags;
if ($bError)
{
throw new CoreException("Maximum number of tags ({$this->iLimit}) reached for {$this->sClass}:{$this->sAttCode}");
}
}
/**
* @return array of tag codes
*/
public function GetValues()
{
$aValues = array();
foreach($this->aPreserved as $sTagCode => $oTag)
{
$aValues[] = $sTagCode;
}
foreach($this->aAdded as $sTagCode => $oTag)
{
$aValues[] = $sTagCode;
}
sort($aValues);
return $aValues;
}
/**
* @return array of tag labels indexed by code
*/
public function GetLabels()
{
$aTags = array();
/** @var \TagSetFieldData $oTag */
foreach($this->aPreserved as $sTagCode => $oTag)
{
try
{
$aTags[$sTagCode] = $oTag->Get('label');
} catch (CoreException $e)
{
IssueLog::Error($e->getMessage());
}
}
foreach($this->aAdded as $sTagCode => $oTag)
{
try
{
$aTags[$sTagCode] = $oTag->Get('label');
} catch (CoreException $e)
{
IssueLog::Error($e->getMessage());
}
}
ksort($aTags);
return $aTags;
}
/**
* @return array of tags indexed by code
*/
public function GetTags()
{
$aTags = array();
foreach($this->aPreserved as $sTagCode => $oTag)
{
$aTags[$sTagCode] = $oTag;
}
foreach($this->aAdded as $sTagCode => $oTag)
{
$aTags[$sTagCode] = $oTag;
}
ksort($aTags);
return $aTags;
}
/**
* @return array of tag labels indexed by code for only the added tags
*/
private function GetAddedCodes()
{
$aTags = array();
foreach($this->aAdded as $sTagCode => $oTag)
{
$aTags[] = $sTagCode;
}
ksort($aTags);
return $aTags;
}
/**
* @return array of tag labels indexed by code for only the removed tags
*/
private function GetRemovedCodes()
{
$aTags = array();
foreach($this->aRemoved as $sTagCode => $oTag)
{
$aTags[] = $sTagCode;
}
ksort($aTags);
return $aTags;
}
/**
* @return array of tag labels indexed by code for only the added tags
*/
private function GetAddedTags()
{
$aTags = array();
foreach($this->aAdded as $sTagCode => $oTag)
{
$aTags[$sTagCode] = $oTag;
}
ksort($aTags);
return $aTags;
}
/**
* @return array of tag labels indexed by code for only the removed tags
*/
private function GetRemovedTags()
{
$aTags = array();
foreach($this->aRemoved as $sTagCode => $oTag)
{
$aTags[$sTagCode] = $oTag;
}
ksort($aTags);
return $aTags;
}
/** Get the delta with another TagSet
*
* $aDelta['added] = array of tag codes for only the added tags
* $aDelta['removed'] = array of tag codes for only the removed tags
*
* @param \ormTagSet $oOtherTagSet
*
* @return array
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \Exception
*/
public function GetDelta(ormSet $oOtherTagSet)
{
$oTag = new ormTagSet($this->sClass, $this->sAttCode, 0);
// Set the initial value
$aOrigTagCodes = $this->GetValues();
$oTag->SetValues($aOrigTagCodes);
// now remove everything
foreach($aOrigTagCodes as $sTagCode)
{
$oTag->Remove($sTagCode);
}
// now add the tags of the other TagSet
foreach($oOtherTagSet->GetValues() as $sTagCode)
{
$oTag->Add($sTagCode);
}
$aDelta = array();
$aDelta['added'] = $oTag->GetAddedCodes();
$aDelta['removed'] = $oTag->GetRemovedCodes();
return $aDelta;
}
/** Get the delta with another TagSet
*
* $aDelta['added] = array of tag labels indexed by code for only the added tags
* $aDelta['removed'] = array of tag labels indexed by code for only the removed tags
*
* @param \ormTagSet $oOtherTagSet
*
* @return array
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \Exception
*/
public function GetDeltaTags(ormTagSet $oOtherTagSet)
{
$oTag = new ormTagSet($this->sClass, $this->sAttCode, 0);
// Set the initial value
$aOrigTagCodes = $this->GetValues();
$oTag->SetValues($aOrigTagCodes);
// now remove everything
foreach($aOrigTagCodes as $sTagCode)
{
$oTag->Remove($sTagCode);
}
// now add the tags of the other TagSet
foreach($oOtherTagSet->GetValues() as $sTagCode)
{
$oTag->Add($sTagCode);
}
$aDelta = array();
$aDelta['added'] = $oTag->GetAddedTags();
$aDelta['removed'] = $oTag->GetRemovedTags();
return $aDelta;
}
/**
* @return string[] list of codes for partial entries
*/
public function GetModified()
{
$aModifiedTagCodes = array_keys($this->aModified);
sort($aModifiedTagCodes);
return $aModifiedTagCodes;
}
/**
* @return string[] list of codes for added entries
*/
public function GetAdded()
{
$aAddedTagCodes = array_keys($this->aAdded);
sort($aAddedTagCodes);
return $aAddedTagCodes;
}
/**
* @return string[] list of codes for removed entries
*/
public function GetRemoved()
{
$aRemovedTagCodes = array_keys($this->aRemoved);
sort($aRemovedTagCodes);
return $aRemovedTagCodes;
}
/**
* Apply a delta to the current ItemSet
* $aDelta['added] = array of added items
* $aDelta['removed'] = array of removed items
*
* @param $aDelta
*
* @throws \CoreException
*/
public function ApplyDelta($aDelta)
{
if (isset($aDelta['removed']))
{
foreach($aDelta['removed'] as $oItem)
{
$this->Remove($oItem);
}
}
if (isset($aDelta['added']))
{
foreach($aDelta['added'] as $oItem)
{
$this->Add($oItem);
}
}
}
/**
* Populates the added and removed arrays for bulk edit
*
* @param string[] $aItems
*
* @throws \CoreException
*/
public function GenerateDiffFromArray($aItems)
{
foreach($this->GetValues() as $oCurrentItem)
{
if (!in_array($oCurrentItem, $aItems))
{
$this->Remove($oCurrentItem);
}
}
foreach($aItems as $oNewItem)
{
$this->Add($oNewItem);
}
// Keep only the aModified list
$this->aRemoved = array();
$this->aAdded = array();
}
/**
* Check whether a tag code is valid or not for this TagSet
*
* @param string $sTagCode
*
* @return bool
*/
public function IsValidTag($sTagCode)
{
try
{
$this->GetTagFromCode($sTagCode);
return true;
} catch (Exception $e)
{
return false;
}
}
/**
* @param string $sTagCode
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
*/
public function Add($sTagCode)
{
if (($this->iLimit != 0) && ($this->Count() == $this->iLimit))
{
throw new CoreException("Maximum number of tags ({$this->iLimit}) reached for {$this->sClass}:{$this->sAttCode}");
}
if ($this->IsTagInList($this->aPreserved, $sTagCode) || $this->IsTagInList($this->aAdded, $sTagCode))
{
// nothing to do, already existing tag
return;
}
// if removed then added again
if (($oTag = $this->RemoveTagFromList($this->aRemoved, $sTagCode)) !== false)
{
// put it back into preserved
$this->aPreserved[$sTagCode] = $oTag;
// no need to add it to aModified : was already done when calling Remove method
}
else
{
$oTag = $this->GetTagFromCode($sTagCode);
$this->aAdded[$sTagCode] = $oTag;
$this->aModified[$sTagCode] = $oTag;
}
}
/**
* @param $sTagCode
*/
public function Remove($sTagCode)
{
if ($this->IsTagInList($this->aRemoved, $sTagCode))
{
// nothing to do, already removed tag
return;
}
$oTag = $this->RemoveTagFromList($this->aAdded, $sTagCode);
if ($oTag !== false)
{
$this->aModified[$sTagCode] = $oTag;
return; // if present in added, can't be in preserved !
}
$oTag = $this->RemoveTagFromList($this->aPreserved, $sTagCode);
if ($oTag !== false)
{
$this->aModified[$sTagCode] = $oTag;
$this->aRemoved[$sTagCode] = $oTag;
}
}
private function IsTagInList($aTagList, $sTagCode)
{
return isset($aTagList[$sTagCode]);
}
/**
* @param \DBObject[] $aTagList
* @param string $sTagCode
*
* @return bool|\DBObject false if not found, else the removed element
*/
private function RemoveTagFromList(&$aTagList, $sTagCode)
{
if (!($this->IsTagInList($aTagList, $sTagCode)))
{
return false;
}
$oTag = $aTagList[$sTagCode];
unset($aTagList[$sTagCode]);
return $oTag;
}
/**
* @param $sTagCode
*
* @return DBObject tag
* @throws \CoreUnexpectedValue
* @throws \CoreException
*/
private function GetTagFromCode($sTagCode)
{
$aAllowedTags = $this->GetAllowedTags();
foreach($aAllowedTags as $oAllowedTag)
{
if ($oAllowedTag->Get('code') === $sTagCode)
{
return $oAllowedTag;
}
}
throw new CoreUnexpectedValue("{$sTagCode} is not defined as a valid tag for {$this->sClass}:{$this->sAttCode}");
}
/**
* @param $sTagCode
*
* @return DBObject tag
* @throws \CoreUnexpectedValue
* @throws \CoreException
*/
public function GetTagFromLabel($sTagLabel)
{
$aAllowedTags = $this->GetAllowedTags();
foreach($aAllowedTags as $oAllowedTag)
{
if ($oAllowedTag->Get('label') === $sTagLabel)
{
return $oAllowedTag->Get('code');
}
}
throw new CoreUnexpectedValue("{$sTagLabel} is not defined as a valid tag for {$this->sClass}:{$this->sAttCode}");
}
/**
* @return \TagSetFieldData[]
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
private function GetAllowedTags()
{
return TagSetFieldData::GetAllowedValues($this->sClass, $this->sAttCode);
}
/**
* Compare Tag Set
*
* @param \ormTagSet $other
*
* @return bool true if same tag set
*/
public function Equals(ormSet $other)
{
if (!($other instanceof ormTagSet))
{
return false;
}
if ($this->GetTagDataClass() !== $other->GetTagDataClass())
{
return false;
}
return implode(' ', $this->GetValues()) === implode(' ', $other->GetValues());
}
public function GetTagDataClass()
{
return TagSetFieldData::GetTagDataClassName($this->sClass, $this->sAttCode);
}
/**
* @return bool
*/
public function DisplayPartial()
{
return $this->m_bDisplayPartial;
}
/**
* @param bool $m_bDisplayPartial
*/
public function SetDisplayPartial($m_bDisplayPartial)
{
$this->m_bDisplayPartial = $m_bDisplayPartial;
}
}

View File

@@ -43,17 +43,61 @@
class SimpleCrypt
{
public static function GetNewDefaultParams()
{
if(function_exists('sodium_crypto_secretbox_open') && function_exists('random_bytes')){
$sEngineName = 'Sodium';
}
else if (function_exists('openssl_decrypt'))
{
$sEngineName = 'OpenSSL';
}
else if(function_exists('mcrypt_module_open')){
$sEngineName = 'Mcrypt';
}
else
{
$sEngineName = 'Simple';
}
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
return $sEngineName::GetNewDefaultParams();
}
/**
* Constructor
* @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt
* @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt, Sodium or OpenSSL
* @throws Exception This library is unkown
*/
function __construct($sEngineName = 'Mcrypt')
{
if (($sEngineName == 'Mcrypt') && (!function_exists('mcrypt_module_open')))
{
// Defaults to Simple encryption if the mcrypt module is not present
$sEngineName = 'Simple';
}
switch($sEngineName){
case 'Sodium':
if(!function_exists('sodium_crypto_secretbox_open')){
$sEngineName = 'Simple';
}
break;
case 'Mcrypt':
if(!function_exists('mcrypt_module_open')){
if (function_exists('openssl_decrypt'))
{
$sEngineName = 'OpenSSLMcryptCompatibility';
}
else
{
$sEngineName = 'Simple';
}
}
break;
case 'OpenSSL':
if(!function_exists('openssl_decrypt')){
$sEngineName = 'Simple';
}
break;
case 'Simple':
break;
default:
throw new Exception(Dict::Format("Core:AttributeEncryptUnknownLibrary", $sEngineName));
}
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
$this->oEngine = new $sEngineName;
}
@@ -150,6 +194,7 @@ class SimpleCrypt
*/
interface CryptEngine
{
public static function GetNewDefaultParams();
function Encrypt($key, $sString);
function Decrypt($key, $encrypted_data);
}
@@ -161,7 +206,12 @@ interface CryptEngine
*/
class SimpleCryptSimpleEngine implements CryptEngine
{
public function Encrypt($key, $sString)
public static function GetNewDefaultParams()
{
return array( 'lib' => 'Simple', 'key' => null);
}
public function Encrypt($key, $sString)
{
$result = '';
for($i=1; $i<=strlen($sString); $i++)
@@ -197,7 +247,13 @@ class SimpleCryptMcryptEngine implements CryptEngine
{
var $alg = MCRYPT_BLOWFISH;
var $td = null;
public static function GetNewDefaultParams()
{
return array('lib' => 'Mcrypt', 'key' => null);
}
public function __construct()
{
$this->td = mcrypt_module_open($this->alg,'','cbc','');
@@ -205,7 +261,7 @@ class SimpleCryptMcryptEngine implements CryptEngine
public function Encrypt($key, $sString)
{
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($this->td), MCRYPT_RAND); // MCRYPT_RAND is the only choice on Windows prior to PHP 5.3
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($this->td), MCRYPT_RAND_URANDOM); // MCRYPT_RAND_URANDOM is now useable since itop requires php >= 5.6
mcrypt_generic_init($this->td, $key, $iv);
if (empty($sString))
{
@@ -223,7 +279,7 @@ class SimpleCryptMcryptEngine implements CryptEngine
$r = mcrypt_generic_init($this->td, $key, $iv);
if (($r < 0) || ($r === false))
{
$decrypted_data = '** decryption error **';
$decrypted_data = Dict::S("Core:AttributeEncryptFailedToDecrypt");
}
else
{
@@ -238,4 +294,121 @@ class SimpleCryptMcryptEngine implements CryptEngine
mcrypt_module_close($this->td);
}
}
?>
/**
* SodiumEngine requires Sodium extension
* Every encryption of the same string with the same key
* will return a different encrypted string.
* The key has to be SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long.
*/
class SimpleCryptSodiumEngine implements CryptEngine
{
public static function GetNewDefaultParams()
{
return array('lib' => 'Sodium', 'key' => bin2hex(sodium_crypto_secretbox_keygen()));
}
public function Encrypt($key, $sString)
{
$key = hex2bin($key);
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$encrypted_string = sodium_crypto_secretbox($sString, $nonce, $key);
sodium_memzero($sString);
sodium_memzero($key);
return base64_encode($nonce.$encrypted_string);
}
public function Decrypt($key, $encrypted_data)
{
$key = hex2bin($key);
$encrypted_data = base64_decode($encrypted_data);
$nonce = mb_substr($encrypted_data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$encrypted_data = mb_substr($encrypted_data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
$plaintext = sodium_crypto_secretbox_open($encrypted_data, $nonce, $key);
if ($plaintext === false)
{
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
}
sodium_memzero($encrypted_data);
sodium_memzero($key);
return $plaintext;
}
}
class SimpleCryptOpenSSLEngine implements CryptEngine
{
public static function GetNewDefaultParams()
{
return array('lib' => 'OpenSSL', 'key' => bin2hex(openssl_random_pseudo_bytes(32)));
}
public function Encrypt($key, $sString)
{
$key = hex2bin($key);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("AES-256-CBC"));
$encrypted_string = openssl_encrypt($sString, "AES-256-CBC", $key, 0 , $iv);
return $iv.$encrypted_string;
}
public function Decrypt($key, $encrypted_data)
{
$key = hex2bin($key);
$iv = mb_substr($encrypted_data, 0, openssl_cipher_iv_length("AES-256-CBC"), '8bit');
$encrypted_data = mb_substr($encrypted_data, openssl_cipher_iv_length("AES-256-CBC"), null, '8bit');
$plaintext = openssl_decrypt($encrypted_data,"AES-256-CBC", $key, 0 , $iv);
if ($plaintext === false)
{
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
}
return trim($plaintext);
}
}
class SimpleCryptOpenSSLMcryptCompatibilityEngine implements CryptEngine
{
public static function GetNewDefaultParams()
{
return array('lib' => 'OpenSSLMcryptCompatibility', 'key' => null);
}
//fix for php < 7.1.8 (keys are Zero padded instead of cycle padded)
static private function MakeOpenSSLBlowfishKey($key)
{
if("$key" === '')
{
return $key;
}
$len = (16+2)*4;
while(strlen($key) < $len)
{
$key .= $key;
}
$key = substr($key, 0, $len);
return $key;
}
public function Encrypt($key, $sString)
{
$key = SimpleCryptOpenSSLMcryptCompatibilityEngine::MakeOpenSSLBlowfishKey($key);
$blockSize = 8;
$len = strlen($sString);
$paddingLen = intval (($len + $blockSize -1) / $blockSize) * $blockSize - $len;
$padding = str_repeat("\0", $paddingLen);
$sData = $sString . $padding;
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("BF-CBC"));
$encrypted_string = openssl_encrypt($sData, "BF-CBC", $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
return $iv.$encrypted_string;
}
public function Decrypt($key, $encrypted_data)
{
$key = SimpleCryptOpenSSLMcryptCompatibilityEngine::MakeOpenSSLBlowfishKey($key);
$iv = mb_substr($encrypted_data, 0, openssl_cipher_iv_length("BF-CBC"), '8bit');
$encrypted_data = mb_substr($encrypted_data, openssl_cipher_iv_length("BF-CBC"), null, '8bit');
$plaintext = openssl_decrypt($encrypted_data,"BF-CBC", $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
if ($plaintext === false)
{
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
}
return trim($plaintext);
}
}

View File

@@ -275,6 +275,7 @@ EOF
$sAttCode = $aFieldSpec['sAttCode'];
$sField = '';
/** @var \DBObject $oObj */
$oObj = $aRow[$sAlias];
if ($oObj == null)
{
@@ -332,11 +333,16 @@ EOF
$sField = utils::TextToHtml($oObj->GetEditValue($sAttCode));
$sData .= "<td x:str>$sField</td>";
}
else if($oAttDef instanceof AttributeString)
else if ($oAttDef instanceof AttributeString)
{
$sField = $oObj->GetAsHTML($sAttCode, $this->bLocalizeOutput);
$sData .= "<td x:str>$sField</td>";
}
else if ($oAttDef instanceof AttributeTagSet)
{
$sField = $oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, '');
$sData .= "<td x:str>$sField</td>";
}
else
{
$rawValue = $oObj->Get($sAttCode);

View File

@@ -95,7 +95,7 @@ class SQLObjectQuery extends SQLQuery
$aFieldDesc = array();
foreach ($this->m_aFields as $sAlias => $oExpression)
{
$aFieldDesc[] = $oExpression->Render()." as <em>$sAlias</em>";
$aFieldDesc[] = $oExpression->RenderExpression(false)." as <em>$sAlias</em>";
}
$sFields = " =&gt; ".implode(', ', $aFieldDesc);
}
@@ -112,7 +112,7 @@ class SQLObjectQuery extends SQLQuery
$oSQLQuery = $aJoinInfo["select"];
if (isset($aJoinInfo["on_expression"]))
{
$sOnCondition = $aJoinInfo["on_expression"]->Render();
$sOnCondition = $aJoinInfo["on_expression"]->RenderExpression(false);
echo "<li>Join '$sJoinType', ON ($sOnCondition)".$oSQLQuery->DisplayHtml()."</li>\n";
}
@@ -339,8 +339,12 @@ class SQLObjectQuery extends SQLQuery
$this->PrepareRendering();
$sFrom = self::ClauseFrom($this->__aFrom, $sIndent);
$sWhere = self::ClauseWhere($this->m_oConditionExpr, $aArgs);
// Sanity
$iLimitCount = (int)$iLimitCount;
if ($iLimitCount > 0)
{
// Sanity
$iLimitStart = (int)$iLimitStart;
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
}
else
@@ -464,7 +468,7 @@ class SQLObjectQuery extends SQLQuery
case "left":
if (isset($aJoinData["on_expression"]))
{
$sJoinCond = $aJoinData["on_expression"]->Render();
$sJoinCond = $aJoinData["on_expression"]->RenderExpression(true);
}
else
{
@@ -531,13 +535,13 @@ class SQLObjectQuery extends SQLQuery
//
foreach($this->m_aFields as $sAlias => $oExpression)
{
$oRootQuery->__aFields["`$sAlias`"] = $oExpression->Render();
$oRootQuery->__aFields["`$sAlias`"] = $oExpression->RenderExpression(true);
}
if ($this->m_aGroupBy)
{
foreach($this->m_aGroupBy as $sAlias => $oExpression)
{
$oRootQuery->__aGroupBy["`$sAlias`"] = $oExpression->Render();
$oRootQuery->__aGroupBy["`$sAlias`"] = $oExpression->RenderExpression(true);
}
}
if ($this->m_bToDelete)
@@ -551,7 +555,7 @@ class SQLObjectQuery extends SQLQuery
if (!is_null($this->m_oSelectedIdField))
{
$oRootQuery->__aSelectedIdFields[] = $this->m_oSelectedIdField->Render();
$oRootQuery->__aSelectedIdFields[] = $this->m_oSelectedIdField->RenderExpression(true);
}
// loop on joins, to complete the list of tables/fields/conditions

View File

@@ -177,7 +177,7 @@ abstract class SQLQuery
}
else
{
return $oConditionExpr->Render($aArgs);
return $oConditionExpr->RenderExpression(true, $aArgs);
}
}

View File

@@ -168,7 +168,7 @@ class SQLUnionQuery extends SQLQuery
}
foreach($this->aSelectExpr as $sSelectAlias => $oExpr)
{
$aSelectAliases[$sSelectAlias] = $oExpr->Render()." AS `$sSelectAlias`";
$aSelectAliases[$sSelectAlias] = $oExpr->RenderExpression(true)." AS `$sSelectAlias`";
}
$sSelect = implode(",$sLineSep ", $aSelectAliases);

View File

@@ -150,15 +150,18 @@ abstract class TabularBulkExport extends BulkExport
// Add the reconciliation keys
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
{
$sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
if (!array_key_exists($sAttCodeEx, $aResult))
{
$oRemoteAttDef = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
if ($this->IsExportableField($sRemoteClass, $sRemoteAttCode, $oRemoteAttDef))
{
$aResult[$sAttCodeEx] = array('code' => $sAttCodeEx, 'unique_label' => $oAttDef->GetLabel().'->'.$oRemoteAttDef->GetLabel(), 'label' => MetaModel::GetLabel($sRemoteClass, $sRemoteAttCode), 'attdef' => $oRemoteAttDef);
}
}
if (!empty($sRemoteAttCode))
{
$sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
if (!array_key_exists($sAttCodeEx, $aResult))
{
$oRemoteAttDef = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
if ($this->IsExportableField($sRemoteClass, $sRemoteAttCode, $oRemoteAttDef))
{
$aResult[$sAttCodeEx] = array('code' => $sAttCodeEx, 'unique_label' => $oAttDef->GetLabel().'->'.$oRemoteAttDef->GetLabel(), 'label' => MetaModel::GetLabel($sRemoteClass, $sRemoteAttCode), 'attdef' => $oRemoteAttDef);
}
}
}
}
break;

View File

@@ -0,0 +1,394 @@
<?php
// Copyright (C) 2018 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/>
/**
* <p>Stores data for {@link AttributeTagSet} fields
*
* <p>We will have an implementation for each class/field to be able to customize rights (generated in
* \MFCompiler::CompileClass).<br> Only this abstract class will exists in the DB : the implementations won't had any
* new field.
*
* @since 2.6 N°931 tag fields
*/
abstract class TagSetFieldData extends cmdbAbstractObject
{
private static $m_aAllowedValues = array();
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
'category' => 'bizmodel',
'key_type' => 'autoincrement',
'name_attcode' => array('label'),
'state_attcode' => '',
'reconc_keys' => array('code'),
'db_table' => 'priv_tagfielddata',
'db_key_field' => 'id',
'db_finalclass_field' => 'finalclass',
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("code", array(
"allowed_values" => null,
"sql" => 'code',
"default_value" => '',
"is_null_allowed" => false,
"depends_on" => array(),
"validation_pattern" => '^[a-zA-Z][a-zA-Z0-9]{3,}$',
)));
MetaModel::Init_AddAttribute(new AttributeString("label", array(
"allowed_values" => null,
"sql" => 'label',
"default_value" => '',
"is_null_allowed" => false,
"depends_on" => array()
)));
MetaModel::Init_AddAttribute(new AttributeHTML("description", array(
"allowed_values" => null,
"sql" => 'description',
"default_value" => '',
"is_null_allowed" => true,
"depends_on" => array()
)));
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array(
"allowed_values" => null,
"sql" => 'obj_class',
"default_value" => '',
"is_null_allowed" => false,
"depends_on" => array()
)));
MetaModel::Init_AddAttribute(new AttributeString("obj_attcode", array(
"allowed_values" => null,
"sql" => 'obj_attcode',
"default_value" => '',
"is_null_allowed" => false,
"depends_on" => array()
)));
MetaModel::Init_SetZListItems('details', array('code', 'label', 'description'));
MetaModel::Init_SetZListItems('standard_search', array('code', 'label', 'description'));
MetaModel::Init_SetZListItems('list', array('code', 'label', 'description'));
}
public function ComputeValues()
{
$sClassName = get_class($this);
$aRes = static::ExtractTagFieldName($sClassName);
$this->_Set('obj_class', $aRes['obj_class']);
$this->_Set('obj_attcode', $aRes['obj_attcode']);
}
public static function GetTagDataClassName($sClass, $sAttCode)
{
$sTagSuffix = $sClass.'__'.$sAttCode;
return 'TagSetFieldDataFor_'.$sTagSuffix;
}
/**
* Extract Tag class and attcode from the TagFieldData class name
*
* @param $sClassName
*
* @return string[]
* @throws \CoreException
*/
public static function ExtractTagFieldName($sClassName)
{
$aRes = array();
// Extract class and attcode from class name using pattern TagSetFieldDataFor_<class>_<attcode>>;
if (preg_match('@^TagSetFieldDataFor_(?<class>\w+)__(?<attcode>\w+)$@', $sClassName, $aMatches))
{
$aRes['obj_class'] = $aMatches['class'];
$aRes['obj_attcode'] = $aMatches['attcode'];
}
else
{
throw new CoreException("Bad Class name format: $sClassName");
}
return $aRes;
}
/**
* @param \DeletionPlan $oDeletionPlan
*
* @throws \CoreException
*/
public function DoCheckToDelete(&$oDeletionPlan)
{
parent::DoCheckToDelete($oDeletionPlan);
$sTagCode = $this->Get('code');
if ($this->IsCodeUsed($sTagCode))
{
$this->m_aDeleteIssues[] = Dict::S('Core:TagSetFieldData:ErrorDeleteUsedTag');
}
// Clear cache
$sClass = $this->Get('obj_class');
$sAttCode = $this->Get('obj_attcode');
$sTagDataClass = self::GetTagDataClassName($sClass, $sAttCode);
unset(self::$m_aAllowedValues[$sTagDataClass]);
}
/**
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @throws \Exception
*/
public function DoCheckToWrite()
{
$this->ComputeValues();
$sClass = $this->Get('obj_class');
$sAttCode = $this->Get('obj_attcode');
$iMaxLen = 20;
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef instanceof AttributeTagSet)
{
$iMaxLen = $oAttDef->GetTagCodeMaxLength();
}
$sTagCode = $this->Get('code');
// Check code syntax
$iMax = $iMaxLen - 1;
if (!preg_match("@^[a-zA-Z][a-zA-Z0-9]{2,$iMax}$@", $sTagCode))
{
$this->m_aCheckIssues[] = Dict::Format('Core:TagSetFieldData:ErrorTagCodeSyntax', $iMaxLen);
}
// Check that the code is not a MySQL stop word
$sSQL = "SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD";
try
{
$aResults = CMDBSource::QueryToArray($sSQL);
} catch (MySQLException $e)
{
IssueLog::Warning($e->getMessage());
$aResults = array();
}
foreach($aResults as $aResult)
{
if ($aResult['value'] == $sTagCode)
{
$this->m_aCheckIssues[] = Dict::S('Core:TagSetFieldData:ErrorTagCodeReservedWord');
break;
}
}
$sTagLabel = $this->Get('label');
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
if (empty($sTagLabel) || (strpos($sTagLabel, $sSepItem) !== false))
{
// Label must not contain | character
$this->m_aCheckIssues[] = Dict::Format('Core:TagSetFieldData:ErrorTagLabelSyntax', $sSepItem);
}
// Check that code and labels are uniques
$id = $this->GetKey();
$sClassName = get_class($this);
if (empty($id))
{
$oSearch = DBSearch::FromOQL("SELECT $sClassName WHERE (code = :tag_code OR label = :tag_label)");
}
else
{
$oSearch = DBSearch::FromOQL("SELECT $sClassName WHERE id != :id AND (code = :tag_code OR label = :tag_label)");
}
$aArgs = array('id' => $id, 'tag_code' => $sTagCode, 'tag_label' => $sTagLabel);
$oSet = new DBObjectSet($oSearch, array(), $aArgs);
if ($oSet->CountExceeds(0))
{
$this->m_aCheckIssues[] = Dict::S('Core:TagSetFieldData:ErrorDuplicateTagCodeOrLabel');
}
// Clear cache
$sTagDataClass = self::GetTagDataClassName($sClass, $sAttCode);
unset(self::$m_aAllowedValues[$sTagDataClass]);
parent::DoCheckToWrite();
}
/**
* @throws \CoreException
*/
public function OnUpdate()
{
parent::OnUpdate();
$aChanges = $this->ListChanges();
if (array_key_exists('code', $aChanges))
{
$sTagCode = $this->GetOriginal('code');
if ($this->IsCodeUsed($sTagCode))
{
throw new CoreException(Dict::S('Core:TagSetFieldData:ErrorCodeUpdateNotAllowed'));
}
}
if (array_key_exists('obj_class', $aChanges))
{
throw new CoreException(Dict::S('Core:TagSetFieldData:ErrorClassUpdateNotAllowed'));
}
if (array_key_exists('obj_attcode', $aChanges))
{
throw new CoreException(Dict::S('Core:TagSetFieldData:ErrorAttCodeUpdateNotAllowed'));
}
}
private function IsCodeUsed($sTagCode)
{
try
{
$sClass = $this->Get('obj_class');
$sAttCode = $this->Get('obj_attcode');
$oSearch = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'");
$oSet = new DBObjectSet($oSearch);
if ($oSet->CountExceeds(0))
{
return true;
}
}
catch (Exception $e)
{
IssueLog::Warning($e->getMessage());
}
return false;
}
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
if ($sAttCode == 'code')
{
if ((!$this->IsNew()) && ($this->IsCodeUsed($this->Get('code'))))
{
return OPT_ATT_READONLY;
}
}
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
}
/**
* Display Tag Usage
*
* @param \WebPage $oPage
* @param bool $bEditMode
*
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
parent::DisplayBareRelations($oPage, $bEditMode);
if (!$bEditMode)
{
$sClass = $this->Get('obj_class');
$sAttCode = $this->Get('obj_attcode');
$sTagCode = $this->Get('code');
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'");
$oSet = new DBObjectSet($oFilter);
$iCount = $oSet->Count();
$oPage->SetCurrentTab(Dict::Format('Core:TagSetFieldData:WhereIsThisTagTab', $iCount));
if ($iCount === 0)
{
$sNoEntries = Dict::S('Core:TagSetFieldData:NoEntryFound');
$oPage->add("<p>$sNoEntries</p>");
return;
}
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'");
$oSet = new DBObjectSet($oFilter);
if ($oSet->CountExceeds(0))
{
$sClassLabel = MetaModel::GetName($sClass);
$oPage->add("<h2>$sClassLabel</h2>");
$oResultBlock = new DisplayBlock($oFilter, 'list', false);
$oResultBlock->Display($oPage, 1);
}
}
}
public static function GetClassName($sClass)
{
if ($sClass == 'TagSetFieldData')
{
$aWords = preg_split('/(?=[A-Z]+)/', $sClass);
return trim(implode(' ', $aWords));
}
try
{
$aTagFieldInfo = self::ExtractTagFieldName($sClass);
} catch (CoreException $e)
{
return $sClass;
}
$sClassDesc = MetaModel::GetName($aTagFieldInfo['obj_class']);
$sAttDesc = MetaModel::GetAttributeDef($aTagFieldInfo['obj_class'], $aTagFieldInfo['obj_attcode'])->GetLabel();
if (Dict::Exists("Class:$sClass"))
{
$sName = Dict::Format("Class:$sClass", $sClassDesc, $sAttDesc);
}
else
{
$sName = Dict::Format('Class:TagSetFieldData', $sClassDesc, $sAttDesc);
}
return $sName;
}
/**
* @param $sClass
* @param $sAttCode
*
* @return \TagSetFieldData[]
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public static function GetAllowedValues($sClass, $sAttCode)
{
$sClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
$sTagDataClass = self::GetTagDataClassName($sClass, $sAttCode);
if (!isset(self::$m_aAllowedValues[$sTagDataClass]))
{
$oSearch = new DBObjectSearch($sTagDataClass);
$oSearch->AddCondition('obj_class', $sClass);
$oSearch->AddCondition('obj_attcode', $sAttCode);
$oSet = new DBObjectSet($oSearch);
self::$m_aAllowedValues[$sTagDataClass] = $oSet->ToArray();
}
return self::$m_aAllowedValues[$sTagDataClass];
}
}

View File

@@ -56,18 +56,20 @@ class TemplateString
{
protected $m_sRaw;
protected $m_aPlaceholders;
public function __construct($sRaw)
{
$this->m_sRaw = $sRaw;
$this->m_aPlaceholders = null;
}
/**
* Split the string into placholders
* @param Hash $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
* @return void
*/
* Split the string into placholders
*
* @param array $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
*
* @throws \Exception
*/
protected function Analyze($aParamTypes = array())
{
if (!is_null($this->m_aPlaceholders)) return;
@@ -100,9 +102,10 @@ class TemplateString
}
/**
* Return the placeholders (for reporting purposes)
* @return void
*/
* Return the placeholders (for reporting purposes)
*
* @return array
*/
public function GetPlaceholders()
{
return $this->m_aPlaceholders;
@@ -110,9 +113,11 @@ class TemplateString
/**
* Check the format when possible
* @param Hash $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
* @return void
*/
*
* @param array $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
*
* @return boolean
*/
public function IsValid($aParamTypes = array())
{
$this->Analyze($aParamTypes);
@@ -135,10 +140,12 @@ class TemplateString
}
/**
* Apply the given parameters to replace the placeholders
* @param Hash $aParamValues Value of the expected parameters: hash array of '<param_id>' => '<value>'
* @return void
*/
* Apply the given parameters to replace the placeholders
*
* @param array $aParamValues Value of the expected parameters: hash array of '<param_id>' => '<value>'
*
* @return string
*/
public function Render($aParamValues = array())
{
$aParamTypes = array();
@@ -174,5 +181,4 @@ class TemplateString
}
return str_replace($aSearch, $aReplace, $this->m_sRaw);
}
}
?>
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010-2018 Combodo SARL
//
// This file is part of iTop.
//
@@ -19,21 +19,25 @@
/**
* Persistent class Trigger and derived
* User defined triggers, that may be used in conjunction with user defined actions
* User defined triggers, that may be used in conjunction with user defined actions
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* A user defined trigger, to customize the application
* A user defined trigger, to customize the application
* A trigger will activate an action
*
* @package iTopORM
*/
abstract class Trigger extends cmdbAbstractObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
@@ -50,25 +54,32 @@ abstract class Trigger extends cmdbAbstractObject
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list", array("linked_class"=>"lnkTriggerAction", "ext_key_to_me"=>"trigger_id", "ext_key_to_remote"=>"action_id", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list", array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "trigger_id", "ext_key_to_remote" => "action_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('finalclass', 'description', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
/**
* @param $aContextArgs
*
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function DoActivate($aContextArgs)
{
// Find the related actions
$oLinkedActions = $this->Get('action_list');
while ($oLink = $oLinkedActions->Fetch())
{
/** @var \DBObject $oLink */
$iActionId = $oLink->Get('action_id');
/** @var \Action $oAction */
$oAction = MetaModel::GetObject('Action', $iActionId);
if ($oAction->IsActive())
{
@@ -76,11 +87,13 @@ abstract class Trigger extends cmdbAbstractObject
}
}
}
/**
* Check whether the given object is in the scope of this trigger
* and can potentially be the subject of notifications
*
* @param DBObject $oObject The object to check
*
* @return bool
*/
public function IsInScope(DBObject $oObject)
@@ -91,8 +104,15 @@ abstract class Trigger extends cmdbAbstractObject
}
}
/**
* Class TriggerOnObject
*/
abstract class TriggerOnObject extends Trigger
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
@@ -109,17 +129,20 @@ abstract class TriggerOnObject extends Trigger
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeClass("target_class", array("class_category"=>"bizmodel", "more_values"=>"User,UserExternal,UserInternal,UserLDAP,UserLocal", "sql"=>"target_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOQL("filter", array("allowed_values"=>null, "sql"=>"filter", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeClass("target_class", array("class_category" => "bizmodel", "more_values" => "User,UserExternal,UserInternal,UserLDAP,UserLocal", "sql" => "target_class", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("filter", array("allowed_values" => null, "sql" => "filter", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('default_search', array('description', 'target_class')); // Default criteria of the search banner
// MetaModel::Init_SetZListItems('standard_search', array('name', 'target_class', 'description')); // Criteria of the search form
// MetaModel::Init_SetZListItems('standard_search', array('name', 'target_class', 'description')); // Criteria of the search form
}
/**
* @throws \CoreException
*/
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
@@ -135,8 +158,7 @@ abstract class TriggerOnObject extends Trigger
{
$this->m_aCheckIssues[] = Dict::Format('TriggerOnObject:WrongFilterClass', $this->Get('target_class'));
}
}
catch(OqlException $e)
} catch (OqlException $e)
{
$this->m_aCheckIssues[] = Dict::Format('TriggerOnObject:WrongFilterQuery', $e->getMessage());
}
@@ -146,21 +168,33 @@ abstract class TriggerOnObject extends Trigger
/**
* Check whether the given object is in the scope of this trigger
* and can potentially be the subject of notifications
*
* @param DBObject $oObject The object to check
*
* @return bool
* @throws \CoreException
*/
public function IsInScope(DBObject $oObject)
{
$sRootClass = $this->Get('target_class');
return ($oObject instanceof $sRootClass);
}
/**
* @param $aContextArgs
*
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function DoActivate($aContextArgs)
{
$bGo = true;
if (isset($aContextArgs['this->object()']))
{
$bGo = $this->IsTargetObject($aContextArgs['this->object()']->GetKey());
/** @var \DBObject $oObject */
$oObject = $aContextArgs['this->object()'];
$bGo = $this->IsTargetObject($oObject->GetKey(), $oObject->ListChanges());
}
if ($bGo)
{
@@ -168,7 +202,18 @@ abstract class TriggerOnObject extends Trigger
}
}
public function IsTargetObject($iObjectId)
/**
* @param $iObjectId
* @param array $aChanges
*
* @return bool
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function IsTargetObject($iObjectId, $aChanges = array())
{
$sFilter = trim($this->Get('filter'));
if (strlen($sFilter) > 0)
@@ -182,14 +227,19 @@ abstract class TriggerOnObject extends Trigger
{
$bRet = true;
}
return $bRet;
}
}
/**
* To trigger notifications when a ticket is updated from the portal
*/
class TriggerOnPortalUpdate extends TriggerOnObject
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
@@ -214,8 +264,15 @@ class TriggerOnPortalUpdate extends TriggerOnObject
}
}
/**
* Class TriggerOnStateChange
*/
abstract class TriggerOnStateChange extends TriggerOnObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
@@ -232,19 +289,25 @@ abstract class TriggerOnStateChange extends TriggerOnObject
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("state", array("allowed_values"=>null, "sql"=>"state", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeClassState("state", array("class_field" => 'target_class', "allowed_values" => null, "sql" => "state", "default_value" => null, "is_null_allowed" => false, "depends_on" => array('target_class'))));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'state', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'state')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
/**
* Class TriggerOnStateEnter
*/
class TriggerOnStateEnter extends TriggerOnStateChange
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
@@ -267,12 +330,18 @@ class TriggerOnStateEnter extends TriggerOnStateChange
MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
/**
* Class TriggerOnStateLeave
*/
class TriggerOnStateLeave extends TriggerOnStateChange
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
@@ -295,12 +364,18 @@ class TriggerOnStateLeave extends TriggerOnStateChange
MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('')); // Criteria of the advanced search form
// MetaModel::Init_SetZListItems('advanced_search', array('')); // Criteria of the advanced search form
}
}
/**
* Class TriggerOnObjectCreate
*/
class TriggerOnObjectCreate extends TriggerOnObject
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
@@ -323,12 +398,146 @@ class TriggerOnObjectCreate extends TriggerOnObject
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
/**
* Class TriggerOnObjectCreate
*/
class TriggerOnObjectDelete extends TriggerOnObject
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",
"reconc_keys" => array('description'),
"db_table" => "priv_trigger_onobjdelete",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
/**
* Class TriggerOnObjectCreate
*/
class TriggerOnObjectUpdate extends TriggerOnObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",
"reconc_keys" => array('description'),
"db_table" => "priv_trigger_onobjupdate",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeClassAttCodeSet('target_attcodes', array("allowed_values" => null, "class_field" => "target_class", "sql" => "target_attcodes", "default_value" => null, "is_null_allowed" => true, "max_items" => 20, "min_items" => 0, "attribute_definition_exclusion_list" => "AttributeDashboard,AttributeExternalField,AttributeFinalClass,AttributeFriendlyName,AttributeObsolescenceDate,AttributeObsolescenceFlag,AttributeSubItem", "attribute_definition_list" => null, "depends_on" => array('target_class'))));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'target_attcodes', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
}
public function IsTargetObject($iObjectId, $aChanges = array())
{
if (!parent::IsTargetObject($iObjectId, $aChanges))
{
return false;
}
// Check the attribute
$oAttCodeSet = $this->Get('target_attcodes');
$aAttCodes = $oAttCodeSet->GetValues();
if (empty($aAttCodes))
{
return true;
}
foreach($aAttCodes as $sAttCode)
{
if (array_key_exists($sAttCode, $aChanges))
{
return true;
}
}
return false;
}
public function ComputeValues()
{
parent::ComputeValues();
// Remove unwanted attribute codes
$aChanges = $this->ListChanges();
if (isset($aChanges['target_attcodes']))
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), 'target_attcodes');
$aArgs = array('this' => $this);
$aAllowedValues = $oAttDef->GetAllowedValues($aArgs);
/** @var \ormSet $oValue */
$oValue = $this->Get('target_attcodes');
$aValues = $oValue->GetValues();
$bChanged = false;
foreach($aValues as $key => $sValue)
{
if (!isset($aAllowedValues[$sValue]))
{
unset($aValues[$key]);
$bChanged = true;
}
}
if ($bChanged)
{
$oValue->SetValues($aValues);
$this->Set('target_attcodes', $oValue);
}
}
}
}
/**
* Class lnkTriggerAction
*/
class lnkTriggerAction extends cmdbAbstractObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
@@ -345,11 +554,11 @@ class lnkTriggerAction extends cmdbAbstractObject
"is_link" => true,
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass"=>"Action", "jointype"=> '', "allowed_values"=>null, "sql"=>"action_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("action_name", array("allowed_values"=>null, "extkey_attcode"=> 'action_id', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "jointype"=> '', "allowed_values"=>null, "sql"=>"trigger_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("trigger_name", array("allowed_values"=>null, "extkey_attcode"=> 'trigger_id', "target_attcode"=>"description")));
MetaModel::Init_AddAttribute(new AttributeInteger("order", array("allowed_values"=>null, "sql"=>"order", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass" => "Action", "jointype" => '', "allowed_values" => null, "sql" => "action_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("action_name", array("allowed_values" => null, "extkey_attcode" => 'action_id', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass" => "Trigger", "jointype" => '', "allowed_values" => null, "sql" => "trigger_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("trigger_name", array("allowed_values" => null, "extkey_attcode" => 'trigger_id', "target_attcode" => "description")));
MetaModel::Init_AddAttribute(new AttributeInteger("order", array("allowed_values" => null, "sql" => "order", "default_value" => 0, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('action_id', 'trigger_id', 'order')); // Attributes to be displayed for a list
@@ -360,8 +569,15 @@ class lnkTriggerAction extends cmdbAbstractObject
}
}
/**
* Class TriggerOnThresholdReached
*/
class TriggerOnThresholdReached extends TriggerOnObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
@@ -379,15 +595,14 @@ class TriggerOnThresholdReached extends TriggerOnObject
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("stop_watch_code", array("allowed_values"=>null, "sql"=>"stop_watch_code", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("threshold_index", array("allowed_values"=>null, "sql"=>"threshold_index", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeClassAttCodeSet('stop_watch_code', array("allowed_values" => null, "class_field" => "target_class", "sql" => "stop_watch_code", "default_value" => null, "is_null_allowed" => false, "max_items" => 1, "min_items" => 1, "attribute_definition_exclusion_list" => null, "attribute_definition_list" => "AttributeStopWatch", "include_child_classes_attributes" => true, "depends_on" => array('target_class'))));
MetaModel::Init_AddAttribute(new AttributeString("threshold_index", array("allowed_values" => null, "sql" => "threshold_index", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'stop_watch_code', 'threshold_index', 'filter', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('target_class', 'threshold_index', 'threshold_index')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
?>

View File

@@ -289,13 +289,13 @@ abstract class User extends cmdbAbstractObject
$oSet = $this->Get('profile_list');
if ($oSet->Count() == 0)
{
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:AtLeastOneProfileIsNeeded');
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AtLeastOneProfileIsNeeded');
}
}
// Only administrators can manage administrators
if (UserRights::IsAdministrator($this) && !UserRights::IsAdministrator())
{
$this->m_aCheckIssues[] = Dict::Format('UI:Login:Error:AccessRestricted');
$this->m_aCheckIssues[] = Dict::S('UI:Login:Error:AccessRestricted');
}
if (!UserRights::IsAdministrator())
@@ -304,30 +304,37 @@ abstract class User extends cmdbAbstractObject
$oAddon = UserRights::GetModuleInstance();
if (!is_null($oUser) && method_exists($oAddon, 'GetUserOrgs'))
{
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
if (count($aOrgs) > 0)
if ((empty($this->GetOriginal('contactid')) && !($this->IsNew())) || empty($this->Get('contactid')))
{
// Check that the modified User belongs to one of our organization
if (!in_array($this->GetOriginal('org_id'), $aOrgs) && !in_array($this->Get('org_id'), $aOrgs))
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:PersonIsMandatory');
}
else
{
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
if (count($aOrgs) > 0)
{
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:UserOrganizationNotAllowed');
}
// Check users with restricted organizations when allowed organizations have changed
if ($this->IsNew() || array_key_exists('allowed_org_list', $aChanges))
{
$oSet = $this->get('allowed_org_list');
if ($oSet->Count() == 0)
// Check that the modified User belongs to one of our organization
if (!in_array($this->GetOriginal('org_id'), $aOrgs) && !in_array($this->Get('org_id'), $aOrgs))
{
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:AtLeastOneOrganizationIsNeeded');
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:UserOrganizationNotAllowed');
}
else
// Check users with restricted organizations when allowed organizations have changed
if ($this->IsNew() || array_key_exists('allowed_org_list', $aChanges))
{
$aModifiedLinks = $oSet->ListModifiedLinks();
foreach($aModifiedLinks as $oLink)
$oSet = $this->get('allowed_org_list');
if ($oSet->Count() == 0)
{
if (!in_array($oLink->Get('allowed_org_id'), $aOrgs))
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AtLeastOneOrganizationIsNeeded');
}
else
{
$aModifiedLinks = $oSet->ListModifiedLinks();
foreach ($aModifiedLinks as $oLink)
{
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:OrganizationNotAllowed');
if (!in_array($oLink->Get('allowed_org_id'), $aOrgs))
{
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:OrganizationNotAllowed');
}
}
}
}
@@ -1218,8 +1225,9 @@ class UserRights
}
/**
* @param $sProfileName Profile name to search for
* @param $oUser User|null
* @param string $sProfileName Profile name to search for
* @param User|null $oUser
*
* @return bool
*/
public static function HasProfile($sProfileName, $oUser = null)
@@ -1233,8 +1241,6 @@ class UserRights
* @param Bool Reset admin cache as well
* @return void
*/
// Reset cached data
//
public static function FlushPrivileges($bResetAdminCache = false)
{
if ($bResetAdminCache)
@@ -1255,12 +1261,16 @@ class UserRights
}
static $m_aCacheUsers;
/**
* Find a user based on its login and its type of authentication
*
* @param string $sLogin Login/identifier of the user
* @param string $sAuthentication Type of authentication used: internal|external|any
* @param bool $bAllowDisabledUsers Whether or not to retrieve disabled users (status != enabled)
*
* @return User The found user or null
* @throws \OQLException
*/
protected static function FindUser($sLogin, $sAuthentication = 'any', $bAllowDisabledUsers = false)
{
@@ -1321,6 +1331,24 @@ class UserRights
{
$_SESSION['profile_list'] = self::ListProfiles();
}
$oConfig = MetaModel::GetConfig();
$bSessionIdRegeneration = $oConfig->Get('regenerate_session_id_enabled');
if ($bSessionIdRegeneration)
{
// Protection against session fixation/injection: generate a new session id.
// Alas a PHP bug (technically a bug in the memcache session handler, https://bugs.php.net/bug.php?id=71187)
// causes session_regenerate_id to fail with a catchable fatal error in PHP 7.0 if the session handler is memcache(d).
// The bug has been fixed in PHP 7.2, but in case session_regenerate_id()
// fails we just silently ignore the error and keep the same session id...
$old_error_handler = set_error_handler(array(__CLASS__, 'VoidErrorHandler'));
session_regenerate_id();
if ($old_error_handler !== null)
{
set_error_handler($old_error_handler);
}
}
}
public static function _ResetSessionCache()
@@ -1334,6 +1362,19 @@ class UserRights
unset($_SESSION['archive_allowed']);
}
}
/**
* Fake error handler to silently discard fatal errors
* @param int $iErrNo
* @param string $sErrStr
* @param string $sErrFile
* @param int $iErrLine
* @return boolean
*/
public static function VoidErrorHandler($iErrno, $sErrStr, $sErrFile, $iErrLine)
{
return true; // Ignore the error
}
}
/**

View File

@@ -1,5 +1,5 @@
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
$version: "v2.5.2";
$version: "v2.6.1";
// Base colors
$gray-base: #000 !default;
@@ -29,13 +29,12 @@ $frame-background-color: $gray-extra-light;
$text-color: #000;
$box-radius: 0px;
$box-shadow-regular: 0 1px 1px rgba(0, 0, 0, 0.15);
// - Boxes
//$search-criteria-box-color: #2D2D2D;
//$search-criteria-box-bg-color: #f0f3f5;
//$search-criteria-box-border-color: #3f7294;
//$search-criteria-box-border: 1px solid $search-criteria-box-border-color;
//$search-criteria-box-radius: 1px;
//
$hyperlink-color: $complement-color;
$hyperlink-text-decoration: none;
////////////
// Search //
$search-criteria-box-color: #2D2D2D;
$search-criteria-box-picto-color: #E87C1E;
$search-criteria-box-bg-color: #EEEEEE;

View File

@@ -1,115 +0,0 @@
/* Styles for jQuery menu widget
Author: Maggie Wachs, maggie@filamentgroup.com
Date: September 2008
*/
/* REQUIRED STYLES - the menus will only render correctly with these rules */
.positionHelper { z-index: 2999; }
.fg-menu-container { position: absolute; top:0; left:-999px; padding: .4em; overflow: hidden; }
.fg-menu-container.fg-menu-flyout { overflow: visible; }
.fg-menu, .fg-menu ul { list-style-type:none; padding: 0; margin:0; }
.fg-menu { position:relative; }
.fg-menu-flyout .fg-menu { position:static; }
.fg-menu ul { position:absolute; top:0; }
.fg-menu ul ul { top:-1px; }
.fg-menu-container.fg-menu-ipod .fg-menu-content,
.fg-menu-container.fg-menu-ipod .fg-menu-content ul { background: none !important; }
.fg-menu.fg-menu-scroll,
.fg-menu ul.fg-menu-scroll { overflow: scroll; overflow-x: hidden; }
.fg-menu li { clear:both; float:left; width:100%; margin: 0; padding:0; border: 0; }
.fg-menu li li { font-size:1em; } /* inner li font size must be reset so that they don't blow up */
.fg-menu-flyout ul ul { padding: .4em; }
.fg-menu-flyout li { position:relative; }
.fg-menu-scroll { overflow: scroll; overflow-x: hidden; }
.fg-menu-breadcrumb { margin: 0; padding: 0; }
.fg-menu-footer { margin-top: .4em; padding: .4em; }
.fg-menu-header { margin-bottom: .4em; padding: .4em; }
.fg-menu-breadcrumb li { float: left; list-style: none; margin: 0; padding: 0 .2em; font-size: .9em; opacity: .7; }
.fg-menu-breadcrumb li.fg-menu-prev-list,
.fg-menu-breadcrumb li.fg-menu-current-crumb { clear: left; float: none; opacity: 1; }
.fg-menu-breadcrumb li.fg-menu-current-crumb { padding-top: .2em; }
.fg-menu-breadcrumb a,
.fg-menu-breadcrumb span { float: left; }
.fg-menu-footer a:link,
.fg-menu-footer a:visited { float:left; width:100%; text-decoration: none; }
.fg-menu-footer a:hover,
.fg-menu-footer a:active { }
.fg-menu-footer a span { float:left; cursor: pointer; }
.fg-menu-breadcrumb .fg-menu-prev-list a:link,
.fg-menu-breadcrumb .fg-menu-prev-list a:visited,
.fg-menu-breadcrumb .fg-menu-prev-list a:hover,
.fg-menu-breadcrumb .fg-menu-prev-list a:active { background-image: none; text-decoration:none; }
.fg-menu-breadcrumb .fg-menu-prev-list a { float: left; padding-right: .4em; }
.fg-menu-breadcrumb .fg-menu-prev-list a .ui-icon { float: left; }
.fg-menu-breadcrumb .fg-menu-current-crumb a:link,
.fg-menu-breadcrumb .fg-menu-current-crumb a:visited,
.fg-menu-breadcrumb .fg-menu-current-crumb a:hover,
.fg-menu-breadcrumb .fg-menu-current-crumb a:active { display:block; background-image:none; font-size:1.3em; text-decoration:none; }
/* REQUIRED LINK STYLES: links are "display:block" by default; if the menu options are split into
selectable node links and 'next' links, the script floats the node links left and floats the 'next' links to the right */
.fg-menu a:link,
.fg-menu a:visited,
.fg-menu a:hover,
.fg-menu a:active { float:left; width:92%; padding:.3em 3%; text-decoration:none; outline: 0 !important; }
.fg-menu a { border: 1px dashed transparent; }
.fg-menu a.ui-state-default:link,
.fg-menu a.ui-state-default:visited,
.fg-menu a.ui-state-default:hover,
.fg-menu a.ui-state-default:active,
.fg-menu a.ui-state-hover:link,
.fg-menu a.ui-state-hover:visited,
.fg-menu a.ui-state-hover:hover,
.fg-menu a.ui-state-hover:active,
.fg-menu a.ui-state-active:link,
.fg-menu a.ui-state-active:visited,
.fg-menu a.ui-state-active:hover,
.fg-menu a.ui-state-active:active { border-style: solid; font-weight: normal; }
.fg-menu a span { display:block; cursor:pointer; }
/* SUGGESTED STYLES - for use with jQuery UI Themeroller CSS */
.fg-menu-indicator span { float:left; }
.fg-menu-indicator span.ui-icon { float:right; }
.fg-menu-content.ui-widget-content,
.fg-menu-content ul.ui-widget-content { border:0; }
/* ICONS AND DIVIDERS */
.fg-menu.fg-menu-has-icons a:link,
.fg-menu.fg-menu-has-icons a:visited,
.fg-menu.fg-menu-has-icons a:hover,
.fg-menu.fg-menu-has-icons a:active { padding-left:20px; }
.fg-menu .horizontal-divider hr, .fg-menu .horizontal-divider span { padding:0; margin:5px .6em; }
.fg-menu .horizontal-divider hr { border:0; height:1px; }
.fg-menu .horizontal-divider span { font-size:.9em; text-transform: uppercase; padding-left:.2em; }

View File

@@ -92,6 +92,22 @@ table.listResults td .view-image img {
margin-left: auto;
margin-right: auto;
}
table.listResults > tbody > tr > * {
transition: background-color 400ms linear;
}
table.listResults > tbody > tr:hover > * {
cursor: pointer;
}
table.listResults > tbody > tr.selected:hover > * {
/* hover on lines is currently done toggling td.hover, and having a rule for links */
background-color: #f3b37b;
color: #000;
}
table.listResults > tbody > tr:hover > * {
/* hover on lines is currently done toggling td.hover, and having a rule for links */
background-color: #fdf5d0;
color: #000;
}
.edit-image .view-image {
display: inline-block;
}
@@ -139,6 +155,7 @@ table.listResults td .view-image img {
vertical-align: middle;
max-width: 90% !important;
max-height: 90% !important;
cursor: zoom-in;
}
.details .view-image .helper-middle {
display: inline-block;
@@ -179,7 +196,6 @@ tr.green td, .wizContainer tr.green td {
background-color: #b3e5b4;
}
tr td.hover, tr.even td.hover, .hover a, .hover a:visited, .hover a:hover, .wizContainer tr.even td.hover, .wizContainer tr td.hover {
background-color: #fdf5d0;
color: #000;
}
th {
@@ -327,10 +343,10 @@ a.small_action {
padding-left: 5px;
padding-top: 2px;
padding-bottom: 2px;
background: #ea7d1e url(../images/actions_left.png?v=v2.5.2) no-repeat left;
background: #ea7d1e url(../images/actions_left.png?v=v2.6.1) no-repeat left;
}
.actions_details span {
background: url(../images/actions_right.png?v=v2.5.2) no-repeat right;
background: url(../images/actions_right.png?v=v2.6.1) no-repeat right;
color: #fff;
font-weight: bold;
padding-top: 2px;
@@ -390,7 +406,7 @@ input.textSearch {
}
.ui-accordion-content ul {
list-style: none;
list-style-image: url(data:0);
list-style-image: none;
padding-left: 16px;
margin-top: 8px;
}
@@ -401,7 +417,7 @@ input.textSearch {
padding: 8px 0px 8px 8px;
margin: 0;
list-style: none;
list-style-image: url(data:0);
list-style-image: none;
border: 0;
}
.nothing {
@@ -504,7 +520,7 @@ div.actions_menu > ul {
nowidth: 70px;
padding-left: 5px;
/* Nasty work-around for IE... en attendant mieux */
background: #ea7d1e url(../images/actions_left.png?v=v2.5.2) no-repeat top left;
background: #ea7d1e url(../images/actions_left.png?v=v2.6.1) no-repeat top left;
cursor: pointer;
margin: 0;
}
@@ -516,7 +532,7 @@ div.actions_menu > ul > li {
height: 17px;
padding-right: 16px;
padding-left: 4px;
background: url(../images/actions_right.png?v=v2.5.2) no-repeat top right transparent;
background: url(../images/actions_right.png?v=v2.6.1) no-repeat top right transparent;
font-weight: bold;
color: #fff;
vertical-align: middle;
@@ -537,6 +553,9 @@ div.actions_menu > ul > li {
padding: 0;
height: 25px;
}
.itop_popup > ul > li > ul, #logOffBtn > ul > li > ul {
box-shadow: 3px 3px 5px 0px rgba(0, 0, 0, 0.5);
}
.itop_popup li a, #logOffBtn li a {
display: block;
padding: 5px 12px;
@@ -659,7 +678,7 @@ td a.dp-choose-date, a.dp-choose-date, td a.dp-choose-date:hover, a.dp-choose-da
display: block;
text-indent: -2000px;
overflow: hidden;
background: url(../images/calendar.png?v=v2.5.2) no-repeat;
background: url(../images/calendar.png?v=v2.6.1) no-repeat;
}
td a.dp-choose-date.dp-disabled, a.dp-choose-date.dp-disabled {
background-position: 0 -20px;
@@ -871,6 +890,9 @@ input.dp-applied {
left: 0px;
margin-top: -1px;
}
.search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_buttons, .search_form_handler .sf_criterion_area .sf_more_criterion .sfc_form_group .sfc_fg_buttons, .search_form_handler .sf_criterion_area .sf_button .sfc_form_group .sfc_fg_buttons, .search_form_handler .sf_criterion_area .search_form_criteria .sfm_content .sfc_fg_buttons, .search_form_handler .sf_criterion_area .sf_more_criterion .sfm_content .sfc_fg_buttons, .search_form_handler .sf_criterion_area .sf_button .sfm_content .sfc_fg_buttons {
white-space: nowrap;
}
.search_form_handler .sf_criterion_area .search_form_criteria {
/* Non editable criteria */
/* Draft criteria (modifications not applied) */
@@ -1115,6 +1137,15 @@ input.dp-applied {
.search_form_handler .sf_criterion_area .search_form_criteria.search_form_criteria_enum .sfc_form_group .sfc_fg_operator_in > label .sfc_op_content {
width: 100%;
}
.search_form_handler .sf_criterion_area .search_form_criteria.search_form_criteria_tag_set .sfc_form_group .sfc_fg_operator_in > label {
display: inline-block;
width: 100%;
line-height: initial;
white-space: nowrap;
}
.search_form_handler .sf_criterion_area .search_form_criteria.search_form_criteria_tag_set .sfc_form_group .sfc_fg_operator_in > label .sfc_op_content {
width: 100%;
}
.search_form_handler .sf_criterion_area .search_form_criteria.search_form_criteria_numeric .sfc_fg_operators .sfc_fg_operator.sfc_fg_operator_between .sfc_op_content_from_outer {
display: inline;
}
@@ -1301,19 +1332,19 @@ input.dp-applied {
}
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
table.listResults tr.odd td.truncated, table.listResults tr td.truncated, .wizContainer table.listResults tr.odd td.truncated, .wizContainer table.listResults tr td.truncated {
background: url(../images/truncated.png?v=v2.5.2) bottom repeat-x;
background: url(../images/truncated.png?v=v2.6.1) bottom repeat-x;
}
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
table.listResults tr.even td.truncated, .wizContainer table.listResults tr.even td.truncated {
background: #f9f9f1 url(../images/truncated.png?v=v2.5.2) bottom repeat-x;
background: #f9f9f1 url(../images/truncated.png?v=v2.6.1) bottom repeat-x;
}
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
table.listResults tr.even td.hover.truncated, .wizContainer table.listResults tr.even td.hover.truncated {
background: #fdf5d0 url(../images/truncated.png?v=v2.5.2) bottom repeat-x;
background: #fdf5d0 url(../images/truncated.png?v=v2.6.1) bottom repeat-x;
}
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
table.listResults tr.odd td.hover.truncated, table.listResults tr td.hover.truncated, .wizContainer table.listResults tr.odd td.hover.truncated, .wizContainer table.listResults tr td.hover.truncated {
background: #fdf5d0 url(../images/truncated.png?v=v2.5.2) bottom repeat-x;
background: #fdf5d0 url(../images/truncated.png?v=v2.6.1) bottom repeat-x;
}
table.listResults.truncated {
border-bottom: 0;
@@ -1421,7 +1452,7 @@ div#logo {
div#logo div {
height: 88px;
width: 244px;
background: url(../images/itop-logo-2.png?v=v2.5.2) left no-repeat;
background: url(../images/itop-logo-2.png?v=v2.6.1) left no-repeat;
}
#left-pane .ui-layout-north {
overflow: hidden;
@@ -1513,7 +1544,7 @@ div#logo div {
}
#global-search-image {
vertical-align: middle;
background: url(../images/search.png?v=v2.5.2) center center no-repeat;
background: url(../images/search.png?v=v2.6.1) center center no-repeat;
display: inline-block;
width: 28px;
height: 30px;
@@ -1542,7 +1573,7 @@ span.ui-icon {
margin: 0 2px;
}
.ui-layout-button-pin-down {
background: url(../images/splitter-bkg.png?v=v2.5.2) transparent;
background: url(../images/splitter-bkg.png?v=v2.6.1) transparent;
width: 16px;
background-position: -144px -144px;
}
@@ -1918,6 +1949,22 @@ fieldset .details > .field_container {
.field_container > div > div.field_value .attribute-edit .field_input_zone.field_input_extkey > .field_input_btn > img {
max-width: 20px;
}
.field_container > div > div.field_value .attribute-edit .field_input_zone.field_input_set .selectize-control {
width: 100%;
}
.field_container > div > div.field_value .attribute-edit .field_input_zone.field_input_set .selectize-control .selectize-dropdown, .field_container > div > div.field_value .attribute-edit .field_input_zone.field_input_set .selectize-control .selectize-input, .field_container > div > div.field_value .attribute-edit .field_input_zone.field_input_set .selectize-control .selectize-input input {
font-size: 12px;
}
.field_container > div > div.field_value .attribute-edit .field_input_zone.field_input_set .selectize-control .selectize-input {
padding: 2px 2px 0px 2px;
/* padding-bottom = padding-top - item margin-bottom */
border: 1px solid #ababab;
border-radius: 0;
}
.field_container > div > div.field_value .attribute-edit .field_input_zone.field_input_set .selectize-control .selectize-input .attribute-set-item.partial-code {
color: rgba(34, 34, 34, 0.6);
background-color: #eaeaea;
}
.one-col-details .details .field_container.field_small {
/* On a single column, field labels can take more width but they are limited so it doesn't feel weird when all labels are short */
}
@@ -2042,7 +2089,7 @@ img.prev, img.first, img.next, img.last {
}
div.actions_button {
float: right;
background: #ea7d1e url("../images/actions_left.png?v=v2.5.2") no-repeat scroll left top;
background: #ea7d1e url("../images/actions_left.png?v=v2.6.1") no-repeat scroll left top;
padding-left: 5px;
margin-top: 0;
margin-right: 10px;
@@ -2050,7 +2097,7 @@ div.actions_button {
vertical-align: middle;
}
div.actions_button a, .actions_button a:hover, .actions_button a:visited {
background: #ea7d1e url(../images/actions_bkg.png?v=v2.5.2) no-repeat scroll right top;
background: #ea7d1e url(../images/actions_bkg.png?v=v2.6.1) no-repeat scroll right top;
color: #fff;
padding-right: 8px;
cursor: pointer;
@@ -2074,10 +2121,10 @@ select#org_id {
cursor: not-allowed;
}
.dragHover {
background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.5.2);
background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.6.1);
}
.edit_mode .dashlet {
background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.5.2);
background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.6.1);
padding: 5px;
margin: 0;
position: relative;
@@ -2087,6 +2134,17 @@ select#org_id {
padding: 5px;
margin: 0;
}
/* Prevent cursor clicking on the calendar (eg. While editing dashlet) */
.dashlet-blocker {
position: absolute;
z-index: 9;
/* To be above calendar links & all, but below .close-box (9) */
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: not-allowed;
}
td.layout_cell {
height: 50px;
/* min-height does not work */
@@ -2111,7 +2169,7 @@ table.prop_table {
top: 0;
right: 0;
z-index: 10;
background: transparent url(../images/delete.png?v=v2.5.2) no-repeat center;
background: transparent url(../images/delete.png?v=v2.6.1) no-repeat center;
}
td.prop_value {
text-align: left;
@@ -2199,6 +2257,35 @@ a.summary, a.summary:hover {
padding: 5px;
text-align: center;
}
.dashboard-title {
display: block;
float: left;
color: #000;
font-weight: bold;
font-size: 1.2em;
margin: 10px;
}
.dashboard_contents {
width: 100%;
background-color: #fff;
}
.dashboard-selector {
display: block;
float: right;
margin-top: 10px;
}
.dashboard-selector .selector-label {
display: inline-block;
margin-left: 10px;
margin-right: 10px;
vertical-align: super;
}
#DashboardMenu {
display: block;
float: right;
font-size: 12px;
margin: 10px;
}
#DashboardMenu > ul > li {
list-style: none;
vertical-align: middle;
@@ -2301,29 +2388,19 @@ a.summary, a.summary:hover {
border-radius: 4px;
margin-bottom: 10px;
}
.header_message {
padding: 1em;
font-size: 10pt;
background: #fff;
border: 1px solid #999;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
margin-bottom: 10px;
}
.message_info {
border: 1px solid #993;
background: url(../images/info-mini.png?v=v2.5.2) 1em 1em no-repeat #ffc;
background: url(../images/info-mini.png?v=v2.6.1) 1em 1em no-repeat #ffc;
padding-left: 3em;
}
.message_ok {
border: 1px solid #393;
background: url(../images/ok.png?v=v2.5.2) 1em 1em no-repeat #cfc;
background: url(../images/ok.png?v=v2.6.1) 1em 1em no-repeat #cfc;
padding-left: 3em;
}
.message_error {
border: 1px solid #933;
background: url(../images/error.png?v=v2.5.2) 1em 1em no-repeat #fcc;
background: url(../images/error.png?v=v2.6.1) 1em 1em no-repeat #fcc;
padding-left: 3em;
}
.fg-menu a img {
@@ -2454,18 +2531,18 @@ div.explain-printable {
}
#hiddeable_chapters .ui-tabs .ui-tabs-nav li.hideable-chapter span {
padding-left: 20px;
background: url(../images/eye-open-555.png?v=v2.5.2) 2px center no-repeat;
background: url(../images/eye-open-555.png?v=v2.6.1) 2px center no-repeat;
}
#hiddeable_chapters .ui-tabs .ui-tabs-nav li.hideable-chapter.strikethrough span {
text-decoration: line-through;
background: url(../images/eye-closed-555.png?v=v2.5.2) 2px center no-repeat;
background: url(../images/eye-closed-555.png?v=v2.6.1) 2px center no-repeat;
}
.printable-version legend {
padding-left: 26px;
background: #1c94c4 url(../images/eye-open-fff.png?v=v2.5.2) 8px center no-repeat;
background: #1c94c4 url(../images/eye-open-fff.png?v=v2.6.1) 8px center no-repeat;
}
.printable-version .strikethrough legend {
background: #1c94c4 url(../images/eye-closed-fff.png?v=v2.5.2) 8px center no-repeat;
background: #1c94c4 url(../images/eye-closed-fff.png?v=v2.6.1) 8px center no-repeat;
}
.printable-version fieldset.strikethrough span {
display: none;
@@ -2588,7 +2665,6 @@ span.search-button, span.refresh-button {
margin-right: 5px;
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
filter: gray;
opacity: 0.5;
}
#itop-breadcrumb .breadcrumb-item a {
@@ -2617,7 +2693,7 @@ span.search-button, span.refresh-button {
#itop-breadcrumb .breadcrumb-item a::after {
content: '';
position: absolute;
background-image: url(../images/breadcrumb-separator.png?v=v2.5.2);
background-image: url(../images/breadcrumb-separator.png?v=v2.6.1);
background-repeat: no-repeat;
width: 8px;
height: 16px;
@@ -2841,3 +2917,271 @@ table.listResults .originColor {
#ds_flash {
margin-bottom: 5px;
}
.menu-icon-select {
max-height: 300px;
position: absolute;
z-index: 201;
overflow-x: hidden;
overflow-y: auto;
}
.menu-icon-select > .ui-menu-item {
padding: 0.3em 3%;
}
.attribute.attribute-set .attribute-set-item::after {
content: ",";
margin-right: 0.5em;
}
.attribute.attribute-set .attribute-set-item:last-of-type::after {
content: "";
margin-right: 0;
}
.attribute.attribute-set.history-added .attribute-set-item {
font-weight: bold;
}
.attribute.attribute-set.history-removed .attribute-set-item {
text-decoration: line-through;
font-style: italic;
}
.attribute-edit .attribute-set .attribute-set-item, .attribute-tag-set.attribute-set .attribute-set-item, .selectize-control > .selectize-input > .item[data-value], .selectize-control.multi > .selectize-input > .item[data-value], .selectize-control > .selectize-input > .item[data-value].active, .selectize-control.multi > .selectize-input > .item[data-value].active {
display: inline-block;
margin-right: 3px;
margin-bottom: 3px;
padding: 4px 6px;
max-width: 120px;
background: #fdfdfd none;
border-radius: 2px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15), 0 0 1px 1px #f1f1f1;
color: #222;
text-shadow: none;
vertical-align: middle;
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.attribute-edit .attribute-set .attribute-set-item::after, .attribute-tag-set.attribute-set .attribute-set-item::after, .selectize-control > .selectize-input > .item[data-value]::after, .selectize-control.multi > .selectize-input > .item[data-value]::after, .selectize-control > .selectize-input > .item[data-value].active::after, .selectize-control.multi > .selectize-input > .item[data-value].active::after {
content: "";
margin-right: 0;
}
.selectize-control > .selectize-input.has-items:after, .selectize-control.multi > .selectize-input.has-items:after {
content: "+";
display: inline-block;
font-size: 17px;
font-weight: bold;
color: #222;
}
.selectize-control > .selectize-input > .item[data-value], .selectize-control.multi > .selectize-input > .item[data-value] {
padding-right: 1.5em !important;
border: 1px solid #ddd;
}
.selectize-control > .selectize-input > .item[data-value] > .remove, .selectize-control.multi > .selectize-input > .item[data-value] > .remove {
padding-top: 0.3em;
border: none;
}
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 36px;
height: 20px;
vertical-align: baseline;
}
/* Hide default HTML checkbox */
.switch input {
display: none;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: 0.4s;
}
.slider:before {
position: absolute;
content: "";
height: 15px;
width: 15px;
left: 3px;
bottom: 3px;
background-color: white;
transition: 0.4s;
}
input:checked + .slider {
background-color: #ea7d1e;
}
input:focus + .slider {
box-shadow: 0 0 1px #ea7d1e;
}
input:checked + .slider:before {
transform: translateX(14.5px);
}
/* Rounded sliders */
.slider.round {
border-radius: 20px;
}
.slider.round:before {
border-radius: 7px;
}
#newsroom_menu li span {
display: block;
padding: 5px 12px;
text-decoration: none;
white-space: nowrap;
}
#newsroom_menu li li p {
text-align: left;
margin: 2px;
margin-left: 5px;
margin-right: 5px;
cursor: pointer;
background-color: transparent;
}
#newsroom_menu > ul > li > ul {
margin-top: 8px;
}
#newsroom_menu > ul > li > ul > li {
display: block;
border-bottom: 1px #ddd solid;
padding-top: 10px;
padding-bottom: 10px;
text-align: left;
}
#newsroom_menu > ul > li > ul > li:hover, #newsroom_menu > ul > li > ul > li:hover h1, #newsroom_menu > ul > li > ul > li:hover h2, #newsroom_menu > ul > li > ul > li:hover h3 {
background: #ea7d1e;
color: #fff;
}
#newsroom_menu li li:last-of-type {
border-bottom: 0;
}
#newsroom_menu li {
list-style: none;
cursor: pointer;
color: #000;
}
#newsroom_menu li ul {
width: 300px;
}
#top-left-newsroom-cell {
padding-right: 11px !important;
}
#newsroom_menu_icon {
position: relative;
top: 8px;
}
#newsroom_menu_counter_container {
position: relative;
top: -26px;
left: 4px;
}
#newsroom_menu_counter {
display: inline-block;
padding: 2px;
border-radius: 8px;
text-align: center;
width: 12px;
box-shadow: 1px 1px 2px 0px #777;
font-size: 8pt;
background-color: #1c94c4;
color: #fff;
cursor: pointer;
}
.newsroom_extra_messages_counter {
display: inline-block !important;
padding: 2px !important;
border-radius: 8px;
text-align: center;
font-size: 8pt;
min-width: 12px;
background-color: #808080;
color: #fff;
float: right;
margin-right: 5px;
}
.newsroom_menu_item > div > img {
float: left;
width: 32px;
max-height: 32px;
margin-top: 0;
margin-bottom: 0;
margin-left: 5px;
margin-right: 5px;
}
#newsroom_menu_dismiss_all, #newsroom_menu_show_all {
text-align: center !important;
}
#newsroom_show_all_submenu {
text-align: center;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
#newsroom_show_all_submenu > ul {
padding-top: 10px;
padding-bottom: 10px;
}
#newsroom_show_all_submenu > ul > li > ul {
margin-top: 8px;
}
#newsroom_show_all_submenu > ul > li > ul > li {
display: block;
border-bottom: 1px #ddd solid;
padding-left: 5px;
padding-top: 4px;
padding-bottom: 6px;
text-align: left;
}
#newsroom_show_all_submenu > ul > li > ul > li:hover {
background: #ea7d1e;
color: #fff;
}
#newsroom_show_all_submenu li ul {
width: 200px;
color: #000;
}
.no-padding {
padding: 0 !important;
}
.newsroom_menu_item_date {
padding: 0 !important;
margin-bottom: -10px;
font-size: 8pt;
text-align: right;
margin-top: -2px;
margin-right: 5px;
color: #fff;
}
#newsroom_display_size {
height: 1em;
width: 3em;
}
#newsroom_no_new_message p {
text-align: center !important;
cursor: default !important;
font-style: italic;
}
#newsroom_no_new_message {
color: #444 !important;
}
#newsroom_no_new_message:hover {
cursor: default !important;
color: #444 !important;
background-color: #fff !important;
}
#newsroom_menu h1 {
font-weight: 700;
font-size: 16px;
margin-bottom: 0.5em;
}
#newsroom_menu h2 {
font-weight: 700;
font-size: 14px;
margin-bottom: 0.5em;
}
#newsroom_menu h3, #newsroom_menu h4, #newsroom_menu h5 {
font-weight: 700;
font-size: 12px;
margin-bottom: 0.5em;
}

View File

@@ -116,6 +116,29 @@ table.listResults td .view-image {
}
}
table.listResults > tbody > tr.selected > * {
}
table.listResults > tbody > tr > * {
transition: background-color 400ms linear;
}
table.listResults > tbody > tr:hover > * {
cursor: pointer;
}
table.listResults > tbody > tr.selected:hover > * {
/* hover on lines is currently done toggling td.hover, and having a rule for links */
background-color: lighten($combodo-orange, 20%);
color: $text-color;
}
table.listResults > tbody > tr:hover > * {
/* hover on lines is currently done toggling td.hover, and having a rule for links */
background-color: #fdf5d0;
color: $text-color;
}
.edit-image {
.view-image {
display: inline-block;
@@ -179,6 +202,7 @@ table.listResults td .view-image {
vertical-align: middle;
max-width: 90% !important;
max-height: 90% !important;
cursor: zoom-in;
}
.helper-middle {
// Helper to center the image (requires a span dedicated to this)
@@ -225,7 +249,7 @@ tr.green td, .wizContainer tr.green td {
}
tr td.hover, tr.even td.hover, .hover a, .hover a:visited, .hover a:hover, .wizContainer tr.even td.hover, .wizContainer tr td.hover {
background-color: #fdf5d0;
//background-color: #fdf5d0;
color: $text-color;
}
@@ -457,7 +481,7 @@ input.textSearch {
.ui-accordion-content ul {
list-style:none;
list-style-image: url(data:0);
list-style-image: none;
padding-left:16px;
margin-top: 8px;
}
@@ -470,7 +494,7 @@ input.textSearch {
padding: 8px 0px 8px 8px;
margin:0;
list-style:none;
list-style-image: url(data:0);
list-style-image: none;
border: 0;
}
@@ -605,7 +629,9 @@ div.actions_menu > ul > li {
padding: 0;
height: 25px;
}
.itop_popup > ul > li > ul, #logOffBtn > ul > li > ul {
box-shadow: 3px 3px 5px 0px rgba(0,0,0,0.5);
}
.itop_popup li a, #logOffBtn li a {
display: block;
padding: 5px 12px;
@@ -974,6 +1000,10 @@ input.dp-applied {
min-width: 100%;
left: 0px;
margin-top: -1px;
.sfc_fg_buttons{
white-space: nowrap;
}
}
}
@@ -1288,6 +1318,22 @@ input.dp-applied {
}
}
}
&.search_form_criteria_tag_set{
.sfc_form_group{
.sfc_fg_operator_in{
> label{
display: inline-block;
width: 100%;
line-height: initial;
white-space: nowrap;
.sfc_op_content{
width: 100%;
}
}
}
}
}
&.search_form_criteria_numeric {
//.sfc_form_group.advanced {
// .sfc_fg_operator_between {
@@ -2222,6 +2268,28 @@ fieldset .details>.field_container {
}
}
}
&.field_input_set{
.selectize-control{
width: 100%;
.selectize-dropdown,
.selectize-input,
.selectize-input input{
font-size: 12px;
}
.selectize-input{
padding: 2px 2px 0px 2px; /* padding-bottom = padding-top - item margin-bottom */
border: 1px solid #ABABAB;
border-radius: 0;
.attribute-set-item.partial-code{
color: transparentize($gray-darker, 0.4);
background-color: lighten($gray-lighter, 5%);
}
}
}
}
}
}
}
@@ -2410,6 +2478,16 @@ select#org_id {
padding: 5px;
margin:0;
}
/* Prevent cursor clicking on the calendar (eg. While editing dashlet) */
.dashlet-blocker {
position: absolute;
z-index: 9; /* To be above calendar links & all, but below .close-box (9) */
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: not-allowed;
}
td.layout_cell {
height: 50px; /* min-height does not work */
vertical-align: top;
@@ -2526,6 +2604,48 @@ a.summary, a.summary:hover {
text-align: center;
}
.dashboard-title-line {
}
.dashboard-title {
display: block;
float: left;
color: $text-color;
font-weight: bold;
font-size: 1.2em;
margin: 10px;
}
.dashboard_contents {
width: 100%;
background-color: $white;
}
.dashboard-selector {
display: block;
float: right;
margin-top: 10px;
.selector-label {
display: inline-block;
margin-left: 10px;
margin-right: 10px;
vertical-align: super;
}
}
#DashboardMenu {
display: block;
float: right;
font-size: 12px;
margin: 10px;
}
#DashboardMenu > ul > li {
list-style: none;
vertical-align: middle;
@@ -2632,16 +2752,6 @@ a.summary, a.summary:hover {
border-radius: 4px;
margin-bottom: 10px;
}
.header_message {
padding: 1em;
font-size: 10pt;
background: #fff;
border: 1px solid #999;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
margin-bottom: 10px;
}
.message_info {
border: 1px solid #993;
background: url(../images/info-mini.png?v=#{$version}) 1em 1em no-repeat #ffc;
@@ -2934,7 +3044,6 @@ span.search-button, span.refresh-button {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
filter: gray;
// IE has no filter option: at least, have some effect when hovering...
opacity: 0.5;
@@ -3231,4 +3340,334 @@ table.listResults .originColor{
//Impact analysis filter
#ds_flash{
margin-bottom: 5px;
}
}
/////////
// Icon select
.menu-icon-select{
max-height : 300px;
position : absolute;
z-index : 201;
overflow-x : hidden;
overflow-y : auto;
}
.menu-icon-select > .ui-menu-item{
padding: .3em 3%;
}
//////////////////////
// Set attribute //
// - Readonly (object viewing, objects list)
.attribute {
&.attribute-set {
.attribute-set-item{
&::after{
content: ",";
margin-right: 0.5em;
}
&:last-of-type::after{
content: "";
margin-right: 0;
}
}
&.history-added {
.attribute-set-item {
font-weight: bold;
}
}
&.history-removed {
.attribute-set-item {
text-decoration: line-through;
font-style: italic;
}
}
}
}
// - Edit is always styled like the selectize items, see below.
%attribute-set-item-edition{
display: inline-block;
margin-right: 3px;
margin-bottom: 3px;
padding: 4px 6px;
max-width: 120px;
background: #fdfdfd none;
border-radius: 2px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15), 0 0 1px 1px rgb(241, 241, 241, 0.7);
color: $gray-darker;
text-shadow: none;
vertical-align: middle;
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&::after{
content: "";
margin-right: 0;
}
}
.attribute-edit .attribute-set .attribute-set-item{
@extend %attribute-set-item-edition;
}
//////////////////////
// TagSet attribute //
// Always styled like the selectize items, see below.
.attribute-tag-set.attribute-set{
.attribute-set-item{
@extend %attribute-set-item-edition;
}
}
//////////////////////
// Selectize widget //
//
.selectize-control,
.selectize-control.multi{
> .selectize-input{
&.has-items:after {
content: "+";
display: inline-block;
font-size: 17px;
font-weight: bold;
color: $gray-darker;
}
> .item[data-value]{
@extend %attribute-set-item-edition;
padding-right: 1.5em !important;
border: 1px solid $gray-lighter;
&.active{
@extend %attribute-set-item-edition;
}
> .remove {
padding-top: 0.3em;
border: none;
}
}
}
}
// Round Toggle
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 36px;
height: 20px;
vertical-align: baseline;
}
/* Hide default HTML checkbox */
.switch input {display:none;}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 15px;
width: 15px;
left: 3px;
bottom: 3px;
background-color: white;
transition: .4s;
}
input:checked + .slider {
background-color: $combodo-orange;
}
input:focus + .slider {
box-shadow: 0 0 1px $combodo-orange;
}
input:checked + .slider:before {
transform: translateX(14.5px);
}
/* Rounded sliders */
.slider.round {
border-radius: 20px;
}
.slider.round:before {
border-radius: 7px;
}
///////////////////////////
// Newsroom menu
#newsroom_menu li span {
display: block;
padding: 5px 12px;
text-decoration: none;
white-space: nowrap;
}
#newsroom_menu li li p {
text-align: left;
margin: 2px;
margin-left: 5px;
margin-right: 5px;
cursor: pointer;
background-color: transparent;
}
#newsroom_menu > ul > li > ul {
margin-top: 8px;
}
#newsroom_menu > ul > li > ul > li {
display: block;
border-bottom: 1px #ddd solid;
padding-top: 10px;
padding-bottom: 10px;
text-align: left;
}
#newsroom_menu > ul > li > ul > li:hover, #newsroom_menu > ul > li > ul > li:hover h1, #newsroom_menu > ul > li > ul > li:hover h2, #newsroom_menu > ul > li > ul > li:hover h3 {
background: $popup-menu-highlight-color;
color: $popup-menu-text-higlight-color;
}
#newsroom_menu li li:last-of-type {
border-bottom: 0;
}
#newsroom_menu li {
list-style: none;
cursor: pointer;
color: $popup-menu-text-color;
}
#newsroom_menu li ul {
width: 300px;
}
#top-left-newsroom-cell {
padding-right: 11px !important;
}
#newsroom_menu_icon {
position:relative;
top:8px;
}
#newsroom_menu_counter_container {
position:relative;
top:-26px;
left:4px;
}
#newsroom_menu_counter {
display:inline-block;
padding:2px;
border-radius:8px;
text-align:center;
width: 12px;
box-shadow: 1px 1px 2px 0px $gray;
font-size: 8pt;
background-color:$complement-color;
color: $white;
cursor: pointer;
}
.newsroom_extra_messages_counter {
display:inline-block !important;
padding:2px !important;
border-radius:8px;
text-align:center;
font-size: 8pt;
min-width: 12px;
background-color:$gray-light;
color: $white;
float: right;
margin-right: 5px;
}
.newsroom_menu_item > div > img {
float:left;
width:32px;
max-height:32px;
margin-top: 0;
margin-bottom: 0;
margin-left:5px;
margin-right: 5px;
}
#newsroom_menu_dismiss_all, #newsroom_menu_show_all {
text-align: center !important;
}
#newsroom_show_all_submenu {
text-align: center;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
#newsroom_show_all_submenu > ul {
padding-top: 10px;
padding-bottom: 10px;
}
#newsroom_show_all_submenu > ul > li > ul {
margin-top: 8px;
}
#newsroom_show_all_submenu > ul > li > ul > li {
display: block;
border-bottom: 1px #ddd solid;
padding-left: 5px;
padding-top: 4px;
padding-bottom: 6px;
text-align: left;
}
#newsroom_show_all_submenu > ul > li > ul > li:hover {
background: $popup-menu-highlight-color;
color: $popup-menu-text-higlight-color;
}
#newsroom_show_all_submenu li ul {
width: 200px;
color:#000;
}
.no-padding {
padding: 0 !important;
}
.newsroom_menu_item_date {
padding: 0 !important;
margin-bottom: -10px;
font-size: 8pt;
text-align: right;
margin-top: -2px;
margin-right: 5px;
color: $white;
}
#newsroom_display_size {
height: 1em;
width: 3em;
}
#newsroom_no_new_message p {
text-align: center !important;
cursor: default !important;
font-style: italic;
}
#newsroom_no_new_message {
color: $gray-dark !important;
}
#newsroom_no_new_message:hover {
cursor: default !important;
color: $gray-dark !important;
background-color: $white !important;
}
#newsroom_menu h1 {
font-weight: 700;
font-size: 16px;
margin-bottom: 0.5em;
}
#newsroom_menu h2 {
font-weight: 700;
font-size: 14px;
margin-bottom: 0.5em;
}
#newsroom_menu h3, #newsroom_menu h4, #newsroom_menu h5 {
font-weight: 700;
font-size: 12px;
margin-bottom: 0.5em;
}

View File

@@ -16,12 +16,21 @@ div.graph_config { display:none; }
h2.printable-tab-title {
page-break-after: avoid;
}
h1 {
page-break-after: avoid;
}
div#tabbedContent_0 { border:none; }
p a, .ui-widget-content td a, p a:hover, .ui-widget-content td a:hover, p a:visited, .ui-widget-content td a:visited, td a, td a:visited, td a:hover {
padding-left: 0;
background: transparent;
}
body { margin:none; }
body {
margin:10px;
}
@page {
margin: 1cm;
}
.printable-tab {
-webkit-region-break-inside: avoid;
page-break-inside: avoid;

394
css/selectize.default.css Normal file
View File

@@ -0,0 +1,394 @@
/**
* selectize.default.css (v0.12.4) - Default Theme
* Copyright (c) 20132015 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
visibility: visible !important;
background: #f2f2f2 !important;
background: rgba(0, 0, 0, 0.06) !important;
border: 0 none !important;
-webkit-box-shadow: inset 0 0 12px 4px #ffffff;
box-shadow: inset 0 0 12px 4px #ffffff;
}
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
content: '!';
visibility: hidden;
}
.selectize-control.plugin-drag_drop .ui-sortable-helper {
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.selectize-dropdown-header {
position: relative;
padding: 5px 8px;
border-bottom: 1px solid #d0d0d0;
background: #f8f8f8;
-webkit-border-radius: 3px 3px 0 0;
-moz-border-radius: 3px 3px 0 0;
border-radius: 3px 3px 0 0;
}
.selectize-dropdown-header-close {
position: absolute;
right: 8px;
top: 50%;
color: #303030;
opacity: 0.4;
margin-top: -12px;
line-height: 20px;
font-size: 20px !important;
}
.selectize-dropdown-header-close:hover {
color: #000000;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup {
border-right: 1px solid #f2f2f2;
border-top: 0 none;
float: left;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
border-right: 0 none;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
display: none;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
border-top: 0 none;
}
.selectize-control.plugin-remove_button [data-value] {
position: relative;
padding-right: 24px !important;
}
.selectize-control.plugin-remove_button [data-value] .remove {
z-index: 1;
/* fixes ie bug (see #392) */
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 17px;
text-align: center;
font-weight: bold;
font-size: 12px;
color: inherit;
text-decoration: none;
vertical-align: middle;
display: inline-block;
padding: 2px 0 0 0;
border-left: 1px solid #0073bb;
-webkit-border-radius: 0 2px 2px 0;
-moz-border-radius: 0 2px 2px 0;
border-radius: 0 2px 2px 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-control.plugin-remove_button [data-value] .remove:hover {
background: rgba(0, 0, 0, 0.05);
}
.selectize-control.plugin-remove_button [data-value].active .remove {
border-left-color: #00578d;
}
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
background: none;
}
.selectize-control.plugin-remove_button .disabled [data-value] .remove {
border-left-color: #aaaaaa;
}
.selectize-control.plugin-remove_button .remove-single {
position: absolute;
right: 28px;
top: 6px;
font-size: 23px;
}
.selectize-control {
position: relative;
}
.selectize-dropdown,
.selectize-input,
.selectize-input input {
color: #303030;
font-family: inherit;
font-size: 13px;
line-height: 18px;
-webkit-font-smoothing: inherit;
}
.selectize-input,
.selectize-control.single .selectize-input.input-active {
background: #ffffff;
cursor: text;
display: inline-block;
}
.selectize-input {
border: 1px solid #d0d0d0;
padding: 8px 8px;
display: inline-block;
width: 100%;
overflow: hidden;
position: relative;
z-index: 1;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.selectize-control.multi .selectize-input.has-items {
padding: 5px 8px 2px;
}
.selectize-input.full {
background-color: #ffffff;
}
.selectize-input.disabled,
.selectize-input.disabled * {
cursor: default !important;
}
.selectize-input.focus {
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
}
.selectize-input.dropdown-active {
-webkit-border-radius: 3px 3px 0 0;
-moz-border-radius: 3px 3px 0 0;
border-radius: 3px 3px 0 0;
}
.selectize-input > * {
vertical-align: baseline;
display: -moz-inline-stack;
display: inline-block;
zoom: 1;
*display: inline;
}
.selectize-control.multi .selectize-input > div {
cursor: pointer;
margin: 0 3px 3px 0;
padding: 2px 6px;
background: #1da7ee;
color: #ffffff;
border: 1px solid #0073bb;
}
.selectize-control.multi .selectize-input > div.active {
background: #92c836;
color: #ffffff;
border: 1px solid #00578d;
}
.selectize-control.multi .selectize-input.disabled > div,
.selectize-control.multi .selectize-input.disabled > div.active {
color: #ffffff;
background: #d2d2d2;
border: 1px solid #aaaaaa;
}
.selectize-input > input {
display: inline-block !important;
padding: 0 !important;
min-height: 0 !important;
max-height: none !important;
max-width: 100% !important;
margin: 0 1px !important;
text-indent: 0 !important;
border: 0 none !important;
background: none !important;
line-height: inherit !important;
-webkit-user-select: auto !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
.selectize-input > input::-ms-clear {
display: none;
}
.selectize-input > input:focus {
outline: none !important;
}
.selectize-input::after {
content: ' ';
display: block;
clear: left;
}
.selectize-input.dropdown-active::before {
content: ' ';
display: block;
position: absolute;
background: #f0f0f0;
height: 1px;
bottom: 0;
left: 0;
right: 0;
}
.selectize-dropdown {
position: absolute;
z-index: 10;
border: 1px solid #d0d0d0;
background: #ffffff;
margin: -1px 0 0 0;
border-top: 0 none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-webkit-border-radius: 0 0 3px 3px;
-moz-border-radius: 0 0 3px 3px;
border-radius: 0 0 3px 3px;
}
.selectize-dropdown [data-selectable] {
cursor: pointer;
overflow: hidden;
}
.selectize-dropdown [data-selectable] .highlight {
background: rgba(125, 168, 208, 0.2);
-webkit-border-radius: 1px;
-moz-border-radius: 1px;
border-radius: 1px;
}
.selectize-dropdown [data-selectable],
.selectize-dropdown .optgroup-header {
padding: 5px 8px;
}
.selectize-dropdown .optgroup:first-child .optgroup-header {
border-top: 0 none;
}
.selectize-dropdown .optgroup-header {
color: #303030;
background: #ffffff;
cursor: default;
}
.selectize-dropdown .active {
background-color: #f5fafd;
color: #495c68;
}
.selectize-dropdown .active.create {
color: #495c68;
}
.selectize-dropdown .create {
color: rgba(48, 48, 48, 0.5);
}
.selectize-dropdown-content {
overflow-y: auto;
overflow-x: hidden;
max-height: 200px;
-webkit-overflow-scrolling: touch;
}
.selectize-control.single .selectize-input,
.selectize-control.single .selectize-input input {
cursor: pointer;
}
.selectize-control.single .selectize-input.input-active,
.selectize-control.single .selectize-input.input-active input {
cursor: text;
}
.selectize-control.single .selectize-input:after {
content: ' ';
display: block;
position: absolute;
top: 50%;
right: 15px;
margin-top: -3px;
width: 0;
height: 0;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #808080 transparent transparent transparent;
}
.selectize-control.single .selectize-input.dropdown-active:after {
margin-top: -4px;
border-width: 0 5px 5px 5px;
border-color: transparent transparent #808080 transparent;
}
.selectize-control.rtl.single .selectize-input:after {
left: 15px;
right: auto;
}
.selectize-control.rtl .selectize-input > input {
margin: 0 4px 0 -2px !important;
}
.selectize-control .selectize-input.disabled {
opacity: 0.5;
background-color: #fafafa;
}
.selectize-control.multi .selectize-input.has-items {
padding-left: 5px;
padding-right: 5px;
}
.selectize-control.multi .selectize-input.disabled [data-value] {
color: #999;
text-shadow: none;
background: none;
-webkit-box-shadow: none;
box-shadow: none;
}
.selectize-control.multi .selectize-input.disabled [data-value],
.selectize-control.multi .selectize-input.disabled [data-value] .remove {
border-color: #e6e6e6;
}
.selectize-control.multi .selectize-input.disabled [data-value] .remove {
background: none;
}
.selectize-control.multi .selectize-input [data-value] {
text-shadow: 0 1px 0 rgba(0, 51, 83, 0.3);
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background-color: #1b9dec;
background-image: -moz-linear-gradient(top, #1da7ee, #178ee9);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1da7ee), to(#178ee9));
background-image: -webkit-linear-gradient(top, #1da7ee, #178ee9);
background-image: -o-linear-gradient(top, #1da7ee, #178ee9);
background-image: linear-gradient(to bottom, #1da7ee, #178ee9);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1da7ee', endColorstr='#ff178ee9', GradientType=0);
-webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
}
.selectize-control.multi .selectize-input [data-value].active {
background-color: #0085d4;
background-image: -moz-linear-gradient(top, #008fd8, #0075cf);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#008fd8), to(#0075cf));
background-image: -webkit-linear-gradient(top, #008fd8, #0075cf);
background-image: -o-linear-gradient(top, #008fd8, #0075cf);
background-image: linear-gradient(to bottom, #008fd8, #0075cf);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff008fd8', endColorstr='#ff0075cf', GradientType=0);
}
.selectize-control.single .selectize-input {
-webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
background-color: #f9f9f9;
background-image: -moz-linear-gradient(top, #fefefe, #f2f2f2);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f2f2f2));
background-image: -webkit-linear-gradient(top, #fefefe, #f2f2f2);
background-image: -o-linear-gradient(top, #fefefe, #f2f2f2);
background-image: linear-gradient(to bottom, #fefefe, #f2f2f2);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffefefe', endColorstr='#fff2f2f2', GradientType=0);
}
.selectize-control.single .selectize-input,
.selectize-dropdown.single {
border-color: #b8b8b8;
}
.selectize-dropdown .optgroup-header {
padding-top: 7px;
font-weight: bold;
font-size: 0.85em;
}
.selectize-dropdown .optgroup {
border-top: 1px solid #f0f0f0;
}
.selectize-dropdown .optgroup:first-child {
border-top: 0 none;
}

View File

@@ -39,7 +39,3 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Class:UserExternal' => 'Externí uživatel',
'Class:UserExternal+' => 'Uživatel definovaný mimo iTop',
));
?>

View File

@@ -24,7 +24,3 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'Class:UserExternal' => 'Extern Bruger',
'Class:UserExternal+' => 'Bruger udenfor iTop',
));
?>

View File

@@ -26,7 +26,3 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:UserExternal' => 'Externer Benutzer',
'Class:UserExternal+' => 'Benutzer außerhalb von iTop',
));
?>

View File

@@ -1,27 +1,24 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Localized data
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
// Dictionnay conventions
@@ -42,7 +39,3 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:UserExternal' => 'External user',
'Class:UserExternal+' => 'User authentified outside of iTop',
));
?>

View File

@@ -38,7 +38,3 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:UserExternal' => 'Usuario Externo',
'Class:UserExternal+' => 'Usuario Autenticado fuera de iTop',
));
?>

View File

@@ -23,7 +23,3 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:UserExternal' => 'Utilisateur externe à iTop',
'Class:UserExternal+' => 'Utilisateur authentifié à l\'extérieur d\'iTop',
));
?>

View File

@@ -23,7 +23,3 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Class:UserExternal' => 'Külső felhasználó',
'Class:UserExternal+' => '',
));
?>

View File

@@ -37,7 +37,3 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:UserExternal' => 'Esterno utente',
'Class:UserExternal+' => 'Utente autenticato al di fuori di iTop',
));
?>

View File

@@ -24,7 +24,3 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'Class:UserExternal' => '外部ユーザー',
'Class:UserExternal+' => '外部認証ユーザー',
));
?>

View File

@@ -27,7 +27,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-external/2.5.2',
'authent-external/2.6.1',
array(
// Identification
//

Some files were not shown because too many files have changed in this diff Show More