Compare commits

..

52 Commits
2.5.0 ... 2.5.1

Author SHA1 Message Date
Molkobain
a37698a9de Fix regression introduced in commit 10b7fa6 (Web queries have issue with line breaks in cell value) 2018-10-17 13:04:57 +02:00
steffunky
11ba459d1b N°1590: Advanced search: fixed a bug when selecting foreign keys would not add items (selectionMode is mandatory) 2018-10-12 10:48:32 +02:00
Molkobain
215786d740 Update readme.txt for iTop 2.5.1 2018-10-10 14:40:08 +02:00
Molkobain
fe9ca2b9cc Update readme.txt for iTop 2.5.1 2018-10-10 14:38:18 +02:00
steffunky
41a479b48d 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:30:12 +02:00
Pierre Goiffon
eb630efdf5 N°1658 update advanced search german translation (UI:Search:NoAutoSubmit:ExplainText) 2018-10-09 18:12:37 +02:00
Pierre Goiffon
91b7130671 N°1556 Advanced Search : fix removing only criteria in first row (was generating "AND 1") 2018-10-09 18:01:31 +02:00
Pierre Goiffon
80be4b4371 idea files improvements
- use Combodo preset
- JS code style (use tab for indentation, newlines in if/else statements)
2018-10-09 18:01:31 +02:00
Denis Flaven
21d7de7d8d Use a longer timeout to let the IFRAME load itself and inform iTop Hub
of the updated configuration. (Bug N°1672)
2018-10-09 17:36:30 +02:00
Molkobain
4a431be0a9 (Cherry picked from commit 5099060) N°1203 : Correctly use 'from' field for test email notifications 2018-10-08 17:43:55 +02:00
Molkobain
b84ac80aaa (Cherry pick from develop ab1715e) N°1576 Portal: Security hardening (missed one file in previous commit). 2018-10-08 13:05:45 +02:00
Eric
b6d0843e55 Fix dashboard edition when a bad OQL is present in dashlet 'Group By' 2018-10-04 14:33:29 +02:00
Molkobain
396fc2cdcd N°1658 Fix hard-coded translation in search page when the form has not been automatically submitted. 2018-10-03 17:43:06 +02:00
Molkobain
11308dc76a N°1059 Fix new empty caselog entry on bulk modification. 2018-10-03 17:24:08 +02:00
Molkobain
7fddd6acdc N°1624 Fix bulk transition integrity exception when "org_id" was not checked. 2018-10-03 15:08:54 +02:00
Molkobain
10b7fa6014 N°1647 Fix Excel web queries import. (JS script error popups) 2018-10-03 10:16:15 +02:00
Molkobain
cbe749af13 N°1645 Fix reset password dictionary entries (hyperlink had wrongly escaped characters) 2018-10-02 16:48:58 +02:00
Molkobain
c5f3598f4e N°1652 Fix broken search form when user has no read right on objects. 2018-10-02 16:40:26 +02:00
Molkobain
12e9e453d8 N°1644 Fix PHP 7.2 compatibility issue (count() on none array) 2018-10-02 14:57:51 +02:00
Stephen Abello
6e50a1c4be (Retrofit from develop 7d37b065) Form prefill: add possibility to change attributes flag on the fly 2018-10-02 11:11:46 +02:00
Guillaume Lajarige
e3e416b467 (Retrofit from deveop ab1715ed) N°1576 Portal: Security hardening. 2018-10-02 11:08:49 +02:00
Guillaume Lajarige
fcb6a4069a (Retrofit from develop 526d4c4d) N°1575 Portal: Security hardening. 2018-10-02 11:06:27 +02:00
Stephen Abello
02f83c4f52 (Retrofit from develop 9db47428) N°1559: Fixed external attributes selection on export form 2018-10-02 10:57:49 +02:00
Guillaume Lajarige
452c6c3df6 (Retrofit from develop 3d1ccf20) N°1566 Fix security message in the browser console ("Unsafe attempt to load URL data:image/svg+xml;utf8") 2018-10-02 10:56:19 +02:00
Guillaume Lajarige
2966efcfde (Retrofit from develop af43e22f) N°1578 Fix "Run Query" page hotkeys behavior in some configurations due to a wrong url. 2018-10-02 10:55:04 +02:00
Guillaume Lajarige
1cf36a5d01 (Retrofit from develop 5e1452f9) N°1580 Portal: Default image of image attributes not correctly displayed in object forms. 2018-10-02 10:52:22 +02:00
Molkobain
1973f7526e Update .gitignore to match the one from develop 2018-10-02 10:48:32 +02:00
Guillaume Lajarige
866dfe4531 (Retrofit from develop 07056613) N°1611 Fix "UTF-8 Characters Malformed" error due to wrong file encoding. 2018-10-02 10:38:27 +02:00
Molkobain
f617cb556b Merge branch 'support/2.5' of https://github.com/Combodo/iTop into support/2.5 2018-10-02 10:21:13 +02:00
bruno DA SILVA
a7000b2582 [WIP] jenkinsfile integration
cherry picked from commits :
79157c465a
e473c46dc3
148309245b
5dc39fe068
47c7a3c5e3
85b7e86e58
3fca465f1d
7955dd86f4
bef4ac83a4
bde83fc705
2018-09-26 09:00:25 +02:00
Eric
27332931d0 N°1626 - Error 500 when returning from a move to test 2018-09-17 11:42:32 +02:00
Eric
36e32b23e2 N°1585: Fix request uri too long (search form) 2018-09-11 09:36:10 +02:00
Eric
b9708c8d37 N°1585: Fix ajax request uri too long on auto-complete 2018-09-05 14:47:20 +02:00
Eric
10683d943f N°1620: Fix 'forgot your password?' link 2018-09-03 16:50:30 +02:00
Molkobain
9ad8c6a96d Add .idea project files 2018-09-03 15:53:00 +02:00
Molkobain
c74d9bbafb Add .gitignore 2018-09-03 15:51:43 +02:00
Eric Espié
22df5d09fb Retrofit from trunk
N°1595 - Setup : Blocking error on backup failure [from revision 6010]

SVN:2.5[6023]
2018-08-28 12:14:56 +00:00
Pierre Goiffon
931593a59e (Retrofit from trunk) N°1572 Add composer.json for PHP language level (r5967)
SVN:2.5[6007]
2018-08-17 08:42:41 +00:00
Stephen Abello
7092dc6f86 (Retrofit from trunk) German Translation: typos (UserRequest #18704)
SVN:2.5[6002]
2018-08-08 10:12:43 +00:00
Pierre Goiffon
c3f80a5876 (Retrofit from trunk) 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 (r5991)

SVN:2.5[5992]
2018-08-01 09:13:15 +00:00
Pierre Goiffon
821eb4df8c (Retrofit from trunk) PHP 7.2 compatibility: fix another count(null) (r5988)
SVN:2.5[5989]
2018-07-30 10:31:22 +00:00
Pierre Goiffon
e276587fdc N°931 change iTop 2.6 MySQL requirements from 5.5.3 to 5.6 (to have fulltext on InnoDB)
SVN:2.5[5978]
2018-07-25 07:57:31 +00:00
Eric Espié
a454a43111 Retrofit from trunk
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.
[from revision 5951]

SVN:2.5[5956]
2018-07-19 08:17:46 +00:00
Eric Espié
045f58144e Retrofit from trunk
N°1553 - Search: Fix operator on indexed attributes.
It was previously always forced to '=', now it's only defaulted to '='
[from revision 5950]

SVN:2.5[5955]
2018-07-19 08:14:26 +00:00
Eric Espié
8ffea22f0e Retrofit from trunk
N°1561 - Fix auto-complete error when the friendlyname depends on other classes
[from revision 5948]

SVN:2.5[5954]
2018-07-19 08:10:32 +00:00
Eric Espié
c630676792 Retrofit from trunk
N°1555 - Search: ExternalField label not displayed.
The search is not restricted for external fields anymore.
[from revision 5945]

SVN:2.5[5953]
2018-07-19 08:06:39 +00:00
Eric Espié
0bbb586094 Retrofit from trunk
Advanced search: Fix an error when using search form from an union.
[from revision 5940]

SVN:2.5[5944]
2018-07-17 12:23:27 +00:00
Eric Espié
efa1f4ee43 Retrofit from trunk
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.
[from revision 5939]

SVN:2.5[5943]
2018-07-17 12:22:09 +00:00
Eric Espié
e184eb6aae Retrofit from trunk
DBObject->GetOriginal() hardening (now support attributes not set: for example sla_tto_passed for UserRequest until it is closed)
[from revision 5932]

SVN:2.5[5942]
2018-07-17 12:19:26 +00:00
Pierre Goiffon
6e9fcb81f0 (Retrofit from trunk) Fix setup for PHP 5.5 (cannot use expression as default field value) (r5933)
SVN:2.5[5934]
2018-07-12 07:46:36 +00:00
Denis Flaven
774ecb4003 (retrofit from trunk) 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:2.5[5931]
2018-07-05 13:02:10 +00:00
Guillaume Lajarige
c555d1274b Creating SVN branch for iTop 2.5
SVN:2.5[5920]
2018-06-28 08:22:50 +00:00
76 changed files with 6691 additions and 5137 deletions

122
.gitignore vendored Normal file
View File

@@ -0,0 +1,122 @@
/toolkit/
/conf/*
/env-*/*
# composer reserver directory, from sources, populate/update using "composer install"
vendor/*
test/vendor/*
# all datas but listing prevention
data/*
!data/.htaccess
!data/index.php
!data/web.config
# iTop extensions
extensions/*
!extensions/readme.txt
# all logs but listing prevention
log/*
!log/.htaccess
!log/index.php
!log/web.config
# Jetbrains
.idea/**
!.idea/encodings.xml
!.idea/codeStyles
!.idea/codeStyles/*
!.idea/inspectionProfiles
!.idea/inspectionProfiles/*
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
### Eclipse template
.metadata
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet

45
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,45 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<PHPCodeStyleSettings>
<option name="CONCAT_SPACES" value="false" />
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
<option name="PHPDOC_WRAP_LONG_LINES" value="true" />
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
<option name="LOWER_CASE_NULL_CONST" value="true" />
<option name="BLANK_LINES_BEFORE_RETURN_STATEMENT" value="1" />
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
<option name="PHPDOC_USE_FQCN" value="true" />
</PHPCodeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="BRACE_STYLE" value="2" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="PHP">
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
<option name="BRACE_STYLE" value="2" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_WRAP" value="5" />
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Combodo" />
</state>
</component>

6
.idea/encodings.xml generated Normal file
View File

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

View File

@@ -0,0 +1,154 @@
<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>
<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>
<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>
<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

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -x
# create target dirs
mkdir -p var
mkdir -p toolkit
# cleanup target dirs
rm -rf toolkit/*
# fill target dirs
curl http://www.combodo.com/documentation/iTopDataModelToolkit-2.3.zip | tar xvz --directory toolkit
cp -r .jenkins/configuration/default-environment/unattended_install/* toolkit

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -x
# on the root dir
composer install
# under the test dir
cd test
composer install

13
.jenkins/bin/init/debug.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -x
whoami
pwd
ls
echo "$BRANCH_NAME:${BRANCH_NAME}"
echo "printenv :"
printenv

8
.jenkins/bin/tests/phpunit.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -x
cd test
export DEBUG_UNIT_TEST="0"
php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --teamcity

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -x
cd toolkit
php unattended_install.php default-params.xml

View File

@@ -0,0 +1,285 @@
<?php
/**
*
* Configuration file, generated by the iTop configuration wizard
*
* The file is used in MetaModel::LoadConfig() which does all the necessary initialization job
*
*/
$MySettings = array(
// access_message: Message displayed to the users when there is any access restriction
// default: 'iTop is temporarily frozen, please wait... (the admin team)'
'access_message' => 'iTop is temporarily frozen, please wait... (the admin team)',
// access_mode: Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3
// default: 3
'access_mode' => 3,
'allowed_login_types' => 'form|basic|external',
// apc_cache.enabled: If set, the APC cache is allowed (the PHP extension must also be active)
// default: true
'apc_cache.enabled' => true,
// apc_cache.query_ttl: Time to live set in APC for the prepared queries (seconds - 0 means no timeout)
// default: 3600
'apc_cache.query_ttl' => 3600,
// app_root_url: Root URL used for navigating within the application, or from an email to the application (you can put $SERVER_NAME$ as a placeholder for the server's name)
// default: ''
'app_root_url' => 'http://127.0.0.1/itop/svn/trunk/',
// buttons_position: Position of the forms buttons: bottom | top | both
// default: 'both'
'buttons_position' => 'both',
// cas_include_path: The path where to find the phpCAS library
// default: '/usr/share/php'
'cas_include_path' => '/usr/share/php',
// cron_max_execution_time: Duration (seconds) of the page cron.php, must be shorter than php setting max_execution_time and shorter than the web server response timeout
// default: 600
'cron_max_execution_time' => 600,
// csv_file_default_charset: Character set used by default for downloading and uploading data as a CSV file. Warning: it is case sensitive (uppercase is preferable).
// default: 'ISO-8859-1'
'csv_file_default_charset' => 'ISO-8859-1',
'csv_import_charsets' => array (
),
// csv_import_history_display: Display the history tab in the import wizard
// default: false
'csv_import_history_display' => false,
// date_and_time_format: Format for date and time display (per language)
// default: array (
// 'default' =>
// array (
// 'date' => 'Y-m-d',
// 'time' => 'H:i:s',
// 'date_time' => '$date $time',
// ),
// )
'date_and_time_format' => array (
'default' =>
array (
'date' => 'Y-m-d',
'time' => 'H:i:s',
'date_time' => '$date $time',
),
'FR FR' =>
array (
'date' => 'd/m/Y',
'time' => 'H:i:s',
'date_time' => '$date $time',
),
),
'db_host' => '',
'db_name' => 'itop_ci_main',
'db_pwd' => 'c8mb0do',
'db_subname' => '',
'db_user' => 'root',
// deadline_format: The format used for displaying "deadline" attributes: any string with the following placeholders: $date$, $difference$
// default: '$difference$'
'deadline_format' => '$difference$',
'default_language' => 'EN US',
// disable_attachments_download_legacy_portal: Disable attachments download from legacy portal
// default: true
'disable_attachments_download_legacy_portal' => true,
// draft_attachments_lifetime: Lifetime (in seconds) of drafts' attachments and inline images: after this duration, the garbage collector will delete them.
// default: 3600
'draft_attachments_lifetime' => 3600,
// email_asynchronous: If set, the emails are sent off line, which requires cron.php to be activated. Exception: some features like the email test utility will force the serialized mode
// default: false
'email_asynchronous' => false,
// email_default_sender_address: Default address provided in the email from header field.
// default: ''
'email_default_sender_address' => '',
// email_default_sender_label: Default label provided in the email from header field.
// default: ''
'email_default_sender_label' => '',
// email_transport: Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocole)
// default: 'PHPMail'
'email_transport' => 'SMTP',
// email_transport_smtp.host: host name or IP address (optional)
// default: 'localhost'
'email_transport_smtp.host' => 'smtp.combodo.com',
// email_transport_smtp.password: Authentication password (optional)
// default: ''
'email_transport_smtp.password' => '++combodo++',
// email_transport_smtp.port: port number (optional)
// default: 25
'email_transport_smtp.port' => 25,
// email_transport_smtp.username: Authentication user (optional)
// default: ''
'email_transport_smtp.username' => 'test2@combodo.com',
// email_validation_pattern: Regular expression to validate/detect the format of an eMail address
// default: '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}'
'email_validation_pattern' => '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}',
'encryption_key' => '@iT0pEncr1pti0n!',
'ext_auth_variable' => '$_SERVER[\'REMOTE_USER\']',
'fast_reload_interval' => '60',
// graphviz_path: Path to the Graphviz "dot" executable for graphing objects lifecycle
// default: '/usr/bin/dot'
'graphviz_path' => '/usr/bin/dot',
// inline_image_max_display_width: The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.
// default: '250'
'inline_image_max_display_width' => 250,
// inline_image_max_storage_width: The maximum width (in pixels) when uploading images to be used inside an HTML formatted attribute. Images larger than the given size will be downsampled before storing them in the database.
// default: '1600'
'inline_image_max_storage_width' => 1600,
// link_set_attribute_qualifier: Link set from string: attribute qualifier (encloses both the attcode and the value)
// default: '\''
'link_set_attribute_qualifier' => '\'',
// link_set_attribute_separator: Link set from string: attribute separator
// default: ';'
'link_set_attribute_separator' => ';',
// link_set_item_separator: Link set from string: line separator
// default: '|'
'link_set_item_separator' => '|',
// link_set_value_separator: Link set from string: value separator (between the attcode and the value itself
// default: ':'
'link_set_value_separator' => ':',
'log_global' => true,
'log_issue' => true,
'log_notification' => true,
'log_web_service' => true,
// max_combo_length: The maximum number of elements in a drop-down list. If more then an autocomplete will be used
// default: 50
'max_combo_length' => 50,
'max_display_limit' => '15',
// max_linkset_output: Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.
// default: 100
'max_linkset_output' => 100,
'min_display_limit' => '10',
// online_help: Hyperlink to the online-help web page
// default: 'http://www.combodo.com/itop-help'
'online_help' => 'http://www.combodo.com/itop-help',
// php_path: Path to the php executable in CLI mode
// default: 'php'
'php_path' => 'php',
// portal_tickets: CSV list of classes supported in the portal
// default: 'UserRequest'
'portal_tickets' => 'UserRequest',
'query_cache_enabled' => true,
// search_manual_submit: Force manual submit of search requests (class => true)
// default: false
'search_manual_submit' => array (
'Person' => true,
),
'secure_connection_required' => false,
// session_name: The name of the cookie used to store the PHP session id
// default: 'iTop'
'session_name' => 'iTop',
// shortcut_actions: Actions that are available as direct buttons next to the "Actions" menu
// default: 'UI:Menu:Modify,UI:Menu:New'
'shortcut_actions' => 'UI:Menu:Modify,UI:Menu:New',
// source_dir: Source directory for the datamodel files. (which gets compiled to env-production).
// default: ''
'source_dir' => 'datamodels/2.x/',
'standard_reload_interval' => '300',
// synchro_trace: Synchronization details: none, display, save (includes 'display')
// default: 'none'
'synchro_trace' => 'none',
// timezone: Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitely configured in PHP
// default: 'Europe/Paris'
'timezone' => 'Europe/Paris',
// tracking_level_linked_set_default: Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)
// default: 1
'tracking_level_linked_set_default' => 0,
// url_validation_pattern: Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)
// default: '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?'
'url_validation_pattern' => '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?',
);
/**
*
* Modules specific settings
*
*/
$MyModuleSettings = array(
'itop-attachments' => array (
'allowed_classes' => array (
0 => 'Ticket',
),
'position' => 'relations',
'preview_max_width' => 290,
),
'itop-backup' => array (
'mysql_bindir' => '',
'week_days' => 'monday, tuesday, wednesday, thursday, friday',
'time' => '23:30',
'retention_count' => 5,
'enabled' => true,
'debug' => false,
),
'molkobain-console-tooltips' => array (
'decoration_class' => 'fas fa-question',
'enabled' => true,
),
);
/**
*
* Data model modules to be loaded. Names are specified as relative paths
*
*/
$MyModules = array(
'addons' => array (
'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
),
);
?>

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<installation>
<mode>upgrade</mode>
<preinstall>
<copies type="array"/>
</preinstall>
<source_dir>datamodels/2.x/</source_dir>
<datamodel_version>2.5.0</datamodel_version>
<previous_configuration_file>/var/lib/jenkins/workspace/iTop-CI/unattended_install/default-config-itop.php</previous_configuration_file>
<extensions_dir>extensions</extensions_dir>
<target_env>production</target_env>
<workspace_dir></workspace_dir>
<database>
<server></server>
<user>root</user>
<pwd>c8mb0do</pwd>
<name>itop_ci</name>
<db_tls_enabled></db_tls_enabled>
<db_tls_ca></db_tls_ca>
<prefix></prefix>
</database>
<url>http://127.0.0.1/itop/svn/trunk/</url>
<graphviz_path>/usr/bin/dot</graphviz_path>
<admin_account>
<user>admin</user>
<pwd>admin</pwd>
<language>EN US</language>
</admin_account>
<language>EN US</language>
<selected_modules type="array">
<item>authent-external</item>
<item>authent-local</item>
<item>itop-backup</item>
<item>itop-config</item>
<item>itop-profiles-itil</item>
<item>itop-sla-computation</item>
<item>itop-tickets</item>
<item>itop-welcome-itil</item>
<item>itop-config-mgmt</item>
<item>itop-attachments</item>
<item>itop-datacenter-mgmt</item>
<item>itop-endusers-devices</item>
<item>itop-storage-mgmt</item>
<item>itop-virtualization-mgmt</item>
<item>itop-bridge-virtualization-storage</item>
<item>itop-service-mgmt</item>
<item>itop-request-mgmt</item>
<item>itop-portal</item>
<item>itop-portal-base</item>
<item>itop-change-mgmt</item>
</selected_modules>
<selected_extensions type="array">
<item>itop-config-mgmt-core</item>
<item>itop-config-mgmt-datacenter</item>
<item>itop-config-mgmt-end-user</item>
<item>itop-config-mgmt-storage</item>
<item>itop-config-mgmt-virtualization</item>
<item>itop-service-mgmt-enterprise</item>
<item>itop-ticket-mgmt-simple-ticket</item>
<item>itop-ticket-mgmt-simple-ticket-enhanced-portal</item>
<item>itop-change-mgmt-simple</item>
</selected_extensions>
<sample_data>1</sample_data>
<old_addon></old_addon>
<options>
<generate_config>1</generate_config>
</options>
<mysql_bindir></mysql_bindir>
</installation>

View File

@@ -0,0 +1,190 @@
<?php
//this scrit will be run under the ./toolkit directory, relatively to the document root
require_once('../approot.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/application/clipage.class.inc.php');
require_once(APPROOT.'/core/config.class.inc.php');
require_once(APPROOT.'/core/log.class.inc.php');
require_once(APPROOT.'/core/kpi.class.inc.php');
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
require_once(APPROOT.'/setup/setuppage.class.inc.php');
require_once(APPROOT.'/setup/wizardcontroller.class.inc.php');
require_once(APPROOT.'/setup/wizardsteps.class.inc.php');
require_once(APPROOT.'/setup/applicationinstaller.class.inc.php');
/////////////////////////////////////////////////
$sParamFile = utils::ReadParam('response_file', 'default-params.xml', true /* CLI allowed */, 'raw_data');
$bCheckConsistency = (utils::ReadParam('check_consistency', '0', true /* CLI allowed */) == '1');
$oParams = new XMLParameters($sParamFile);
$sMode = $oParams->Get('mode');
if ($sMode == 'install')
{
echo "Installation mode detected.\n";
$bClean = utils::ReadParam('clean', false, true /* CLI allowed */);
if ($bClean)
{
echo "Cleanup mode detected.\n";
$sTargetEnvironment = $oParams->Get('target_env', '');
if ($sTargetEnvironment == '')
{
$sTargetEnvironment = 'production';
}
$sTargetDir = APPROOT.'env-'.$sTargetEnvironment;
// Configuration file
$sConfigFile = APPCONF.$sTargetEnvironment.'/'.ITOP_CONFIG_FILE;
if (file_exists($sConfigFile))
{
echo "Trying to delete the configuration file: '$sConfigFile'.\n";
@chmod($sConfigFile, 0770); // RWX for owner and group, nothing for others
unlink($sConfigFile);
}
else
{
echo "No config file to delete ($sConfigFile does not exist).\n";
}
// env-xxx directory
if (file_exists($sTargetDir))
{
if (is_dir($sTargetDir))
{
echo "Emptying the target directory '$sTargetDir'.\n";
SetupUtils::tidydir($sTargetDir);
}
else
{
die("ERROR the target dir '$sTargetDir' exists, but is NOT a directory !!!\nExiting.\n");
}
}
else
{
echo "No target directory to delete ($sTargetDir does not exist).\n";
}
// Database
$aDBSettings = $oParams->Get('database', array());
$sDBServer = $aDBSettings['server'];
$sDBUser = $aDBSettings['user'];
$sDBPwd = $aDBSettings['pwd'];
$sDBName = $aDBSettings['name'];
$sDBPrefix = $aDBSettings['prefix'];
if ($sDBPrefix != '')
{
die("Cleanup not implemented for a partial database (prefix= '$sDBPrefix')\nExiting.");
}
$oMysqli = new mysqli($sDBServer, $sDBUser, $sDBPwd);
if ($oMysqli->connect_errno)
{
die("Cannot connect to the MySQL server (".$mysqli->connect_errno . ") ".$mysqli->connect_error."\nExiting");
}
else
{
if ($oMysqli->select_db($sDBName))
{
echo "Deleting database '$sDBName'\n";
$oMysqli->query("DROP DATABASE `$sDBName`");
}
else
{
echo "The database '$sDBName' does not seem to exist. Nothing to cleanup.\n";
}
}
}
}
$bHasErrors = false;
$aChecks = SetupUtils::CheckBackupPrerequisites(APPROOT.'data'); // mmm should be the backup destination dir
$aSelectedModules = $oParams->Get('selected_modules');
$sSourceDir = $oParams->Get('source_dir', 'datamodels/latest');
$sExtensionDir = $oParams->Get('extensions_dir', 'extensions');
$aChecks = array_merge($aChecks, SetupUtils::CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules));
foreach($aChecks as $oCheckResult)
{
switch($oCheckResult->iSeverity)
{
case CheckResult::ERROR:
$bHasErrors = true;
$sHeader = "Error";
break;
case CheckResult::WARNING:
$sHeader = "Warning";
break;
case CheckResult::INFO:
default:
$sHeader = "Info";
break;
}
echo $sHeader.": ".$oCheckResult->sLabel;
if (strlen($oCheckResult->sDescription))
{
echo ' - '.$oCheckResult->sDescription;
}
echo "\n";
}
if ($bHasErrors)
{
echo "Encountered stopper issues. Aborting...\n";
die;
}
$bFoundIssues = false;
$bInstall = utils::ReadParam('install', true, true /* CLI allowed */);
if ($bInstall)
{
echo "Starting the unattended installation...\n";
$oWizard = new ApplicationInstaller($oParams);
$bRes = $oWizard->ExecuteAllSteps();
if (!$bRes)
{
echo "\nencountered installation issues!";
$bFoundIssues = true;
}
}
else
{
echo "No installation requested.\n";
}
if (!$bFoundIssues && $bCheckConsistency)
{
echo "Checking data model consistency.\n";
ob_start();
$sCheckRes = '';
try
{
MetaModel::CheckDefinitions(false);
$sCheckRes = ob_get_clean();
}
catch(Exception $e)
{
$sCheckRes = ob_get_clean()."\nException: ".$e->getMessage();
}
if (strlen($sCheckRes) > 0)
{
echo $sCheckRes;
echo "\nfound consistency issues!";
$bFoundIssues = true;
}
}
if (!$bFoundIssues)
{
// last line: used to check the install
// the only way to track issues in case of Fatal error or even parsing error!
echo "\ninstalled!";
exit;
}

62
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,62 @@
pipeline {
agent any
stages {
stage('init') {
parallel {
stage('debug') {
steps {
sh './.jenkins/bin/init/debug.sh'
}
}
stage('append files to project') {
steps {
sh './.jenkins/bin/init/append_files.sh'
}
}
stage('composer install') {
steps {
sh './.jenkins/bin/init/composer_install.sh'
}
}
}
}
stage('unattended_install') {
parallel {
stage('unattended_install default env') {
steps {
sh './.jenkins/bin/unattended_install/default_env.sh'
}
}
}
}
stage('test') {
parallel {
stage('phpunit') {
steps {
sh './.jenkins/bin/tests/phpunit.sh'
}
}
}
}
}
post {
always {
junit 'var/test/phpunit-log.junit.xml'
}
failure {
slackSend(channel: "#jenkins-itop", color: '#FF0000', message: "Ho no! Build failed! (${currentBuild.result}), Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
}
environment {
DEBUG_UNIT_TEST = '0'
}
options {
timeout(time: 20, unit: 'MINUTES')
}
}

View File

@@ -402,9 +402,9 @@ class URP_UserOrg extends UserRightsBaseClassGUI
*/
protected function CheckIfOrgIsAllowed()
{
if (UserRights::IsAdministrator()) { return; }
if (!UserRights::IsLoggedIn() || UserRights::IsAdministrator()) { return; }
$oUser = UserRights::GetUserObject();
$oUser = UserRights::GetUserObject();
$oAddon = UserRights::GetModuleInstance();
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
if (count($aOrgs) > 0)

View File

@@ -1757,6 +1757,27 @@ EOF
$sConfigJS = json_encode($aConfig);
$oPage->add_ready_script("$('#$iId').ckeditor(function() { /* callback code */ }, $sConfigJS);"); // Transform $iId into a CKEdit
$oPage->add_ready_script(
<<<EOF
$('#$iId').bind('update', function(evt){
BlockField('cke_$iId', $('#$iId').attr('disabled'));
//Delayed execution - ckeditor must be properly initialized before setting readonly
var retryCount = 0;
var oMe = $('#$iId');
var delayedSetReadOnly = function () {
if (oMe.data('ckeditorInstance').editable() == undefined && retryCount++ < 10) {
setTimeout(delayedSetReadOnly, retryCount * 100); //Wait a while longer each iteration
}
else
{
oMe.data('ckeditorInstance').setReadOnly(oMe.prop('disabled'));
}
};
setTimeout(delayedSetReadOnly, 50);
});
EOF
);
break;
case 'HTML':
@@ -2380,7 +2401,7 @@ EOF
return $oObj->DisplayModifyForm( $oPage, $aExtraParams);
}
public function DisplayStimulusForm(WebPage $oPage, $sStimulus)
public function DisplayStimulusForm(WebPage $oPage, $sStimulus, $aPrefillFormParam = null)
{
$sClass = get_class($this);
$iKey = $this->GetKey();
@@ -2422,6 +2443,12 @@ EOF
$oPage->add("<h1>$sActionDetails</h1>\n");
$sTargetState = $aTransitions[$sStimulus]['target_state'];
$aExpectedAttributes = $this->GetTransitionAttributes($sStimulus /*, current state*/);
if ($aPrefillFormParam != null)
{
$aPrefillFormParam['expected_attributes'] = $aExpectedAttributes;
$this->PrefillForm('state_change', $aPrefillFormParam);
$aExpectedAttributes = $aPrefillFormParam['expected_attributes'];
}
$sButtonsPosition = MetaModel::GetConfig()->Get('buttons_position');
if ($sButtonsPosition == 'bottom')
{
@@ -2488,7 +2515,7 @@ EOF
else
{
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs);
if (count($aAllowedValues) == 1)
if (is_array($aAllowedValues) && count($aAllowedValues) == 1)
{
$aValues = array_keys($aAllowedValues);
$this->Set($sAttCode, $aValues[0]);
@@ -3505,7 +3532,7 @@ EOF
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
if (count($aNewIssues) > 0)
if (is_array($aNewIssues) && count($aNewIssues) > 0)
{
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
}
@@ -3683,7 +3710,7 @@ EOF
$currValue = $oObj->Get($sAttCode);
if ($oAttDef instanceof AttributeCaseLog)
{
$currValue = ' '; // Don't put an empty string, in case the field would be considered as mandatory...
$currValue = ''; // Put a single scalar value to force caselog to mock a new entry. For more info see N°1059.
}
if (is_object($currValue)) continue; // Skip non scalar values...
if(!array_key_exists($currValue, $aValues[$sAttCode]))

View File

@@ -330,31 +330,62 @@ EOF
protected function GetGroupByOptions($sOql)
{
$oQuery = $this->oModelReflection->GetQuery($sOql);
$sClass = $oQuery->GetClass();
$aGroupBy = array();
foreach($this->oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType)
try
{
if ($sAttType == 'AttributeLinkedSet') continue;
if (is_subclass_of($sAttType, 'AttributeLinkedSet')) continue;
if ($sAttType == 'AttributeFriendlyName') continue;
if (is_subclass_of($sAttType, 'AttributeFriendlyName')) continue;
if ($sAttType == 'AttributeExternalField') continue;
if (is_subclass_of($sAttType, 'AttributeExternalField')) continue;
if ($sAttType == 'AttributeOneWayPassword') continue;
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
$aGroupBy[$sAttCode] = $sLabel;
if (is_subclass_of($sAttType, 'AttributeDateTime') || $sAttType == 'AttributeDateTime')
$oQuery = $this->oModelReflection->GetQuery($sOql);
$sClass = $oQuery->GetClass();
foreach($this->oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType)
{
$aGroupBy[$sAttCode.':hour'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Hour', $sLabel);
$aGroupBy[$sAttCode.':month'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Month', $sLabel);
$aGroupBy[$sAttCode.':day_of_week'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek', $sLabel);
$aGroupBy[$sAttCode.':day_of_month'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-DayOfMonth', $sLabel);
if ($sAttType == 'AttributeLinkedSet')
{
continue;
}
if (is_subclass_of($sAttType, 'AttributeLinkedSet'))
{
continue;
}
if ($sAttType == 'AttributeFriendlyName')
{
continue;
}
if (is_subclass_of($sAttType, 'AttributeFriendlyName'))
{
continue;
}
if ($sAttType == 'AttributeExternalField')
{
continue;
}
if (is_subclass_of($sAttType, 'AttributeExternalField'))
{
continue;
}
if ($sAttType == 'AttributeOneWayPassword')
{
continue;
}
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
$aGroupBy[$sAttCode] = $sLabel;
if (is_subclass_of($sAttType, 'AttributeDateTime') || $sAttType == 'AttributeDateTime')
{
$aGroupBy[$sAttCode.':hour'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Hour', $sLabel);
$aGroupBy[$sAttCode.':month'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Month',
$sLabel);
$aGroupBy[$sAttCode.':day_of_week'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek',
$sLabel);
$aGroupBy[$sAttCode.':day_of_month'] = Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-DayOfMonth',
$sLabel);
}
}
asort($aGroupBy);
}
catch(Exception $e)
{
// Bad OQL is ignored
}
asort($aGroupBy);
return $aGroupBy;
}
@@ -1219,25 +1250,32 @@ abstract class DashletGroupBy extends Dashlet
protected function GetNumericAttributes($sOql)
{
$aFunctionAttributes = array();
$oQuery = $this->oModelReflection->GetQuery($sOql);
$sClass = $oQuery->GetClass();
if (is_null($sClass))
try
{
return $aFunctionAttributes;
}
foreach($this->oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType)
{
switch ($sAttType)
$oQuery = $this->oModelReflection->GetQuery($sOql);
$sClass = $oQuery->GetClass();
if (is_null($sClass))
{
case 'AttributeDecimal':
case 'AttributeDuration':
case 'AttributeInteger':
case 'AttributePercentage':
case 'AttributeSubItem': // TODO: Known limitation: no unit displayed (values in sec)
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
$aFunctionAttributes[$sAttCode] = $sLabel;
break;
return $aFunctionAttributes;
}
foreach($this->oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType)
{
switch ($sAttType)
{
case 'AttributeDecimal':
case 'AttributeDuration':
case 'AttributeInteger':
case 'AttributePercentage':
case 'AttributeSubItem': // TODO: Known limitation: no unit displayed (values in sec)
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
$aFunctionAttributes[$sAttCode] = $sLabel;
break;
}
}
}
catch (Exception $e)
{
// Ignore bad OQL
}
return $aFunctionAttributes;

View File

@@ -326,7 +326,8 @@ class DisplayBlock
{
$aQueryParams = $aExtraParams['query_params'];
}
if ($this->m_sStyle != 'links')
// In case of search, the context filtering is done by the search itself
if (($this->m_sStyle != 'links') && ($this->m_sStyle != 'search'))
{
$oAppContext = new ApplicationContext();
$sClass = $this->m_oFilter->GetClass();
@@ -651,16 +652,21 @@ class DisplayBlock
if (isset($aExtraParams['update_history']) && true == $aExtraParams['update_history'])
{
$sSearchFilter = $this->m_oSet->GetFilter()->serialize();
// Limit the size of the URL (N°1585 - request uri too long)
if (strlen($sSearchFilter) < SERVER_MAX_URL_LENGTH)
{
$seventAttachedData = json_encode(array(
'filter' => $sSearchFilter,
'breadcrumb_id' => "ui-search-".$this->m_oSet->GetClass(),
'breadcrumb_label' => MetaModel::GetName($this->m_oSet->GetClass()),
'breadcrumb_max_count' => utils::GetConfig()->Get('breadcrumb.max_count'),
'breadcrumb_instance_id' => MetaModel::GetConfig()->GetItopInstanceid(),
'breadcrumb_icon' => utils::GetAbsoluteUrlAppRoot().'images/breadcrumb-search.png'
));
$seventAttachedData = json_encode(array(
'filter' => $this->m_oSet->GetFilter()->serialize(),
'breadcrumb_id' => "ui-search-".$this->m_oSet->GetClass(),
'breadcrumb_label' => MetaModel::GetName($this->m_oSet->GetClass()),
'breadcrumb_max_count' => utils::GetConfig()->Get('breadcrumb.max_count'),
'breadcrumb_instance_id'=> MetaModel::GetConfig()->GetItopInstanceid(),
'breadcrumb_icon' => utils::GetAbsoluteUrlAppRoot().'images/breadcrumb-search.png'
));
$oPage->add_ready_script("$('body').trigger('update_history.itop', [$seventAttachedData])");
$oPage->add_ready_script("$('body').trigger('update_history.itop', [$seventAttachedData])");
}
}
}
break;

View File

@@ -230,7 +230,8 @@ class LoginWebPage extends NiceWebPage
try
{
UserRights::Login($sAuthUser); // Set the user's language (if possible!)
$oUser = UserRights::GetUserObject();
/** @var UserInternal $oUser */
$oUser = UserRights::GetUserObject();
if ($oUser == null)
{
throw new Exception(Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser));
@@ -254,6 +255,7 @@ class LoginWebPage extends NiceWebPage
$sToken = substr(md5(APPROOT.uniqid()), 0, 16);
$oUser->Set('reset_pwd_token', $sToken);
CMDBObject::SetTrackInfo('Reset password');
$oUser->AllowWrite(true);
$oUser->DBUpdate();
$oEmail = new Email();

View File

@@ -1,49 +1,62 @@
<?php
// Copyright (C) 2010-2016 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/>
/**
* File to include to initialize the datamodel in memory
*
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/core/contexttag.class.inc.php');
session_name('itop-'.md5(APPROOT));
session_start();
$sSwitchEnv = utils::ReadParam('switch_env', null);
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)))
{
$_SESSION['itop_env'] = $sSwitchEnv;
$sEnv = $sSwitchEnv;
// TODO: reset the credentials as well ??
}
else if (isset($_SESSION['itop_env']))
{
$sEnv = $_SESSION['itop_env'];
}
else
{
$sEnv = ITOP_DEFAULT_ENV;
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, true /* $bAllowCache */, false /* $bTraceSourceFiles */, $sEnv);
<?php
// Copyright (C) 2010-2016 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/>
/**
* File to include to initialize the datamodel in memory
*
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/core/contexttag.class.inc.php');
session_name('itop-'.md5(APPROOT));
session_start();
$sSwitchEnv = utils::ReadParam('switch_env', null);
$bAllowCache = true;
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)) && isset($_SESSION['itop_env']) && ($_SESSION['itop_env'] !== $sSwitchEnv))
{
$_SESSION['itop_env'] = $sSwitchEnv;
$sEnv = $sSwitchEnv;
$bAllowCache = false;
// Reset the opcache since otherwise the PHP "model" files may still be cached !!
if (function_exists('opcache_reset'))
{
// Zend opcode cache
opcache_reset();
}
if (function_exists('apc_clear_cache'))
{
// APC(u) cache
apc_clear_cache();
}
// TODO: reset the credentials as well ??
}
else if (isset($_SESSION['itop_env']))
{
$sEnv = $_SESSION['itop_env'];
}
else
{
$sEnv = ITOP_DEFAULT_ENV;
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);

File diff suppressed because it is too large Load Diff

18
composer.json Normal file
View File

@@ -0,0 +1,18 @@
{
"require": {
"php": ">=5.6.0",
"ext-mysql": "*",
"ext-ldap": "*",
"ext-mcrypt": "*",
"ext-cli": "*",
"ext-soap": "*",
"ext-json": "*",
"ext-zip": "*",
"ext-mysqli": "*"
},
"config": {
"platform": {
"php": "5.6.0"
}
}
}

View File

@@ -360,6 +360,7 @@ class ActionEmail extends ActionNotification
$sTestBody .= "</div>\n";
$oEmail->SetBody($sTestBody, 'text/html', $sStyles);
$oEmail->SetRecipientTO($this->Get('test_recipient'));
$oEmail->SetRecipientFrom($sFrom);
$oEmail->SetReferences($sReference);
$oEmail->SetMessageId($sMessageId);
}

View File

@@ -1578,7 +1578,7 @@ class AttributeDBFieldVoid extends AttributeDefinition
protected function GetSQLCol($bFullSpec = false)
{
return 'VARCHAR(255)'
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.CMDBSource::GetSqlStringColumnDefinition()
.($bFullSpec ? $this->GetSQLColSpec() : '');
}
protected function GetSQLColSpec()
@@ -2205,7 +2205,7 @@ class AttributeString extends AttributeDBField
protected function GetSQLCol($bFullSpec = false)
{
return 'VARCHAR(255)'
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.CMDBSource::GetSqlStringColumnDefinition()
.($bFullSpec ? $this->GetSQLColSpec() : '');
}
@@ -2606,7 +2606,7 @@ class AttributePassword extends AttributeString
protected function GetSQLCol($bFullSpec = false)
{
return "VARCHAR(64)"
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.CMDBSource::GetSqlStringColumnDefinition()
.($bFullSpec ? $this->GetSQLColSpec() : '');
}
@@ -2739,7 +2739,7 @@ class AttributeText extends AttributeString
protected function GetSQLCol($bFullSpec = false)
{
return "TEXT".CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
return "TEXT".CMDBSource::GetSqlStringColumnDefinition();
}
public function GetSQLColumns($bFullSpec = false)
@@ -2749,7 +2749,7 @@ class AttributeText extends AttributeString
if ($this->GetOptional('format', null) != null )
{
// Add the extra column only if the property 'format' is specified for the attribute
$aColumns[$this->Get('sql').'_format'] = "ENUM('text','html')".CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
$aColumns[$this->Get('sql').'_format'] = "ENUM('text','html')".CMDBSource::GetSqlStringColumnDefinition();
if ($bFullSpec)
{
$aColumns[$this->Get('sql').'_format'].= " DEFAULT 'text'"; // default 'text' is for migrating old records
@@ -3072,7 +3072,7 @@ class AttributeLongText extends AttributeText
{
protected function GetSQLCol($bFullSpec = false)
{
return "LONGTEXT".CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
return "LONGTEXT".CMDBSource::GetSqlStringColumnDefinition();
}
public function GetMaxSize()
@@ -3254,7 +3254,7 @@ class AttributeCaseLog extends AttributeLongText
{
$aColumns = array();
$aColumns[$this->GetCode()] = 'LONGTEXT' // 2^32 (4 Gb)
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
.CMDBSource::GetSqlStringColumnDefinition();
$aColumns[$this->GetCode().'_index'] = 'BLOB';
return $aColumns;
}
@@ -3642,13 +3642,13 @@ class AttributeEnum extends AttributeString
// make sure that this string will match the field type returned by the DB
// (used to perform a comparison between the current DB format and the data model)
return "ENUM(".implode(",", $aValues).")"
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.CMDBSource::GetSqlStringColumnDefinition()
.($bFullSpec ? $this->GetSQLColSpec() : '');
}
else
{
return "VARCHAR(255)"
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.CMDBSource::GetSqlStringColumnDefinition()
.($bFullSpec ? " DEFAULT ''" : ""); // ENUM() is not an allowed syntax!
}
}
@@ -5463,7 +5463,7 @@ class AttributeURL extends AttributeString
protected function GetSQLCol($bFullSpec = false)
{
return "VARCHAR(2048)"
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.CMDBSource::GetSqlStringColumnDefinition()
.($bFullSpec ? $this->GetSQLColSpec() : '');
}
@@ -5640,8 +5640,8 @@ class AttributeBlob extends AttributeDefinition
{
$aColumns = array();
$aColumns[$this->GetCode().'_data'] = 'LONGBLOB'; // 2^32 (4 Gb)
$aColumns[$this->GetCode().'_mimetype'] = 'VARCHAR(255)'.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
$aColumns[$this->GetCode().'_filename'] = 'VARCHAR(255)'.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
$aColumns[$this->GetCode().'_mimetype'] = 'VARCHAR(255)'.CMDBSource::GetSqlStringColumnDefinition();
$aColumns[$this->GetCode().'_filename'] = 'VARCHAR(255)'.CMDBSource::GetSqlStringColumnDefinition();
return $aColumns;
}
@@ -5835,6 +5835,32 @@ class AttributeImage extends AttributeBlob
{
return '\\Combodo\\iTop\\Form\\Field\\ImageField';
}
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
if ($oFormField === null)
{
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
parent::MakeFormField($oObject, $oFormField);
// Generating urls
$value = $oObject->Get($this->GetCode());
if (is_object($value) && !$value->IsEmpty())
{
$oFormField->SetDownloadUrl($value->GetDownloadURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
$oFormField->SetDisplayUrl($value->GetDisplayURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
}
else
{
$oFormField->SetDownloadUrl($this->Get('default_image'));
$oFormField->SetDisplayUrl($this->Get('default_image'));
}
return $oFormField;
}
}
/**
* A stop watch is an ormStopWatch object, it is stored as several columns in the database
@@ -6779,7 +6805,7 @@ class AttributeOneWayPassword extends AttributeDefinition
public function GetImportColumns()
{
$aColumns = array();
$aColumns[$this->GetCode()] = 'TINYTEXT'.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
$aColumns[$this->GetCode()] = 'TINYTEXT'.CMDBSource::GetSqlStringColumnDefinition();
return $aColumns;
}
@@ -6853,7 +6879,7 @@ class AttributeTable extends AttributeDBField
protected function GetSQLCol($bFullSpec = false)
{
return "LONGTEXT".CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
return "LONGTEXT".CMDBSource::GetSqlStringColumnDefinition();
}
public function GetMaxSize()
@@ -7261,7 +7287,7 @@ class AttributeRedundancySettings extends AttributeDBField
protected function GetSQLCol($bFullSpec = false)
{
return "VARCHAR(20)"
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.CMDBSource::GetSqlStringColumnDefinition()
.($bFullSpec ? $this->GetSQLColSpec() : '');
}

View File

@@ -108,16 +108,6 @@ class MySQLHasGoneAwayException extends MySQLException
*/
class CMDBSource
{
/**
* SQL charset & collation declaration for text columns
*
* Using an attribute instead of a constant to avoid crash in the setup for older PHP versions
*
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-column.html
* @since 2.5 #1001 switch to utf8mb4
*/
public static $SQL_STRING_COLUMNS_CHARSET_DEFINITION = ' CHARACTER SET '.DEFAULT_CHARACTER_SET.' COLLATE '.DEFAULT_COLLATION;
protected static $m_sDBHost;
protected static $m_sDBUser;
protected static $m_sDBPwd;
@@ -136,6 +126,20 @@ class CMDBSource
/** @var mysqli $m_oMysqli */
protected static $m_oMysqli;
/**
* SQL charset & collation declaration for text columns
*
* Using a function instead of a constant or attribute to avoid crash in the setup for older PHP versions (cannot
* use expression as value)
*
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-column.html
* @since 2.5 #1001 switch to utf8mb4
*/
public static function GetSqlStringColumnDefinition()
{
return ' CHARACTER SET '.DEFAULT_CHARACTER_SET.' COLLATE '.DEFAULT_COLLATION;
}
/**
* @param Config $oConfig
*
@@ -1059,7 +1063,7 @@ class CMDBSource
}
return 'ALTER TABLE `'.$sTableName.'` '.self::$SQL_STRING_COLUMNS_CHARSET_DEFINITION.';';
return 'ALTER TABLE `'.$sTableName.'` '.self::GetSqlStringColumnDefinition().';';
}
@@ -1205,6 +1209,6 @@ class CMDBSource
return null;
}
return 'ALTER DATABASE'.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION.';';
return 'ALTER DATABASE'.CMDBSource::GetSqlStringColumnDefinition().';';
}
}

View File

@@ -127,5 +127,3 @@ class SecurityException extends CoreException
class ArchivedObjectException extends CoreException
{
}
?>

View File

@@ -568,7 +568,8 @@ abstract class DBObject implements iDisplay
{
throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this));
}
return $this->m_aOrigValues[$sAttCode];
$aOrigValues = $this->m_aOrigValues;
return isset($aOrigValues[$sAttCode]) ? $aOrigValues[$sAttCode] : null;
}
/**

View File

@@ -497,6 +497,8 @@ class DBObjectSearch extends DBSearch
* @param bool $bPositiveMatch if true will add a IN filter, else a NOT IN
*
* @throws \CoreException
*
* @since 2.5 N°1418
*/
public function AddConditionForInOperatorUsingParam($sFilterCode, $aValues, $bPositiveMatch = true)
{
@@ -506,7 +508,7 @@ class DBObjectSearch extends DBSearch
$sInParamName = $this->GenerateUniqueParamName();
$oParamExpression = new VariableExpression($sInParamName);
$this->SetInternalParams(array($sInParamName => $aValues));
$this->GetInternalParamsByRef()[$sInParamName] = $aValues;
$oListExpression = new ListExpression(array($oParamExpression));
@@ -1086,11 +1088,45 @@ class DBObjectSearch extends DBSearch
return $this->m_aParams = $aParams;
}
/**
* @return array <strong>warning</strong> : array returned by value
* @see self::GetInternalParamsByRef to get the attribute by reference
*/
public function GetInternalParams()
{
return $this->m_aParams;
}
/**
* @return array
* @see http://php.net/manual/en/language.references.return.php
* @since 2.5.1 N°1582
*/
public function &GetInternalParamsByRef()
{
return $this->m_aParams;
}
/**
* @param string $sKey
* @param mixed $value
* @param bool $bDoNotOverride
*
* @throws \CoreUnexpectedValue if $bDoNotOverride and $sKey already exists
*/
public function AddInternalParam($sKey, $value, $bDoNotOverride = false)
{
if ($bDoNotOverride)
{
if (array_key_exists($sKey, $this->m_aParams))
{
throw new CoreUnexpectedValue("The key $sKey already exists with value : ".$this->m_aParams[$sKey]);
}
}
$this->m_aParams[$sKey] = $value;
}
public function GetQueryParams($bExcludeMagicParams = true)
{
$aParams = array();

File diff suppressed because it is too large Load Diff

View File

@@ -234,7 +234,6 @@ EOF
}
}
$sData = '';
$sData .= '<style>table br {mso-data-placement:same-cell;}</style>'; // Trick for Excel: keep line breaks inside the same cell !
$sData .= "<table border=\"1\">\n";
$sData .= "<tr>\n";
foreach($aData as $sLabel)

View File

@@ -228,10 +228,23 @@ class ValueSetObjects extends ValueSetDefinition
}
}
$oExpression = DBObjectSearch::GetPolymorphicExpression($oFilter->GetClass(), 'friendlyname');
$aFields = $oExpression->ListRequiredFields();
$sClass = $oFilter->GetClass();
foreach($aFields as $sField)
{
$aFieldItems = explode('.', $sField);
if ($aFieldItems[0] != $sClass)
{
$sOperation = 'contains';
break;
}
}
switch ($sOperation)
{
case 'equals':
$aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($oFilter->GetClass());
$aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($sClass);
$sClassAlias = $oFilter->GetClassAlias();
$aFilters = array();
$oValueExpr = new ScalarExpression($sContains);
@@ -247,7 +260,7 @@ class ValueSetObjects extends ValueSetDefinition
$oFilter = new DBUnionSearch($aFilters);
break;
case 'start_with':
$aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($oFilter->GetClass());
$aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($sClass);
$sClassAlias = $oFilter->GetClassAlias();
$aFilters = array();
$oValueExpr = new ScalarExpression($sContains.'%');

View File

@@ -2588,7 +2588,6 @@ span.search-button, span.refresh-button {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
filter: gray;
filter: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' height='0'><filter id='greyscale'><feColorMatrix type='matrix' values='0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0' /></filter></svg>#greyscale");
opacity: 0.5;
}
#itop-breadcrumb .breadcrumb-item a {

View File

@@ -2934,7 +2934,6 @@ span.search-button, span.refresh-button {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
filter: gray;
filter: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' height='0'><filter id='greyscale'><feColorMatrix type='matrix' values='0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0' /></filter></svg>#greyscale");
// IE has no filter option: at least, have some effect when hovering...
opacity: 0.5;

View File

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

View File

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

View File

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

View File

@@ -1,44 +1,44 @@
<?php
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Localized data
*
* @copyright Copyright (C) 2010-2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
*/
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Attachments:TabTitle_Count' => 'Anexos (%1$d)',
'Attachments:EmptyTabTitle' => 'Anexos',
'Attachments:FieldsetTitle' => 'Anexos',
'Attachments:DeleteBtn' => 'Borrar',
'Attachments:History_File_Added' => 'Anexo %1$s agregado.',
'Attachments:History_File_Removed' => 'Anexo %1$s removido.',
'Attachments:AddAttachment' => 'Agregar Anexo: ',
'Attachments:UploadNotAllowedOnThisSystem' => 'La carga de archivos NO está permitida en este sistema.',
'Attachment:Max_Go' => '(Tamaño Máximo de Archivo: %1$s Gb)',
'Attachment:Max_Mo' => '(Tamaño Máximo de Archivo: %1$s Mb)',
'Attachment:Max_Ko' => '(Tamaño Máximo de Archivo: %1$s Kb)',
'Attachments:NoAttachment' => 'No hay Anexo. ',
'Class:Attachment' => 'Anexo',
'Class:Attachment+' => 'Anexo',
'Attachments:PreviewNotAvailable' => 'Vista preliminar no disponible para este tipo de Anexo.',
));
<?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/>
/**
* Localized data
*
* @copyright Copyright (C) 2010-2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
*/
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Attachments:TabTitle_Count' => 'Anexos (%1$d)',
'Attachments:EmptyTabTitle' => 'Anexos',
'Attachments:FieldsetTitle' => 'Anexos',
'Attachments:DeleteBtn' => 'Borrar',
'Attachments:History_File_Added' => 'Anexo %1$s agregado.',
'Attachments:History_File_Removed' => 'Anexo %1$s removido.',
'Attachments:AddAttachment' => 'Agregar Anexo: ',
'Attachments:UploadNotAllowedOnThisSystem' => 'La carga de archivos NO está permitida en este sistema.',
'Attachment:Max_Go' => '(Tamaño Máximo de Archivo: %1$s Gb)',
'Attachment:Max_Mo' => '(Tamaño Máximo de Archivo: %1$s Mb)',
'Attachment:Max_Ko' => '(Tamaño Máximo de Archivo: %1$s Kb)',
'Attachments:NoAttachment' => 'No hay Anexo. ',
'Class:Attachment' => 'Anexo',
'Class:Attachment+' => 'Anexo',
'Attachments:PreviewNotAvailable' => 'Vista preliminar no disponible para este tipo de Anexo.',
));

View File

@@ -129,7 +129,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:ApplicationSolution+' => '',
'Class:ApplicationSolution/Attribute:functionalcis_list' => 'CIs',
'Class:ApplicationSolution/Attribute:functionalcis_list+' => '',
'Class:ApplicationSolution/Attribute:businessprocess_list' => 'Geschäftsprozesse',
'Class:ApplicationSolution/Attribute:businessprocess_list' => 'Business-Prozesse',
'Class:ApplicationSolution/Attribute:businessprocess_list+' => '',
'Class:ApplicationSolution/Attribute:status' => 'Status',
'Class:ApplicationSolution/Attribute:status+' => '',
@@ -139,7 +139,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:ApplicationSolution/Attribute:status/Value:inactive+' => '',
'Class:BusinessProcess' => 'Business-Prozess',
'Class:BusinessProcess+' => '',
'Class:BusinessProcess/Attribute:applicationsolutions_list' => 'Applikationslösungen',
'Class:BusinessProcess/Attribute:applicationsolutions_list' => 'Anwendungslösungen',
'Class:BusinessProcess/Attribute:applicationsolutions_list+' => '',
'Class:BusinessProcess/Attribute:status' => 'Status',
'Class:BusinessProcess/Attribute:status+' => '',
@@ -373,9 +373,9 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:lnkApplicationSolutionToFunctionalCI/Attribute:applicationsolution_id+' => '',
'Class:lnkApplicationSolutionToFunctionalCI/Attribute:functionalci_id' => 'FunctionalCI',
'Class:lnkApplicationSolutionToFunctionalCI/Attribute:functionalci_id+' => '',
'Class:lnkApplicationSolutionToBusinessProcess' => 'Verknüpfung Anwendungslösung/Geschäftsprozess',
'Class:lnkApplicationSolutionToBusinessProcess' => 'Verknüpfung Anwendungslösung/Business-Prozess',
'Class:lnkApplicationSolutionToBusinessProcess+' => '',
'Class:lnkApplicationSolutionToBusinessProcess/Attribute:businessprocess_id' => 'Geschäftsprozes',
'Class:lnkApplicationSolutionToBusinessProcess/Attribute:businessprocess_id' => 'Business-Prozess',
'Class:lnkApplicationSolutionToBusinessProcess/Attribute:businessprocess_id+' => '',
'Class:lnkApplicationSolutionToBusinessProcess/Attribute:applicationsolution_id' => 'Anwendungslösung',
'Class:lnkApplicationSolutionToBusinessProcess/Attribute:applicationsolution_id+' => '',
@@ -999,7 +999,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:lnkApplicationSolutionToFunctionalCI/Attribute:applicationsolution_name+' => '',
'Class:lnkApplicationSolutionToFunctionalCI/Attribute:functionalci_name' => 'FunctionalCI-Name',
'Class:lnkApplicationSolutionToFunctionalCI/Attribute:functionalci_name+' => '',
'Class:lnkApplicationSolutionToBusinessProcess/Attribute:businessprocess_name' => 'Geschäftsprozess-Name',
'Class:lnkApplicationSolutionToBusinessProcess/Attribute:businessprocess_name' => 'Business-Prozess-Name',
'Class:lnkApplicationSolutionToBusinessProcess/Attribute:businessprocess_name+' => '',
'Class:lnkApplicationSolutionToBusinessProcess/Attribute:applicationsolution_name' => 'Applikationslösungs-Name',
'Class:lnkApplicationSolutionToBusinessProcess/Attribute:applicationsolution_name+' => '',
@@ -1039,7 +1039,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Menu:ConfigManagementOverview+' => 'Übersicht',
'Menu:Contact' => 'Kontakte',
'Menu:Contact+' => 'Kontakte',
'Menu:Contact:Count' => '%1$d Kontakte',
'Menu:Contact:Count' => '%1$d kontakten',
'Menu:Person' => 'Personen',
'Menu:Person+' => 'Alle Personen',
'Menu:Team' => 'Teams',

View File

@@ -138,7 +138,7 @@ $(function()
if (this.options.redirect_after_completion_url != '')
{
var sUrl = this.options.redirect_after_completion_url;
window.setTimeout(function() { window.location.href = sUrl; }, 500);
window.setTimeout(function() { window.location.href = sUrl; }, 3000);
}
}
else

View File

@@ -51,10 +51,10 @@ class BrowseBrickController extends BrickController
// Getting current browse mode (First from router pamater, then default brick value)
$sBrowseMode = (!empty($sBrowseMode)) ? $sBrowseMode : $oBrick->GetDefaultBrowseMode();
// Getting current dataloading mode (First from router parameter, then query parameter, then default brick value)
$sDataLoading = ($sDataLoading !== null) ? $sDataLoading : ( ($oRequest->query->get('sDataLoading') !== null) ? $oRequest->query->get('sDataLoading') : $oBrick->GetDataLoading() );
$sDataLoading = ($sDataLoading !== null) ? $sDataLoading : $oApp['request_manipulator']->ReadParam('sDataLoading', $oBrick->GetDataLoading());
// Getting search value
$sSearchValue = $oRequest->get('sSearchValue', null);
if ($sSearchValue !== null)
$sSearchValue = $oApp['request_manipulator']->ReadParam('sSearchValue', '');
if (!empty($sSearchValue))
{
$sDataLoading = AbstractBrick::ENUM_DATA_LOADING_LAZY;
}
@@ -109,7 +109,7 @@ class BrowseBrickController extends BrickController
// Adding search clause
// Note : For know the search is naive and looks only for the exact match. It doesn't search for words separately
if ($sSearchValue !== null)
if (!empty($sSearchValue))
{
// - Cleaning the search value by exploding and trimming spaces
$aSearchValues = explode(' ', $sSearchValue);
@@ -182,7 +182,7 @@ class BrowseBrickController extends BrickController
{
$aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search']->SetSelectedClasses($aLevelsClasses);
if ($sSearchValue !== null)
if (!empty($sSearchValue))
{
// Note : This could be way more simpler if we had a SetInternalParam($sParam, $value) verb
$aQueryParams = $aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search']->GetInternalParams();
@@ -216,8 +216,8 @@ class BrowseBrickController extends BrickController
{
case BrowseBrick::ENUM_BROWSE_MODE_LIST:
// Retrieving parameters
$iPageNumber = (int) $oRequest->get('iPageNumber', 1);
$iListLength = (int) $oRequest->get('iListLength', BrowseBrick::DEFAULT_LIST_LENGTH);
$iPageNumber = (int) $oApp['request_manipulator']->ReadParam('iPageNumber', 1, FILTER_SANITIZE_NUMBER_INT);
$iListLength = (int) $oApp['request_manipulator']->ReadParam('iListLength', BrowseBrick::DEFAULT_LIST_LENGTH, FILTER_SANITIZE_NUMBER_INT);
// Getting total records number
$oCountSet = new DBObjectSet($oQuery);
@@ -232,8 +232,8 @@ class BrowseBrickController extends BrickController
case BrowseBrick::ENUM_BROWSE_MODE_TREE:
case BrowseBrick::ENUM_BROWSE_MODE_MOSAIC:
// Retrieving parameters
$sLevelAlias = $oRequest->get('sLevelAlias');
$sNodeId = $oRequest->get('sNodeId');
$sLevelAlias = $oApp['request_manipulator']->ReadParam('sLevelAlias', '');
$sNodeId = $oApp['request_manipulator']->ReadParam('sNodeId', '');
// If no values for those parameters, we might be loading page in lazy mode for the first time, therefore the URL doesn't have those informations.
if (empty($sLevelAlias))
@@ -631,8 +631,9 @@ class BrowseBrickController extends BrickController
if ($aLevelsProperties[$key][$sOptionalAttribute] !== null)
{
$sPropertyName = substr($sOptionalAttribute, 0, -4);
$oAttDef = MetaModel::GetAttributeDef(get_class($value), $aLevelsProperties[$key][$sOptionalAttribute]);
$tmpAttValue = $value->Get($aLevelsProperties[$key][$sOptionalAttribute]);
$tmpAttValue = $value->GetAsHTML($aLevelsProperties[$key][$sOptionalAttribute]);
if($sOptionalAttribute === 'image_att')
{
if (is_object($tmpAttValue) && !$tmpAttValue->IsEmpty())
@@ -641,7 +642,7 @@ class BrowseBrickController extends BrickController
}
else
{
$tmpAttValue = MetaModel::GetAttributeDef(get_class($value), $aLevelsProperties[$key][$sOptionalAttribute])->Get('default_image');
$tmpAttValue = $oAttDef->Get('default_image');
}
}
@@ -655,7 +656,7 @@ class BrowseBrickController extends BrickController
foreach ($aLevelsProperties[$key]['fields'] as $aField)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($value), $aField['code']);
$aRow[$key]['fields'][$aField['code']] = $oAttDef->GetValueLabel($value->Get($aField['code']));
$aRow[$key]['fields'][$aField['code']] = $oAttDef->GetAsHTML($value->Get($aField['code']));
}
}
}
@@ -723,8 +724,9 @@ class BrowseBrickController extends BrickController
if ($aLevelsProperties[$aCurrentRowKeys[0]][$sOptionalAttribute] !== null)
{
$sPropertyName = substr($sOptionalAttribute, 0, -4);
$oAttDef = MetaModel::GetAttributeDef(get_class($aCurrentRowValues[0]), $aLevelsProperties[$aCurrentRowKeys[0]][$sOptionalAttribute]);
$tmpAttValue = $aCurrentRowValues[0]->Get($aLevelsProperties[$aCurrentRowKeys[0]][$sOptionalAttribute]);
$tmpAttValue = $aCurrentRowValues[0]->GetAsHTML($aLevelsProperties[$aCurrentRowKeys[0]][$sOptionalAttribute]);
if($sOptionalAttribute === 'image_att')
{
if (is_object($tmpAttValue) && !$tmpAttValue->IsEmpty())
@@ -733,7 +735,7 @@ class BrowseBrickController extends BrickController
}
else
{
$tmpAttValue = MetaModel::GetAttributeDef(get_class($aCurrentRowValues[0]), $aLevelsProperties[$aCurrentRowKeys[0]][$sOptionalAttribute])->Get('default_image');
$tmpAttValue = $oAttDef->Get('default_image');
}
}

View File

@@ -50,22 +50,22 @@ class ManageBrickController extends BrickController
{
const EXCEL_EXPORT_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/manage/popup-export-excel.html.twig';
/**
* @param \Symfony\Component\HttpFoundation\Request $oRequest
* @param \Silex\Application $oApp
* @param string $sBrickId
* @param string $sDisplayMode
* @param string $sGroupingTab
* @param string $sDataLoading
*
* @return \Symfony\Component\HttpFoundation\Response
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \OQLException
*/
public function DisplayAction(Request $oRequest, Application $oApp, $sBrickId, $sGroupingTab, $sDisplayMode = null, $sDataLoading = null)
/**
* @param Request $oRequest
* @param Application $oApp
* @param string $sBrickId
* @param string $sGroupingTab
* @param string $sDisplayMode
*
* @return Response
*
* @throws \Exception
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \MySQLException
* @throws \OQLException
*/
public function DisplayAction(Request $oRequest, Application $oApp, $sBrickId, $sGroupingTab, $sDisplayMode = null)
{
/** @var ManageBrick $oBrick */
$oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId);
@@ -160,7 +160,7 @@ class ManageBrickController extends BrickController
$oScopeHelper = $oApp['scope_validator'];
$oScopeHelper->AddScopeToQuery($oQuery, $sClass);
$aData = array();
$this->ManageSearchValue($oRequest, $aData, $oQuery, $sClass);
$this->ManageSearchValue($oApp, $aData, $oQuery, $sClass);
// Grouping tab
if ($oBrick->HasGroupingTabs())
@@ -256,11 +256,11 @@ class ManageBrickController extends BrickController
$bHasScope = true;
// Getting current dataloading mode (First from router parameter, then query parameter, then default brick value)
$sDataLoading = ($oRequest->get('sDataLoading') !== null) ? $oRequest->get('sDataLoading') : $oBrick->GetDataLoading();
$sDataLoading = $oApp['request_manipulator']->ReadParam('sDataLoading', $oBrick->GetDataLoading());
// - Retrieving the grouping areas to display
$sGroupingArea = $oRequest->get('sGroupingArea');
if (!is_null($sGroupingArea))
$sGroupingArea = $oApp['request_manipulator']->ReadParam('sGroupingArea', '');
if (!empty($sGroupingArea))
{
$bNeedDetails = true;
}
@@ -340,7 +340,7 @@ class ManageBrickController extends BrickController
}
// - Retrieving the current grouping tab to display if necessary and altering the query to do so
if ($sGroupingTab === null)
if (empty($sGroupingTab))
{
if ($oBrick->HasGroupingTabs())
{
@@ -361,7 +361,7 @@ class ManageBrickController extends BrickController
}
// - Adding search clause if necessary
$this->ManageSearchValue($oRequest, $aData, $oQuery, $sClass, $aColumnsAttrs);
$this->ManageSearchValue($oApp, $aData, $oQuery, $sClass, $aColumnsAttrs);
// Preparing areas
// - We need to retrieve distinct values for the grouping attribute
@@ -411,7 +411,7 @@ class ManageBrickController extends BrickController
}
// - If specified or lazy loading, we truncate the $aGroupingAreasValues to keep only this one
if ($sGroupingArea !== null)
if (!empty($sGroupingArea))
{
$aGroupingAreasValues = array($sGroupingArea => $aGroupingAreasValues[$sGroupingArea]);
}
@@ -467,8 +467,8 @@ class ManageBrickController extends BrickController
if ($sDataLoading === AbstractBrick::ENUM_DATA_LOADING_LAZY)
{
// Retrieving parameters
$iPageNumber = (int)$oRequest->get('iPageNumber', 1);
$iListLength = (int)$oRequest->get('iListLength', ManageBrick::DEFAULT_LIST_LENGTH);
$iPageNumber = (int)$oApp['request_manipulator']->ReadParam('iPageNumber', 1, FILTER_SANITIZE_NUMBER_INT);
$iListLength = (int)$oApp['request_manipulator']->ReadParam('iListLength', ManageBrick::DEFAULT_LIST_LENGTH, FILTER_SANITIZE_NUMBER_INT);
// Getting total records number
$oCountSet = new DBObjectSet($oQuery);
@@ -577,7 +577,7 @@ class ManageBrickController extends BrickController
$oAttDef = MetaModel::GetAttributeDef($sCurrentClass, $sItemAttr);
if ($oAttDef->IsExternalKey())
{
$sValue = $oCurrentRow->Get($sItemAttr.'_friendlyname');
$sValue = $oCurrentRow->GetAsHTML($sItemAttr.'_friendlyname');
// Adding a view action on the external keys
if ($oCurrentRow->Get($sItemAttr) !== $oAttDef->GetNullValue())
@@ -595,13 +595,22 @@ class ManageBrickController extends BrickController
}
}
}
elseif ($oAttDef instanceof AttributeSubItem || $oAttDef instanceof AttributeDuration)
{
$sValue = $oAttDef->GetAsHTML($oCurrentRow->Get($sItemAttr));
}
elseif ($oAttDef instanceof AttributeImage)
{
$oOrmDoc = $oCurrentRow->Get($sItemAttr);
if (is_object($oOrmDoc) && !$oOrmDoc->IsEmpty())
{
$sUrl = $oApp['url_generator']->generate('p_object_document_display', array('sObjectClass' => get_class($oCurrentRow), 'sObjectId' => $oCurrentRow->GetKey(), 'sObjectField' => $sItemAttr, 'cache' => 86400));
}
else
{
$sUrl = $oAttDef->Get('default_image');
}
$sValue = '<img src="' . $sUrl . '" />';
}
else
{
$sValue = $oAttDef->GetValueLabel($oCurrentRow->Get($sItemAttr));
$sValue = $oAttDef->GetAsHTML($oCurrentRow->Get($sItemAttr));
}
unset($oAttDef);
@@ -736,20 +745,24 @@ class ManageBrickController extends BrickController
return $aData;
}
/**
* @param Request $oRequest
* @param array $aData
* @param DBSearch $oQuery
* @param string $sClass
*/
protected function ManageSearchValue(Request $oRequest, &$aData, DBSearch &$oQuery, $sClass, $aColumnsAttrs)
/**
* @param Application $oApp
* @param array $aData
* @param DBSearch $oQuery
* @param string $sClass
* @param array $aColumnsAttrs
*
* @throws \Exception
* @throws \CoreException
*/
protected function ManageSearchValue(Application $oApp, &$aData, DBSearch &$oQuery, $sClass, $aColumnsAttrs)
{
// Getting search value
$sSearchValue = $oRequest->get('sSearchValue', null);
$sSearchValue = $oApp['request_manipulator']->ReadParam('sSearchValue', '');
// - Adding search clause if necessary
// Note : This is a very naive search at the moment
if ($sSearchValue !== null)
if (!empty($sSearchValue))
{
// Putting only valid attributes as one can define attributes of leaf classes in the brick definition (<fields>), but at this stage we are working on the abstract class.
// Note: This won't fix everything as the search will not be looking in all fields.

View File

@@ -40,6 +40,7 @@ use ListExpression;
use ScalarExpression;
use DBObjectSet;
use AttributeEnum;
use AttributeImage;
use AttributeFinalClass;
use AttributeFriendlyName;
use UserRights;
@@ -61,6 +62,8 @@ class ObjectController extends AbstractController
const ENUM_MODE_VIEW = 'view';
const ENUM_MODE_EDIT = 'edit';
const ENUM_MODE_CREATE = 'create';
const DEFAULT_PAGE_NUMBER = 1;
const DEFAULT_LIST_LENGTH = 10;
/**
@@ -97,6 +100,8 @@ class ObjectController extends AbstractController
$oApp->abort(404, Dict::S('UI:ObjectDoesNotExist'));
}
$sOperation = $oApp['request_manipulator']->ReadParam('operation', '');
$aData = array('sMode' => 'view');
$aData['form'] = $this->HandleForm($oRequest, $oApp, $aData['sMode'], $sObjectClass, $sObjectId);
$aData['form']['title'] = Dict::Format('Brick:Portal:Object:Form:View:Title', MetaModel::GetName($sObjectClass), $oObject->GetName());
@@ -117,7 +122,7 @@ class ObjectController extends AbstractController
if ($oRequest->isXmlHttpRequest())
{
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if ($oRequest->request->get('operation') === null)
if (empty($sOperation))
{
$oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData);
}
@@ -129,8 +134,8 @@ class ObjectController extends AbstractController
else
{
// Adding brick if it was passed
$sBrickId = $oRequest->get('sBrickId');
if ($sBrickId !== null)
$sBrickId = $oApp['request_manipulator']->ReadParam('sBrickId', '');
if (!empty($sBrickId))
{
$oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId);
if ($oBrick !== null)
@@ -172,6 +177,8 @@ class ObjectController extends AbstractController
$oApp->abort(404, Dict::S('UI:ObjectDoesNotExist'));
}
$sOperation = $oApp['request_manipulator']->ReadParam('operation', '');
$aData = array('sMode' => 'edit');
$aData['form'] = $this->HandleForm($oRequest, $oApp, $aData['sMode'], $sObjectClass, $sObjectId);
$aData['form']['title'] = Dict::Format('Brick:Portal:Object:Form:Edit:Title', MetaModel::GetName($sObjectClass), $aData['form']['object_name']);
@@ -180,7 +187,7 @@ class ObjectController extends AbstractController
if ($oRequest->isXmlHttpRequest())
{
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if ($oRequest->request->get('operation') === null)
if (empty($sOperation))
{
$oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData);
}
@@ -192,8 +199,8 @@ class ObjectController extends AbstractController
else
{
// Adding brick if it was passed
$sBrickId = $oRequest->get('sBrickId');
if ($sBrickId !== null)
$sBrickId = $oApp['request_manipulator']->ReadParam('sBrickId', '');
if (!empty($sBrickId))
{
$oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId);
if ($oBrick !== null)
@@ -225,6 +232,8 @@ class ObjectController extends AbstractController
$oApp->abort(404, Dict::S('UI:ObjectDoesNotExist'));
}
$sOperation = $oApp['request_manipulator']->ReadParam('operation', '');
$aData = array('sMode' => 'create');
$aData['form'] = $this->HandleForm($oRequest, $oApp, $aData['sMode'], $sObjectClass);
$aData['form']['title'] = Dict::Format('Brick:Portal:Object:Form:Create:Title', MetaModel::GetName($sObjectClass));
@@ -233,7 +242,7 @@ class ObjectController extends AbstractController
if ($oRequest->isXmlHttpRequest())
{
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if ($oRequest->request->get('operation') === null)
if (empty($sOperation))
{
$oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData);
}
@@ -245,8 +254,8 @@ class ObjectController extends AbstractController
else
{
// Adding brick if it was passed
$sBrickId = $oRequest->get('sBrickId');
if ($sBrickId !== null)
$sBrickId = $oApp['request_manipulator']->ReadParam('sBrickId', '');
if (!empty($sBrickId))
{
$oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId);
if ($oBrick !== null)
@@ -347,7 +356,7 @@ class ObjectController extends AbstractController
}
// Retrieving request parameters
$sOperation = $oRequest->request->get('operation');
$sOperation = $oApp['request_manipulator']->ReadParam('operation', '');
// Retrieving form properties
$aStimuliForms = ApplicationHelper::GetLoadedFormFromClass($oApp, $sObjectClass, 'apply_stimulus');
@@ -382,7 +391,7 @@ class ObjectController extends AbstractController
// TODO : This is a ugly patch to avoid showing a modal with a readonly form to the user as it would prevent user from finishing the transition.
// Instead, we apply the stimulus directly here and then go to the edited object.
if ($sOperation === null)
if (empty($sOperation))
{
if (isset($aData['form']['editable_fields_count']) && $aData['form']['editable_fields_count'] === 0)
{
@@ -390,7 +399,7 @@ class ObjectController extends AbstractController
$oSubRequest = $oRequest;
$oSubRequest->request->set('operation', 'submit');
$oSubRequest->request->set('stimulus_code', null);
$oSubRequest->request->set('stimulus_code', '');
$aData = array('sMode' => 'apply_stimulus');
$aData['form'] = $this->HandleForm($oSubRequest, $oApp, $aData['sMode'], $sObjectClass, $sObjectId, $aFormProperties);
@@ -405,7 +414,7 @@ class ObjectController extends AbstractController
if ($oRequest->isXmlHttpRequest())
{
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if ($sOperation === null)
if (empty($sOperation))
{
$oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData);
}
@@ -429,9 +438,8 @@ class ObjectController extends AbstractController
public static function HandleForm(Request $oRequest, Application $oApp, $sMode, $sObjectClass, $sObjectId = null, $aFormProperties = null)
{
$aFormData = array();
$oRequestParams = $oRequest->request;
$sOperation = $oRequestParams->get('operation');
$bModal = ($oRequest->isXmlHttpRequest() && ($oRequest->request->get('operation') === null) );
$sOperation = $oApp['request_manipulator']->ReadParam('operation', '');
$bModal = ($oRequest->isXmlHttpRequest() && empty($sOperation));
// - Retrieve form properties
if ($aFormProperties === null)
@@ -440,14 +448,14 @@ class ObjectController extends AbstractController
}
// - Create and
if ($sOperation === null)
if (empty($sOperation))
{
// Retrieving action rules
//
// Note : The action rules must be a base64-encoded JSON object, this is just so users are tempted to changes values.
// But it would not be a security issue as it only presets values in the form.
$sActionRulesToken = $oRequest->get('ar_token');
$aActionRules = ($sActionRulesToken !== null) ? ContextManipulatorHelper::DecodeRulesToken($sActionRulesToken) : array();
$sActionRulesToken = $oApp['request_manipulator']->ReadParam('ar_token', '');
$aActionRules = (!empty($sActionRulesToken)) ? ContextManipulatorHelper::DecodeRulesToken($sActionRulesToken) : array();
// Preparing object
if ($sObjectId === null)
@@ -520,9 +528,11 @@ class ObjectController extends AbstractController
}
else
{
$aPrefillFormParam = array('user' => $_SESSION["auth_user"],
$aPrefillFormParam = array(
'user' => $_SESSION["auth_user"],
'origin' => 'portal',
'stimulus' => $oRequestParams->get('apply_stimulus')['code']);
'stimulus' => $oApp['request_manipulator']->ReadParam('apply_stimulus', null)['code'],
);
$oObject->PrefillForm('state_change', $aPrefillFormParam);
}
@@ -560,9 +570,9 @@ class ObjectController extends AbstractController
else
{
// Update / Submit / Cancel
$sFormManagerClass = $oRequestParams->get('formmanager_class');
$sFormManagerData = $oRequestParams->get('formmanager_data');
if ($sFormManagerClass === null || $sFormManagerData === null)
$sFormManagerClass = $oApp['request_manipulator']->ReadParam('formmanager_class', '', FILTER_UNSAFE_RAW);
$sFormManagerData = $oApp['request_manipulator']->ReadParam('formmanager_data', '', FILTER_UNSAFE_RAW);
if ( empty($sFormManagerClass) || empty($sFormManagerData) )
{
IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Parameters formmanager_class and formamanager_data must be defined.');
$oApp->abort(500, 'Parameters formmanager_class and formmanager_data must be defined.');
@@ -584,13 +594,13 @@ class ObjectController extends AbstractController
{
case 'submit':
// Applying modification to object
$aFormData['validation'] = $oFormManager->OnSubmit(array('currentValues' => $oRequestParams->get('current_values'), 'attachmentIds' => $oRequest->get('attachment_ids'), 'formProperties' => $aFormProperties, 'applyStimulus' => $oRequestParams->get('apply_stimulus')));
$aFormData['validation'] = $oFormManager->OnSubmit(array('currentValues' => $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW), 'attachmentIds' => $oApp['request_manipulator']->ReadParam('attachment_ids', array(), FILTER_UNSAFE_RAW), 'formProperties' => $aFormProperties, 'applyStimulus' => $oApp['request_manipulator']->ReadParam('apply_stimulus', null)));
if ($aFormData['validation']['valid'] === true)
{
// Note : We don't use $sObjectId there as it can be null if we are creating a new one. Instead we use the id from the created object once it has been seralized
// Check if stimulus has to be applied
$sStimulusCode = ($oRequestParams->get('stimulus_code') !== null && $oRequestParams->get('stimulus_code') !== '') ? $oRequestParams->get('stimulus_code') : null;
if ($sStimulusCode !== null)
$sStimulusCode = $oApp['request_manipulator']->ReadParam('stimulus_code', '');
if (!empty($sStimulusCode))
{
$aFormData['validation']['redirection'] = array(
'url' => $oApp['url_generator']->generate('p_object_apply_stimulus', array('sObjectClass' => $sObjectClass, 'sObjectId' => $oFormManager->GetObject()->GetKey(), 'sStimulusCode' => $sStimulusCode)),
@@ -598,17 +608,17 @@ class ObjectController extends AbstractController
);
}
// Otherwise, we show the object if there is no default
else
{
// else
// {
// $aFormData['validation']['redirection'] = array(
// 'alternative_url' => $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $oFormManager->GetObject()->GetKey()))
// );
}
// }
}
break;
case 'update':
$oFormManager->OnUpdate(array('currentValues' => $oRequestParams->get('current_values'), 'formProperties' => $aFormProperties));
$oFormManager->OnUpdate(array('currentValues' => $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW), 'formProperties' => $aFormProperties));
break;
case 'cancel':
@@ -627,11 +637,11 @@ class ObjectController extends AbstractController
// Preparing fields list regarding the operation
if ($sOperation === 'update')
{
$aRequestedFields = $oRequestParams->get('requested_fields');
$sFormPath = $oRequestParams->get('form_path');
$aRequestedFields = $oApp['request_manipulator']->ReadParam('requested_fields', array(), FILTER_UNSAFE_RAW);
$sFormPath = $oApp['request_manipulator']->ReadParam('form_path', '');
// Checking if the update was on a subform, if so we need to make the rendering for that part only
if ($sFormPath !== null && $sFormPath !== $oFormManager->GetForm()->GetId())
if ( !empty($sFormPath) && $sFormPath !== $oFormManager->GetForm()->GetId() )
{
$oSubForm = $oFormManager->GetForm()->FindSubForm($sFormPath);
$oSubFormRenderer = new BsFormRenderer($oSubForm);
@@ -716,8 +726,8 @@ class ObjectController extends AbstractController
//
// Note : The action rules must be a base64-encoded JSON object, this is just so users are tempted to changes values.
// But it would not be a security issue as it only presets values in the form.
$sActionRulesToken = $oRequest->get('ar_token');
$aActionRules = ($sActionRulesToken !== null) ? ContextManipulatorHelper::DecodeRulesToken($sActionRulesToken) : array();
$sActionRulesToken = $oApp['request_manipulator']->ReadParam('ar_token', '');
$aActionRules = (!empty($sActionRulesToken)) ? ContextManipulatorHelper::DecodeRulesToken($sActionRulesToken) : array();
// Preparing object
$oApp['context_manipulator']->PrepareObject($aActionRules, $oHostObject);
}
@@ -725,7 +735,7 @@ class ObjectController extends AbstractController
// Updating host object with form data / values
$sFormManagerClass = $aRequestContent['formmanager_class'];
$sFormManagerData = $aRequestContent['formmanager_data'];
if ($sFormManagerClass !== null && $sFormManagerData !== null)
if (!empty($sFormManagerClass) && !empty($sFormManagerData))
{
$oFormManager = $sFormManagerClass::FromJSON($sFormManagerData);
$oFormManager->SetApplication($oApp);
@@ -837,7 +847,7 @@ class ObjectController extends AbstractController
'sTargetAttCode' => $sTargetAttCode,
'sHostObjectClass' => $sHostObjectClass,
'sHostObjectId' => $sHostObjectId,
'sActionRulesToken' => $oRequest->get('ar_token')
'sActionRulesToken' => $oApp['request_manipulator']->ReadParam('ar_token', ''),
);
// Checking security layers
@@ -860,16 +870,15 @@ class ObjectController extends AbstractController
//
// Note : The action rules must be a base64-encoded JSON object, this is just so users are tempted to changes values.
// But it would not be a security issue as it only presets values in the form.
$aActionRules = ($aData['sActionRulesToken'] !== null) ? ContextManipulatorHelper::DecodeRulesToken($aData['sActionRulesToken']) : array();
$aActionRules = !empty($aData['sActionRulesToken']) ? ContextManipulatorHelper::DecodeRulesToken($aData['sActionRulesToken']) : array();
// Preparing object
$oApp['context_manipulator']->PrepareObject($aActionRules, $oHostObject);
}
// Updating host object with form data / values
$oRequestParams = $oRequest->request;
$sFormManagerClass = $oRequestParams->get('formmanager_class');
$sFormManagerData = $oRequestParams->get('formmanager_data');
if ($sFormManagerClass !== null && $sFormManagerData !== null)
$sFormManagerClass = $oApp['request_manipulator']->ReadParam('formmanager_class', '', FILTER_UNSAFE_RAW);
$sFormManagerData = $oApp['request_manipulator']->ReadParam('formmanager_data', '', FILTER_UNSAFE_RAW);
if ( !empty($sFormManagerClass) && !empty($sFormManagerData) )
{
$oFormManager = $sFormManagerClass::FromJSON($sFormManagerData);
$oFormManager->SetApplication($oApp);
@@ -885,18 +894,18 @@ class ObjectController extends AbstractController
}
// Updating host object
$oFormManager->OnUpdate(array('currentValues' => $oRequestParams->get('current_values')));
$oFormManager->OnUpdate(array('currentValues' => $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW)));
$oHostObject = $oFormManager->GetObject();
}
// Retrieving request parameters
$iPageNumber = ($oRequest->get('iPageNumber') !== null) ? $oRequest->get('iPageNumber') : 1;
$iListLength = ($oRequest->get('iListLength') !== null) ? $oRequest->get('iListLength') : static::DEFAULT_LIST_LENGTH;
$bInitalPass = ($oRequest->get('draw') === null) ? true : false;
$sQuery = $oRequest->get('sSearchValue');
$sFormPath = $oRequest->get('sFormPath');
$sFieldId = $oRequest->get('sFieldId');
$aObjectIdsToIgnore = $oRequest->get('aObjectIdsToIgnore');
$iPageNumber = $oApp['request_manipulator']->ReadParam('iPageNumber', static::DEFAULT_PAGE_NUMBER, FILTER_SANITIZE_NUMBER_INT);
$iListLength = $oApp['request_manipulator']->ReadParam('iListLength', static::DEFAULT_LIST_LENGTH, FILTER_SANITIZE_NUMBER_INT);
$bInitalPass = $oApp['request_manipulator']->HasParam('draw') ? false : true;
$sQuery = $oApp['request_manipulator']->ReadParam('sSearchValue', '');
$sFormPath = $oApp['request_manipulator']->ReadParam('sFormPath', '');
$sFieldId = $oApp['request_manipulator']->ReadParam('sFieldId', '');
$aObjectIdsToIgnore = $oApp['request_manipulator']->ReadParam('aObjectIdsToIgnore', null, FILTER_UNSAFE_RAW);
// Building search query
// - Retrieving target object class from attcode
@@ -977,7 +986,7 @@ class ObjectController extends AbstractController
// - Adding query condition
$aInternalParams['this'] = $oHostObject;
if ($sQuery !== null)
if (!empty($sQuery))
{
$oFullExpr = null;
for ($i = 0; $i < count($aAttCodes); $i++)
@@ -1352,9 +1361,9 @@ class ObjectController extends AbstractController
}
// Retrieving ormDocument's host object
$sObjectClass = $oRequest->get('sObjectClass');
$sObjectId = $oRequest->get('sObjectId');
$sObjectField = $oRequest->get('sObjectField');
$sObjectClass = $oApp['request_manipulator']->ReadParam('sObjectClass', '');
$sObjectId = $oApp['request_manipulator']->ReadParam('sObjectId', '');
$sObjectField = $oApp['request_manipulator']->ReadParam('sObjectField', '');
// When reaching to an Attachment, we have to check security on its host object instead of the Attachment itself
if($sObjectClass === 'Attachment')
@@ -1394,8 +1403,7 @@ class ObjectController extends AbstractController
}
else
{
$sCache = $oRequest->get('cache');
$iCacheSec = ($sCache !== null) ? (int) $sCache : 0;
$iCacheSec = $oApp['request_manipulator']->ReadParam('cache', 0, FILTER_SANITIZE_NUMBER_INT);
}
$aHeaders = array();
@@ -1436,16 +1444,16 @@ class ObjectController extends AbstractController
// Retrieving sOperation from request only if it wasn't forced (determined by the route)
if ($sOperation === null)
{
$sOperation = $oRequest->get('operation');
$sOperation = $oApp['request_manipulator']->ReadParam('operation', null);
}
switch ($sOperation)
{
case 'add':
$sFieldName = $oRequest->get('field_name');
$sObjectClass = $oRequest->get('object_class');
$sTempId = $oRequest->get('temp_id');
$sFieldName = $oApp['request_manipulator']->ReadParam('field_name', '');
$sObjectClass = $oApp['request_manipulator']->ReadParam('object_class', '');
$sTempId = $oApp['request_manipulator']->ReadParam('temp_id', '');
if (($sObjectClass === null) || ($sTempId === null))
if (empty($sObjectClass) || empty($sTempId))
{
$aData['error'] = Dict::Format('UI:Error:2ParametersMissing', 'object_class', 'temp_id');
}
@@ -1484,7 +1492,7 @@ class ObjectController extends AbstractController
// - Route
$aRouteParams = array(
'sObjectClass' => 'Attachment',
'sObjectId' => $oRequest->get('sAttachmentId'),
'sObjectId' => $oApp['request_manipulator']->ReadParam('sAttachmentId', null),
'sObjectField' => 'contents',
);
$sRedirectRoute = $oApp['url_generator']->generate('p_object_document_download', $aRouteParams);
@@ -1519,10 +1527,10 @@ class ObjectController extends AbstractController
$aData = array();
// Retrieving parameters
$sObjectClass = $oRequest->Get('sObjectClass');
$aObjectIds = $oRequest->Get('aObjectIds');
$aObjectAttCodes = $oRequest->Get('aObjectAttCodes');
if ($sObjectClass === null || $aObjectIds === null || $aObjectAttCodes === null)
$sObjectClass = $oApp['request_manipulator']->ReadParam('sObjectClass', '');
$aObjectIds = $oApp['request_manipulator']->ReadParam('aObjectIds', array(), FILTER_UNSAFE_RAW);
$aObjectAttCodes = $oApp['request_manipulator']->ReadParam('aObjectAttCodes', array(), FILTER_UNSAFE_RAW);
if ( empty($sObjectClass) || empty($aObjectIds) || empty($aObjectAttCodes) )
{
IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : sObjectClass, sObjectId and aObjectAttCodes expected, "' . $sObjectClass . '", "' . $sObjectId . '" given.');
$oApp->abort(500, 'Invalid request data, some informations are missing');
@@ -1590,7 +1598,7 @@ class ObjectController extends AbstractController
if ($oAttDef->IsExternalKey())
{
$aAttData['value'] = $oObject->Get($oAttDef->GetCode() . '_friendlyname');
$aAttData['value'] = $oObject->GetAsHTML($oAttDef->GetCode() . '_friendlyname');
// Checking if user can access object's external key
if (SecurityHelper::IsActionAllowed($oApp, UR_ACTION_READ, $oAttDef->GetTargetClass()))
@@ -1603,9 +1611,22 @@ class ObjectController extends AbstractController
// We skip it
continue;
}
elseif ($oAttDef instanceof AttributeImage)
{
$oOrmDoc = $oObject->Get($oAttDef->GetCode());
if (is_object($oOrmDoc) && !$oOrmDoc->IsEmpty())
{
$sUrl = $oApp['url_generator']->generate('p_object_document_display', array('sObjectClass' => get_class($oObject), 'sObjectId' => $oObject->GetKey(), 'sObjectField' => $oAttDef->GetCode(), 'cache' => 86400));
}
else
{
$sUrl = $oAttDef->Get('default_image');
}
$aAttData['value'] = '<img src="' . $sUrl . '" />';
}
else
{
$aAttData['value'] = $oAttDef->GetValueLabel($oObject->Get($oAttDef->GetCode()));
$aAttData['value'] = $oAttDef->GetAsHTML($oObject->Get($oAttDef->GetCode()));
if ($oAttDef instanceof AttributeFriendlyName)
{

View File

@@ -71,7 +71,7 @@ class UserProfileBrickController extends BrickController
// If this is ajax call, we are just submiting preferences or password forms
if ($oRequest->isXmlHttpRequest())
{
$aCurrentValues = $oRequest->request->get('current_values');
$aCurrentValues = $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW);
$sFormType = $aCurrentValues['form_type'];
if ($sFormType === PreferencesFormManager::FORM_TYPE)
{
@@ -120,10 +120,9 @@ class UserProfileBrickController extends BrickController
public function HandlePreferencesForm(Request $oRequest, Application $oApp, $sFormMode)
{
$aFormData = array();
$oRequestParams = $oRequest->request;
// Handling form
$sOperation = $oRequestParams->get('operation');
$sOperation = $oApp['request_manipulator']->ReadParam('operation', null);
// - Create
if ($sOperation === null)
{
@@ -143,8 +142,8 @@ class UserProfileBrickController extends BrickController
// - Submit
else if ($sOperation === 'submit')
{
$sFormManagerClass = $oRequestParams->get('formmanager_class');
$sFormManagerData = $oRequestParams->get('formmanager_data');
$sFormManagerClass = $oApp['request_manipulator']->ReadParam('formmanager_class', null, FILTER_UNSAFE_RAW);
$sFormManagerData = $oApp['request_manipulator']->ReadParam('formmanager_data', null, FILTER_UNSAFE_RAW);
if ($sFormManagerClass === null || $sFormManagerData === null)
{
IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Parameters formmanager_class and formamanager_data must be defined.');
@@ -154,7 +153,7 @@ class UserProfileBrickController extends BrickController
// Rebuilding manager from json
$oFormManager = $sFormManagerClass::FromJSON($sFormManagerData);
// Applying modification to object
$aFormData['validation'] = $oFormManager->OnSubmit(array('currentValues' => $oRequestParams->get('current_values')));
$aFormData['validation'] = $oFormManager->OnSubmit(array('currentValues' => $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW)));
// Reloading page only if preferences were changed
if (($aFormData['validation']['valid'] === true) && !empty($aFormData['validation']['messages']['success']))
{
@@ -188,10 +187,9 @@ class UserProfileBrickController extends BrickController
public function HandlePasswordForm(Request $oRequest, Application $oApp)
{
$aFormData = array();
$oRequestParams = $oRequest->request;
// Handling form
$sOperation = $oRequestParams->get('operation');
$sOperation = $oApp['request_manipulator']->ReadParam('operation', null);
// - Create
if ($sOperation === null)
{
@@ -206,8 +204,8 @@ class UserProfileBrickController extends BrickController
// - Submit
else if ($sOperation === 'submit')
{
$sFormManagerClass = $oRequestParams->get('formmanager_class');
$sFormManagerData = $oRequestParams->get('formmanager_data');
$sFormManagerClass = $oApp['request_manipulator']->ReadParam('formmanager_class', null, FILTER_UNSAFE_RAW);
$sFormManagerData = $oApp['request_manipulator']->ReadParam('formmanager_data', null, FILTER_UNSAFE_RAW);
if ($sFormManagerClass === null || $sFormManagerData === null)
{
IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Parameters formmanager_class and formamanager_data must be defined.');
@@ -217,7 +215,7 @@ class UserProfileBrickController extends BrickController
// Rebuilding manager from json
$oFormManager = $sFormManagerClass::FromJSON($sFormManagerData);
// Applying modification to object
$aFormData['validation'] = $oFormManager->OnSubmit(array('currentValues' => $oRequestParams->get('current_values')));
$aFormData['validation'] = $oFormManager->OnSubmit(array('currentValues' => $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW)));
}
else
{
@@ -244,11 +242,10 @@ class UserProfileBrickController extends BrickController
public function HandlePictureForm(Request $oRequest, Application $oApp, $sFormMode)
{
$aFormData = array();
$oRequestParams = $oRequest->request;
$sPictureAttCode = 'picture';
// Handling form
$sOperation = $oRequestParams->get('operation');
$sOperation = $oApp['request_manipulator']->ReadParam('operation', null);
// - No operation specified
if ($sOperation === null)
{

View File

@@ -0,0 +1,119 @@
<?php
/**
* Copyright (C) 2012-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/>
*/
namespace Combodo\iTop\Portal\Helper;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* RequestManipulatorHelper class
*
* Handle basic requests manipulation.
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @since 2.5.1
*/
class RequestManipulatorHelper
{
/** @var \Symfony\Component\HttpFoundation\RequestStack $oRequestStack */
protected $oRequestStack;
/**
* RequestManipulatorHelper constructor.
*
* @param \Symfony\Component\HttpFoundation\RequestStack $oRequestStack
*/
public function __construct(RequestStack &$oRequestStack)
{
$this->oRequestStack = $oRequestStack;
}
/**
* @return \Symfony\Component\HttpFoundation\Request
*/
public function GetCurrentRequest()
{
return $this->oRequestStack->getCurrentRequest();
}
/**
* Returns if the request has a $sKey parameter.
* This looks in the GET arguments first, then PATH and finally the POST data.
*
* @param string $sKey
*
* @return bool
*/
public function HasParam($sKey)
{
if ($this->GetCurrentRequest()->query->has($sKey))
{
return true;
}
if ($this->GetCurrentRequest()->attributes->has($sKey))
{
return true;
}
if ($this->GetCurrentRequest()->request->has($sKey))
{
return true;
}
return false;
}
/**
* Returns the $sKey parameter from the request filtered with $iFilter.
* This looks in the GET arguments first, then the PATH and finally the POST data.
*
* Note: It is inspired by the \Symfony\Component\HttpFoundation\ParameterBag::filter() function and was necessary as we sometimes have parameters that can be either in the GET/PATH/POST arguments and need to be filtered. Silex only offer the possibility to filter parameter from a single ParameterBag, so we created this helper.
*
* @param string $sKey
* @param mixed $default
* @param int $iFilter Default is FILTER_SANITIZE_STRING
*
* @return mixed|null
*
* @since 2.5.1
*/
public function ReadParam($sKey, $default = null, $iFilter = FILTER_SANITIZE_STRING)
{
if ($this->GetCurrentRequest()->query->has($sKey))
{
return $this->GetCurrentRequest()->query->filter($sKey, $default, $iFilter);
}
if ($this->GetCurrentRequest()->attributes->has($sKey))
{
return $this->GetCurrentRequest()->attributes->filter($sKey, $default, $iFilter);
}
if ($this->GetCurrentRequest()->request->has($sKey))
{
return $this->GetCurrentRequest()->request->filter($sKey, $default, $iFilter);
}
return $default;
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Copyright (C) 2012-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/>
*/
namespace Combodo\iTop\Portal\Provider;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Combodo\iTop\Portal\Helper\RequestManipulatorHelper;
/**
* RequestManipulatorHelper service provider
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @since 2.5.1
*/
class RequestManipulatorServiceProvider implements ServiceProviderInterface
{
/**
* @param \Pimple\Container $oApp
*/
public function register(Container $oApp)
{
$oApp['request_manipulator'] = function ($oApp)
{
$oApp->flush();
$oRequestManipulatorHelper = new RequestManipulatorHelper($oApp['request_stack']);
return $oRequestManipulatorHelper;
};
}
/**
* @param \Pimple\Container $oApp
*/
public function boot(Container $oApp)
{
}
}

View File

@@ -36,6 +36,8 @@ require_once __DIR__ . '/../src/providers/urlgeneratorserviceprovider.class.inc.
require_once __DIR__ . '/../src/helpers/urlgeneratorhelper.class.inc.php';
require_once __DIR__ . '/../src/providers/contextmanipulatorserviceprovider.class.inc.php';
require_once __DIR__ . '/../src/helpers/contextmanipulatorhelper.class.inc.php';
require_once __DIR__ . '/../src/providers/requestmanipulatorserviceprovider.class.inc.php';
require_once __DIR__ . '/../src/helpers/requestmanipulatorhelper.class.inc.php';
require_once __DIR__ . '/../src/providers/scopevalidatorserviceprovider.class.inc.php';
require_once __DIR__ . '/../src/helpers/scopevalidatorhelper.class.inc.php';
require_once __DIR__ . '/../src/providers/lifecyclevalidatorserviceprovider.class.inc.php';
@@ -99,6 +101,9 @@ $oApp->before(function(Symfony\Component\HttpFoundation\Request $oRequest, Silex
die(Dict::S('Portal:ErrorNoContactForThisUser'));
}
// Register request manipulator now that the request has been created.
$oApp->register(new Combodo\iTop\Portal\Provider\RequestManipulatorServiceProvider());
// Enable archived data
utils::InitArchiveMode();

View File

@@ -79,6 +79,10 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:Service/Attribute:org_id+' => '',
'Class:Service/Attribute:organization_name' => 'Provider Name',
'Class:Service/Attribute:organization_name+' => '',
'Class:Service/Attribute:servicefamily_id' => 'Service-Familie',
'Class:Service/Attribute:servicefamily_id+' => '',
'Class:Service/Attribute:servicefamily_name' => 'Service-Familien-Name',
'Class:Service/Attribute:servicefamily_name+' => '',
'Class:Service/Attribute:description' => 'Beschreibung',
'Class:Service/Attribute:description+' => '',
'Class:Service/Attribute:documents_list' => 'Dokumente',

View File

@@ -199,7 +199,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Brick:Portal:OngoingRequests:Tab:OnGoing' => 'Offen',
'Brick:Portal:OngoingRequests:Tab:Resolved' => 'Gelöst',
'Brick:Portal:ClosedRequests:Title' => 'Geschlossene Störungen/Anfragen',
'Class:Ticket/Attribute:operational_status' => 'Status',
'Class:Ticket/Attribute:operational_status' => 'Betriebsstatus',
'Class:Ticket/Attribute:operational_status+' => 'Berechnet nach detailliertem Status',
'Class:Ticket/Attribute:operational_status/Value:ongoing' => 'In Bearbeitung',
'Class:Ticket/Attribute:operational_status/Value:ongoing+' => 'In Bearbeitung',

View File

@@ -1010,7 +1010,7 @@ Ved tilknytningen til en trigger, bliver hver handling tildelt et "rækkefølge"
'UI:ResetPwd-Error-Send' => 'email transport technical issue. Please Contact your administrator.~~',
'UI:ResetPwd-EmailSent' => 'Please check your email box and follow the instructions...~~',
'UI:ResetPwd-EmailSubject' => 'Reset your iTop password~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href=\"%1$s\">enter a new password</a></p>.~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href="%1$s">enter a new password</a></p>.~~',
'UI:ResetPwd-Title' => 'Reset password~~',
'UI:ResetPwd-Error-InvalidToken' => 'Sorry, either the password has already been reset, or you have received several emails. Please make sure that you use the link provided in the very last email received.~~',
'UI:ResetPwd-Error-EnterPassword' => 'Enter a new password for the account \'%1$s\'.~~',

View File

@@ -1177,6 +1177,7 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm
// Search form
'UI:Search:Toggle' => 'Ein-/Ausklappen',
'UI:Search:AutoSubmit:DisabledHint' => 'Automatische Eingabe für diese Klasse deaktiviert',
'UI:Search:NoAutoSubmit:ExplainText' => 'Fügen Sie ein Kriterium in das Suchfeld ein oder klicken Sie auf die Suchschaltfläche, um die Objekte anzuzeigen.',
'UI:Search:Criterion:MoreMenu:AddCriteria' => 'Kriterium hinzufügen',
// - Add new criteria button
'UI:Search:AddCriteria:List:RecentlyUsed:Title' => 'Kürzlich verwendet',

View File

@@ -1409,6 +1409,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
// Search form
'UI:Search:Toggle' => 'Minimize / Expand',
'UI:Search:AutoSubmit:DisabledHint' => 'Auto submit has been disabled for this class',
'UI:Search:NoAutoSubmit:ExplainText' => 'Add some criterion on the search box or click the search button to view the objects.',
'UI:Search:Criterion:MoreMenu:AddCriteria' => 'Add new criteria',
// - Add new criteria button
'UI:Search:AddCriteria:List:RecentlyUsed:Title' => 'Recently used',

View File

@@ -1242,6 +1242,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
// Search form
'UI:Search:Toggle' => 'Réduire / Ouvrir',
'UI:Search:AutoSubmit:DisabledHint' => 'La soumission automatique a été desactivée pour cette classe',
'UI:Search:NoAutoSubmit:ExplainText' => 'Ajoutez des critères dans le formulaire de recherche ou cliquez sur le bouton rechercher pour voir les objets.',
'UI:Search:Criterion:MoreMenu:AddCriteria' => 'Ajouter un critère',
// - Add new criteria button
'UI:Search:AddCriteria:List:RecentlyUsed:Title' => 'Récents',

View File

@@ -822,7 +822,7 @@ Akció kiváltó okhoz rendelésekor kap egy sorszámot , amely meghatározza az
'UI:ResetPwd-Error-Send' => 'email transport technical issue. Please Contact your administrator.~~',
'UI:ResetPwd-EmailSent' => 'Please check your email box and follow the instructions...~~',
'UI:ResetPwd-EmailSubject' => 'Reset your iTop password~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href=\"%1$s\">enter a new password</a></p>.~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href="%1$s">enter a new password</a></p>.~~',
'UI:ResetPwd-Title' => 'Reset password~~',
'UI:ResetPwd-Error-InvalidToken' => 'Sorry, either the password has already been reset, or you have received several emails. Please make sure that you use the link provided in the very last email received.~~',
'UI:ResetPwd-Error-EnterPassword' => 'Enter a new password for the account \'%1$s\'.~~',

View File

@@ -946,7 +946,7 @@ Quando è associata a un trigger, ad ogni azione è assegnato un numero "ordine"
'UI:ResetPwd-Error-Send' => 'email transport technical issue. Please Contact your administrator.~~',
'UI:ResetPwd-EmailSent' => 'Please check your email box and follow the instructions...~~',
'UI:ResetPwd-EmailSubject' => 'Reset your iTop password~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href=\"%1$s\">enter a new password</a></p>.~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href="%1$s">enter a new password</a></p>.~~',
'UI:ResetPwd-Title' => 'Reset password~~',
'UI:ResetPwd-Error-InvalidToken' => 'Sorry, either the password has already been reset, or you have received several emails. Please make sure that you use the link provided in the very last email received.~~',
'UI:ResetPwd-Error-EnterPassword' => 'Enter a new password for the account \'%1$s\'.~~',

View File

@@ -1007,7 +1007,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'UI:ResetPwd-Error-Send' => 'email transport technical issue. Please Contact your administrator.~~',
'UI:ResetPwd-EmailSent' => 'Please check your email box and follow the instructions...~~',
'UI:ResetPwd-EmailSubject' => 'Reset your iTop password~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href=\"%1$s\">enter a new password</a></p>.~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href="%1$s">enter a new password</a></p>.~~',
'UI:ResetPwd-Title' => 'Reset password~~',
'UI:ResetPwd-Error-InvalidToken' => 'Sorry, either the password has already been reset, or you have received several emails. Please make sure that you use the link provided in the very last email received.~~',
'UI:ResetPwd-Error-EnterPassword' => 'Enter a new password for the account \'%1$s\'.~~',

View File

@@ -930,7 +930,7 @@ Tetikleme gerçekleştiriğinde işlemler tanımlanan sıra numarası ile gerçe
'UI:ResetPwd-Error-Send' => 'email transport technical issue. Please Contact your administrator.~~',
'UI:ResetPwd-EmailSent' => 'Please check your email box and follow the instructions...~~',
'UI:ResetPwd-EmailSubject' => 'Reset your iTop password~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href=\"%1$s\">enter a new password</a></p>.~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href="%1$s">enter a new password</a></p>.~~',
'UI:ResetPwd-Title' => 'Reset password~~',
'UI:ResetPwd-Error-InvalidToken' => 'Sorry, either the password has already been reset, or you have received several emails. Please make sure that you use the link provided in the very last email received.~~',
'UI:ResetPwd-Error-EnterPassword' => 'Enter a new password for the account \'%1$s\'.~~',

View File

@@ -928,7 +928,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'UI:ResetPwd-Error-Send' => 'email transport technical issue. Please Contact your administrator.~~',
'UI:ResetPwd-EmailSent' => 'Please check your email box and follow the instructions...~~',
'UI:ResetPwd-EmailSubject' => 'Reset your iTop password~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href=\"%1$s\">enter a new password</a></p>.~~',
'UI:ResetPwd-EmailBody' => '<body><p>You have requested to reset your iTop password.</p><p>Please follow this link (single usage) to <a href="%1$s">enter a new password</a></p>.~~',
'UI:ResetPwd-Title' => 'Reset password~~',
'UI:ResetPwd-Error-InvalidToken' => 'Sorry, either the password has already been reset, or you have received several emails. Please make sure that you use the link provided in the very last email received.~~',
'UI:ResetPwd-Error-EnterPassword' => 'Enter a new password for the account \'%1$s\'.~~',

View File

@@ -193,7 +193,7 @@
var result;
if( data && data.length ) {
for (var i=0; i < data.length; i++) {
if( data[i].result.toLowerCase() == q.toLowerCase() ) {
if( data[i].result.toLowerCase() === q.toLowerCase() ) {
result = data[i];
break;
}
@@ -386,6 +386,7 @@
$.ajax({
// try to leverage ajaxQueue plugin to abort previous requests
mode: "abort",
method: "POST",
// limit abortion to this input
port: "autocomplete" + input.name,
dataType: options.dataType,

View File

@@ -152,7 +152,7 @@ $(function()
_initChooseDefaultOperator: function()
{
//if the class has an index, in order to maximize the performance, we force the default operator to "equal"
if (this.options.field.has_index && typeof this.options.available_operators['='] == 'object')
if (this.options.field.has_index && typeof this.options.available_operators['='] == 'object' && this.options.values.length == 0)
{
this.options.operator = '=';
this.options.available_operators['='].rank = -1;//we want it to be the first displayed

View File

@@ -180,17 +180,11 @@ $(function()
$('body').on('update_history.itop', function(oEvent, oData) {
// if (me.element.parents('.ui-dialog').length != 0)
// {
// //search form in modal are forbidden to update history!
// return;
// }
if ($('.ui-dialog:visible :itop-search_form_handler').length != 0)
{
//if a modal containing a search form is visible then the history update event come from it, whe do not want to update the history in this case! because search form in modal are forbidden to update history!
return;
}
if (me.element.parents('.ui-dialog').length !== 0)
{
//search form in modal are forbidden to update history!
return;
}
var sNewUrl = GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=search';
sNewUrl = sNewUrl + '&filter='+oData['filter'];
@@ -235,18 +229,39 @@ $(function()
}]
};
// - Retrieve criterion
this.elements.criterion_area.find('.sf_criterion_row').each(function(iIdx){
var oCriterionRowElem = $(this);
var iCurrentCriterionRow = 0;
this.elements.criterion_area.find('.sf_criterion_row').each(function (iDomCriterionRowIdx) {
var isFirstRow = (iDomCriterionRowIdx === 0),
oCriterionRowElem = $(this),
oCriteriaRowCriterias = oCriterionRowElem.find('.search_form_criteria');
oCriterion['or'][iIdx] = {'and': []};
oCriterionRowElem.find('.search_form_criteria').each(function(){
var oCriteriaData = $(this).triggerHandler('itop.search.criteria.get_data');
if (null != oCriteriaData)
if (oCriteriaRowCriterias.length === 0)
{
if (!isFirstRow)
{
oCriterion['or'][iIdx]['and'].push(oCriteriaData);
}
});
$(this).remove();
}
}
else
{
oCriteriaRowCriterias.each(function () {
var oCriteriaData = $(this).triggerHandler('itop.search.criteria.get_data');
if (null != oCriteriaData)
{
if (!oCriterion['or'][iCurrentCriterionRow])
{
oCriterion['or'][iCurrentCriterionRow] = {'and': []};
}
oCriterion['or'][iCurrentCriterionRow]['and'].push(oCriteriaData);
}
else
{
$(this).remove();
}
});
iCurrentCriterionRow++;
}
});
// - Update search
this.options.search.criterion = oCriterion;
@@ -736,9 +751,7 @@ $(function()
// Make placeholder if nothing yet
if(oResultAreaElem.html() === '')
{
// TODO: Make a good UI for this POC.
// TODO: Translate sentence.
oResultAreaElem.html('<div class="sf_results_placeholder"><p>Add some criterion on the search box or click the search button to view the objects.</p><p><button type="button">Search<span class="fa fa-search"></span></button></p></div>');
oResultAreaElem.html('<div class="sf_results_placeholder"><p>' + Dict.S('UI:Search:NoAutoSubmit:ExplainText') + '</p><p><button type="button">' + Dict.S('UI:Button:Search') + '<span class="fa fa-search"></span></button></p></div>');
oResultAreaElem.find('button').on('click', function(){
// TODO: Bug: Open "Search for CI", change child classe in the dropdown, click the search button. It submit the search for the original child classe, not the current one; whereas a click on the upper right pictogram does. This might be due to the form reloading.
me._onSubmitClick();
@@ -1001,6 +1014,10 @@ $(function()
}
}
$.extend(oListParams, this.options.list_params);
if (me.element.parents('.ui-dialog').length !== 0)
{
oListParams.update_history = false;
}
oData.list_params = JSON.stringify(oListParams);
if (true === bAbortIfNoChange)

View File

@@ -1,342 +1,342 @@
// 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/>
/**
*
* @param id String the dom identifier of the source input
* @param sTargetClass
* @param sAttCode
* @param oSearchWidgetElmt
* @param sFilter
* @param sTitle
* @constructor
*/
function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sFilter, sTitle)
{
this.id = id;
//this.sOriginalTargetClass = sTargetClass;
this.sTargetClass = sTargetClass;
this.sFilter = sFilter;
this.sTitle = sTitle;
this.sAttCode = sAttCode;
this.oSearchWidgetElmt = oSearchWidgetElmt;
this.emptyHtml = ''; // content to be displayed when the search results are empty (when opening the dialog)
this.emptyOnClose = true; // Workaround for the JQuery dialog being very slow when opening and closing if the content contains many INPUT tags
this.ajax_request = null;
// this.bSelectMode = bSelectMode; // true if the edited field is a SELECT, false if it's an autocomplete
// this.bSearchMode = bSearchMode; // true if selecting a value in the context of a search form
var me = this;
this.Init = function()
{
// make sure that the form is clean
$('#linkedset_'+this.id+' .selection').each( function() { this.checked = false; });
$('#'+this.id+'_btnRemove').prop('disabled', false);
$('<div id="dlg_'+me.id+'"></div>').appendTo(document.body);
// me.trace(dialog);
//TODO : check and remove all unneded code bellow this line!!
$('#'+this.id+'_linksToRemove').val('');
$('#linkedset_'+me.id).on('remove', function() {
// prevent having the dlg div twice
$('#dlg_'+me.id).remove();
});
$('#'+this.iInputId).closest('form').submit(function() {
return me.OnFormSubmit();
});
};
this.StopPendingRequest = function()
{
if (me.ajax_request)
{
me.ajax_request.abort();
me.ajax_request = null;
}
};
this.ShowModalSearchForeignKeys = function()
{
// // Query the server to get the form to search for target objects
// if (me.bSelectMode)
// {
// $('#fstatus_'+me.id).html('<img src="../images/indicator.gif" />');
// }
// else
// {
// $('#label_'+me.id).addClass('dlg_loading');
// }
$('#label_'+me.id).addClass('dlg_loading');
var theMap = {
sAttCode: me.sAttCode,
iInputId: me.id,
sTitle: me.sTitle,
sTargetClass: me.sTargetClass,
// bSearchMode: me.bSearchMode,
operation: 'ShowModalSearchForeignKeys'
};
// Make sure that we cancel any pending request before issuing another
// since responses may arrive in arbitrary order
me.StopPendingRequest();
// Run the query and get the result back directly in HTML
me.ajax_request = $.post( AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
function(data)
{
// $('#dlg_'+me.id).html(data);
$('#dlg_'+me.id).empty().append($(data)); // $(data).filter(':not(script)'));
$('#dlg_'+me.id).dialog('open');
me.UpdateSizes();
me.UpdateButtons();
me.ajax_request = null;
me.ListResultsSearchForeignKeys();
},
'html'
);
};
this.UpdateSizes = function()
{
var dlg = $('#dlg_'+me.id);
// Adjust the dialog's size to fit into the screen
if (dlg.width() > ($(window).width()-40))
{
dlg.width($(window).width()-40);
}
if (dlg.height() > ($(window).height()-70))
{
dlg.height($(window).height()-70);
}
var searchForm = dlg.find('div.display_block:first'); // Top search form, enclosing display_block
var results = $('#SearchResultsToAdd_'+me.id);
var oPadding = {};
var aKeys = ['top', 'right', 'bottom', 'left'];
for(k in aKeys)
{
oPadding[aKeys[k]] = 0;
if (dlg.css('padding-'+aKeys[k]))
{
oPadding[aKeys[k]] = parseInt(dlg.css('padding-'+aKeys[k]).replace('px', ''));
}
}
//var width = dlg.innerWidth() - oPadding['right'] - oPadding['left'] - 22; // 5 (margin-left) + 5 (padding-left) + 5 (padding-right) + 5 (margin-right) + 2 for rounding !
var height = dlg.innerHeight()-oPadding['top']-oPadding['bottom']-22;
var form_height = searchForm.outerHeight();
results.height(height - form_height - 40); // Leave some space for the buttons
};
this.UpdateButtons = function()
{
var okBtn = $('#btn_ok_'+me.id);
if ($('#count_'+me.id).val() > 0)
{
okBtn.removeAttr('disabled');
}
else
{
okBtn.prop('disabled', 'disabled');
}
};
/**
* @return {boolean}
*/
this.ListResultsSearchForeignKeys = function ()
{
var theMap = {
sTargetClass: me.sTargetClass,
iInputId: me.id,
sFilter: me.sFilter
// bSearchMode: me.bSearchMode
};
// Gather the parameters from the search form
$('#fs_'+me.id+' :input').each( function() {
if (this.name !== '')
{
var val = $(this).val(); // supports multiselect as well
if (val !== null)
{
theMap[this.name] = val;
}
}
});
theMap['sRemoteClass'] = theMap['class']; // swap 'class' (defined in the form) and 'remoteClass'
theMap.operation = 'ListResultsSearchForeignKeys'; // Override what is defined in the form itself
theMap.sAttCode = me.sAttCode;
var sSearchAreaId = '#SearchResultsToAdd_'+me.id;
//$(sSearchAreaId).html('<div style="text-align:center;width:100%;height:24px;vertical-align:middle;"><img src="../images/indicator.gif" /></div>');
$(sSearchAreaId).block();
me.UpdateButtons();
// Make sure that we cancel any pending request before issuing another
// since responses may arrive in arbitrary order
me.StopPendingRequest();
// Run the query and display the results
me.ajax_request = $.post(AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
function(data)
{
$(sSearchAreaId).html(data);
$(sSearchAreaId+' .listResults').tableHover();
$('#fr_'+me.id+' input:radio').click(function() { me.UpdateButtons(); });
me.UpdateButtons();
me.ajax_request = null;
$('#count_'+me.id).change(function(){
me.UpdateButtons();
});
me.UpdateSizes();
},
'html'
);
return false; // Don't submit the form, stay in the current page !
};
/**
* @return {boolean}
*/
this.DoAddObjects = function () {
// Gather the parameters from the search form
var theMap = {};
var context = $('#SearchResultsToAdd_'+me.id);
var selectionMode = $(':input[name="selectionMode"]', context);
if (selectionMode.length > 0) {
// Paginated table retrieve the mode and the exceptions
theMap['selectionMode'] = selectionMode.val();
$('#fs_SearchFormToAdd_'+me.id+' :input').each(function () {
theMap[this.name] = this.value;
});
$(':input[name="storedSelection[]"]', context).each(function () {
if (typeof theMap[this.name] === "undefined") {
theMap[this.name] = [];
}
theMap[this.name].push(this.value);
$(this).remove(); // Remove the selection for the next time the dialog re-opens
});
}
// Normal table, retrieve all the checked check-boxes
$(':checked[name="selectObject[]"]', context).each(
function () {
if ((this.name !== '') && ((this.type !== 'checkbox') || (this.checked))) {
var arrayExpr = /\[\]$/;
if (arrayExpr.test(this.name)) {
// Array
if (typeof theMap[this.name] === "undefined") {
theMap[this.name] = [];
}
theMap[this.name].push(this.value);
}
else {
theMap[this.name] = this.value;
}
}
$(this).parents('tr:first').remove(); // Remove the whole line, so that, next time the dialog gets displayed it's no longer there
}
);
theMap["class"] = me.sTargetClass;
theMap['operation'] = 'GetFullListForeignKeysFromSelection';
$('#busy_'+me.iInputId).html('&nbsp;<img src="../images/indicator.gif"/>');
// Run the query and display the results
$.ajax(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {
"data": theMap,
"method": "POST"
})
.done(function (data) {
if (Object.keys(data).length > 0) {
me.oSearchWidgetElmt.trigger("itop.search.criteria_enum.add_selected_values", data);
}
})
.fail(function (data) {
try {
console.error(data);
} catch (e) {
}
})
;
$('#dlg_'+me.id).dialog('close');
return false;
};
// Workaround for a ui.jquery limitation: if the content of
// the dialog contains many INPUTs, closing and opening the
// dialog is very slow. So empty it each time.
this.OnClose = function()
{
me.StopPendingRequest();
// called by the dialog, so in the context 'this' points to the jQueryObject
if (me.emptyOnClose)
{
$('#SearchResultsToAdd_'+me.id).html(me.emptyHtml);
}
$('#label_'+me.id).removeClass('dlg_loading');
$('#label_'+me.id).focus();
me.ajax_request = null;
};
this.DoSelectObjectClass = function()
{
// Retrieving selected value
var oSelectedClass = $('#ac_create_'+me.id+' select');
if(oSelectedClass.length !== 1) return;
// Setting new target class
me.sTargetClass = oSelectedClass.val();
// Opening real creation form
$('#ac_create_'+me.id).dialog('close');
me.CreateObject();
};
this.Update = function()
{
if ($('#'+me.id).prop('disabled'))
{
$('#v_'+me.id).html('');
$('#label_'+me.id).prop('disabled', 'disabled');
$('#label_'+me.id).css({'background': 'transparent'});
$('#mini_add_'+me.id).hide();
$('#mini_tree_'+me.id).hide();
$('#mini_search_'+me.id).hide();
}
else
{
$('#label_'+me.id).removeAttr('disabled');
$('#label_'+me.id).css({'background': '#fff url(../images/ac-background.gif) no-repeat right'});
$('#mini_add_'+me.id).show();
$('#mini_tree_'+me.id).show();
$('#mini_search_'+me.id).show();
}
};
// 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/>
/**
*
* @param id String the dom identifier of the source input
* @param sTargetClass
* @param sAttCode
* @param oSearchWidgetElmt
* @param sFilter
* @param sTitle
* @constructor
*/
function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sFilter, sTitle)
{
this.id = id;
//this.sOriginalTargetClass = sTargetClass;
this.sTargetClass = sTargetClass;
this.sFilter = sFilter;
this.sTitle = sTitle;
this.sAttCode = sAttCode;
this.oSearchWidgetElmt = oSearchWidgetElmt;
this.emptyHtml = ''; // content to be displayed when the search results are empty (when opening the dialog)
this.emptyOnClose = true; // Workaround for the JQuery dialog being very slow when opening and closing if the content contains many INPUT tags
this.ajax_request = null;
// this.bSelectMode = bSelectMode; // true if the edited field is a SELECT, false if it's an autocomplete
// this.bSearchMode = bSearchMode; // true if selecting a value in the context of a search form
var me = this;
this.Init = function()
{
// make sure that the form is clean
$('#linkedset_'+this.id+' .selection').each( function() { this.checked = false; });
$('#'+this.id+'_btnRemove').prop('disabled', false);
$('<div id="dlg_'+me.id+'"></div>').appendTo(document.body);
// me.trace(dialog);
//TODO : check and remove all unneded code bellow this line!!
$('#'+this.id+'_linksToRemove').val('');
$('#linkedset_'+me.id).on('remove', function() {
// prevent having the dlg div twice
$('#dlg_'+me.id).remove();
});
$('#'+this.iInputId).closest('form').submit(function() {
return me.OnFormSubmit();
});
};
this.StopPendingRequest = function()
{
if (me.ajax_request)
{
me.ajax_request.abort();
me.ajax_request = null;
}
};
this.ShowModalSearchForeignKeys = function()
{
// // Query the server to get the form to search for target objects
// if (me.bSelectMode)
// {
// $('#fstatus_'+me.id).html('<img src="../images/indicator.gif" />');
// }
// else
// {
// $('#label_'+me.id).addClass('dlg_loading');
// }
$('#label_'+me.id).addClass('dlg_loading');
var theMap = {
sAttCode: me.sAttCode,
iInputId: me.id,
sTitle: me.sTitle,
sTargetClass: me.sTargetClass,
// bSearchMode: me.bSearchMode,
operation: 'ShowModalSearchForeignKeys'
};
// Make sure that we cancel any pending request before issuing another
// since responses may arrive in arbitrary order
me.StopPendingRequest();
// Run the query and get the result back directly in HTML
me.ajax_request = $.post( AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
function(data)
{
// $('#dlg_'+me.id).html(data);
$('#dlg_'+me.id).empty().append($(data)); // $(data).filter(':not(script)'));
$('#dlg_'+me.id).dialog('open');
me.UpdateSizes();
me.UpdateButtons();
me.ajax_request = null;
me.ListResultsSearchForeignKeys();
},
'html'
);
};
this.UpdateSizes = function()
{
var dlg = $('#dlg_'+me.id);
// Adjust the dialog's size to fit into the screen
if (dlg.width() > ($(window).width()-40))
{
dlg.width($(window).width()-40);
}
if (dlg.height() > ($(window).height()-70))
{
dlg.height($(window).height()-70);
}
var searchForm = dlg.find('div.display_block:first'); // Top search form, enclosing display_block
var results = $('#SearchResultsToAdd_'+me.id);
var oPadding = {};
var aKeys = ['top', 'right', 'bottom', 'left'];
for(k in aKeys)
{
oPadding[aKeys[k]] = 0;
if (dlg.css('padding-'+aKeys[k]))
{
oPadding[aKeys[k]] = parseInt(dlg.css('padding-'+aKeys[k]).replace('px', ''));
}
}
//var width = dlg.innerWidth() - oPadding['right'] - oPadding['left'] - 22; // 5 (margin-left) + 5 (padding-left) + 5 (padding-right) + 5 (margin-right) + 2 for rounding !
var height = dlg.innerHeight()-oPadding['top']-oPadding['bottom']-22;
var form_height = searchForm.outerHeight();
results.height(height - form_height - 40); // Leave some space for the buttons
};
this.UpdateButtons = function()
{
var okBtn = $('#btn_ok_'+me.id);
if ($('#count_'+me.id).val() > 0)
{
okBtn.removeAttr('disabled');
}
else
{
okBtn.prop('disabled', 'disabled');
}
};
/**
* @return {boolean}
*/
this.ListResultsSearchForeignKeys = function ()
{
var theMap = {
sTargetClass: me.sTargetClass,
iInputId: me.id,
sFilter: me.sFilter
// bSearchMode: me.bSearchMode
};
// Gather the parameters from the search form
$('#fs_'+me.id+' :input').each( function() {
if (this.name !== '')
{
var val = $(this).val(); // supports multiselect as well
if (val !== null)
{
theMap[this.name] = val;
}
}
});
theMap['sRemoteClass'] = theMap['class']; // swap 'class' (defined in the form) and 'remoteClass'
theMap.operation = 'ListResultsSearchForeignKeys'; // Override what is defined in the form itself
theMap.sAttCode = me.sAttCode;
var sSearchAreaId = '#SearchResultsToAdd_'+me.id;
//$(sSearchAreaId).html('<div style="text-align:center;width:100%;height:24px;vertical-align:middle;"><img src="../images/indicator.gif" /></div>');
$(sSearchAreaId).block();
me.UpdateButtons();
// Make sure that we cancel any pending request before issuing another
// since responses may arrive in arbitrary order
me.StopPendingRequest();
// Run the query and display the results
me.ajax_request = $.post(AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
function(data)
{
$(sSearchAreaId).html(data);
$(sSearchAreaId+' .listResults').tableHover();
$('#fr_'+me.id+' input:radio').click(function() { me.UpdateButtons(); });
me.UpdateButtons();
me.ajax_request = null;
$('#count_'+me.id).change(function(){
me.UpdateButtons();
});
me.UpdateSizes();
},
'html'
);
return false; // Don't submit the form, stay in the current page !
};
/**
* @return {boolean}
*/
this.DoAddObjects = function () {
// Gather the parameters from the search form
var theMap = {};
var context = $('#SearchResultsToAdd_'+me.id);
var selectionMode = $(':input[name="selectionMode"]', context);
if (selectionMode.length > 0) {
// Paginated table retrieve the mode and the exceptions
theMap['selectionMode'] = (selectionMode.val() == 'negative') ? 'negative' : 'positive';
$('#fs_SearchFormToAdd_'+me.id+' :input').each(function () {
theMap[this.name] = this.value;
});
$(':input[name="storedSelection[]"]', context).each(function () {
if (typeof theMap[this.name] === "undefined") {
theMap[this.name] = [];
}
theMap[this.name].push(this.value);
$(this).remove(); // Remove the selection for the next time the dialog re-opens
});
}
// Normal table, retrieve all the checked check-boxes
$(':checked[name="selectObject[]"]', context).each(
function () {
if ((this.name !== '') && ((this.type !== 'checkbox') || (this.checked))) {
var arrayExpr = /\[\]$/;
if (arrayExpr.test(this.name)) {
// Array
if (typeof theMap[this.name] === "undefined") {
theMap[this.name] = [];
}
theMap[this.name].push(this.value);
}
else {
theMap[this.name] = this.value;
}
}
$(this).parents('tr:first').remove(); // Remove the whole line, so that, next time the dialog gets displayed it's no longer there
}
);
theMap["class"] = me.sTargetClass;
theMap['operation'] = 'GetFullListForeignKeysFromSelection';
$('#busy_'+me.iInputId).html('&nbsp;<img src="../images/indicator.gif"/>');
// Run the query and display the results
$.ajax(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {
"data": theMap,
"method": "POST"
})
.done(function (data) {
if (Object.keys(data).length > 0) {
me.oSearchWidgetElmt.trigger("itop.search.criteria_enum.add_selected_values", data);
}
})
.fail(function (data) {
try {
console.error(data);
} catch (e) {
}
})
;
$('#dlg_'+me.id).dialog('close');
return false;
};
// Workaround for a ui.jquery limitation: if the content of
// the dialog contains many INPUTs, closing and opening the
// dialog is very slow. So empty it each time.
this.OnClose = function()
{
me.StopPendingRequest();
// called by the dialog, so in the context 'this' points to the jQueryObject
if (me.emptyOnClose)
{
$('#SearchResultsToAdd_'+me.id).html(me.emptyHtml);
}
$('#label_'+me.id).removeClass('dlg_loading');
$('#label_'+me.id).focus();
me.ajax_request = null;
};
this.DoSelectObjectClass = function()
{
// Retrieving selected value
var oSelectedClass = $('#ac_create_'+me.id+' select');
if(oSelectedClass.length !== 1) return;
// Setting new target class
me.sTargetClass = oSelectedClass.val();
// Opening real creation form
$('#ac_create_'+me.id).dialog('close');
me.CreateObject();
};
this.Update = function()
{
if ($('#'+me.id).prop('disabled'))
{
$('#v_'+me.id).html('');
$('#label_'+me.id).prop('disabled', 'disabled');
$('#label_'+me.id).css({'background': 'transparent'});
$('#mini_add_'+me.id).hide();
$('#mini_tree_'+me.id).hide();
$('#mini_search_'+me.id).hide();
}
else
{
$('#label_'+me.id).removeAttr('disabled');
$('#label_'+me.id).css({'background': '#fff url(../images/ac-background.gif) no-repeat right'});
$('#mini_add_'+me.id).show();
$('#mini_tree_'+me.id).show();
$('#mini_search_'+me.id).show();
}
};
}

View File

@@ -428,11 +428,7 @@ $(function()
event.stopImmediatePropagation();
var jMe = $(this);
$(this).data('openTimeoutId', setTimeout(function() {
var sDataId = jMe.attr('data-attcode');
if ($('.tooltip-close-button[data-attcode="'+sDataId+'"]').length == 0)
{
jMe.tooltip('open');
}
jMe.tooltip('open');
}, 500));
})
.on( "mouseout", function(event){

View File

@@ -409,8 +409,12 @@ function ToggleDurationField(field_id) {
function PropagateCheckBox(bCurrValue, aFieldsList, bCheck) {
if (bCurrValue == bCheck) {
for (var i = 0; i < aFieldsList.length; i++) {
$('#enable_'+aFieldsList[i]).attr('checked', bCheck);
ToogleField(bCheck, aFieldsList[i]);
var sFieldId = aFieldsList[i];
$('#enable_'+sFieldId).attr('checked', bCheck);
ToogleField(bCheck, sFieldId);
// Cascade propagation
$('#enable_'+sFieldId).trigger('change');
}
}
}

View File

@@ -1205,6 +1205,8 @@ EOF
$aExpectedAttributes = MetaModel::GetTransitionAttributes($sClass, $sStimulus, $sState);
$aDetails = array();
$sFormId = 'apply_stimulus';
$sFormPrefix = $sFormId.'_';
$iFieldIndex = 0;
$aFieldsMap = array();
$aValues = array();
@@ -1220,6 +1222,7 @@ EOF
$sReadyScript = '';
foreach($aExpectedAttributes as $sAttCode => $iExpectCode)
{
$sFieldInputId = $sFormPrefix.$sAttCode;
// Prompt for an attribute if
// - the attribute must be changed or must be displayed to the user for confirmation
// - or the field is mandatory and currently empty
@@ -1232,19 +1235,19 @@ EOF
if (count($aPrerequisites) > 0)
{
// When 'enabling' a field, all its prerequisites must be enabled too
$sFieldList = "['".implode("','", $aPrerequisites)."']";
$oP->add_ready_script("$('#enable_{$sAttCode}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, $sFieldList, true); } );\n");
$sFieldList = "['{$sFormPrefix}".implode("','{$sFormPrefix}", $aPrerequisites)."']";
$oP->add_ready_script("$('#enable_{$sFieldInputId}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, $sFieldList, true); } );\n");
}
$aDependents = MetaModel::GetDependentAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one
if (count($aDependents) > 0)
{
// When 'disabling' a field, all its dependent fields must be disabled too
$sFieldList = "['".implode("','", $aDependents)."']";
$oP->add_ready_script("$('#enable_{$sAttCode}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, $sFieldList, false); } );\n");
$sFieldList = "['{$sFormPrefix}".implode("','{$sFormPrefix}", $aDependents)."']";
$oP->add_ready_script("$('#enable_{$sFieldInputId}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, $sFieldList, false); } );\n");
}
$aArgs = array('this' => $oObj);
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $oObj->Get($sAttCode), $oObj->GetEditValue($sAttCode), $sAttCode, '', $iExpectCode, $aArgs);
$sComments = '<input type="checkbox" checked id="enable_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$sAttCode.'\')"/>';
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $oObj->Get($sAttCode), $oObj->GetEditValue($sAttCode), $sFieldInputId, '', $iExpectCode, $aArgs);
$sComments = '<input type="checkbox" checked id="enable_'.$sFieldInputId.'" onClick="ToogleField(this.checked, \''.$sFieldInputId.'\')"/>';
if (!isset($aValues[$sAttCode]))
{
$aValues[$sAttCode] = array();
@@ -1272,11 +1275,11 @@ EOF
}
$sTip .= "</ul></p>";
$sTip = addslashes($sTip);
$sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );\n";
$sComments .= '<div class="multi_values" id="multi_values_'.$sAttCode.'">'.count($aValues[$sAttCode]).'</div>';
$sReadyScript .= "$('#multi_values_$sFieldInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );\n";
$sComments .= '<div class="multi_values" id="multi_values_'.$sFieldInputId.'">'.count($aValues[$sAttCode]).'</div>';
}
$aDetails[] = array('label' => '<span>'.$oAttDef->GetLabel().'</span>', 'value' => "<span id=\"field_$sAttCode\">$sHTMLValue</span>", 'comments' => $sComments);
$aFieldsMap[$sAttCode] = $sAttCode;
$aDetails[] = array('label' => '<span>'.$oAttDef->GetLabel().'</span>', 'value' => "<span id=\"field_$sFieldInputId\">$sHTMLValue</span>", 'comments' => $sComments);
$aFieldsMap[$sAttCode] = $sFieldInputId;
$iFieldIndex++;
}
}
@@ -1289,7 +1292,7 @@ EOF
$oP->add('</div>');
}
$oP->add("<div class=\"wizContainer\">\n");
$oP->add("<form id=\"apply_stimulus\" method=\"post\" onSubmit=\"return OnSubmit('apply_stimulus');\">\n");
$oP->add("<form id=\"{$sFormId}\" method=\"post\" onSubmit=\"return OnSubmit('{$sFormId}');\">\n");
$oP->add("<table><tr><td>\n");
$oP->details($aDetails);
$oP->add("</td></tr></table>\n");
@@ -1328,7 +1331,7 @@ EOF
$oP->add_ready_script(
<<<EOF
// Starts the validation when the page is ready
CheckFields('apply_stimulus', false);
CheckFields('{$sFormId}', false);
$sReadyScript
EOF
);
@@ -1485,8 +1488,7 @@ EOF
'stimulus' => $sStimulus,
'origin' => 'console'
);
$oObj->PrefillForm('state_change', $aPrefillFormParam);
$oObj->DisplayStimulusForm($oP, $sStimulus);
$oObj->DisplayStimulusForm($oP, $sStimulus, $aPrefillFormParam);
}
else
{

View File

@@ -170,7 +170,7 @@ try
$oP->add("<form method=\"post\">\n");
$oP->add(Dict::S('UI:RunQuery:ExpressionToEvaluate')."<br/>\n");
$oP->add("<textarea cols=\"120\" rows=\"8\" id=\"expression\" name=\"expression\">".htmlentities($sExpression, ENT_QUOTES, 'UTF-8')."</textarea>\n");
$oP->add_linked_script(utils::GetDefaultUrlAppRoot()."/js/jquery.hotkeys.js");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot()."/js/jquery.hotkeys.js");
$oP->add_ready_script(<<<EOF
$("#expression").select();
$("#expression").on("keydown", null, "ctrl+return", function() {

View File

@@ -1,7 +1,7 @@
iTop - version 2.5.0 - 27-jun-2018
iTop - version 2.5.1 - 10-Oct-2018
Readme file
iTop 2.5.0 is the 33rd release of iTop.
iTop 2.5.1 is the 35th release of iTop.
Changes since the previous version
-------------------------------------------------------------------

View File

@@ -235,18 +235,20 @@ if (class_exists('ZipArchive')) // The setup must be able to start even if the "
/**
* Create a normalized backup name, depending on the current date/time and Database
*
* @param sNameSpec string Name and path, eventually containing itop placeholders + time formatting specs
* @param string sMySQLBinDir Name and path, eventually containing itop placeholders + time formatting specs
*/
public function SetMySQLBinDir($sMySQLBinDir)
{
$this->sMySQLBinDir = $sMySQLBinDir;
}
/**
* Create a normalized backup name, depending on the current date/time and Database
*
* @param string sNameSpec Name and path, eventually containing itop placeholders + time formatting specs
*/
/**
* Create a normalized backup name, depending on the current date/time and Database
*
* @param string sNameSpec Name and path, eventually containing itop placeholders + time formatting specs
*
* @return string
*/
public function MakeName($sNameSpec = "__DB__-%Y-%m-%d")
{
$sFileName = $sNameSpec;
@@ -646,7 +648,7 @@ if (class_exists('ZipArchive')) // The setup must be able to start even if the "
{
if (empty($sData))
{
return;
return '';
}
return ' --'.$sCliArgName.'='.self::EscapeShellArg($sData);

View File

@@ -54,7 +54,7 @@ class SetupUtils
const MYSQL_MIN_VERSION = '5.5.3'; // 5.5 branch that is shipped with most distribution, and 5.5.3 to have utf8mb4 (see N°942)
// -- versions that will be the minimum in next iTop major release (warning if not met)
const PHP_NEXT_MIN_VERSION = ''; // no new PHP requirement for now in iTop 2.6
const MYSQL_NEXT_MIN_VERSION = ''; // no new MySQL requirement for now in iTop 2.6
const MYSQL_NEXT_MIN_VERSION = '5.6.0'; // 5.6 to have fulltext on InnoDB for Tags fields (N°931)
// -- First recent version that is not yet validated by Combodo (warning)
const PHP_NOT_VALIDATED_VERSION = '7.3.0';

View File

@@ -680,7 +680,7 @@ class ArchiveTar
*/
public function _error($p_message)
{
$this->error_object = $this->raiseError($p_message);
IssueLog::Error($p_message);
}
/**
@@ -688,7 +688,7 @@ class ArchiveTar
*/
public function _warning($p_message)
{
$this->error_object = $this->raiseError($p_message);
IssueLog::Warning($p_message);
}
/**

View File

@@ -309,11 +309,16 @@ class SearchForm
$aSelectedClasses = $oSearch->GetSelectedClasses();
foreach($aSelectedClasses as $sAlias => $sClassName)
{
$aAllFields['zlist'] = array_merge($aAllFields['zlist'], $aAllFields[$sAlias.'_zlist']);
unset($aAllFields[$sAlias.'_zlist']);
$aAllFields['others'] = array_merge($aAllFields['others'], $aAllFields[$sAlias.'_others']);
unset($aAllFields[$sAlias.'_others']);
if(array_key_exists($sAlias.'_zlist', $aAllFields))
{
$aAllFields['zlist'] = array_merge($aAllFields['zlist'], $aAllFields[$sAlias.'_zlist']);
unset($aAllFields[$sAlias.'_zlist']);
}
if(array_key_exists($sAlias.'_others', $aAllFields))
{
$aAllFields['others'] = array_merge($aAllFields['others'], $aAllFields[$sAlias.'_others']);
unset($aAllFields[$sAlias.'_others']);
}
}
return $aAllFields;
@@ -400,7 +405,7 @@ class SearchForm
protected function IsSubAttribute($oAttDef)
{
return (($oAttDef instanceof AttributeFriendlyName) || ($oAttDef instanceof AttributeExternalField) || ($oAttDef instanceof AttributeSubItem));
return (($oAttDef instanceof AttributeFriendlyName) || ($oAttDef instanceof AttributeSubItem));
}
/**
@@ -480,13 +485,12 @@ class SearchForm
{
$aOrCriterion = array();
$bIsEmptyExpression = true;
$aArgs = MetaModel::PrepareQueryArguments($aArgs, $oSearch->GetInternalParams());
if (method_exists($oSearch, 'GetCriteria'))
{
$oExpression = $oSearch->GetCriteria();
$aArgs = MetaModel::PrepareQueryArguments($aArgs, $oSearch->GetInternalParams());
if (!empty($aArgs))
{
try
@@ -521,12 +525,50 @@ class SearchForm
}
}
// Context induced criteria are read-only
$oAppContext = new ApplicationContext();
$sClass = $oSearch->GetClass();
$aCallSpec = array($sClass, 'MapContextParam');
$aContextParams = array();
if (is_callable($aCallSpec))
{
foreach($oAppContext->GetNames() as $sContextParam)
{
$sParamCode = call_user_func($aCallSpec, $sContextParam); //Map context parameter to the value/filter code depending on the class
if (!is_null($sParamCode))
{
$sParamValue = $oAppContext->GetCurrentValue($sContextParam, null);
if (!is_null($sParamValue))
{
$aContextParams[$sParamCode] = $sParamValue;
}
}
}
}
if ($bIsEmptyExpression)
{
// Add default criterion
$aOrCriterion = $this->GetDefaultCriterion($oSearch);
$aOrCriterion = $this->GetDefaultCriterion($oSearch, $aContextParams);
}
foreach($aContextParams as $sParamCode => $sParamValue)
{
// Add Context criteria in read only mode
$sAlias = $oSearch->GetClassAlias();
$oFieldExpression = new FieldExpression($sParamCode, $sAlias);
$oScalarExpression = new \ScalarExpression($sParamValue);
$oExpression = new \BinaryExpression($oFieldExpression, '=', $oScalarExpression);
$aCriterion = $oExpression->GetCriterion($oSearch, $aArgs);
$aCriterion['is_removable'] = false;
foreach($aOrCriterion as &$aAndExpression)
{
$aAndExpression['and'][] = $aCriterion;
}
}
return array('or' => $aOrCriterion);
}
@@ -640,9 +682,11 @@ class SearchForm
/**
* @param DBObjectSearch $oSearch
* @param $aContextParams
*
* @return array
*/
protected function GetDefaultCriterion($oSearch)
protected function GetDefaultCriterion($oSearch, &$aContextParams = array())
{
$aAndCriterion = array();
$sClass = $oSearch->GetClass();
@@ -661,8 +705,20 @@ class SearchForm
$sAlias = $oSearch->GetClassAlias();
foreach($aList as $sAttCode)
{
$oFieldExpression = new FieldExpression($sAttCode, $sAlias);
$aCriterion = $oFieldExpression->GetCriterion($oSearch);
$oExpression = new FieldExpression($sAttCode, $sAlias);
$bIsRemovable = true;
if (isset($aContextParams[$sAttCode]))
{
// When a context parameter exists, use it with the default search criteria
$oFieldExpression = $oExpression;
$oScalarExpression = new \ScalarExpression($aContextParams[$sAttCode]);
$oExpression = new \BinaryExpression($oFieldExpression, '=', $oScalarExpression);
unset($aContextParams[$sAttCode]);
// Read only mode for search criteria from context
$bIsRemovable = false;
}
$aCriterion = $oExpression->GetCriterion($oSearch);
$aCriterion['is_removable'] = $bIsRemovable;
if (isset($aCriterion['widget']) && ($aCriterion['widget'] != AttributeDefinition::SEARCH_WIDGET_TYPE_RAW))
{
$aAndCriterion[] = $aCriterion;

View File

@@ -29,6 +29,8 @@ use \Combodo\iTop\Form\Field\TextField;
* Description of TextAreaField
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package \Combodo\iTop\Form\Field
* @since 2.3.0
*/
class TextAreaField extends TextField
{
@@ -113,7 +115,7 @@ class TextAreaField extends TextField
{
if ($this->GetFormat() == TextAreaField::ENUM_FORMAT_TEXT)
{
$sValue = $this->GetCurrentValue();
$sValue = \Str::pure2html($this->GetCurrentValue());
$sValue = AttributeText::RenderWikiHtml($sValue);
return "<div>".str_replace("\n", "<br>\n", $sValue).'</div>';
}

View File

@@ -576,6 +576,7 @@ EOF
);
// Target object others attributes
// TODO: Support for AttriubteImage, AttributeBlob
foreach ($this->oField->GetAttributesToDisplay(true) as $sAttCode)
{
if ($sAttCode !== 'id')
@@ -598,7 +599,7 @@ EOF
}
else
{
$aAttProperties['value'] = $oAttDef->GetValueLabel($oRemoteItem->Get($sAttCode));
$aAttProperties['value'] = $oAttDef->GetAsHTML($oRemoteItem->Get($sAttCode));
if ($oAttDef instanceof AttributeFriendlyName)
{

View File

@@ -112,6 +112,26 @@ class ItopDataTestCase extends ItopTestCase
return $oMyObj;
}
/**
* @param string $sClass
* @param $iKey
* @param array $aParams
*
* @return DBObject
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
*/
protected static function updateObject($sClass, $iKey, $aParams)
{
$oMyObj = MetaModel::GetObject($sClass, $iKey);
foreach($aParams as $sAttCode => $oValue)
{
$oMyObj->Set($sAttCode, $oValue);
}
$oMyObj->DBUpdate();
return $oMyObj;
}
/**
* Create an Organization in database

View File

@@ -26,9 +26,8 @@
namespace Combodo\iTop\Test\UnitTest\Core;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use DBObject;
use PHPUnit\Framework\TestCase;
/**
@@ -36,7 +35,7 @@ use PHPUnit\Framework\TestCase;
* @preserveGlobalState disabled
* @backupGlobals disabled
*/
class DBObjectTest extends ItopTestCase
class DBObjectTest extends ItopDataTestCase
{
protected function setUp()
{
@@ -50,7 +49,7 @@ class DBObjectTest extends ItopTestCase
*/
public function testGetUIPage()
{
$this->assertEquals('UI.php', DBObject::GetUIPage());
static::assertEquals('UI.php', DBObject::GetUIPage());
}
/**
@@ -61,7 +60,7 @@ class DBObjectTest extends ItopTestCase
*/
public function testIsValidPKeyOK($key, $res)
{
$this->assertEquals(DBObject::IsValidPKey($key), $res);
static::assertEquals(DBObject::IsValidPKey($key), $res);
}
public function keyProviderOK()
@@ -80,4 +79,11 @@ class DBObjectTest extends ItopTestCase
array('PHP_INT_MIN', false));
}
public function testGetOriginal()
{
$oObject = $this->CreateUserRequest(190664);
static::assertNull($oObject->GetOriginal('sla_tto_passed'));
}
}

View File

@@ -731,9 +731,18 @@ try
$sMimeType = $oExporter->GetMimeType();
if ($sMimeType == 'text/html')
{
$oP = new NiceWebPage('iTop export');
// Note: Using NiceWebPage only for HTML export as it includes JS scripts & files, which makes no sense in other export formats. More over, it breaks Excel spreadsheet import.
if($oExporter instanceof HTMLBulkExport)
{
$oP = new NiceWebPage('iTop export');
$oP->add_ready_script("$('table.listResults').tablesorter({widgets: ['MyZebra']});");
}
else
{
$oP = new WebPage('iTop export');
$oP->add_style("table br { mso-data-placement:same-cell; }"); // Trick for Excel: keep line breaks inside the same cell !
}
$oP->add_style("body { overflow: auto; }");
$oP->add_ready_script("$('table.listResults').tablesorter({widgets: ['MyZebra']});");
}
else
{