mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 23:44:11 +01:00
Compare commits
180 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b190d0e385 | ||
|
|
93f273a287 | ||
|
|
04e7616c84 | ||
|
|
219b970703 | ||
|
|
76c139253e | ||
|
|
02b09c2535 | ||
|
|
10cfb373f2 | ||
|
|
69578d5d07 | ||
|
|
97d6d413bb | ||
|
|
7e0d5d64ce | ||
|
|
3f8f57fa9a | ||
|
|
eb2a615bd2 | ||
|
|
0432727ace | ||
|
|
e6d61d1ebd | ||
|
|
f916f9cde8 | ||
|
|
5e61388b65 | ||
|
|
910bbe1160 | ||
|
|
9addc4a7ca | ||
|
|
5ed71ecb96 | ||
|
|
dab0e372d0 | ||
|
|
dfd1d5fe35 | ||
|
|
d289457c0c | ||
|
|
f52b3bff0d | ||
|
|
b6b17733bf | ||
|
|
c1c2d027c3 | ||
|
|
5269096ecd | ||
|
|
e4c68936a0 | ||
|
|
4db5d4c08d | ||
|
|
3511867ba3 | ||
|
|
7934f9b9dc | ||
|
|
7f2eef4a24 | ||
|
|
8a65a592f3 | ||
|
|
7d6b019cfa | ||
|
|
5e48400cb1 | ||
|
|
252562ace9 | ||
|
|
c9c32b0de1 | ||
|
|
770ac8ffe5 | ||
|
|
dcfdb2d0a9 | ||
|
|
0cbf34ba5a | ||
|
|
f1037147a9 | ||
|
|
bea52d5fb9 | ||
|
|
b6fac4b411 | ||
|
|
d8a77c22a3 | ||
|
|
ed3c387712 | ||
|
|
312a5b246b | ||
|
|
903de43589 | ||
|
|
2d67594ccf | ||
|
|
65f9f86bcc | ||
|
|
efaf53e568 | ||
|
|
81a2a9278c | ||
|
|
e15d4bfab6 | ||
|
|
77880c3675 | ||
|
|
3559425fc1 | ||
|
|
d8c4251c03 | ||
|
|
9437e689fd | ||
|
|
a79459bc53 | ||
|
|
500bd15843 | ||
|
|
3e8dd2f4a5 | ||
|
|
d0fade9ce1 | ||
|
|
51a49dfce8 | ||
|
|
066b71686d | ||
|
|
be633001a5 | ||
|
|
84426c6634 | ||
|
|
dbaf924171 | ||
|
|
8adf743cc7 | ||
|
|
75450ded1d | ||
|
|
bcca6ac720 | ||
|
|
865f9f4f67 | ||
|
|
a7e54d4bad | ||
|
|
2beb795f9a | ||
|
|
6847d8a5c7 | ||
|
|
06985d3cf2 | ||
|
|
8c7f7abaab | ||
|
|
e8d314e1f6 | ||
|
|
e29f1825be | ||
|
|
908a48e0a1 | ||
|
|
9b854dbcc7 | ||
|
|
7757f1f2d2 | ||
|
|
a353317746 | ||
|
|
723eb90160 | ||
|
|
8ea5be4ead | ||
|
|
b3f827ed5e | ||
|
|
eaf8a187aa | ||
|
|
34f64c61f6 | ||
|
|
8154e718a1 | ||
|
|
b5369a0c03 | ||
|
|
2bc61caab1 | ||
|
|
8f0a5fcaf9 | ||
|
|
fe3512cb5f | ||
|
|
fdc987f367 | ||
|
|
ec1dcc8df6 | ||
|
|
47ed863da9 | ||
|
|
88290f9e91 | ||
|
|
cfdbc8ae62 | ||
|
|
aaa8f6d311 | ||
|
|
1c983e8093 | ||
|
|
92a9a8c65f | ||
|
|
27217815d1 | ||
|
|
2fe4265223 | ||
|
|
2b71ea108a | ||
|
|
1aa5185c93 | ||
|
|
834ac00d37 | ||
|
|
8e6379a112 | ||
|
|
da217a1cb3 | ||
|
|
a683634a05 | ||
|
|
69ad10785b | ||
|
|
9aead898e2 | ||
|
|
a48ebfefba | ||
|
|
4748717e50 | ||
|
|
d90b1a3d82 | ||
|
|
ed719e13c7 | ||
|
|
2d705c6697 | ||
|
|
c98ad106c4 | ||
|
|
3694108f42 | ||
|
|
8cf75f826f | ||
|
|
ad9726b64c | ||
|
|
e32e275f40 | ||
|
|
195056492e | ||
|
|
af338de17f | ||
|
|
a6aa183e26 | ||
|
|
b5074c4cee | ||
|
|
8b9589744b | ||
|
|
8259a79cd2 | ||
|
|
a23ea9a01f | ||
|
|
949b213f9d | ||
|
|
a940adc4ba | ||
|
|
5d994edd62 | ||
|
|
b1ca1f2630 | ||
|
|
58e315d7f6 | ||
|
|
1059befa39 | ||
|
|
0f5130611d | ||
|
|
a1271da74a | ||
|
|
0d40235791 | ||
|
|
dd63f2b817 | ||
|
|
8f84c3b84b | ||
|
|
00c58bb245 | ||
|
|
3a876d5c75 | ||
|
|
147916062b | ||
|
|
0de6f98add | ||
|
|
a076792e77 | ||
|
|
2d2a6857de | ||
|
|
373641e01d | ||
|
|
d11eceac62 | ||
|
|
3965806fa0 | ||
|
|
2625d2da80 | ||
|
|
02d32a556d | ||
|
|
71fcc6f026 | ||
|
|
7168860a0b | ||
|
|
684c88e0b8 | ||
|
|
5691ca0327 | ||
|
|
84741c19f0 | ||
|
|
86f649affc | ||
|
|
4f5c987d8b | ||
|
|
e7b5953feb | ||
|
|
e441e5e78a | ||
|
|
6be9a87c15 | ||
|
|
43daa2ef08 | ||
|
|
caa2a05bf4 | ||
|
|
fc39d8aca9 | ||
|
|
cf12578289 | ||
|
|
44952d1ea0 | ||
|
|
7f15eed9a8 | ||
|
|
c2f5cafaf3 | ||
|
|
81822efa0f | ||
|
|
923a025f1c | ||
|
|
a4b6f4e37c | ||
|
|
f0c73451a2 | ||
|
|
db6e813cba | ||
|
|
d74e3e6b42 | ||
|
|
b740cb2afd | ||
|
|
6ad3c40c42 | ||
|
|
f49c8ce188 | ||
|
|
acf828b72e | ||
|
|
bac92716f3 | ||
|
|
07257cc2d2 | ||
|
|
87ba67225a | ||
|
|
2ad3b3c27e | ||
|
|
92a640e41a | ||
|
|
842df7646b | ||
|
|
01b38d2ed6 |
@@ -90,7 +90,7 @@ ij_xml_attribute_wrap = normal
|
||||
ij_xml_block_comment_at_first_column = true
|
||||
ij_xml_keep_blank_lines = 2
|
||||
ij_xml_keep_indents_on_empty_lines = false
|
||||
ij_xml_keep_line_breaks = false
|
||||
ij_xml_keep_line_breaks = true
|
||||
ij_xml_keep_line_breaks_in_text = true
|
||||
ij_xml_keep_whitespaces = false
|
||||
ij_xml_keep_whitespaces_around_cdata = preserve
|
||||
@@ -110,6 +110,7 @@ ij_shell_keep_column_alignment_padding = false
|
||||
ij_shell_minify_program = false
|
||||
ij_shell_redirect_followed_by_space = false
|
||||
ij_shell_switch_cases_indented = false
|
||||
ij_shell_use_unix_line_separator = true
|
||||
|
||||
[{*.cjs,*.js}]
|
||||
indent_style = tab
|
||||
@@ -279,7 +280,7 @@ ij_javascript_while_brace_force = always
|
||||
ij_javascript_while_on_new_line = false
|
||||
ij_javascript_wrap_comments = false
|
||||
|
||||
[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}]
|
||||
[{*.ctp, *.hphp, *.inc, *.module, *.php, *.php4, *.php5, *.phtml}]
|
||||
indent_style = tab
|
||||
ij_continuation_indent_size = 4
|
||||
ij_smart_tabs = true
|
||||
@@ -288,8 +289,8 @@ ij_php_align_assignments = false
|
||||
ij_php_align_class_constants = false
|
||||
ij_php_align_group_field_declarations = false
|
||||
ij_php_align_inline_comments = false
|
||||
ij_php_align_key_value_pairs = false
|
||||
ij_php_align_multiline_array_initializer_expression = false
|
||||
ij_php_align_key_value_pairs = true
|
||||
ij_php_align_multiline_array_initializer_expression = true
|
||||
ij_php_align_multiline_binary_operation = false
|
||||
ij_php_align_multiline_chained_methods = false
|
||||
ij_php_align_multiline_extends_list = false
|
||||
@@ -514,7 +515,7 @@ ij_json_wrap_long_lines = false
|
||||
indent_style = tab
|
||||
ij_smart_tabs = true
|
||||
ij_visual_guides = none
|
||||
ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
|
||||
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
|
||||
ij_html_align_attributes = true
|
||||
ij_html_align_text = false
|
||||
ij_html_attribute_wrap = normal
|
||||
@@ -541,7 +542,21 @@ ij_html_space_inside_empty_tag = false
|
||||
ij_html_text_wrap = normal
|
||||
ij_html_uniform_ident = false
|
||||
|
||||
[{*.yaml, *.yml}]
|
||||
[{*.markdown,*.md}]
|
||||
ij_visual_guides = none
|
||||
ij_markdown_force_one_space_after_blockquote_symbol = true
|
||||
ij_markdown_force_one_space_after_header_symbol = true
|
||||
ij_markdown_force_one_space_after_list_bullet = true
|
||||
ij_markdown_force_one_space_between_words = true
|
||||
ij_markdown_keep_indents_on_empty_lines = false
|
||||
ij_markdown_max_lines_around_block_elements = 1
|
||||
ij_markdown_max_lines_around_header = 1
|
||||
ij_markdown_max_lines_between_paragraphs = 1
|
||||
ij_markdown_min_lines_around_block_elements = 1
|
||||
ij_markdown_min_lines_around_header = 1
|
||||
ij_markdown_min_lines_between_paragraphs = 1
|
||||
|
||||
[{*.yaml,*.yml}]
|
||||
indent_size = 2
|
||||
ij_visual_guides = none
|
||||
ij_yaml_align_values_properties = do_not_align
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* script used to sort license file (usefull for autogeneration)
|
||||
* Example: php
|
||||
* script used to sort license file (useful for autogeneration)
|
||||
*
|
||||
* Requirements :
|
||||
* * bash (on Windows, use Git Bash)
|
||||
* * composer (if you use the phar version, mind to create a `Composer` alias !)
|
||||
* * JQ command
|
||||
* to install on Windows :
|
||||
* `curl -L -o /usr/bin/jq.exe https://github.com/stedolan/jq/releases/latest/download/jq-win64.exe`
|
||||
* this is a Windows port : https://stedolan.github.io/jq/
|
||||
*
|
||||
* Licenses sources :
|
||||
* * `composer licenses --format json` (see https://getcomposer.org/doc/03-cli.md#licenses)
|
||||
* * keep every existing nodes with `/licenses/license[11]/product/@scope` not in ['lib', 'datamodels']
|
||||
* ⚠ If licenses were added manually, they might be removed by this tool ! Be very careful to check for the result before pushing !
|
||||
*
|
||||
* To launch, check requirements and run `php updateLicenses.php`
|
||||
* The target license file path is in `$xmlFilePath`
|
||||
*/
|
||||
|
||||
$iTopFolder = __DIR__ . "/../../" ;
|
||||
@@ -51,39 +66,83 @@ function get_license_nodes($file_path)
|
||||
$xp = new DOMXPath($dom);
|
||||
|
||||
$licenseList = $xp->query('/licenses/license');
|
||||
$licenses = iterator_to_array($licenseList);
|
||||
$licenses = iterator_to_array($licenseList);
|
||||
|
||||
usort($licenses, 'sort_by_product');
|
||||
|
||||
return $licenses;
|
||||
}
|
||||
|
||||
/** @noinspection SuspiciousAssignmentsInspection */
|
||||
function fix_product_name(DOMNode &$oProductNode)
|
||||
{
|
||||
$sProductNameOrig = $oProductNode->nodeValue;
|
||||
|
||||
// sample : `C:\Dev\wamp64\www\itop-27\.make\license/../..//lib/symfony/polyfill-ctype`
|
||||
$sProductNameFixed = remove_dir_from_string($sProductNameOrig, 'lib/');
|
||||
|
||||
// sample : `C:\Dev\wamp64\www\itop-27\.make\license/../..//datamodels/2.x/authent-cas/vendor/apereo/phpcas`
|
||||
$sProductNameFixed = remove_dir_from_string($sProductNameFixed, 'vendor/');
|
||||
|
||||
$oProductNode->nodeValue = $sProductNameFixed;
|
||||
}
|
||||
|
||||
function remove_dir_from_string($sString, $sNeedle)
|
||||
{
|
||||
if (strpos($sString, $sNeedle) === false) {
|
||||
return $sString;
|
||||
}
|
||||
|
||||
$sStringTmp = strstr($sString, $sNeedle);
|
||||
$sStringFixed = str_replace($sNeedle, '', $sStringTmp);
|
||||
|
||||
// DEBUG trace O:)
|
||||
// echo "$sNeedle = $sString => $sStringFixed\n";
|
||||
|
||||
return $sStringFixed;
|
||||
}
|
||||
|
||||
$old_licenses = get_license_nodes($xmlFilePath);
|
||||
|
||||
//generate file with updated licenses
|
||||
$generated_license_file_path = __DIR__."/provfile.xml";
|
||||
exec("bash " . __DIR__ . "/gen-community-license.sh $iTopFolder > ". $generated_license_file_path);
|
||||
echo "- Generating licences...";
|
||||
exec("bash ".__DIR__."/gen-community-license.sh $iTopFolder > ".$generated_license_file_path);
|
||||
echo "OK!\n";
|
||||
|
||||
echo "- Get licenses nodes...";
|
||||
$new_licenses = get_license_nodes($generated_license_file_path);
|
||||
exec("rm -f ". $generated_license_file_path);
|
||||
unlink($generated_license_file_path);
|
||||
|
||||
foreach ($old_licenses as $b) {
|
||||
$aProductNode = get_product_node($b);
|
||||
|
||||
if (get_scope($aProductNode) !== "lib" && get_scope($aProductNode) !== "datamodels" )
|
||||
{
|
||||
if (get_scope($aProductNode) !== "lib" && get_scope($aProductNode) !== "datamodels") {
|
||||
$new_licenses[] = $b;
|
||||
}
|
||||
}
|
||||
|
||||
usort($new_licenses, 'sort_by_product');
|
||||
echo "OK!\n";
|
||||
|
||||
echo "- Overwritting Combodo license file...";
|
||||
$new_dom = new DOMDocument("1.0");
|
||||
$new_dom->formatOutput = true;
|
||||
$root = $new_dom->createElement("licenses");
|
||||
$new_dom->appendChild($root);
|
||||
|
||||
foreach ($new_licenses as $b) {
|
||||
$node = $new_dom->importNode($b,true);
|
||||
$root->appendChild($new_dom->importNode($b,true));
|
||||
$node = $new_dom->importNode($b, true);
|
||||
|
||||
// N°3870 fix when running script in Windows
|
||||
// fix should be in gen-community-license.sh but it is easier to do it here !
|
||||
if (strncasecmp(PHP_OS, 'WIN', 3) === 0) {
|
||||
$oProductNodeOrig = get_product_node($node);
|
||||
fix_product_name($oProductNodeOrig);
|
||||
}
|
||||
|
||||
$root->appendChild($node);
|
||||
}
|
||||
|
||||
$new_dom->save($xmlFilePath);
|
||||
$new_dom->save($xmlFilePath);
|
||||
echo "OK!\n";
|
||||
77
.make/release/changelog.php
Normal file
77
.make/release/changelog.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* Usage :
|
||||
* `php changelog.php 2.7.4`
|
||||
*
|
||||
* As argument is passed the git ref (tag name or sha1) we want to use as reference
|
||||
*
|
||||
* Outputs :
|
||||
*
|
||||
* 1. List of bugs as CSV :
|
||||
* bug ref;link
|
||||
* Example :
|
||||
* <code>
|
||||
* Bug_ref;Bug_URL;sha1
|
||||
* 1234;https://support.combodo.com/pages/UI.php?operation=details&class=Bug&id=1234;949b213f9|b1ca1f263|a1271da74
|
||||
* </code>
|
||||
*
|
||||
* 2. List of commits sha1/message without bug ref
|
||||
* Example :
|
||||
* <code>
|
||||
* sha1;subject
|
||||
* a6aa183e2;:bookmark: Prepare 2.7.5
|
||||
* </code>
|
||||
*/
|
||||
|
||||
|
||||
if (count($argv) === 1) {
|
||||
echo '⚠ You must pass the base tag/sha1 as parameter';
|
||||
exit(1);
|
||||
}
|
||||
$sBaseReference = $argv[1];
|
||||
|
||||
|
||||
//--- Get log
|
||||
$sGitLogCommand = 'git log --decorate --pretty="%h;%s" --date-order --no-merges '.$sBaseReference.'..HEAD';
|
||||
$sGitLogRaw = shell_exec($sGitLogCommand);
|
||||
|
||||
|
||||
//--- Analyze log
|
||||
$aGitLogLines = preg_split('/\n/', trim($sGitLogRaw));;
|
||||
$aLogLinesWithBugRef = [];
|
||||
$aLogLineNoBug = [];
|
||||
foreach ($aGitLogLines as $sLogLine) {
|
||||
$sBugRef = preg_match('/[nN]°(\d{3,4})/', $sLogLine, $aLineBugRef);
|
||||
if (($sBugRef === false) || empty($aLineBugRef)) {
|
||||
$aLogLineNoBug[] = $sLogLine;
|
||||
continue;
|
||||
}
|
||||
|
||||
$iBugId = $aLineBugRef[1];
|
||||
$sSha = substr($sLogLine, 0, 9);
|
||||
|
||||
if (array_key_exists($iBugId, $aLogLinesWithBugRef)) {
|
||||
$aBugShaRefs = $aLogLinesWithBugRef[$iBugId];
|
||||
$aBugShaRefs[] = $sSha;
|
||||
$aLogLinesWithBugRef[$iBugId] = $aBugShaRefs;
|
||||
} else {
|
||||
$aLogLinesWithBugRef[$iBugId] = [$sSha];
|
||||
}
|
||||
}
|
||||
$aBugsList = array_keys($aLogLinesWithBugRef);
|
||||
sort($aBugsList, SORT_NUMERIC);
|
||||
|
||||
|
||||
//-- Output results
|
||||
echo "# Bugs included\n";
|
||||
echo "Bug_ref;Bug_URL;sha1\n";
|
||||
foreach ($aBugsList as $sBugRef) {
|
||||
$sShaRefs = implode('|', $aLogLinesWithBugRef[$sBugRef]);
|
||||
echo "{$sBugRef};https://support.combodo.com/pages/UI.php?operation=details&class=Bug&id={$sBugRef};$sShaRefs\n";
|
||||
}
|
||||
echo "\n";
|
||||
echo "# Logs line without bug referenced\n";
|
||||
echo "sha1;subject\n";
|
||||
foreach ($aLogLineNoBug as $sLogLine) {
|
||||
echo "$sLogLine\n";
|
||||
}
|
||||
59
README.md
59
README.md
@@ -21,6 +21,19 @@ iTop also offers mass import tools and web services to integrate with your IT
|
||||
- [Data synchronization][18] (for data federation)
|
||||
|
||||
|
||||
## Latest release
|
||||
|
||||
- [Changes since the previous version][62]
|
||||
- [New features][63]
|
||||
- [Installation notes][64]
|
||||
- [Download][65]
|
||||
|
||||
[62]: https://www.itophub.io/wiki/page?id=latest:release:change_log
|
||||
[63]: https://www.itophub.io/wiki/page?id=latest:release:start
|
||||
[64]: https://www.itophub.io/wiki/page?id=latest:install:start
|
||||
[65]: https://sourceforge.net/projects/itop/files/latest/download
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
- [iTop Forums][1]: community support
|
||||
@@ -49,47 +62,6 @@ iTop also offers mass import tools and web services to integrate with your IT
|
||||
|
||||
|
||||
|
||||
## Last releases
|
||||
|
||||
### Versions 2.7.*
|
||||
- 2.7.1 published on April 8, 2020
|
||||
- [Changes since the previous version][62]
|
||||
- [New features][63]
|
||||
- [Migration notes][64]
|
||||
- [Download iTop 2.7.0-2][65]
|
||||
|
||||
[62]: https://www.itophub.io/wiki/page?id=2_7_0:release:change_log
|
||||
[63]: https://www.itophub.io/wiki/page?id=2_7_0:release:2_7_whats_new
|
||||
[64]: https://www.itophub.io/wiki/page?id=2_7_0:install:260_to_270_migration_notes
|
||||
[65]: https://sourceforge.net/projects/itop/files/itop/2.7.0-2
|
||||
|
||||
|
||||
### Versions 2.6.*
|
||||
- 2.6.0 published on January 9, 2019
|
||||
- [Changes since the previous version][58]
|
||||
- [New features][59]
|
||||
- [Migration notes][60]
|
||||
- [Download iTop 2.6.3][61]
|
||||
|
||||
[58]: https://www.itophub.io/wiki/page?id=2_6_0:release:change_log
|
||||
[59]: https://www.itophub.io/wiki/page?id=2_6_0:release:2_6_whats_new
|
||||
[60]: https://www.itophub.io/wiki/page?id=2_6_0:install:250_to_260_migration_notes
|
||||
[61]: https://sourceforge.net/projects/itop/files/itop/2.6.3
|
||||
|
||||
|
||||
### Versions 2.5.*
|
||||
- 2.5.0 published on July 11, 2018
|
||||
- [Changes since the previous version][54]
|
||||
- [New features][55]
|
||||
- [Migration notes][56]
|
||||
- [Download iTop 2.5.1][57]
|
||||
|
||||
[54]: https://www.itophub.io/wiki/page?id=2_5_0:release:change_log
|
||||
[55]: https://www.itophub.io/wiki/page?id=2_5_0:release:2_5_whats_new
|
||||
[56]: https://www.itophub.io/wiki/page?id=2_5_0:install:240_to_250_migration_notes
|
||||
[57]: https://sourceforge.net/projects/itop/files/itop/2.5.1
|
||||
|
||||
|
||||
## About Us
|
||||
|
||||
iTop development is sponsored, led and supported by [Combodo][0].
|
||||
@@ -123,8 +95,9 @@ We would like to give a special thank you to the people from the community who c
|
||||
- Lassiter, Dennis
|
||||
- Lazcano, Federico
|
||||
- Lucas, Jonathan
|
||||
- Malik, Remie
|
||||
- Rosenke, Stephan
|
||||
- Malik, Remie
|
||||
- Mindêllo de Andrade, Lucas (a.k.a @rokam)
|
||||
- Rosenke, Stephan
|
||||
- Seki, Shoji
|
||||
- Shilov, Vladimir
|
||||
- Tulio, Marco
|
||||
|
||||
@@ -72,15 +72,14 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public abstract function ListSupportedLoginModes();
|
||||
abstract public function ListSupportedLoginModes();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function LoginAction($sLoginState, &$iErrorCode)
|
||||
{
|
||||
switch ($sLoginState)
|
||||
{
|
||||
switch ($sLoginState) {
|
||||
case LoginWebPage::LOGIN_STATE_START:
|
||||
return $this->OnStart($iErrorCode);
|
||||
|
||||
|
||||
@@ -852,28 +852,29 @@ class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
$bCustomized = false;
|
||||
|
||||
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false))
|
||||
{
|
||||
$sDashboardFileSanitized = utils::RealPath($sDashboardFile, APPROOT);
|
||||
if (false === $sDashboardFileSanitized) {
|
||||
throw new SecurityException('Invalid dashboard file !');
|
||||
}
|
||||
|
||||
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false)) {
|
||||
// Search for an eventual user defined dashboard
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oUDSearch->AddCondition('menu_code', $sDashBoardId, '=');
|
||||
$oUDSet = new DBObjectSet($oUDSearch);
|
||||
if ($oUDSet->Count() > 0)
|
||||
{
|
||||
if ($oUDSet->Count() > 0) {
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
$sDashboardDefinition = $oUserDashboard->Get('contents');
|
||||
$bCustomized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
||||
} else {
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
|
||||
}
|
||||
|
||||
if ($sDashboardDefinition !== false)
|
||||
@@ -881,7 +882,7 @@ class RuntimeDashboard extends Dashboard
|
||||
$oDashboard = new RuntimeDashboard($sDashBoardId);
|
||||
$oDashboard->FromXml($sDashboardDefinition);
|
||||
$oDashboard->SetCustomFlag($bCustomized);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFile);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -851,7 +851,7 @@ class DisplayBlock
|
||||
foreach($aStates as $sStateValue)
|
||||
{
|
||||
$sHtmlValue=$aGroupBy['group1']->MakeValueLabel($this->m_oFilter, $sStateValue, $sStateValue);
|
||||
$aStateLabels[$sStateValue] = html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8');
|
||||
$aStateLabels[$sStateValue] = strip_tags($sHtmlValue);
|
||||
|
||||
$aCounts[$sStateValue] = (array_key_exists($sStateValue, $aCountsQueryResults))
|
||||
? $aCountsQueryResults[$sStateValue]
|
||||
|
||||
@@ -624,7 +624,7 @@ JS
|
||||
ShowDebug();
|
||||
$('#logOffBtn>ul').popupmenu();
|
||||
|
||||
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry,.caselog_entry_html').toggle(); });
|
||||
$('body').on('click', '.caselog_header', function () { $(this).toggleClass('open').next('.caselog_entry,.caselog_entry_html').toggle(); });
|
||||
|
||||
$(document).ajaxSend(function(event, jqxhr, options) {
|
||||
jqxhr.setRequestHeader('X-Combodo-Ajax', 'true');
|
||||
|
||||
@@ -35,29 +35,25 @@ register_shutdown_function(function()
|
||||
$sReservedMemory = null;
|
||||
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
|
||||
{
|
||||
// Remove stack trace from MySQLException
|
||||
// Remove stack trace from MySQLException (since 2.7.2 see N°3174)
|
||||
$sMessage = $err['message'];
|
||||
if (strpos($sMessage, 'MySQLException') !== false)
|
||||
{
|
||||
if (strpos($sMessage, 'MySQLException') !== false) {
|
||||
$iStackTracePos = strpos($sMessage, 'Stack trace:');
|
||||
if ($iStackTracePos !== false)
|
||||
{
|
||||
if ($iStackTracePos !== false) {
|
||||
$sMessage = substr($sMessage, 0, $iStackTracePos);
|
||||
}
|
||||
}
|
||||
IssueLog::error($sMessage);
|
||||
if (strpos($err['message'], 'Allowed memory size of') !== false)
|
||||
{
|
||||
// Log additional info but message from $err (since 2.7.6 N°4174)
|
||||
$aErrToLog = $err;
|
||||
unset($aErrToLog['message']);
|
||||
IssueLog::error($sMessage, null, $aErrToLog);
|
||||
if (strpos($err['message'], 'Allowed memory size of') !== false) {
|
||||
$sLimit = ini_get('memory_limit');
|
||||
echo "<p>iTop: Allowed memory size of $sLimit exhausted, contact your administrator to increase 'memory_limit' in php.ini</p>\n";
|
||||
}
|
||||
elseif (strpos($err['message'], 'Maximum execution time') !== false)
|
||||
{
|
||||
} elseif (strpos($err['message'], 'Maximum execution time') !== false) {
|
||||
$sLimit = ini_get('max_execution_time');
|
||||
echo "<p>iTop: Maximum execution time of $sLimit exceeded, contact your administrator to increase 'max_execution_time' in php.ini</p>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
echo "<p>iTop: An error occurred, check server error log for more information.</p>\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
class privUITransaction
|
||||
{
|
||||
/**
|
||||
@@ -99,9 +97,10 @@ class privUITransaction
|
||||
}
|
||||
|
||||
/**
|
||||
* The original (and by default) mechanism for storing transaction information
|
||||
* as an array in the $_SESSION variable
|
||||
* The original mechanism for storing transaction information as an array in the $_SESSION variable
|
||||
*
|
||||
* Warning, since 2.6.0 the session is regenerated on each login (see PR #20) !
|
||||
* Also, we saw some problems when using memcached as the PHP session implementation (see N°1835)
|
||||
*/
|
||||
class privUITransactionSession
|
||||
{
|
||||
@@ -194,9 +193,35 @@ class privUITransactionSession
|
||||
*/
|
||||
class privUITransactionFile
|
||||
{
|
||||
/** @var int Value to use when no user logged */
|
||||
const UNAUTHENTICATED_USER_ID = -666;
|
||||
|
||||
/**
|
||||
* @return int current user id, or {@see self::UNAUTHENTICATED_USER_ID} if no user logged
|
||||
*
|
||||
* @since 2.6.5 2.7.6 3.0.0 N°4289 method creation
|
||||
*/
|
||||
private static function GetCurrentUserId()
|
||||
{
|
||||
$iCurrentUserId = UserRights::GetConnectedUserId();
|
||||
if ('' === $iCurrentUserId) {
|
||||
$iCurrentUserId = static::UNAUTHENTICATED_USER_ID;
|
||||
}
|
||||
|
||||
return $iCurrentUserId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new transaction id, store it in the session and return its id
|
||||
*
|
||||
* @param void
|
||||
*
|
||||
* @return int The new transaction identifier
|
||||
*
|
||||
* @throws \SecurityException
|
||||
* @throws \Exception
|
||||
*
|
||||
* @since 2.6.5 2.7.6 3.0.0 security hardening + throws SecurityException if no user logged
|
||||
*/
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
@@ -213,24 +238,36 @@ class privUITransactionFile
|
||||
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_writable(APPROOT.'data/transactions'))
|
||||
{
|
||||
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
|
||||
}
|
||||
self::CleanupOldTransactions();
|
||||
$id = basename(tempnam(APPROOT.'data/transactions', static::GetUserPrefix()));
|
||||
self::Info('GetNewTransactionId: Created transaction: '.$id);
|
||||
|
||||
return (string)$id;
|
||||
$iCurrentUserId = static::GetCurrentUserId();
|
||||
|
||||
self::CleanupOldTransactions();
|
||||
|
||||
$sTransactionIdFullPath = tempnam(APPROOT.'data/transactions', static::GetUserPrefix());
|
||||
file_put_contents($sTransactionIdFullPath, $iCurrentUserId, LOCK_EX);
|
||||
|
||||
$sTransactionIdFileName = basename($sTransactionIdFullPath);
|
||||
self::Info('GetNewTransactionId: Created transaction: '.$sTransactionIdFileName);
|
||||
|
||||
return $sTransactionIdFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
|
||||
* the session so that another call to IsTransactionValid for the same transaction id
|
||||
* will return false
|
||||
*
|
||||
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
|
||||
* @param bool $bRemoveTransaction True if the transaction must be removed
|
||||
*
|
||||
* @return bool True if the transaction is valid, false otherwise
|
||||
*
|
||||
* @since 2.6.5 2.7.6 3.0.0 N°4289 security hardening
|
||||
*/
|
||||
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
||||
{
|
||||
@@ -244,53 +281,53 @@ class privUITransactionFile
|
||||
|
||||
clearstatcache(true, $sFilepath);
|
||||
$bResult = file_exists($sFilepath);
|
||||
if ($bResult)
|
||||
|
||||
if (false === $bResult) {
|
||||
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions:\n".implode("\n", self::GetPendingTransactions()));
|
||||
return false;
|
||||
}
|
||||
|
||||
$iCurrentUserId = static::GetCurrentUserId();
|
||||
$sTransactionIdUserId = file_get_contents($sFilepath);
|
||||
if ($iCurrentUserId != $sTransactionIdUserId) {
|
||||
self::Info("IsTransactionValid: Transaction '$id' not existing for current user. Pending transactions:\n".implode("\n", self::GetPendingTransactions()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($bRemoveTransaction)
|
||||
{
|
||||
if ($bRemoveTransaction)
|
||||
$bResult = @unlink($sFilepath);
|
||||
if (!$bResult)
|
||||
{
|
||||
$bResult = @unlink($sFilepath);
|
||||
if (!$bResult)
|
||||
{
|
||||
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
|
||||
}
|
||||
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
||||
}
|
||||
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the transaction specified by its id
|
||||
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
|
||||
* @return void
|
||||
* @return bool true if the token can be removed
|
||||
*
|
||||
* @since 2.6.5 2.7.6 3.0.0 N°4289 security hardening
|
||||
*/
|
||||
public static function RemoveTransaction($id)
|
||||
{
|
||||
$bSuccess = true;
|
||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
||||
clearstatcache(true, $sFilepath);
|
||||
if(!file_exists($sFilepath))
|
||||
{
|
||||
$bSuccess = false;
|
||||
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
||||
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||
$bResult = static::IsTransactionValid($id, true);
|
||||
if (false === $bResult) {
|
||||
self::Error("RemoveTransaction: Transaction '$id' is invalid. Pending transactions:\n"
|
||||
.implode("\n", self::GetPendingTransactions()));
|
||||
return false;
|
||||
}
|
||||
$bSuccess = @unlink($sFilepath);
|
||||
if (!$bSuccess)
|
||||
{
|
||||
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('RemoveTransaction: OK '.$id);
|
||||
}
|
||||
return $bSuccess;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -363,22 +400,35 @@ class privUITransactionFile
|
||||
{
|
||||
self::Write('Error | '.$sText);
|
||||
}
|
||||
|
||||
|
||||
protected static function IsLogEnabled() {
|
||||
$oConfig = MetaModel::GetConfig();
|
||||
if (is_null($oConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$bLogTransactions = $oConfig->Get('log_transactions');
|
||||
if (true === $bLogTransactions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function Write($sText)
|
||||
{
|
||||
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
|
||||
if ($bLogEnabled)
|
||||
{
|
||||
if (false === static::IsLogEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
|
||||
if ($hLogFile !== false)
|
||||
{
|
||||
if ($hLogFile !== false) {
|
||||
flock($hLogFile, LOCK_EX);
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
fwrite($hLogFile, "$sDate | $sText\n");
|
||||
fflush($hLogFile);
|
||||
flock($hLogFile, LOCK_UN);
|
||||
fclose($hLogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,10 +372,10 @@ EOF
|
||||
$sHTML .= "</form>\n";
|
||||
$sHTML .= '</div></div>';
|
||||
|
||||
$sDialogTitle = addslashes($sTitle);
|
||||
$sDialogTitleSanitized = addslashes(utils::HtmlToText($sTitle));
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#ac_dlg_{$this->iId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
|
||||
$('#ac_dlg_{$this->iId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$sDialogTitleSanitized', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
|
||||
$('#fs_{$this->iId}').bind('submit.uiAutocomplete', oACWidget_{$this->iId}.DoSearchObjects);
|
||||
$('#dc_{$this->iId}').resize(oACWidget_{$this->iId}.UpdateSizes);
|
||||
EOF
|
||||
|
||||
@@ -563,48 +563,93 @@ class utils
|
||||
|
||||
public static function ReadFromFile($sFileName)
|
||||
{
|
||||
if (!file_exists($sFileName)) return false;
|
||||
if (!file_exists($sFileName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return file_get_contents($sFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert a value expressed in a 'user friendly format'
|
||||
* as in php.ini, e.g. 256k, 2M, 1G etc. Into a number of bytes
|
||||
* @param mixed $value The value as read from php.ini
|
||||
* @return number
|
||||
* @param mixed $value The value as read from php.ini (eg 256k, 2M, 1G etc.)
|
||||
*
|
||||
* @return int conversion to number of bytes
|
||||
*
|
||||
* @since 2.7.5 3.0.0 convert to int numeric values
|
||||
*
|
||||
* @link https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes Shorthand bytes value reference in PHP.net FAQ
|
||||
*/
|
||||
public static function ConvertToBytes( $value )
|
||||
public static function ConvertToBytes($value)
|
||||
{
|
||||
$iReturn = $value;
|
||||
if ( !is_numeric( $value ) )
|
||||
{
|
||||
$iLength = strlen( $value );
|
||||
$iReturn = substr( $value, 0, $iLength - 1 );
|
||||
$sUnit = strtoupper( substr( $value, $iLength - 1 ) );
|
||||
switch ( $sUnit )
|
||||
{
|
||||
case 'G':
|
||||
$iReturn *= 1024;
|
||||
case 'M':
|
||||
$iReturn *= 1024;
|
||||
case 'K':
|
||||
$iReturn *= 1024;
|
||||
}
|
||||
}
|
||||
return $iReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the memory limit is at least what is required
|
||||
*
|
||||
* @param int $memoryLimit set limit in bytes
|
||||
* @param int $requiredLimit required limit in bytes
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsMemoryLimitOk($memoryLimit, $requiredLimit)
|
||||
{
|
||||
return ($memoryLimit >= $requiredLimit) || ($memoryLimit == -1);
|
||||
}
|
||||
if (!is_numeric($value)) {
|
||||
$iLength = strlen($value);
|
||||
$iReturn = substr($value, 0, $iLength - 1);
|
||||
$sUnit = strtoupper(substr($value, $iLength - 1));
|
||||
switch ($sUnit) {
|
||||
case 'G':
|
||||
$iReturn *= 1024;
|
||||
case 'M':
|
||||
$iReturn *= 1024;
|
||||
case 'K':
|
||||
$iReturn *= 1024;
|
||||
}
|
||||
} else {
|
||||
$iReturn = (int)$value;
|
||||
}
|
||||
|
||||
return $iReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the memory limit is at least what is required
|
||||
*
|
||||
* @param int $iMemoryLimit set limit in bytes, use {@link utils::ConvertToBytes()} to convert current php.ini value
|
||||
* @param int $iRequiredLimit required limit in bytes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsMemoryLimitOk($iMemoryLimit, $iRequiredLimit)
|
||||
{
|
||||
if ($iMemoryLimit === -1) {
|
||||
// -1 means : no limit (see https://www.php.net/manual/fr/ini.core.php#ini.memory-limit)
|
||||
return true;
|
||||
}
|
||||
|
||||
return ($iMemoryLimit >= $iRequiredLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set memory_limit to required value
|
||||
*
|
||||
* @param string $sRequiredLimit required limit, for example '512M'
|
||||
*
|
||||
* @return bool|null null if nothing was done, true if modifying memory_limit was successful, false otherwise
|
||||
*
|
||||
* @uses utils::ConvertToBytes()
|
||||
* @uses \ini_get('memory_limit')
|
||||
* @uses \ini_set()
|
||||
* @uses utils::ConvertToBytes()
|
||||
*
|
||||
* @since 2.7.5 N°3806
|
||||
*/
|
||||
public static function SetMinMemoryLimit($sRequiredLimit)
|
||||
{
|
||||
$iRequiredLimit = static::ConvertToBytes($sRequiredLimit);
|
||||
$sMemoryLimit = trim(ini_get('memory_limit'));
|
||||
if (empty($sMemoryLimit)) {
|
||||
// On some PHP installations, memory_limit does not exist as a PHP setting!
|
||||
// (encountered on a 5.2.0 under Windows)
|
||||
// In that case, ini_set will not work
|
||||
return false;
|
||||
}
|
||||
$iMemoryLimit = static::ConvertToBytes($sMemoryLimit);
|
||||
|
||||
if (static::IsMemoryLimitOk($iMemoryLimit, $iRequiredLimit)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ini_set('memory_limit', $iRequiredLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
|
||||
@@ -733,7 +778,7 @@ class utils
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @since 2.7.0 N°2478 always call {@link MetaModel::GetConfig} first, cache is only set when loading from disk
|
||||
* @since 2.7.0 N°2478 this method will now always call {@link MetaModel::GetConfig} first, and cache in this class is only set when loading from disk
|
||||
*/
|
||||
public static function GetConfig()
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"ext-soap": "*",
|
||||
"combodo/tcpdf": "6.3.5",
|
||||
"nikic/php-parser": "^3.1",
|
||||
"pear/archive_tar": "1.4.10",
|
||||
"pear/archive_tar": "1.4.14",
|
||||
"pelago/emogrifier": "2.1.0",
|
||||
"scssphp/scssphp": "1.0.6",
|
||||
"swiftmailer/swiftmailer": "5.4.12",
|
||||
|
||||
29
composer.lock
generated
29
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "27af144ea2acf2c138f587052a4ceddc",
|
||||
"content-hash": "9993a936d8716519fc42cc50600cd84f",
|
||||
"packages": [
|
||||
{
|
||||
"name": "combodo/tcpdf",
|
||||
@@ -168,16 +168,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pear/archive_tar",
|
||||
"version": "1.4.10",
|
||||
"version": "1.4.14",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pear/Archive_Tar.git",
|
||||
"reference": "bbb4f10f71a1da2715ec6d9a683f4f23c507a49b"
|
||||
"reference": "4d761c5334c790e45ef3245f0864b8955c562caa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/bbb4f10f71a1da2715ec6d9a683f4f23c507a49b",
|
||||
"reference": "bbb4f10f71a1da2715ec6d9a683f4f23c507a49b",
|
||||
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/4d761c5334c790e45ef3245f0864b8955c562caa",
|
||||
"reference": "4d761c5334c790e45ef3245f0864b8955c562caa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -230,7 +230,21 @@
|
||||
"archive",
|
||||
"tar"
|
||||
],
|
||||
"time": "2020-09-15T14:13:23+00:00"
|
||||
"support": {
|
||||
"issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Archive_Tar",
|
||||
"source": "https://github.com/pear/Archive_Tar"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/mrook",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/michielrook",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2021-07-20T13:53:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pear/console_getopt",
|
||||
@@ -2590,5 +2604,6 @@
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
"php": "5.6.0"
|
||||
}
|
||||
},
|
||||
"plugin-api-version": "2.1.0"
|
||||
}
|
||||
|
||||
@@ -314,42 +314,54 @@ class ActionEmail extends ActionNotification
|
||||
{
|
||||
$this->m_iRecipients = 0;
|
||||
$this->m_aMailErrors = array();
|
||||
$bRes = false; // until we do succeed in sending the email
|
||||
|
||||
|
||||
// Determine recicipients
|
||||
//
|
||||
$sTo = $this->FindRecipients('to', $aContextArgs);
|
||||
$sCC = $this->FindRecipients('cc', $aContextArgs);
|
||||
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
|
||||
|
||||
|
||||
$sFrom = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
|
||||
$sReplyTo = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
|
||||
|
||||
|
||||
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
|
||||
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
|
||||
|
||||
|
||||
$oObj = $aContextArgs['this->object()'];
|
||||
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/), MetaModel::GetEnvironmentId());
|
||||
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/),
|
||||
MetaModel::GetEnvironmentId());
|
||||
$sReference = '<'.$sMessageId.'>';
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
throw $e;
|
||||
}
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
|
||||
if (!is_null($oLog))
|
||||
{
|
||||
catch (Exception $e) {
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
throw $e;
|
||||
}
|
||||
finally {
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
}
|
||||
|
||||
if (!is_null($oLog)) {
|
||||
// Note: we have to secure this because those values are calculated
|
||||
// inside the try statement, and we would like to keep track of as
|
||||
// many data as we could while some variables may still be undefined
|
||||
if (isset($sTo)) $oLog->Set('to', $sTo);
|
||||
if (isset($sCC)) $oLog->Set('cc', $sCC);
|
||||
if (isset($sBCC)) $oLog->Set('bcc', $sBCC);
|
||||
if (isset($sFrom)) $oLog->Set('from', $sFrom);
|
||||
if (isset($sSubject)) $oLog->Set('subject', $sSubject);
|
||||
if (isset($sBody)) $oLog->Set('body', $sBody);
|
||||
if (isset($sTo)) {
|
||||
$oLog->Set('to', $sTo);
|
||||
}
|
||||
if (isset($sCC)) {
|
||||
$oLog->Set('cc', $sCC);
|
||||
}
|
||||
if (isset($sBCC)) {
|
||||
$oLog->Set('bcc', $sBCC);
|
||||
}
|
||||
if (isset($sFrom)) {
|
||||
$oLog->Set('from', $sFrom);
|
||||
}
|
||||
if (isset($sSubject)) {
|
||||
$oLog->Set('subject', $sSubject);
|
||||
}
|
||||
if (isset($sBody)) {
|
||||
$oLog->Set('body', $sBody);
|
||||
}
|
||||
}
|
||||
$sStyles = file_get_contents(APPROOT.'css/email.css');
|
||||
$sStyles .= MetaModel::GetConfig()->Get('email_css');
|
||||
@@ -439,4 +451,3 @@ class ActionEmail extends ActionNotification
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
42
core/apc-service.class.inc.php
Normal file
42
core/apc-service.class.inc.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class ApcService
|
||||
* @since 2.7.6 N°4125
|
||||
*/
|
||||
class ApcService {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $function_name
|
||||
* @return bool
|
||||
* @see function_exists()
|
||||
*/
|
||||
public function function_exists($function_name) {
|
||||
return function_exists($function_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $key
|
||||
* @return mixed
|
||||
* @see apc_fetch()
|
||||
*/
|
||||
function apc_fetch($key)
|
||||
{
|
||||
return apc_fetch($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $key
|
||||
* @param $var
|
||||
* @param int $ttl
|
||||
* @return array|bool
|
||||
* @see apc_store()
|
||||
*/
|
||||
function apc_store($key, $var = NULL, $ttl = 0)
|
||||
{
|
||||
return apc_store($key, $var, $ttl);
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -7978,6 +7978,13 @@ class AttributeImage extends AttributeBlob
|
||||
{
|
||||
$oDoc = parent::MakeRealValue($proposedValue, $oHostObj);
|
||||
|
||||
if (($oDoc instanceof ormDocument)
|
||||
&& (false === $oDoc->IsEmpty())
|
||||
&& ($oDoc->GetMimeType() === 'image/svg+xml')) {
|
||||
$sCleanSvg = HTMLSanitizer::Sanitize($oDoc->GetData(), 'svg_sanitizer');
|
||||
$oDoc = new ormDocument($sCleanSvg, $oDoc->GetMimeType(), $oDoc->GetFileName());
|
||||
}
|
||||
|
||||
// The validation of the MIME Type is done by CheckFormat below
|
||||
return $oDoc;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ class MySQLException extends CoreException
|
||||
*/
|
||||
public function __construct($sIssue, $aContext, $oException = null, $oMysqli = null)
|
||||
{
|
||||
|
||||
if ($oException != null)
|
||||
{
|
||||
$aContext['mysql_errno'] = $oException->getCode();
|
||||
@@ -58,6 +59,11 @@ class MySQLException extends CoreException
|
||||
$aContext['mysql_error'] = CMDBSource::GetError();
|
||||
}
|
||||
parent::__construct($sIssue, $aContext);
|
||||
//if is connection error, don't log the default message with password in
|
||||
if (mysqli_connect_errno()) {
|
||||
error_log($this->message);
|
||||
error_reporting(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +154,17 @@ class CMDBSource
|
||||
|
||||
/** @var mysqli $m_oMysqli */
|
||||
protected static $m_oMysqli;
|
||||
/**
|
||||
* The mysqli object is really hard to mock ! This attribute is used only in certain methods, so that we can mock only a very little subset of the mysqli object.
|
||||
* We are setting it in {@see Init}, by default it is a copy of {@see $m_oMysqli}
|
||||
* The mock can be injected using the setter {@see SetMySQLiForQuery}
|
||||
*
|
||||
* @var mysqli $oMySQLiForQuery
|
||||
* @see GetMySQLiForQuery
|
||||
* @see SetMySQLiForQuery
|
||||
* @since 2.7.5 N°3513 new var to allow mock in tests ({@see \Combodo\iTop\Test\UnitTest\Core\TransactionsTest})
|
||||
*/
|
||||
protected static $oMySQLiForQuery;
|
||||
|
||||
/**
|
||||
* @var int number of level for nested transactions : 0 if no transaction was ever opened, +1 for each 'START TRANSACTION' sent
|
||||
@@ -214,6 +231,7 @@ class CMDBSource
|
||||
self::$m_sDBTlsCA = empty($sTlsCA) ? null : $sTlsCA;
|
||||
|
||||
self::$m_oMysqli = self::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, true);
|
||||
self::SetMySQLiForQuery(self::$m_oMysqli);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,12 +245,12 @@ class CMDBSource
|
||||
*
|
||||
* @return \mysqli
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @uses IsOpenedDbConnectionUsingTls when asking for a TLS connection, to check if it was really opened using TLS
|
||||
*/
|
||||
public static function GetMysqliInstance(
|
||||
$sDbHost, $sUser, $sPwd, $sSource = '', $bTlsEnabled = false, $sTlsCa = null, $bCheckTlsAfterConnection = false
|
||||
) {
|
||||
$oMysqli = null;
|
||||
|
||||
$sServer = null;
|
||||
$iPort = null;
|
||||
self::InitServerAndPort($sDbHost, $sServer, $iPort);
|
||||
@@ -262,7 +280,7 @@ class CMDBSource
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser), $e);
|
||||
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser),$e);
|
||||
}
|
||||
|
||||
if ($bTlsEnabled
|
||||
@@ -331,41 +349,41 @@ class CMDBSource
|
||||
* parameters were used.<br>
|
||||
* This method can be called to ensure that the DB connection really uses TLS.
|
||||
*
|
||||
* <p>We're using this object connection : {@link self::$m_oMysqli}
|
||||
* <p>We're using our own mysqli instance to do the check as this check is done when creating the mysqli instance : the consumer
|
||||
* might want a dedicated object, and if so we don't want to overwrite the one saved in CMDBSource !<br>
|
||||
* This is the case for example with {@see \iTopMutex} !
|
||||
*
|
||||
* @param \mysqli $oMysqli
|
||||
*
|
||||
* @return boolean true if the connection was really established using TLS
|
||||
* @return boolean true if the connection was really established using TLS, false otherwise
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @used-by GetMysqliInstance
|
||||
* @uses IsMySqlVarNonEmpty
|
||||
* @uses 'ssl_version' MySQL var
|
||||
* @uses 'ssl_cipher' MySQL var
|
||||
*/
|
||||
private static function IsOpenedDbConnectionUsingTls($oMysqli)
|
||||
{
|
||||
if (self::$m_oMysqli == null)
|
||||
{
|
||||
self::$m_oMysqli = $oMysqli;
|
||||
}
|
||||
|
||||
$bNonEmptySslVersionVar = self::IsMySqlVarNonEmpty('ssl_version');
|
||||
$bNonEmptySslCipherVar = self::IsMySqlVarNonEmpty('ssl_cipher');
|
||||
$bNonEmptySslVersionVar = self::IsMySqlVarNonEmpty('ssl_version', $oMysqli);
|
||||
$bNonEmptySslCipherVar = self::IsMySqlVarNonEmpty('ssl_cipher', $oMysqli);
|
||||
|
||||
return ($bNonEmptySslVersionVar && $bNonEmptySslCipherVar);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sVarName
|
||||
* @param mysqli $oMysqli connection to use for the query
|
||||
*
|
||||
* @return bool
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @uses SHOW STATUS queries
|
||||
* @uses 'SHOW SESSION STATUS' queries
|
||||
*/
|
||||
private static function IsMySqlVarNonEmpty($sVarName)
|
||||
private static function IsMySqlVarNonEmpty($sVarName, $oMysqli)
|
||||
{
|
||||
try
|
||||
{
|
||||
$sResult = self::QueryToScalar("SHOW SESSION STATUS LIKE '$sVarName'", 1);
|
||||
$sResult = self::QueryToScalar("SHOW SESSION STATUS LIKE '$sVarName'", 1, $oMysqli);
|
||||
}
|
||||
catch (MySQLQueryHasNoResultException $e)
|
||||
{
|
||||
@@ -530,6 +548,24 @@ class CMDBSource
|
||||
return self::$m_oMysqli;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
private static function GetMySQLiForQuery()
|
||||
{
|
||||
return self::$oMySQLiForQuery;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used for test purpose (mysqli mock)
|
||||
* @param $oMySQLi
|
||||
*/
|
||||
private static function SetMySQLiForQuery($oMySQLi)
|
||||
{
|
||||
self::$oMySQLiForQuery = $oMySQLi;
|
||||
}
|
||||
|
||||
public static function GetErrNo()
|
||||
{
|
||||
if (self::$m_oMysqli->errno != 0)
|
||||
@@ -665,10 +701,15 @@ class CMDBSource
|
||||
*/
|
||||
private static function DBQuery($sSql)
|
||||
{
|
||||
$sShortSQL = substr(preg_replace("/\s+/", " ", substr($sSql, 0, 180)), 0, 150);
|
||||
if (substr_compare($sShortSQL, "SELECT", 0, strlen("SELECT")) !== 0) {
|
||||
IssueLog::Trace("$sShortSQL", 'cmdbsource');
|
||||
}
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
$oResult = self::GetMySQLiForQuery()->query($sSql);
|
||||
}
|
||||
catch (mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -680,7 +721,7 @@ class CMDBSource
|
||||
{
|
||||
$aContext = array('query' => $sSql);
|
||||
|
||||
$iMySqlErrorNo = self::$m_oMysqli->errno;
|
||||
$iMySqlErrorNo = self::GetMySQLiForQuery()->errno;
|
||||
$aMySqlHasGoneAwayErrorCodes = MySQLHasGoneAwayException::getErrorCodes();
|
||||
if (in_array($iMySqlErrorNo, $aMySqlHasGoneAwayErrorCodes))
|
||||
{
|
||||
@@ -702,7 +743,7 @@ class CMDBSource
|
||||
private static function LogDeadLock(Exception $e)
|
||||
{
|
||||
// checks MySQL error code
|
||||
$iMySqlErrorNo = self::$m_oMysqli->errno;
|
||||
$iMySqlErrorNo = self::GetMySQLiForQuery()->errno;
|
||||
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK)))
|
||||
{
|
||||
return;
|
||||
@@ -710,7 +751,7 @@ class CMDBSource
|
||||
|
||||
// Get error info
|
||||
$sUser = UserRights::GetUser();
|
||||
$oError = self::$m_oMysqli->query('SHOW ENGINE INNODB STATUS');
|
||||
$oError = self::GetMySQLiForQuery()->query('SHOW ENGINE INNODB STATUS');
|
||||
if ($oError !== false)
|
||||
{
|
||||
$aData = $oError->fetch_all(MYSQLI_ASSOC);
|
||||
@@ -732,7 +773,7 @@ class CMDBSource
|
||||
);
|
||||
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
|
||||
|
||||
IssueLog::Error($sMessage, 'DeadLock', $e->getMessage());
|
||||
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -748,10 +789,15 @@ class CMDBSource
|
||||
*/
|
||||
private static function StartTransaction()
|
||||
{
|
||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||
$bHasExistingTransactions = self::IsInsideTransaction();
|
||||
if (!$bHasExistingTransactions)
|
||||
{
|
||||
IssueLog::Trace("START TRANSACTION $sCaller", 'cmdbsource');
|
||||
self::DBQuery('START TRANSACTION');
|
||||
} else {
|
||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") START TRANSACTION $sCaller", 'cmdbsource');
|
||||
}
|
||||
|
||||
self::AddTransactionLevel();
|
||||
@@ -769,9 +815,12 @@ class CMDBSource
|
||||
*/
|
||||
private static function Commit()
|
||||
{
|
||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||
if (!self::IsInsideTransaction())
|
||||
{
|
||||
// should not happen !
|
||||
IssueLog::Error("No Transaction COMMIT $sCaller", 'cmdbsource');
|
||||
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
||||
}
|
||||
|
||||
@@ -779,8 +828,10 @@ class CMDBSource
|
||||
|
||||
if (self::IsInsideTransaction())
|
||||
{
|
||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") COMMIT $sCaller", 'cmdbsource');
|
||||
return;
|
||||
}
|
||||
IssueLog::Trace("COMMIT $sCaller", 'cmdbsource');
|
||||
self::DBQuery('COMMIT');
|
||||
}
|
||||
|
||||
@@ -799,17 +850,22 @@ class CMDBSource
|
||||
*/
|
||||
private static function Rollback()
|
||||
{
|
||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||
if (!self::IsInsideTransaction())
|
||||
{
|
||||
// should not happen !
|
||||
IssueLog::Error("No Transaction ROLLBACK $sCaller", 'cmdbsource');
|
||||
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
||||
}
|
||||
self::RemoveLastTransactionLevel();
|
||||
if (self::IsInsideTransaction())
|
||||
{
|
||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") ROLLBACK $sCaller", 'cmdbsource');
|
||||
return;
|
||||
}
|
||||
|
||||
IssueLog::Trace("ROLLBACK $sCaller", 'cmdbsource');
|
||||
self::DBQuery('ROLLBACK');
|
||||
}
|
||||
|
||||
@@ -859,6 +915,17 @@ class CMDBSource
|
||||
self::$m_iTransactionLevel = 0;
|
||||
}
|
||||
|
||||
public static function IsDeadlockException(Exception $e)
|
||||
{
|
||||
while ($e instanceof Exception) {
|
||||
if (($e instanceof MySQLException) && ($e->getCode() == 1213)) {
|
||||
return true;
|
||||
}
|
||||
$e = $e->getPrevious();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @deprecated 2.7.0 N°1627 use ItopCounter instead
|
||||
@@ -912,17 +979,21 @@ class CMDBSource
|
||||
/**
|
||||
* @param string $sSql
|
||||
* @param int $iCol beginning at 0
|
||||
* @param mysqli $oMysqli if not null will query using this connection, otherwise will use {@see GetMySQLiForQuery}
|
||||
*
|
||||
* @return string corresponding cell content on the first line
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLQueryHasNoResultException
|
||||
* @since 2.7.5-2 2.7.6 3.0.0 N°4215 new optional mysqli param
|
||||
*/
|
||||
public static function QueryToScalar($sSql, $iCol = 0)
|
||||
public static function QueryToScalar($sSql, $iCol = 0, $oMysqli = null)
|
||||
{
|
||||
$oMysqliToQuery = (is_null($oMysqli)) ? self::GetMySQLiForQuery() : $oMysqli;
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
$oResult = $oMysqliToQuery->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -962,7 +1033,7 @@ class CMDBSource
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
$oResult = self::GetMySQLiForQuery()->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -1044,7 +1115,7 @@ class CMDBSource
|
||||
{
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
$oResult = self::GetMySQLiForQuery()->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -1531,7 +1602,7 @@ class CMDBSource
|
||||
$sSql = "SELECT * FROM `$sTable`";
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
$oResult = self::GetMySQLiForQuery()->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
|
||||
@@ -1113,6 +1113,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'svg_sanitizer' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'The class to use for SVG sanitization : allow to provide a custom made sanitizer',
|
||||
'default' => 'SVGDOMSanitizer',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'inline_image_max_display_width' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.',
|
||||
@@ -1273,6 +1281,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'security.disable_inline_documents_sandbox' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'If true then the sandbox for documents displayed in a browser tab will be disabled; enabling scripts and other interactive content. Note that setting this to true will open the application to potential XSS attacks!',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -149,14 +149,14 @@ class CoreCannotSaveObjectException extends CoreException
|
||||
*
|
||||
* @param array $aContextData containing at least those keys : issues, id, class
|
||||
*/
|
||||
public function __construct($aContextData)
|
||||
public function __construct($aContextData, $oPrevious = null)
|
||||
{
|
||||
$this->aIssues = $aContextData['issues'];
|
||||
$this->iObjectId = $aContextData['id'];
|
||||
$this->sObjectClass = $aContextData['class'];
|
||||
|
||||
$sIssues = implode(', ', $this->aIssues);
|
||||
parent::__construct($sIssues, $aContextData);
|
||||
parent::__construct($sIssues, $aContextData, '', $oPrevious);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2739,50 +2739,72 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
$iTransactionRetry = 1;
|
||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
||||
try
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('START TRANSACTION');
|
||||
}
|
||||
|
||||
// First query built upon on the root class, because the ID must be created first
|
||||
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
|
||||
|
||||
// Then do the leaf class, if different from the root class
|
||||
if ($sClass != $sRootClass)
|
||||
{
|
||||
$this->DBInsertSingleTable($sClass);
|
||||
}
|
||||
|
||||
// Then do the other classes
|
||||
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass)
|
||||
{
|
||||
if ($sParentClass == $sRootClass)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$this->DBInsertSingleTable($sParentClass);
|
||||
}
|
||||
|
||||
$this->OnObjectKeyReady();
|
||||
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('COMMIT');
|
||||
}
|
||||
// TODO Deep clone this object before the transaction (to use it in case of rollback)
|
||||
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iTransactionRetryCount = 1;
|
||||
$iTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iTransactionRetryCount;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
while ($iTransactionRetry > 0) {
|
||||
try {
|
||||
$iTransactionRetry--;
|
||||
if ($bIsTransactionEnabled) {
|
||||
CMDBSource::Query('START TRANSACTION');
|
||||
}
|
||||
|
||||
// First query built upon on the root class, because the ID must be created first
|
||||
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
|
||||
|
||||
// Then do the leaf class, if different from the root class
|
||||
if ($sClass != $sRootClass) {
|
||||
$this->DBInsertSingleTable($sClass);
|
||||
}
|
||||
|
||||
// Then do the other classes
|
||||
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass) {
|
||||
if ($sParentClass == $sRootClass) {
|
||||
continue;
|
||||
}
|
||||
$this->DBInsertSingleTable($sParentClass);
|
||||
}
|
||||
|
||||
$this->OnObjectKeyReady();
|
||||
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
|
||||
if ($bIsTransactionEnabled) {
|
||||
CMDBSource::Query('COMMIT');
|
||||
}
|
||||
break;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
|
||||
{
|
||||
// Deadlock found when trying to get lock; try restarting transaction (only in main transaction)
|
||||
if ($iTransactionRetry > 0)
|
||||
{
|
||||
// wait and retry
|
||||
IssueLog::Error("Insert TRANSACTION Retrying...");
|
||||
usleep(random_int(1, 5) * 1000 * $iTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Error("Insert Deadlock TRANSACTION prevention failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->m_bIsInDB = true;
|
||||
@@ -3123,13 +3145,11 @@ abstract class DBObject implements iDisplay
|
||||
array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \Trigger $oTrigger */
|
||||
try
|
||||
{
|
||||
/** @var \TriggerOnObjectUpdate $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
@@ -3158,9 +3178,11 @@ abstract class DBObject implements iDisplay
|
||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
// TODO Deep clone this object before the transaction (to use it in case of rollback)
|
||||
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iTransactionRetryCount = 1;
|
||||
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iIsTransactionRetryCount;
|
||||
$iTransactionRetry = $iTransactionRetryCount;
|
||||
}
|
||||
while ($iTransactionRetry > 0)
|
||||
{
|
||||
@@ -3228,13 +3250,6 @@ abstract class DBObject implements iDisplay
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
|
||||
// following lines are resetting changes (so after this {@see DBObject::ListChanges()} won't return changes anymore)
|
||||
// new values are already in the object (call {@see DBObject::Get()} to get them)
|
||||
// call {@see DBObject::ListPreviousValuesForUpdatedAttributes()} to get changed fields and previous values
|
||||
$this->m_bDirty = false;
|
||||
$this->m_aTouchedAtt = array();
|
||||
$this->m_aModifiedAtt = array();
|
||||
|
||||
if (count($aChanges) != 0)
|
||||
{
|
||||
$this->RecordAttChanges($aChanges, $aOriginalValues);
|
||||
@@ -3248,18 +3263,18 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
catch (MySQLException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
if ($e->getCode() == 1213)
|
||||
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
|
||||
{
|
||||
// Deadlock found when trying to get lock; try restarting transaction
|
||||
IssueLog::Error($e->getMessage());
|
||||
// Deadlock found when trying to get lock; try restarting transaction (only in main transaction)
|
||||
if ($iTransactionRetry > 0)
|
||||
{
|
||||
// wait and retry
|
||||
IssueLog::Error("Update TRANSACTION Retrying...");
|
||||
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
|
||||
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -3273,10 +3288,11 @@ abstract class DBObject implements iDisplay
|
||||
'id' => $this->GetKey(),
|
||||
'class' => get_class($this),
|
||||
'issues' => $aErrors
|
||||
));
|
||||
), $e);
|
||||
}
|
||||
catch (CoreCannotSaveObjectException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
@@ -3285,6 +3301,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
@@ -3298,6 +3315,13 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
// following lines are resetting changes (so after this {@see DBObject::ListChanges()} won't return changes anymore)
|
||||
// new values are already in the object (call {@see DBObject::Get()} to get them)
|
||||
// call {@see DBObject::ListPreviousValuesForUpdatedAttributes()} to get changed fields and previous values
|
||||
$this->m_bDirty = false;
|
||||
$this->m_aTouchedAtt = array();
|
||||
$this->m_aModifiedAtt = array();
|
||||
|
||||
try
|
||||
{
|
||||
$this->AfterUpdate();
|
||||
@@ -3496,9 +3520,11 @@ abstract class DBObject implements iDisplay
|
||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iIsTransactionRetryCount;
|
||||
// TODO Deep clone this object before the transaction (to use it in case of rollback)
|
||||
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iTransactionRetryCount = 1;
|
||||
$iTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iTransactionRetryCount;
|
||||
}
|
||||
while ($iTransactionRetry > 0)
|
||||
{
|
||||
@@ -3521,18 +3547,18 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
catch (MySQLException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
if ($e->getCode() == 1213)
|
||||
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
|
||||
{
|
||||
// Deadlock found when trying to get lock; try restarting transaction
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($iTransactionRetry > 0)
|
||||
{
|
||||
// wait and retry
|
||||
IssueLog::Error("Delete TRANSACTION Retrying...");
|
||||
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
|
||||
usleep(random_int(1, 5) * 1000 * $iTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -3709,16 +3735,22 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Apply a stimulus (workflow)
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sStimulusCode
|
||||
* @param bool $bDoNotWrite
|
||||
*
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sStimulusCode
|
||||
* @param bool $bDoNotWrite if true we won't save the object !
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
*
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
*
|
||||
* @uses \AttributeStopWatch::Start
|
||||
* @uses \AttributeStopWatch::Stop
|
||||
* @uses \DBObject::DBWrite
|
||||
* @uses \TriggerOnStateLeave::DoActivate
|
||||
* @uses \TriggerOnStateEnter::DoActivate
|
||||
*/
|
||||
public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false)
|
||||
{
|
||||
@@ -3842,8 +3874,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bDoNotWrite)
|
||||
{
|
||||
if (!$bDoNotWrite) {
|
||||
$this->DBWrite();
|
||||
}
|
||||
|
||||
@@ -3851,30 +3882,26 @@ abstract class DBObject implements iDisplay
|
||||
$aParams = array(
|
||||
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
|
||||
'previous_state' => $sPreviousState,
|
||||
'new_state' => $sNewState);
|
||||
'new_state' => $sNewState,
|
||||
);
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN (:class_list) AND t.state=:previous_state"), array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \Trigger $oTrigger */
|
||||
try
|
||||
{
|
||||
while ($oTrigger = $oSet->Fetch()) {
|
||||
/** @var \TriggerOnStateLeave $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN (:class_list) AND t.state=:new_state"), array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \Trigger $oTrigger */
|
||||
try{
|
||||
while ($oTrigger = $oSet->Fetch()) {
|
||||
/** @var \TriggerOnStateEnter $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1004,10 +1004,8 @@ abstract class DBSearch
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a SQL query from the current search
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* Generate a SQL query from the current search
|
||||
*
|
||||
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
* @param array $aArgs
|
||||
* @param null $aAttToLoad
|
||||
@@ -1015,12 +1013,16 @@ abstract class DBSearch
|
||||
* @param int $iLimitCount
|
||||
* @param int $iLimitStart
|
||||
* @param bool $bGetCount
|
||||
* @param bool $bBeautifulSQL
|
||||
*
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
* @throws MissingQueryArgument
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false)
|
||||
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false, $bBeautifulSQL = true)
|
||||
{
|
||||
// Check the order by specification, and prefix with the class alias
|
||||
// and make sure that the ordering columns are going to be selected
|
||||
@@ -1085,8 +1087,7 @@ abstract class DBSearch
|
||||
}
|
||||
try
|
||||
{
|
||||
// $bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
|
||||
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, true);
|
||||
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, $bBeautifulSQL);
|
||||
if ($sClassAlias == '_itop_')
|
||||
{
|
||||
IssueLog::Info('SQL Query (_itop_): '.$sRes);
|
||||
|
||||
@@ -64,6 +64,8 @@ class Dict
|
||||
protected static $m_aLanguages = array(); // array( code => array( 'description' => '...', 'localized_description' => '...') ...)
|
||||
protected static $m_aData = array();
|
||||
protected static $m_sApplicationPrefix = null;
|
||||
/** @var \ApcService $m_oApcService */
|
||||
protected static $m_oApcService = null;
|
||||
|
||||
/**
|
||||
* @param $sLanguageCode
|
||||
@@ -145,15 +147,17 @@ class Dict
|
||||
{
|
||||
// Attempt to find the string in the user language
|
||||
//
|
||||
self::InitLangIfNeeded(self::GetUserLanguage());
|
||||
$sLangCode = self::GetUserLanguage();
|
||||
self::InitLangIfNeeded($sLangCode);
|
||||
|
||||
if (!array_key_exists(self::GetUserLanguage(), self::$m_aData))
|
||||
if (!array_key_exists($sLangCode, self::$m_aData))
|
||||
{
|
||||
IssueLog::Warning("Cannot find $sLangCode in dictionnaries. default labels displayed");
|
||||
// It may happen, when something happens before the dictionaries get loaded
|
||||
return $sStringCode;
|
||||
}
|
||||
$aCurrentDictionary = self::$m_aData[self::GetUserLanguage()];
|
||||
if (array_key_exists($sStringCode, $aCurrentDictionary))
|
||||
$aCurrentDictionary = self::$m_aData[$sLangCode];
|
||||
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
|
||||
{
|
||||
return $aCurrentDictionary[$sStringCode];
|
||||
}
|
||||
@@ -164,7 +168,7 @@ class Dict
|
||||
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
|
||||
|
||||
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
@@ -173,7 +177,7 @@ class Dict
|
||||
self::InitLangIfNeeded('EN US');
|
||||
|
||||
$aDefaultDictionary = self::$m_aData['EN US'];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
@@ -232,7 +236,26 @@ class Dict
|
||||
{
|
||||
self::$m_aLanguages = $aLanguagesList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.7.6 N°4125
|
||||
* @return \ApcService
|
||||
*/
|
||||
public static function GetApcService() {
|
||||
if (self::$m_oApcService === null){
|
||||
self::$m_oApcService = new ApcService();
|
||||
}
|
||||
return self::$m_oApcService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.7.6 N°4125
|
||||
* @param \ApcService $m_oApcService
|
||||
*/
|
||||
public static function SetApcService($oApcService) {
|
||||
self::$m_oApcService = $oApcService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a language from the language dictionary, if not already loaded
|
||||
* @param string $sLangCode Language code
|
||||
@@ -241,20 +264,23 @@ class Dict
|
||||
public static function InitLangIfNeeded($sLangCode)
|
||||
{
|
||||
if (array_key_exists($sLangCode, self::$m_aData)) return true;
|
||||
|
||||
|
||||
$bResult = false;
|
||||
|
||||
if (function_exists('apc_fetch') && (self::$m_sApplicationPrefix !== null))
|
||||
|
||||
if (self::GetApcService()->function_exists('apc_fetch')
|
||||
&& (self::$m_sApplicationPrefix !== null))
|
||||
{
|
||||
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
|
||||
//
|
||||
self::$m_aData[$sLangCode] = apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode);
|
||||
if (self::$m_aData[$sLangCode] === false)
|
||||
{
|
||||
self::$m_aData[$sLangCode] = self::GetApcService()->apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode);
|
||||
if (self::$m_aData[$sLangCode] === false) {
|
||||
unset(self::$m_aData[$sLangCode]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else if (! is_array(self::$m_aData[$sLangCode])) {
|
||||
// N°4125: we dont fix dictionnary corrupted cache (on iTop side).
|
||||
// but we log an error in a dedicated channel to let itop administrator be aware of a potential APCu issue to fix.
|
||||
IssueLog::Error("APCu corrupted data (with $sLangCode dictionnary). APCu configuration and running version should be troubleshooted...", LogChannels::APC);
|
||||
$bResult = true;
|
||||
} else {
|
||||
$bResult = true;
|
||||
}
|
||||
}
|
||||
@@ -263,9 +289,10 @@ class Dict
|
||||
$sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php';
|
||||
require_once($sDictFile);
|
||||
|
||||
if (function_exists('apc_store') && (self::$m_sApplicationPrefix !== null))
|
||||
if (self::GetApcService()->function_exists('apc_store')
|
||||
&& (self::$m_sApplicationPrefix !== null))
|
||||
{
|
||||
apc_store(self::$m_sApplicationPrefix.'-dict-'.$sLangCode, self::$m_aData[$sLangCode]);
|
||||
self::GetApcService()->apc_store(self::$m_sApplicationPrefix.'-dict-'.$sLangCode, self::$m_aData[$sLangCode]);
|
||||
}
|
||||
$bResult = true;
|
||||
}
|
||||
|
||||
@@ -34,43 +34,51 @@ abstract class HTMLSanitizer
|
||||
|
||||
/**
|
||||
* Sanitize an HTML string with the configured sanitizer, falling back to HTMLDOMSanitizer in case of Exception or invalid configuration
|
||||
*
|
||||
* @param string $sHTML
|
||||
* @param string $sConfigKey
|
||||
*
|
||||
* @return string
|
||||
* @noinspection SelfClassReferencingInspection
|
||||
*/
|
||||
public static function Sanitize($sHTML)
|
||||
public static function Sanitize($sHTML, $sConfigKey = 'html_sanitizer')
|
||||
{
|
||||
$sSanitizerClass = MetaModel::GetConfig()->Get('html_sanitizer');
|
||||
if(!class_exists($sSanitizerClass))
|
||||
{
|
||||
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||
$sSanitizerClass = MetaModel::GetConfig()->Get($sConfigKey);
|
||||
if (!class_exists($sSanitizerClass)) {
|
||||
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
||||
} else if (false === is_subclass_of($sSanitizerClass, HTMLSanitizer::class)) {
|
||||
if ($sConfigKey === 'html_sanitizer') {
|
||||
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
||||
}
|
||||
if ($sConfigKey === 'svg_sanitizer') {
|
||||
IssueLog::Error('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.' ! Won\'t sanitize string.');
|
||||
|
||||
return $sHTML;
|
||||
}
|
||||
}
|
||||
else if(!is_subclass_of($sSanitizerClass, 'HTMLSanitizer'))
|
||||
{
|
||||
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
try {
|
||||
$oSanitizer = new $sSanitizerClass();
|
||||
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
if($sSanitizerClass != 'HTMLDOMSanitizer')
|
||||
{
|
||||
IssueLog::Warning('Failed to sanitize an HTML string with "'.$sSanitizerClass.'". The following exception occured: '.$e->getMessage());
|
||||
IssueLog::Warning('Will try to sanitize with HTMLDOMSanitizer.');
|
||||
// try again with the HTMLDOMSanitizer
|
||||
$oSanitizer = new HTMLDOMSanitizer();
|
||||
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Error('Failed to sanitize an HTML string with "HTMLDOMSanitizer". The following exception occured: '.$e->getMessage());
|
||||
IssueLog::Error('The HTML will NOT be sanitized.');
|
||||
$sCleanHTML = $sHTML;
|
||||
catch(Exception $e) {
|
||||
if ($sConfigKey === 'html_sanitizer') {
|
||||
if ($sSanitizerClass !== HTMLDOMSanitizer::class) {
|
||||
IssueLog::Warning('Failed to sanitize an HTML string with "'.$sSanitizerClass.'". The following exception occured: '.$e->getMessage());
|
||||
IssueLog::Warning('Will try to sanitize with HTMLDOMSanitizer.');
|
||||
// try again with the HTMLDOMSanitizer
|
||||
$oSanitizer = new HTMLDOMSanitizer();
|
||||
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
|
||||
} else {
|
||||
IssueLog::Error('Failed to sanitize an HTML string with "HTMLDOMSanitizer". The following exception occured: '.$e->getMessage());
|
||||
IssueLog::Error('The HTML will NOT be sanitized.');
|
||||
$sCleanHTML = $sHTML;
|
||||
}
|
||||
} else {
|
||||
IssueLog::Error('Failed to sanitize string with "'.$sSanitizerClass.'", will return original value ! Exception: '.$e->getMessage());
|
||||
$sCleanHTML = $sHTML;
|
||||
}
|
||||
}
|
||||
return $sCleanHTML;
|
||||
@@ -97,67 +105,167 @@ class HTMLNullSanitizer extends HTMLSanitizer
|
||||
{
|
||||
return $sHTML;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A standard-compliant HTMLSanitizer based on the HTMLPurifier library by Edward Z. Yang
|
||||
* Complete but quite slow
|
||||
* http://htmlpurifier.org
|
||||
* Common implementation for sanitizer using DOM parsing
|
||||
*/
|
||||
/*
|
||||
class HTMLPurifierSanitizer extends HTMLSanitizer
|
||||
abstract class DOMSanitizer extends HTMLSanitizer
|
||||
{
|
||||
protected static $oPurifier = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (self::$oPurifier == null)
|
||||
{
|
||||
$sLibPath = APPROOT.'lib/htmlpurifier/HTMLPurifier.auto.php';
|
||||
if (!file_exists($sLibPath))
|
||||
{
|
||||
throw new Exception("Missing library '$sLibPath', cannot use HTMLPurifierSanitizer.");
|
||||
}
|
||||
require_once($sLibPath);
|
||||
|
||||
$oPurifierConfig = HTMLPurifier_Config::createDefault();
|
||||
$oPurifierConfig->set('Core.Encoding', 'UTF-8'); // defaults to 'UTF-8'
|
||||
$oPurifierConfig->set('HTML.Doctype', 'XHTML 1.0 Strict'); // defaults to 'XHTML 1.0 Transitional'
|
||||
$oPurifierConfig->set('URI.AllowedSchemes', array (
|
||||
'http' => true,
|
||||
'https' => true,
|
||||
'data' => true, // This one is not present by default
|
||||
));
|
||||
$sPurifierCache = APPROOT.'data/HTMLPurifier';
|
||||
if (!is_dir($sPurifierCache))
|
||||
{
|
||||
mkdir($sPurifierCache);
|
||||
}
|
||||
if (!is_dir($sPurifierCache))
|
||||
{
|
||||
throw new Exception("Could not create the cache directory '$sPurifierCache'");
|
||||
}
|
||||
$oPurifierConfig->set('Cache.SerializerPath', $sPurifierCache); // no trailing slash
|
||||
self::$oPurifier = new HTMLPurifier($oPurifierConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/** @var DOMDocument */
|
||||
protected $oDoc;
|
||||
|
||||
abstract public function GetTagsWhiteList();
|
||||
|
||||
abstract public function GetTagsBlackList();
|
||||
|
||||
abstract public function GetAttrsWhiteList();
|
||||
|
||||
abstract public function GetAttrsBlackList();
|
||||
|
||||
abstract public function GetStylesWhiteList();
|
||||
|
||||
public function DoSanitize($sHTML)
|
||||
{
|
||||
$sCleanHtml = self::$oPurifier->purify($sHTML);
|
||||
return $sCleanHtml;
|
||||
$this->oDoc = new DOMDocument();
|
||||
$this->oDoc->preserveWhitespace = true;
|
||||
|
||||
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
|
||||
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
|
||||
// Unfortunately, DOMDocument::loadHTML does not take the tag namespaces into account (once loaded there is no way to know if the tag did have a namespace)
|
||||
// therefore we have to do the transformation upfront
|
||||
$sHTML = preg_replace('@<o:p>(\s| )*</o:p>@', '<br>', $sHTML);
|
||||
|
||||
$this->LoadDoc($sHTML);
|
||||
|
||||
$this->CleanNode($this->oDoc);
|
||||
|
||||
$sCleanHtml = $this->PrintDoc();
|
||||
|
||||
return $sCleanHtml;
|
||||
}
|
||||
|
||||
abstract public function LoadDoc($sHTML);
|
||||
|
||||
/**
|
||||
* @return string cleaned source
|
||||
* @uses \DOMSanitizer::oDoc
|
||||
*/
|
||||
abstract public function PrintDoc();
|
||||
|
||||
protected function CleanNode(DOMNode $oElement)
|
||||
{
|
||||
$aAttrToRemove = array();
|
||||
// Gather the attributes to remove
|
||||
if ($oElement->hasAttributes()) {
|
||||
foreach ($oElement->attributes as $oAttr) {
|
||||
$sAttr = strtolower($oAttr->name);
|
||||
if ((false === empty($this->GetAttrsBlackList()))
|
||||
&& (in_array($sAttr, $this->GetAttrsBlackList(), true))) {
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
} else if ((false === empty($this->GetTagsWhiteList()))
|
||||
&& (false === in_array($sAttr, $this->GetTagsWhiteList()[strtolower($oElement->tagName)]))) {
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
} else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value)) {
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
} else if ($sAttr == 'style') {
|
||||
// Special processing for style tags
|
||||
$sCleanStyle = $this->CleanStyle($oAttr->value);
|
||||
if ($sCleanStyle == '') {
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
} else {
|
||||
$oElement->setAttribute($oAttr->name, $sCleanStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aAttrToRemove as $sName)
|
||||
{
|
||||
$oElement->removeAttribute($sName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($oElement->hasChildNodes())
|
||||
{
|
||||
$aChildElementsToRemove = array();
|
||||
// Gather the child noes to remove
|
||||
foreach($oElement->childNodes as $oNode) {
|
||||
if ($oNode instanceof DOMElement) {
|
||||
$sNodeTagName = strtolower($oNode->tagName);
|
||||
}
|
||||
if (($oNode instanceof DOMElement)
|
||||
&& (false === empty($this->GetTagsBlackList()))
|
||||
&& (in_array($sNodeTagName, $this->GetTagsBlackList(), true))) {
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
} else if (($oNode instanceof DOMElement)
|
||||
&& (false === empty($this->GetTagsWhiteList()))
|
||||
&& (false === array_key_exists($sNodeTagName, $this->GetTagsWhiteList()))) {
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
} else if ($oNode instanceof DOMComment) {
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
} else {
|
||||
// Recurse
|
||||
$this->CleanNode($oNode);
|
||||
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img')) {
|
||||
InlineImage::ProcessImageTag($oNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aChildElementsToRemove as $oDomElement)
|
||||
{
|
||||
$oElement->removeChild($oDomElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function IsValidAttributeContent($sAttributeName, $sValue)
|
||||
{
|
||||
if ((false === empty($this->GetAttrsBlackList()))
|
||||
&& (in_array($sAttributeName, $this->GetAttrsBlackList(), true))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (array_key_exists($sAttributeName, $this->GetAttrsWhiteList())) {
|
||||
return preg_match($this->GetAttrsWhiteList()[$sAttributeName], $sValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function CleanStyle($sStyle)
|
||||
{
|
||||
if (empty($this->GetStylesWhiteList())) {
|
||||
return $sStyle;
|
||||
}
|
||||
|
||||
$aAllowedStyles = array();
|
||||
$aItems = explode(';', $sStyle);
|
||||
{
|
||||
foreach ($aItems as $sItem) {
|
||||
$aElements = explode(':', trim($sItem));
|
||||
if (in_array(trim(strtolower($aElements[0])), $this->GetStylesWhiteList())) {
|
||||
$aAllowedStyles[] = trim($sItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return implode(';', $aAllowedStyles);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
|
||||
class HTMLDOMSanitizer extends DOMSanitizer
|
||||
{
|
||||
protected $oDoc;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
|
||||
* @var array
|
||||
*/
|
||||
protected static $aTagsWhiteList = array(
|
||||
'html' => array(),
|
||||
@@ -214,8 +322,8 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
|
||||
* @var array
|
||||
*/
|
||||
protected static $aStylesWhiteList = array(
|
||||
'background-color',
|
||||
@@ -239,164 +347,200 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
'white-space',
|
||||
);
|
||||
|
||||
public function GetTagsWhiteList()
|
||||
{
|
||||
return static::$aTagsWhiteList;
|
||||
}
|
||||
|
||||
public function GetTagsBlackList()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function GetAttrsWhiteList()
|
||||
{
|
||||
return static::$aAttrsWhiteList;
|
||||
}
|
||||
|
||||
public function GetAttrsBlackList()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function GetStylesWhiteList()
|
||||
{
|
||||
return static::$aStylesWhiteList;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// Building href validation pattern from url and email validation patterns as the patterns are not used the same way in HTML content than in standard attributes value.
|
||||
// eg. "foo@bar.com" vs "mailto:foo@bar.com?subject=Title&body=Hello%20world"
|
||||
if (!array_key_exists('href', self::$aAttrsWhiteList))
|
||||
{
|
||||
if (!array_key_exists('href', self::$aAttrsWhiteList)) {
|
||||
// Regular urls
|
||||
$sUrlPattern = utils::GetConfig()->Get('url_validation_pattern');
|
||||
|
||||
// Mailto urls
|
||||
$sMailtoPattern = '(mailto:(' . utils::GetConfig()->Get('email_validation_pattern') . ')(?:\?(?:subject|body)=([a-zA-Z0-9+\$_.-]*)(?:&(?:subject|body)=([a-zA-Z0-9+\$_.-]*))?)?)';
|
||||
$sMailtoPattern = '(mailto:('.utils::GetConfig()->Get('email_validation_pattern').')(?:\?(?:subject|body)=([a-zA-Z0-9+\$_.-]*)(?:&(?:subject|body)=([a-zA-Z0-9+\$_.-]*))?)?)';
|
||||
|
||||
// Notification placeholders
|
||||
// eg. $this->caller_id$, $this->hyperlink()$, $this->hyperlink(portal)$, $APP_URL$, $MODULES_URL$, ...
|
||||
// Note: Authorize both $xxx$ and %24xxx%24 as the latter one is encoded when used in HTML attributes (eg. a[href])
|
||||
$sPlaceholderPattern = '(\$|%24)[\w-]*(->[\w]*(\([\w-]*?\))?)?(\$|%24)';
|
||||
|
||||
$sPattern = $sUrlPattern . '|' . $sMailtoPattern . '|' . $sPlaceholderPattern;
|
||||
$sPattern = $sUrlPattern.'|'.$sMailtoPattern.'|'.$sPlaceholderPattern;
|
||||
$sPattern = '/'.str_replace('/', '\/', $sPattern).'/i';
|
||||
self::$aAttrsWhiteList['href'] = $sPattern;
|
||||
}
|
||||
}
|
||||
|
||||
public function DoSanitize($sHTML)
|
||||
public function LoadDoc($sHTML)
|
||||
{
|
||||
$this->oDoc = new DOMDocument();
|
||||
$this->oDoc->preserveWhitespace = true;
|
||||
|
||||
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
|
||||
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
|
||||
// Unfortunately, DOMDocument::loadHTML does not take the tag namespaces into account (once loaded there is no way to know if the tag did have a namespace)
|
||||
// therefore we have to do the transformation upfront
|
||||
$sHTML = preg_replace('@<o:p>(\s| )*</o:p>@', '<br>', $sHTML);
|
||||
// Replace badly encoded non breaking space
|
||||
$sHTML = preg_replace('~\xc2\xa0~', ' ', $sHTML);
|
||||
|
||||
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
|
||||
|
||||
$this->CleanNode($this->oDoc);
|
||||
|
||||
$this->oDoc->preserveWhitespace = true;
|
||||
}
|
||||
|
||||
public function PrintDoc()
|
||||
{
|
||||
$oXPath = new DOMXPath($this->oDoc);
|
||||
$sXPath = "//body";
|
||||
$oNodesList = $oXPath->query($sXPath);
|
||||
|
||||
if ($oNodesList->length == 0)
|
||||
{
|
||||
|
||||
if ($oNodesList->length == 0) {
|
||||
// No body, save the whole document
|
||||
$sCleanHtml = $this->oDoc->saveHTML();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Export only the content of the body tag
|
||||
$sCleanHtml = $this->oDoc->saveHTML($oNodesList->item(0));
|
||||
// remove the body tag itself
|
||||
$sCleanHtml = str_replace( array('<body>', '</body>'), '', $sCleanHtml);
|
||||
$sCleanHtml = str_replace(array('<body>', '</body>'), '', $sCleanHtml);
|
||||
}
|
||||
|
||||
|
||||
return $sCleanHtml;
|
||||
}
|
||||
|
||||
protected function CleanNode(DOMNode $oElement)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.6.5 2.7.6 3.0.0 N°4360
|
||||
*/
|
||||
class SVGDOMSanitizer extends DOMSanitizer
|
||||
{
|
||||
public function GetTagsWhiteList()
|
||||
{
|
||||
$aAttrToRemove = array();
|
||||
// Gather the attributes to remove
|
||||
if ($oElement->hasAttributes())
|
||||
{
|
||||
foreach($oElement->attributes as $oAttr)
|
||||
{
|
||||
$sAttr = strtolower($oAttr->name);
|
||||
if (!in_array($sAttr, self::$aTagsWhiteList[strtolower($oElement->tagName)]))
|
||||
{
|
||||
// Forbidden (or unknown) attribute
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value))
|
||||
{
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else if ($sAttr == 'style')
|
||||
{
|
||||
// Special processing for style tags
|
||||
$sCleanStyle = $this->CleanStyle($oAttr->value);
|
||||
if ($sCleanStyle == '')
|
||||
{
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oElement->setAttribute($oAttr->name, $sCleanStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aAttrToRemove as $sName)
|
||||
{
|
||||
$oElement->removeAttribute($sName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($oElement->hasChildNodes())
|
||||
{
|
||||
$aChildElementsToRemove = array();
|
||||
// Gather the child noes to remove
|
||||
foreach($oElement->childNodes as $oNode)
|
||||
{
|
||||
if (($oNode instanceof DOMElement) && (!array_key_exists(strtolower($oNode->tagName), self::$aTagsWhiteList)))
|
||||
{
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
}
|
||||
else if ($oNode instanceof DOMComment)
|
||||
{
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recurse
|
||||
$this->CleanNode($oNode);
|
||||
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img'))
|
||||
{
|
||||
InlineImage::ProcessImageTag($oNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aChildElementsToRemove as $oDomElement)
|
||||
{
|
||||
$oElement->removeChild($oDomElement);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function CleanStyle($sStyle)
|
||||
/**
|
||||
* @return string[]
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
|
||||
*/
|
||||
public function GetTagsBlackList()
|
||||
{
|
||||
$aAllowedStyles = array();
|
||||
$aItems = explode(';', $sStyle);
|
||||
{
|
||||
foreach($aItems as $sItem)
|
||||
{
|
||||
$aElements = explode(':', trim($sItem));
|
||||
if (in_array(trim(strtolower($aElements[0])), static::$aStylesWhiteList))
|
||||
{
|
||||
$aAllowedStyles[] = trim($sItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode(';', $aAllowedStyles);
|
||||
return [
|
||||
'script',
|
||||
];
|
||||
}
|
||||
|
||||
protected function IsValidAttributeContent($sAttributeName, $sValue)
|
||||
|
||||
public function GetAttrsWhiteList()
|
||||
{
|
||||
if (array_key_exists($sAttributeName, self::$aAttrsWhiteList))
|
||||
{
|
||||
return preg_match(self::$aAttrsWhiteList[$sAttributeName], $sValue);
|
||||
}
|
||||
return true;
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Events#document_event_attributes
|
||||
*/
|
||||
public function GetAttrsBlackList()
|
||||
{
|
||||
return [
|
||||
'onbegin',
|
||||
'onbegin',
|
||||
'onrepeat',
|
||||
'onabort',
|
||||
'onerror',
|
||||
'onerror',
|
||||
'onscroll',
|
||||
'onunload',
|
||||
'oncopy',
|
||||
'oncut',
|
||||
'onpaste',
|
||||
'oncancel',
|
||||
'oncanplay',
|
||||
'oncanplaythrough',
|
||||
'onchange',
|
||||
'onclick',
|
||||
'onclose',
|
||||
'oncuechange',
|
||||
'ondblclick',
|
||||
'ondrag',
|
||||
'ondragend',
|
||||
'ondragenter',
|
||||
'ondragleave',
|
||||
'ondragover',
|
||||
'ondragstart',
|
||||
'ondrop',
|
||||
'ondurationchange',
|
||||
'onemptied',
|
||||
'onended',
|
||||
'onerror',
|
||||
'onfocus',
|
||||
'oninput',
|
||||
'oninvalid',
|
||||
'onkeydown',
|
||||
'onkeypress',
|
||||
'onkeyup',
|
||||
'onload',
|
||||
'onloadeddata',
|
||||
'onloadedmetadata',
|
||||
'onloadstart',
|
||||
'onmousedown',
|
||||
'onmouseenter',
|
||||
'onmouseleave',
|
||||
'onmousemove',
|
||||
'onmouseout',
|
||||
'onmouseover',
|
||||
'onmouseup',
|
||||
'onmousewheel',
|
||||
'onpause',
|
||||
'onplay',
|
||||
'onplaying',
|
||||
'onprogress',
|
||||
'onratechange',
|
||||
'onreset',
|
||||
'onresize',
|
||||
'onscroll',
|
||||
'onseeked',
|
||||
'onseeking',
|
||||
'onselect',
|
||||
'onshow',
|
||||
'onstalled',
|
||||
'onsubmit',
|
||||
'onsuspend',
|
||||
'ontimeupdate',
|
||||
'ontoggle',
|
||||
'onvolumechange',
|
||||
'onwaiting',
|
||||
'onactivate',
|
||||
'onfocusin',
|
||||
'onfocusout',
|
||||
];
|
||||
}
|
||||
|
||||
public function GetStylesWhiteList()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function LoadDoc($sHTML)
|
||||
{
|
||||
@$this->oDoc->loadXml($sHTML, LIBXML_NOBLANKS);
|
||||
}
|
||||
|
||||
public function PrintDoc()
|
||||
{
|
||||
return $this->oDoc->saveXML();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,31 +176,29 @@ class InlineImage extends DBObject
|
||||
$sOQL = 'SELECT InlineImage WHERE temp_id = :temp_id';
|
||||
$oSearch = DBObjectSearch::FromOQL($sOQL);
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('temp_id' => $sTempId));
|
||||
$aInlineImagesId = array();
|
||||
while($oInlineImage = $oSet->Fetch())
|
||||
{
|
||||
$aInlineImagesId[] = $oInlineImage->GetKey();
|
||||
$aInlineImagesId = array();
|
||||
while ($oInlineImage = $oSet->Fetch()) {
|
||||
$aInlineImagesId[] = $oInlineImage->GetKey();
|
||||
$oInlineImage->SetItem($oObject);
|
||||
$oInlineImage->Set('temp_id', '');
|
||||
$oInlineImage->DBUpdate();
|
||||
}
|
||||
IssueLog::Trace('FinalizeInlineImages (see $aInlineImagesId for the id list)', 'InlineImage', array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
IssueLog::Trace('FinalizeInlineImages (see $aInlineImagesId for the id list)', LogChannels::INLINE_IMAGE, array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Trace('FinalizeInlineImages "error" $iTransactionId is null', 'InlineImage', array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
else {
|
||||
IssueLog::Trace('FinalizeInlineImages "error" $iTransactionId is null', LogChannels::INLINE_IMAGE, array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,12 +218,12 @@ class InlineImage extends DBObject
|
||||
$aInlineImagesId[] = $oInlineImage->GetKey();
|
||||
$oInlineImage->DBDelete();
|
||||
}
|
||||
IssueLog::Trace('OnFormCancel', 'InlineImage', array(
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
IssueLog::Trace('OnFormCancel', LogChannels::INLINE_IMAGE, array(
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -565,17 +563,17 @@ JS
|
||||
|
||||
protected function AfterInsert()
|
||||
{
|
||||
IssueLog::Trace(__METHOD__, 'InlineImage', array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
|
||||
parent::AfterInsert();
|
||||
@@ -583,17 +581,17 @@ JS
|
||||
|
||||
protected function AfterUpdate()
|
||||
{
|
||||
IssueLog::Trace(__METHOD__, 'InlineImage', array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
|
||||
parent::AfterUpdate();
|
||||
@@ -601,17 +599,17 @@ JS
|
||||
|
||||
protected function AfterDelete()
|
||||
{
|
||||
IssueLog::Trace(__METHOD__, 'InlineImage', array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
|
||||
parent::AfterDelete();
|
||||
|
||||
@@ -398,10 +398,10 @@ class MonthlyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
|
||||
*/
|
||||
protected function GetFileSuffix($oDate)
|
||||
{
|
||||
$sWeekYear = $oDate->format('o');
|
||||
$sWeekNumber = $oDate->format('m');
|
||||
$sMonthYear = $oDate->format('o');
|
||||
$sMonthNumber = $oDate->format('m');
|
||||
|
||||
return $sWeekYear.'-month'.$sWeekNumber;
|
||||
return $sMonthYear.'-month'.$sMonthNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,6 +502,7 @@ class FileLog
|
||||
protected function Write($sText, $sLevel = '', $sChannel = '', $aContext = array())
|
||||
{
|
||||
$sTextPrefix = empty($sLevel) ? '' : (str_pad($sLevel, 7).' | ');
|
||||
$sTextPrefix .= str_pad(UserRights::GetUserId(), 5)." | ";
|
||||
$sTextSuffix = empty($sChannel) ? '' : " | $sChannel";
|
||||
$sText = "{$sTextPrefix}{$sText}{$sTextSuffix}";
|
||||
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
|
||||
@@ -516,12 +517,9 @@ class FileLog
|
||||
{
|
||||
flock($hLogFile, LOCK_EX);
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
if (empty($aContext))
|
||||
{
|
||||
if (empty($aContext)) {
|
||||
fwrite($hLogFile, "$sDate | $sText\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sContext = var_export($aContext, true);
|
||||
fwrite($hLogFile, "$sDate | $sText\n$sContext\n");
|
||||
}
|
||||
@@ -532,6 +530,22 @@ class FileLog
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple enum like class to factorize channels values as constants
|
||||
* Channels are used especially as parameters in {@see \LogAPI} methods
|
||||
*
|
||||
* @since 2.7.5 3.0.0 N°4012
|
||||
*/
|
||||
class LogChannels
|
||||
{
|
||||
const DEADLOCK = 'DeadLock';
|
||||
const INLINE_IMAGE = 'InlineImage';
|
||||
const PORTAL = 'portal';
|
||||
const APC = 'apc';
|
||||
}
|
||||
|
||||
|
||||
abstract class LogAPI
|
||||
{
|
||||
const CHANNEL_DEFAULT = '';
|
||||
@@ -543,11 +557,11 @@ abstract class LogAPI
|
||||
const LEVEL_DEBUG = 'Debug';
|
||||
const LEVEL_TRACE = 'Trace';
|
||||
/**
|
||||
* @var string default log level, can be overrided
|
||||
* @see GetMinLogLevel
|
||||
* @var string default log level, can be overrided
|
||||
* @since 2.7.1 N°2977
|
||||
*/
|
||||
const LEVEL_DEFAULT = self::LEVEL_OK;
|
||||
const LEVEL_DEFAULT = self::LEVEL_OK;
|
||||
|
||||
protected static $aLevelsPriority = array(
|
||||
self::LEVEL_ERROR => 400,
|
||||
@@ -604,36 +618,29 @@ abstract class LogAPI
|
||||
|
||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
|
||||
{
|
||||
if (! static::$m_oFileLog)
|
||||
{
|
||||
if (!static::$m_oFileLog) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! isset(self::$aLevelsPriority[$sLevel]))
|
||||
{
|
||||
if (!isset(self::$aLevelsPriority[$sLevel])) {
|
||||
IssueLog::Error("invalid log level '{$sLevel}'");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_null($sChannel))
|
||||
{
|
||||
if (is_null($sChannel)) {
|
||||
$sChannel = static::CHANNEL_DEFAULT;
|
||||
}
|
||||
|
||||
$sMinLogLevel = self::GetMinLogLevel($sChannel);
|
||||
|
||||
if ($sMinLogLevel === false || $sMinLogLevel === 'false')
|
||||
{
|
||||
if ($sMinLogLevel === false || $sMinLogLevel === 'false') {
|
||||
return;
|
||||
}
|
||||
if (is_string($sMinLogLevel))
|
||||
{
|
||||
if (! isset(self::$aLevelsPriority[$sMinLogLevel]))
|
||||
{
|
||||
if (is_string($sMinLogLevel)) {
|
||||
if (!isset(self::$aLevelsPriority[$sMinLogLevel])) {
|
||||
throw new Exception("invalid configuration for log_level '{$sMinLogLevel}' is not within the list: ".implode(',', array_keys(self::$aLevelsPriority)));
|
||||
}
|
||||
elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel])
|
||||
{
|
||||
} elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel]) {
|
||||
//priority too low regarding the conf, do not log this
|
||||
return;
|
||||
}
|
||||
@@ -740,7 +747,7 @@ class DeadLockLog extends LogAPI
|
||||
return self::CHANNEL_WAIT_TIMEOUT;
|
||||
break;
|
||||
case 1213:
|
||||
return self::CHANNEL_DEADLOCK_FOUND;
|
||||
return self::CHANNEL_DEADLOCK_FOUND;
|
||||
break;
|
||||
default:
|
||||
return self::CHANNEL_DEFAULT;
|
||||
@@ -749,17 +756,21 @@ class DeadLockLog extends LogAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iMySQLErrNo will be converted to channel using {@link GetChannelFromMysqlErrorNo}
|
||||
* @param string $sLevel
|
||||
* @param string $sMessage
|
||||
* @param null $iMysqlErroNo
|
||||
* @param int $iMysqlErrorNumber will be converted to channel using {@link GetChannelFromMysqlErrorNo}
|
||||
* @param array $aContext
|
||||
*
|
||||
* @throws \Exception
|
||||
* @noinspection PhpParameterNameChangedDuringInheritanceInspection
|
||||
*
|
||||
* @since 2.7.1 method creation
|
||||
* @since 2.7.5 3.0.0 rename param names and fix phpdoc (thanks Hipska !)
|
||||
*/
|
||||
public static function Log($iMySQLErrNo, $sMessage, $iMysqlErroNo = null, $aContext = array())
|
||||
public static function Log($sLevel, $sMessage, $iMysqlErrorNumber = null, $aContext = array())
|
||||
{
|
||||
$sChannel = self::GetChannelFromMysqlErrorNo($iMysqlErroNo);
|
||||
parent::Log($iMySQLErrNo, $sMessage, $sChannel, $aContext);
|
||||
$sChannel = self::GetChannelFromMysqlErrorNo($iMysqlErrorNumber);
|
||||
parent::Log($sLevel, $sMessage, $sChannel, $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5366,7 +5366,7 @@ abstract class MetaModel
|
||||
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` ADD $sKeyFieldDefinition";
|
||||
if (!$bTableToCreate)
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sKeyField] = "ADD $sKeyFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sKeyField] = "ADD $sKeyFieldDefinition";
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -5379,7 +5379,7 @@ abstract class MetaModel
|
||||
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable`, DROP PRIMARY KEY, ADD PRIMARY key(`$sKeyField`)";
|
||||
if (!$bTableToCreate)
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
}
|
||||
}
|
||||
if (self::IsAutoIncrementKey($sClass) && !CMDBSource::IsAutoIncrement($sTable, $sKeyField))
|
||||
@@ -5388,7 +5388,7 @@ abstract class MetaModel
|
||||
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
if (!$bTableToCreate)
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5451,7 +5451,7 @@ abstract class MetaModel
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sField] = "ADD $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sField] = "ADD $sFieldDefinition";
|
||||
$aAdditionalRequests = self::GetAdditionalRequestAfterAlter($sClass, $sTable, $sField);
|
||||
if (!empty($aAdditionalRequests))
|
||||
{
|
||||
@@ -5495,7 +5495,7 @@ abstract class MetaModel
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][] = "ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
$aAlterTableItems[$sTable]['index'][] = "ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5536,7 +5536,7 @@ abstract class MetaModel
|
||||
if (CMDBSource::HasIndex($sTable, $sField))
|
||||
{
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` DROP INDEX `$sIndexName`";
|
||||
$aAlterTableItems[$sTable][] = "DROP INDEX `$sIndexName`";
|
||||
$aAlterTableItems[$sTable]['index'][] = "DROP INDEX `$sIndexName`";
|
||||
}
|
||||
$sSugFixAfterChange = "ALTER TABLE `$sTable` ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
$sAlterTableItemsAfterChange = "ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
@@ -5550,7 +5550,7 @@ abstract class MetaModel
|
||||
{
|
||||
$aErrors[$sClass][$sAttCode][] = "field '$sField' in table '$sTable' has a wrong type: found <code>$sActualFieldSpec</code> while expecting <code>$sDBFieldSpec</code>";
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
}
|
||||
|
||||
// Create indexes (external keys only... so far)
|
||||
@@ -5565,7 +5565,7 @@ abstract class MetaModel
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][] = $sAlterTableItemsAfterChange;
|
||||
$aAlterTableItems[$sTable]['index'][] = $sAlterTableItemsAfterChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5625,9 +5625,12 @@ abstract class MetaModel
|
||||
{
|
||||
$aAlterTableItems[$sTable] = array();
|
||||
}
|
||||
array_unshift($aAlterTableItems[$sTable], "DROP INDEX `$sIndexId`");
|
||||
if (isset($aAlterTableItems[$sTable]['index']))
|
||||
{
|
||||
array_unshift($aAlterTableItems[$sTable]['index'], "DROP INDEX `$sIndexId`");
|
||||
}
|
||||
}
|
||||
$aAlterTableItems[$sTable][] = "ADD INDEX `$sIndexId` ($sColumns)";
|
||||
$aAlterTableItems[$sTable]['index'][] = "ADD INDEX `$sIndexId` ($sColumns)";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5645,7 +5648,7 @@ abstract class MetaModel
|
||||
// without specifying the value of this unknown column
|
||||
$sFieldDefinition = "`$sField` ".CMDBSource::GetFieldType($sTable, $sField).' NULL';
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
}
|
||||
$aSugFix[$sClass][$sAttCode][] = "-- Recommended action: ALTER TABLE `$sTable` DROP `$sField`";
|
||||
}
|
||||
@@ -5664,7 +5667,10 @@ abstract class MetaModel
|
||||
{
|
||||
$aAlterTableItems[$sTable] = array();
|
||||
}
|
||||
array_unshift($aAlterTableItems[$sTable], "DROP INDEX `$sIndexId`");
|
||||
if (isset($aAlterTableItems[$sTable]['index']))
|
||||
{
|
||||
array_unshift($aAlterTableItems[$sTable]['index'], "DROP INDEX `$sIndexId`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5696,8 +5702,16 @@ abstract class MetaModel
|
||||
}
|
||||
foreach ($aAlterTableItems as $sTable => $aChangeList)
|
||||
{
|
||||
$sChangeList = implode(', ', $aChangeList);
|
||||
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
|
||||
if (isset($aAlterTableItems[$sTable]['field']))
|
||||
{
|
||||
$sChangeList = implode(', ', $aChangeList['field']);
|
||||
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
|
||||
}
|
||||
if (isset($aAlterTableItems[$sTable]['index']))
|
||||
{
|
||||
$sChangeList = implode(', ', $aChangeList['index']);
|
||||
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
|
||||
}
|
||||
// Add request right after the ALTER TABLE
|
||||
if (isset($aPostTableAlteration[$sTable]))
|
||||
{
|
||||
@@ -7316,9 +7330,11 @@ abstract class MetaModel
|
||||
* @param string $sInput
|
||||
* @param array $aParams
|
||||
*
|
||||
* @return mixed
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
static public function ApplyParams($sInput, $aParams)
|
||||
public static function ApplyParams($sInput, $aParams)
|
||||
{
|
||||
$aParams = static::AddMagicPlaceholders($aParams);
|
||||
|
||||
@@ -7328,7 +7344,7 @@ abstract class MetaModel
|
||||
|
||||
$aSearches = array();
|
||||
$aReplacements = array();
|
||||
foreach($aParams as $sSearch => $replace)
|
||||
foreach ($aParams as $sSearch => $replace)
|
||||
{
|
||||
// Some environment parameters are objects, we just need scalars
|
||||
if (is_object($replace))
|
||||
|
||||
@@ -242,6 +242,8 @@ class iTopMutex
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @since 2.7.5 3.0.0 N°3968 specify `wait_timeout` for the mutex dedicated connection
|
||||
*/
|
||||
public function InitMySQLSession()
|
||||
{
|
||||
@@ -254,10 +256,36 @@ class iTopMutex
|
||||
|
||||
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
|
||||
|
||||
if (!$this->hDBLink)
|
||||
{
|
||||
if (!$this->hDBLink) {
|
||||
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
||||
}
|
||||
|
||||
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,
|
||||
// since the lock will be released if/when the connection times out.
|
||||
// Source https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html :
|
||||
// > A lock obtained with GET_LOCK() is released explicitly by executing RELEASE_LOCK() or implicitly when your session terminates
|
||||
//
|
||||
// BEWARE: If you want to check the value of this variable, when run from an interactive console `SHOW VARIABLES LIKE 'wait_timeout'`
|
||||
// will actually returns the value of the variable `interactive_timeout` which may be quite different.
|
||||
$sSql = "SHOW VARIABLES LIKE 'wait_timeout'";
|
||||
$result = mysqli_query($this->hDBLink, $sSql);
|
||||
if (!$result) {
|
||||
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
|
||||
}
|
||||
if ($aRow = mysqli_fetch_array($result, MYSQLI_BOTH)) {
|
||||
$iTimeout = (int)$aRow[1];
|
||||
} else {
|
||||
mysqli_free_result($result);
|
||||
throw new Exception("No result for query '".$sSql."'");
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
|
||||
if ($iTimeout < 86400) {
|
||||
$result = mysqli_query($this->hDBLink, 'SET SESSION wait_timeout=86400');
|
||||
if ($result === false) {
|
||||
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -597,7 +597,7 @@ static public $yy_action = array(
|
||||
** defined, then do no error processing.
|
||||
*/
|
||||
const YYNOCODE = 119;
|
||||
const YYSTACKDEPTH = 100;
|
||||
const YYSTACKDEPTH = 1000;
|
||||
const YYNSTATE = 175;
|
||||
const YYNRULE = 125;
|
||||
const YYERRORSYMBOL = 76;
|
||||
@@ -1175,6 +1175,10 @@ static public $yy_action = array(
|
||||
}
|
||||
/* Here code is inserted which will execute if the parser
|
||||
** stack ever overflows */
|
||||
#line 30 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
#line 1186 "..\oql-parser.php"
|
||||
return;
|
||||
}
|
||||
$yytos = new OQLParser_yyStackEntry;
|
||||
@@ -1474,116 +1478,116 @@ static public $yy_action = array(
|
||||
** function yy_r0($yymsp){ ... } // User supplied code
|
||||
** #line <lineno> <thisfile>
|
||||
*/
|
||||
#line 29 "..\oql-parser.y"
|
||||
#line 37 "..\oql-parser.y"
|
||||
function yy_r0(){ $this->my_result = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1483 "..\oql-parser.php"
|
||||
#line 33 "..\oql-parser.y"
|
||||
#line 1488 "..\oql-parser.php"
|
||||
#line 41 "..\oql-parser.y"
|
||||
function yy_r3(){
|
||||
$this->_retvalue = new OqlUnionQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1488 "..\oql-parser.php"
|
||||
#line 40 "..\oql-parser.y"
|
||||
#line 1493 "..\oql-parser.php"
|
||||
#line 48 "..\oql-parser.y"
|
||||
function yy_r5(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor));
|
||||
}
|
||||
#line 1493 "..\oql-parser.php"
|
||||
#line 43 "..\oql-parser.y"
|
||||
#line 1498 "..\oql-parser.php"
|
||||
#line 51 "..\oql-parser.y"
|
||||
function yy_r6(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor));
|
||||
}
|
||||
#line 1498 "..\oql-parser.php"
|
||||
#line 47 "..\oql-parser.y"
|
||||
#line 1503 "..\oql-parser.php"
|
||||
#line 55 "..\oql-parser.y"
|
||||
function yy_r7(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -4]->minor);
|
||||
}
|
||||
#line 1503 "..\oql-parser.php"
|
||||
#line 50 "..\oql-parser.y"
|
||||
#line 1508 "..\oql-parser.php"
|
||||
#line 58 "..\oql-parser.y"
|
||||
function yy_r8(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -6]->minor);
|
||||
}
|
||||
#line 1508 "..\oql-parser.php"
|
||||
#line 55 "..\oql-parser.y"
|
||||
#line 1513 "..\oql-parser.php"
|
||||
#line 63 "..\oql-parser.y"
|
||||
function yy_r9(){
|
||||
$this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1513 "..\oql-parser.php"
|
||||
#line 58 "..\oql-parser.y"
|
||||
#line 1518 "..\oql-parser.php"
|
||||
#line 66 "..\oql-parser.y"
|
||||
function yy_r10(){
|
||||
array_push($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
$this->_retvalue = $this->yystack[$this->yyidx + -2]->minor;
|
||||
}
|
||||
#line 1519 "..\oql-parser.php"
|
||||
#line 63 "..\oql-parser.y"
|
||||
#line 1524 "..\oql-parser.php"
|
||||
#line 71 "..\oql-parser.y"
|
||||
function yy_r11(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1522 "..\oql-parser.php"
|
||||
#line 64 "..\oql-parser.y"
|
||||
#line 1527 "..\oql-parser.php"
|
||||
#line 72 "..\oql-parser.y"
|
||||
function yy_r12(){ $this->_retvalue = null; }
|
||||
#line 1525 "..\oql-parser.php"
|
||||
#line 66 "..\oql-parser.y"
|
||||
#line 1530 "..\oql-parser.php"
|
||||
#line 74 "..\oql-parser.y"
|
||||
function yy_r13(){
|
||||
// insert the join statement on top of the existing list
|
||||
array_unshift($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor);
|
||||
// and return the updated array
|
||||
$this->_retvalue = $this->yystack[$this->yyidx + 0]->minor;
|
||||
}
|
||||
#line 1533 "..\oql-parser.php"
|
||||
#line 72 "..\oql-parser.y"
|
||||
#line 1538 "..\oql-parser.php"
|
||||
#line 80 "..\oql-parser.y"
|
||||
function yy_r14(){
|
||||
$this->_retvalue = Array($this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1538 "..\oql-parser.php"
|
||||
#line 78 "..\oql-parser.y"
|
||||
#line 1543 "..\oql-parser.php"
|
||||
#line 86 "..\oql-parser.y"
|
||||
function yy_r16(){
|
||||
// create an array with one single item
|
||||
$this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1544 "..\oql-parser.php"
|
||||
#line 83 "..\oql-parser.y"
|
||||
#line 1549 "..\oql-parser.php"
|
||||
#line 91 "..\oql-parser.y"
|
||||
function yy_r17(){
|
||||
// create an array with one single item
|
||||
$this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1550 "..\oql-parser.php"
|
||||
#line 88 "..\oql-parser.y"
|
||||
function yy_r18(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1553 "..\oql-parser.php"
|
||||
#line 89 "..\oql-parser.y"
|
||||
function yy_r19(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1556 "..\oql-parser.php"
|
||||
#line 90 "..\oql-parser.y"
|
||||
function yy_r20(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1559 "..\oql-parser.php"
|
||||
#line 91 "..\oql-parser.y"
|
||||
function yy_r21(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1562 "..\oql-parser.php"
|
||||
#line 92 "..\oql-parser.y"
|
||||
function yy_r22(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1565 "..\oql-parser.php"
|
||||
#line 93 "..\oql-parser.y"
|
||||
function yy_r23(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1568 "..\oql-parser.php"
|
||||
#line 94 "..\oql-parser.y"
|
||||
function yy_r24(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1571 "..\oql-parser.php"
|
||||
#line 95 "..\oql-parser.y"
|
||||
function yy_r25(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1574 "..\oql-parser.php"
|
||||
#line 1555 "..\oql-parser.php"
|
||||
#line 96 "..\oql-parser.y"
|
||||
function yy_r26(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1577 "..\oql-parser.php"
|
||||
function yy_r18(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1558 "..\oql-parser.php"
|
||||
#line 97 "..\oql-parser.y"
|
||||
function yy_r19(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1561 "..\oql-parser.php"
|
||||
#line 98 "..\oql-parser.y"
|
||||
function yy_r27(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1580 "..\oql-parser.php"
|
||||
function yy_r20(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1564 "..\oql-parser.php"
|
||||
#line 99 "..\oql-parser.y"
|
||||
function yy_r21(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1567 "..\oql-parser.php"
|
||||
#line 100 "..\oql-parser.y"
|
||||
function yy_r22(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1570 "..\oql-parser.php"
|
||||
#line 101 "..\oql-parser.y"
|
||||
function yy_r23(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1573 "..\oql-parser.php"
|
||||
#line 102 "..\oql-parser.y"
|
||||
function yy_r24(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1576 "..\oql-parser.php"
|
||||
#line 103 "..\oql-parser.y"
|
||||
function yy_r31(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); }
|
||||
#line 1583 "..\oql-parser.php"
|
||||
function yy_r25(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1579 "..\oql-parser.php"
|
||||
#line 104 "..\oql-parser.y"
|
||||
function yy_r32(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; }
|
||||
#line 1586 "..\oql-parser.php"
|
||||
#line 105 "..\oql-parser.y"
|
||||
function yy_r33(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1589 "..\oql-parser.php"
|
||||
function yy_r26(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1582 "..\oql-parser.php"
|
||||
#line 106 "..\oql-parser.y"
|
||||
function yy_r27(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1585 "..\oql-parser.php"
|
||||
#line 111 "..\oql-parser.y"
|
||||
function yy_r31(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); }
|
||||
#line 1588 "..\oql-parser.php"
|
||||
#line 112 "..\oql-parser.y"
|
||||
function yy_r32(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; }
|
||||
#line 1591 "..\oql-parser.php"
|
||||
#line 113 "..\oql-parser.y"
|
||||
function yy_r33(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1594 "..\oql-parser.php"
|
||||
#line 119 "..\oql-parser.y"
|
||||
function yy_r37(){
|
||||
if ($this->yystack[$this->yyidx + -1]->minor == 'MATCHES')
|
||||
{
|
||||
@@ -1594,44 +1598,44 @@ static public $yy_action = array(
|
||||
$this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
}
|
||||
#line 1601 "..\oql-parser.php"
|
||||
#line 128 "..\oql-parser.y"
|
||||
#line 1606 "..\oql-parser.php"
|
||||
#line 136 "..\oql-parser.y"
|
||||
function yy_r42(){
|
||||
$this->_retvalue = new ListOqlExpression($this->yystack[$this->yyidx + -1]->minor);
|
||||
}
|
||||
#line 1606 "..\oql-parser.php"
|
||||
#line 131 "..\oql-parser.y"
|
||||
#line 1611 "..\oql-parser.php"
|
||||
#line 139 "..\oql-parser.y"
|
||||
function yy_r43(){
|
||||
$this->_retvalue = new NestedQueryOqlExpression($this->yystack[$this->yyidx + -1]->minor);
|
||||
}
|
||||
#line 1611 "..\oql-parser.php"
|
||||
#line 146 "..\oql-parser.y"
|
||||
#line 1616 "..\oql-parser.php"
|
||||
#line 154 "..\oql-parser.y"
|
||||
function yy_r47(){
|
||||
$this->_retvalue = array();
|
||||
}
|
||||
#line 1616 "..\oql-parser.php"
|
||||
#line 157 "..\oql-parser.y"
|
||||
#line 1621 "..\oql-parser.php"
|
||||
#line 165 "..\oql-parser.y"
|
||||
function yy_r51(){ $this->_retvalue = new IntervalOqlExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1619 "..\oql-parser.php"
|
||||
#line 170 "..\oql-parser.y"
|
||||
#line 1624 "..\oql-parser.php"
|
||||
#line 178 "..\oql-parser.y"
|
||||
function yy_r61(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1622 "..\oql-parser.php"
|
||||
#line 172 "..\oql-parser.y"
|
||||
#line 1627 "..\oql-parser.php"
|
||||
#line 180 "..\oql-parser.y"
|
||||
function yy_r63(){ $this->_retvalue = new ScalarOqlExpression(null); }
|
||||
#line 1625 "..\oql-parser.php"
|
||||
#line 174 "..\oql-parser.y"
|
||||
#line 1630 "..\oql-parser.php"
|
||||
#line 182 "..\oql-parser.y"
|
||||
function yy_r64(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1628 "..\oql-parser.php"
|
||||
#line 175 "..\oql-parser.y"
|
||||
#line 1633 "..\oql-parser.php"
|
||||
#line 183 "..\oql-parser.y"
|
||||
function yy_r65(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); }
|
||||
#line 1631 "..\oql-parser.php"
|
||||
#line 176 "..\oql-parser.y"
|
||||
#line 1636 "..\oql-parser.php"
|
||||
#line 184 "..\oql-parser.y"
|
||||
function yy_r66(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1634 "..\oql-parser.php"
|
||||
#line 179 "..\oql-parser.y"
|
||||
#line 1639 "..\oql-parser.php"
|
||||
#line 187 "..\oql-parser.y"
|
||||
function yy_r67(){ $this->_retvalue = new VariableOqlExpression(substr($this->yystack[$this->yyidx + 0]->minor, 1)); }
|
||||
#line 1637 "..\oql-parser.php"
|
||||
#line 181 "..\oql-parser.y"
|
||||
#line 1642 "..\oql-parser.php"
|
||||
#line 189 "..\oql-parser.y"
|
||||
function yy_r68(){
|
||||
if ($this->yystack[$this->yyidx + 0]->minor[0] == '`')
|
||||
{
|
||||
@@ -1643,22 +1647,22 @@ static public $yy_action = array(
|
||||
}
|
||||
$this->_retvalue = new OqlName($name, $this->m_iColPrev);
|
||||
}
|
||||
#line 1650 "..\oql-parser.php"
|
||||
#line 192 "..\oql-parser.y"
|
||||
#line 1655 "..\oql-parser.php"
|
||||
#line 200 "..\oql-parser.y"
|
||||
function yy_r69(){$this->_retvalue=(int)$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1653 "..\oql-parser.php"
|
||||
#line 193 "..\oql-parser.y"
|
||||
#line 1658 "..\oql-parser.php"
|
||||
#line 201 "..\oql-parser.y"
|
||||
function yy_r70(){$this->_retvalue=(int)-$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1656 "..\oql-parser.php"
|
||||
#line 194 "..\oql-parser.y"
|
||||
#line 1661 "..\oql-parser.php"
|
||||
#line 202 "..\oql-parser.y"
|
||||
function yy_r71(){$this->_retvalue=new OqlHexValue($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1659 "..\oql-parser.php"
|
||||
#line 195 "..\oql-parser.y"
|
||||
#line 1664 "..\oql-parser.php"
|
||||
#line 203 "..\oql-parser.y"
|
||||
function yy_r72(){$this->_retvalue=stripslashes(substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2)); }
|
||||
#line 1662 "..\oql-parser.php"
|
||||
#line 198 "..\oql-parser.y"
|
||||
#line 1667 "..\oql-parser.php"
|
||||
#line 206 "..\oql-parser.y"
|
||||
function yy_r73(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1665 "..\oql-parser.php"
|
||||
#line 1670 "..\oql-parser.php"
|
||||
|
||||
/**
|
||||
* placeholder for the left hand side in a reduce operation.
|
||||
@@ -1759,6 +1763,10 @@ static public $yy_action = array(
|
||||
}
|
||||
/* Here code is inserted which will be executed whenever the
|
||||
** parser fails */
|
||||
#line 33 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
#line 1775 "..\oql-parser.php"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1772,8 +1780,8 @@ static public $yy_action = array(
|
||||
{
|
||||
#line 25 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
#line 1781 "..\oql-parser.php"
|
||||
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
#line 1791 "..\oql-parser.php"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1940,19 +1948,47 @@ throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCo
|
||||
} while ($yymajor != self::YYNOCODE && $this->yyidx >= 0);
|
||||
}
|
||||
}
|
||||
#line 263 "..\oql-parser.y"
|
||||
#line 271 "..\oql-parser.y"
|
||||
|
||||
|
||||
class OQLParserException extends OQLException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue)
|
||||
{
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserSyntaxErrorException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserStackOverFlowException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Stack overflow";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserParseFailureException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParser extends OQLParserRaw
|
||||
{
|
||||
// dirty, but working for us (no other mean to get the final result :-(
|
||||
@@ -2005,4 +2041,4 @@ class OQLParser extends OQLParserRaw
|
||||
}
|
||||
}
|
||||
|
||||
#line 2014 "..\oql-parser.php"
|
||||
#line 2052 "..\oql-parser.php"
|
||||
|
||||
@@ -23,7 +23,15 @@ later : solve the 2 remaining shift-reduce conflicts (JOIN)
|
||||
%name OQLParser_
|
||||
%declare_class {class OQLParserRaw}
|
||||
%syntax_error {
|
||||
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
}
|
||||
/* Bug N°4052 Parser stack size too small for huge OQL requests */
|
||||
%stack_size 1000
|
||||
%stack_overflow {
|
||||
throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
}
|
||||
%parse_failure {
|
||||
throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
}
|
||||
|
||||
result ::= union(X). { $this->my_result = X; }
|
||||
@@ -263,15 +271,43 @@ func_name(A) ::= F_INET_NTOA(X). { A=X; }
|
||||
%code {
|
||||
|
||||
class OQLParserException extends OQLException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue)
|
||||
{
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserSyntaxErrorException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserStackOverFlowException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Stack overflow";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserParseFailureException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParser extends OQLParserRaw
|
||||
{
|
||||
// dirty, but working for us (no other mean to get the final result :-(
|
||||
|
||||
@@ -1 +1 @@
|
||||
2020-09-29
|
||||
2021-06-03
|
||||
@@ -55,7 +55,7 @@ class OQLClassTreeOptimizer
|
||||
$sJoinedClass = $oJoin->GetOOQLClassNode()->GetNodeClass();
|
||||
$sExtKeyAttCode = $oJoin->GetLeftField();
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($oCurrentClassNode->GetNodeClass(), $sExtKeyAttCode);
|
||||
if ($sJoinedClass == $oExtKeyAttDef->GetTargetClass()) {
|
||||
if (($oExtKeyAttDef instanceof AttributeExternalKey) && ($sJoinedClass == $oExtKeyAttDef->GetTargetClass())) {
|
||||
// The join is not used, remove from tree
|
||||
$oCurrentClassNode->RemoveJoin($sLeftKey, $index);
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ final class ormTagSet extends ormSet
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tags indexed by code
|
||||
* @return array index: code, value: corresponding {@see \TagSetFieldData}
|
||||
*/
|
||||
public function GetTags()
|
||||
{
|
||||
|
||||
@@ -340,7 +340,7 @@ EOF
|
||||
}
|
||||
else if ($oAttDef instanceof AttributeTagSet)
|
||||
{
|
||||
$sField = $oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, '');
|
||||
$sField = utils::HtmlEntities($oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, ''));
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
else
|
||||
|
||||
@@ -263,6 +263,7 @@ abstract class TriggerOnObject extends Trigger
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($sFilter);
|
||||
$oSearch->AddCondition('id', $iObjectId, '=');
|
||||
$oSearch->AllowAllData();
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$bRet = ($oSet->Count() > 0);
|
||||
}
|
||||
|
||||
@@ -952,6 +952,21 @@ class UserRights
|
||||
return self::$m_oRealUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|string ID of the connected user : if impersonate then use {@see m_oRealUser}, else {@see m_oUser}. If no user set then return ''
|
||||
* @since 2.6.5 2.7.6 3.0.0 N°4289 method creation
|
||||
*/
|
||||
public static function GetConnectedUserId() {
|
||||
if (false === is_null(static::$m_oRealUser)) {
|
||||
return static::$m_oRealUser->GetKey();
|
||||
}
|
||||
if (false === is_null(static::$m_oUser)) {
|
||||
return static::$m_oUser->GetKey();
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function GetRealUserId()
|
||||
{
|
||||
if (is_null(self::$m_oRealUser))
|
||||
@@ -1212,7 +1227,7 @@ class UserRights
|
||||
elseif ((self::$m_oUser !== null) && ($oUser->GetKey() == self::$m_oUser->GetKey()))
|
||||
{
|
||||
// Data about the current user can be found into the session data
|
||||
if (array_key_exists('profile_list', $_SESSION))
|
||||
if ((false === utils::IsModeCLI()) && array_key_exists('profile_list', $_SESSION))
|
||||
{
|
||||
$aProfiles = $_SESSION['profile_list'];
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
|
||||
$version: "v2.7.4";
|
||||
$version: "v2.7.6";
|
||||
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||
|
||||
// Base colors
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* Localized data
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2013 XXXXX
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
* @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'CAS:Error:UserNotAllowed' => 'User not allowed~~',
|
||||
'CAS:Login:SignIn' => 'Sign in with CAS~~',
|
||||
'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~',
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'CAS:Error:UserNotAllowed' => 'Usuario no permitido',
|
||||
'CAS:Login:SignIn' => 'Iniciar sesión con CAS',
|
||||
'CAS:Login:SignInTooltip' => 'Click para autenticarse con servidor CAS',
|
||||
));
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-cas/2.7.4',
|
||||
'authent-cas/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
//
|
||||
// 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
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*/
|
||||
@@ -34,7 +34,7 @@
|
||||
//
|
||||
// Class: UserExternal
|
||||
//
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:UserExternal' => 'Usuario Externo',
|
||||
'Class:UserExternal+' => 'Usuario Autenticado fuera de iTop',
|
||||
));
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-external/2.7.4',
|
||||
'authent-external/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
//
|
||||
// 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
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*/
|
||||
@@ -34,7 +34,7 @@
|
||||
//
|
||||
// Class: UserLDAP
|
||||
//
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
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',
|
||||
|
||||
@@ -9,7 +9,7 @@ if (function_exists('ldap_connect'))
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-ldap/2.7.4',
|
||||
'authent-ldap/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
//
|
||||
// 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
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*/
|
||||
@@ -34,24 +34,24 @@
|
||||
//
|
||||
// Class: UserLocal
|
||||
//
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
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',
|
||||
|
||||
'Class:UserLocal/Attribute:expiration' => 'Password expiration~~',
|
||||
'Class:UserLocal/Attribute:expiration+' => 'Password expiration status (requires an extension to have an effect)~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Can expire~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'Never expire~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'Expired~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
'Class:UserLocal/Attribute:expiration' => 'Expiración de contraseña',
|
||||
'Class:UserLocal/Attribute:expiration+' => 'Estatus de expiración de contraseña (requiere de una extensión para que tenga efecto)',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Puede expirar',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire+' => '',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'Nunca expirar',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'Expirado',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Renovación de contraseña',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Cuando fue el último cambio de contraseña',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La contraseña debe ser de al menos 8 caracteres e incluír mayúsculas, minúsculas, números y caracteres especiales.',
|
||||
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension~~'
|
||||
'UserLocal:password:expiration' => 'El siguiente campo requiere una extensión'
|
||||
));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-local/2.7.4',
|
||||
'authent-local/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -222,13 +222,11 @@ class DatabaseAnalyzer
|
||||
{
|
||||
$oFixSearch->AddCondition($sAttCode, $sValue, '=');
|
||||
}
|
||||
$aFixit[] = $oFixSearch->MakeSelectQuery().';';
|
||||
$aFixit[] = $oFixSearch->MakeSelectQuery([], [], null, null, 0, 0, false, false).';';
|
||||
$aFixit[] = "";
|
||||
}
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixit;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private function GetUniquenessRuleMessage($sUniquenessRuleId)
|
||||
@@ -326,13 +324,8 @@ class DatabaseAnalyzer
|
||||
{
|
||||
$sField = MetaModel::DBGetClassField($sClass);
|
||||
$sRootField = MetaModel::DBGetClassField($sRootClass);
|
||||
$sSelWrongRecs = <<<SQL
|
||||
SELECT `$sTable`.`$sKeyField` AS id
|
||||
FROM `$sTable`
|
||||
JOIN `$sRootTable` ON `$sRootTable`.`$sRootKey` = `$sTable`.`$sKeyField`
|
||||
WHERE `$sTable`.`$sField` != `$sRootTable`.`$sRootField`
|
||||
SQL;
|
||||
// Copy the finalclass of the root table
|
||||
$sSelWrongRecs = "SELECT `$sTable`.`$sKeyField` AS id FROM `$sTable` JOIN `$sRootTable` ON `$sRootTable`.`$sRootKey` = `$sTable`.`$sKeyField` WHERE `$sTable`.`$sField` != `$sRootTable`.`$sRootField`";
|
||||
// Copy the final class of the root table
|
||||
$sFixItRequest = "UPDATE `$sTable`,`$sRootTable` SET `$sTable`.`$sField` = `$sRootTable`.`$sRootField` WHERE `$sTable`.`$sKeyField` = `$sRootTable`.`$sRootKey`";
|
||||
$this->ExecQuery($sSelWrongRecs, $sFixItRequest, Dict::Format('DBAnalyzer-Integrity-FinalClass', $sField, $sTable, $sRootTable), $sClass, $aErrorsAndFixes);
|
||||
}
|
||||
@@ -360,65 +353,79 @@ SQL;
|
||||
|
||||
// Note: a class/table may have an external key on itself
|
||||
$sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id, `$sTable`.`$sExtKeyField` AS value";
|
||||
$sFilter = "FROM `$sTable` LEFT JOIN `$sRemoteTable` AS `{$sRemoteTable}_1` ON `$sTable`.`$sExtKeyField` = `{$sRemoteTable}_1`.`$sRemoteKey`";
|
||||
$sFrom = "FROM `$sTable`";
|
||||
$sJoin = "LEFT JOIN `$sRemoteTable` AS `{$sRemoteTable}_1` ON `$sTable`.`$sExtKeyField` = `{$sRemoteTable}_1`.`$sRemoteKey`";
|
||||
|
||||
$sFilter = $sFilter." WHERE `{$sRemoteTable}_1`.`$sRemoteKey` IS NULL";
|
||||
$sFilter = "WHERE `{$sRemoteTable}_1`.`$sRemoteKey` IS NULL";
|
||||
// Exclude the records pointing to 0/null from the errors (separate test below)
|
||||
$sFilter .= " AND `$sTable`.`$sExtKeyField` IS NOT NULL";
|
||||
$sFilter .= " AND `$sTable`.`$sExtKeyField` != 0";
|
||||
|
||||
$sSelWrongRecs = "$sSelect $sFilter";
|
||||
$sSelWrongRecs = "$sSelect $sFrom $sJoin $sFilter";
|
||||
|
||||
$sErrorDesc = Dict::Format('DBAnalyzer-Integrity-InvalidExtKey', $sAttCode, $sTable, $sExtKeyField);
|
||||
$this->ExecQuery($sSelWrongRecs, '', $sErrorDesc, $sClass, $aErrorsAndFixes);
|
||||
$aFixIt = [];
|
||||
// Fix it request needs the values of the enum to generate the requests
|
||||
if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['values']))
|
||||
{
|
||||
$aFixIt = array();
|
||||
$aFixIt[] = "-- Remove inconsistant entries:";
|
||||
$sIds = implode(', ', array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']));
|
||||
$aFixIt[] = "DELETE `$sTable` FROM `$sTable` WHERE `$sTable`.`$sExtKeyField` IN ($sIds)";
|
||||
$aFixIt[] = "";
|
||||
$aFixIt[] = "-- Or fix inconsistant values: Replace XXX with the appropriate value";
|
||||
foreach (array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']) as $sKey)
|
||||
{
|
||||
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sExtKeyField` = XXX WHERE `$sTable`.`$sExtKeyField` = '$sKey'";
|
||||
}
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt;
|
||||
}
|
||||
|
||||
if (!$oAttDef->IsNullAllowed())
|
||||
{
|
||||
$sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id";
|
||||
$sDelete = "DELETE `$sTable`";
|
||||
$sFilter = "FROM `$sTable` WHERE `$sTable`.`$sExtKeyField` IS NULL OR `$sTable`.`$sExtKeyField` = 0";
|
||||
$sSelWrongRecs = "$sSelect $sFilter";
|
||||
$sFixItRequest = "$sDelete $sFilter";
|
||||
$sErrorDesc = Dict::Format('DBAnalyzer-Integrity-MissingExtKey', $sAttCode, $sTable, $sExtKeyField);
|
||||
$this->ExecQuery($sSelWrongRecs, $sFixItRequest, $sErrorDesc, $sClass, $aErrorsAndFixes);
|
||||
if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['count']) && ($aErrorsAndFixes[$sClass][$sErrorDesc]['count'] > 0))
|
||||
{
|
||||
$aFixIt = $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'];
|
||||
$aFixIt[] = "-- Alternate fix";
|
||||
$aFixIt[] = "-- Replace XXX with the appropriate value";
|
||||
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sExtKeyField` = XXX WHERE `$sTable`.`$sExtKeyField` IS NULL OR `$sTable`.`$sExtKeyField` = 0";
|
||||
$aAdditionalFixIt = $this->GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField);
|
||||
if ($oAttDef->IsNullAllowed()) {
|
||||
$aFixIt[] = "-- Fix inconsistant values: remove the external key";
|
||||
foreach (array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']) as $sKey) {
|
||||
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sExtKeyField` = 0 WHERE `$sTable`.`$sExtKeyField` = '$sKey'";
|
||||
}
|
||||
} else {
|
||||
$aAdditionalFixIt = $this->GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField, $sFilter, $sJoin);
|
||||
foreach ($aAdditionalFixIt as $sFixIt)
|
||||
{
|
||||
$aFixIt[] = $sFixIt;
|
||||
}
|
||||
|
||||
$aFixIt[] = "-- Alternate fix: remove inconsistant entries:";
|
||||
$sIds = implode(', ', array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']));
|
||||
$aFixIt[] = "DELETE `$sTable` FROM `$sTable` WHERE `$sTable`.`$sExtKeyField` IN ($sIds)";
|
||||
|
||||
$aFixIt[] = "-- Alternate fix: update inconsistant values: Replace XXX with the appropriate value";
|
||||
foreach (array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']) as $sKey) {
|
||||
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sExtKeyField` = XXX WHERE `$sTable`.`$sExtKeyField` = '$sKey'";
|
||||
}
|
||||
}
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt;
|
||||
}
|
||||
|
||||
if (!$oAttDef->IsNullAllowed()) {
|
||||
$sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id";
|
||||
$sDelete = "DELETE `$sTable`";
|
||||
$sFrom = "FROM `$sTable`";
|
||||
$sFilter = "WHERE `$sTable`.`$sExtKeyField` IS NULL OR `$sTable`.`$sExtKeyField` = 0";
|
||||
$sSelWrongRecs = "$sSelect $sFrom $sFilter";
|
||||
$sFixItRequest = "$sDelete $sFrom $sFilter";
|
||||
$sErrorDesc = Dict::Format('DBAnalyzer-Integrity-MissingExtKey', $sAttCode, $sTable, $sExtKeyField);
|
||||
$this->ExecQuery($sSelWrongRecs, '', $sErrorDesc, $sClass, $aErrorsAndFixes);
|
||||
$aFixIt = [];
|
||||
if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['count']) && ($aErrorsAndFixes[$sClass][$sErrorDesc]['count'] > 0))
|
||||
{
|
||||
$aAdditionalFixIt = $this->GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField, $sFilter);
|
||||
foreach ($aAdditionalFixIt as $sFixIt)
|
||||
{
|
||||
$aFixIt[] = $sFixIt;
|
||||
}
|
||||
$aFixIt[] = "-- Alternate fix: remove inconsistant entries:";
|
||||
$aFixIt[] = $sFixItRequest;
|
||||
$aFixIt[] = "-- Alternate fix: replace XXX with the appropriate value";
|
||||
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sExtKeyField` = XXX WHERE `$sTable`.`$sExtKeyField` IS NULL OR `$sTable`.`$sExtKeyField` = 0";
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField)
|
||||
private function GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField, $sFilter, $sJoin = '')
|
||||
{
|
||||
$aFixIt = array();
|
||||
if ($sTable == 'ticket' && $sExtKeyField == 'org_id')
|
||||
{
|
||||
$aFixIt[] = "-- Alternate fix: set the ticket org to the caller org";
|
||||
$aFixIt[] = "UPDATE ticket AS t JOIN contact AS c ON t.caller_id=c.id SET t.org_id=c.org_id WHERE t.org_id IS NULL OR t.org_id = 0";
|
||||
$aFixIt[] = "UPDATE ticket JOIN contact AS c ON ticket.caller_id=c.id $sJoin SET ticket.org_id=c.org_id $sFilter";
|
||||
}
|
||||
return $aFixIt;
|
||||
}
|
||||
@@ -441,7 +448,8 @@ SQL;
|
||||
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode);
|
||||
if (!is_null($aAllowedValues) && count($aAllowedValues) > 0)
|
||||
{
|
||||
$sExpectedValues = implode(",", CMDBSource::Quote(array_keys($aAllowedValues), true));
|
||||
$aAllowedValues = array_keys($aAllowedValues);
|
||||
$sExpectedValues = implode(",", CMDBSource::Quote($aAllowedValues, true));
|
||||
|
||||
$aCols = $oAttDef->GetSQLExpressions(); // Workaround a PHP bug: sometimes issuing a Notice if invoking current(somefunc())
|
||||
$sMyAttributeField = current($aCols); // get the first column for the moment
|
||||
@@ -458,15 +466,18 @@ SQL;
|
||||
if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['fixit']))
|
||||
{
|
||||
$aFixIt = $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'];
|
||||
$aFixIt[] = "-- Alternative: Replace 'XXX' with the appropriate value";
|
||||
$aFixIt[] = "-- Alternative: Replace enums with the appropriate value";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aFixIt = array("-- Replace 'XXX' with the appropriate value");
|
||||
$aFixIt = ["-- Replace enums with the appropriate value"];
|
||||
}
|
||||
foreach (array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']) as $sKey)
|
||||
{
|
||||
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sMyAttributeField` = 'XXX' WHERE `$sTable`.`$sMyAttributeField` = '$sKey'";
|
||||
foreach ($aAllowedValues as $sAllowedValue) {
|
||||
$aFixIt[] = "-- Replace $sKey by $sAllowedValue";
|
||||
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sMyAttributeField` = '$sAllowedValue' WHERE `$sTable`.`$sMyAttributeField` = '$sKey'";
|
||||
}
|
||||
}
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt;
|
||||
}
|
||||
|
||||
@@ -31,15 +31,16 @@ const MAX_RESULTS = 10;
|
||||
* @param ApplicationContext $oAppContext
|
||||
*
|
||||
* @return \iTopWebPage
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
* @throws MySQLException
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppContext)
|
||||
{
|
||||
$iShowId = intval(utils::ReadParam('show_id', '0'));
|
||||
$sErrorLabelSelection = utils::ReadParam('error_selection', '');
|
||||
$sClassSelection = utils::ReadParam('class_selection', '');
|
||||
$bVerbose = utils::ReadParam('verbose', 0);
|
||||
if (!empty($sClassSelection))
|
||||
{
|
||||
$aClassSelection = explode(",", $sClassSelection);
|
||||
@@ -109,11 +110,16 @@ function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppCon
|
||||
|
||||
if ($iShowId == 3)
|
||||
{
|
||||
DisplayInconsistenciesReport($aResults);
|
||||
DisplayInconsistenciesReport($aResults, $bVerbose);
|
||||
}
|
||||
|
||||
$oP->p(Dict::S('DBTools:ErrorsFound'));
|
||||
|
||||
if ($iShowId > 0) {
|
||||
$oP->p(Dict::S('DBTools:Disclaimer'));
|
||||
$oP->p(Dict::S('DBTools:Indication'));
|
||||
}
|
||||
|
||||
$oP->add('<table class="listResults"><tr><th>'.Dict::S('DBTools:Class').'</th><th>'.Dict::S('DBTools:Count').'</th><th>'.Dict::S('DBTools:Error').'</th></tr>');
|
||||
$bTable = true;
|
||||
foreach($aResults as $sClass => $aErrorList)
|
||||
@@ -148,7 +154,7 @@ function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppCon
|
||||
$oP->p(Dict::S('DBTools:SQLquery'));
|
||||
$sQuery = $aError['query'];
|
||||
$oP->add('<div style="padding: 15px; background: #f1f1f1;">');
|
||||
$oP->add('<code>'.$sQuery.'</code>');
|
||||
$oP->add('<pre>'.$sQuery.'</pre>');
|
||||
$oP->add('</div>');
|
||||
|
||||
if (isset($aError['fixit']))
|
||||
@@ -163,27 +169,26 @@ function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppCon
|
||||
$oP->add('<br></div>');
|
||||
}
|
||||
|
||||
$oP->p(Dict::S('DBTools:SQLresult'));
|
||||
$sQueryResult = '';
|
||||
$iCount = count($aError['res']);
|
||||
$iMaxCount = MAX_RESULTS;
|
||||
foreach($aError['res'] as $aRes)
|
||||
{
|
||||
$iMaxCount--;
|
||||
if ($iMaxCount < 0)
|
||||
{
|
||||
$sQueryResult .= 'Displayed '.MAX_RESULTS."/$iCount results.<br>";
|
||||
break;
|
||||
if ($bVerbose) {
|
||||
$oP->p(Dict::S('DBTools:SQLresult'));
|
||||
$sQueryResult = '';
|
||||
$iCount = count($aError['res']);
|
||||
$iMaxCount = MAX_RESULTS;
|
||||
foreach ($aError['res'] as $aRes) {
|
||||
$iMaxCount--;
|
||||
if ($iMaxCount < 0) {
|
||||
$sQueryResult .= 'Displayed '.MAX_RESULTS."/$iCount results.<br>";
|
||||
break;
|
||||
}
|
||||
foreach ($aRes as $sKey => $sValue) {
|
||||
$sQueryResult .= "'$sKey'='$sValue' ";
|
||||
}
|
||||
$sQueryResult .= '<br>';
|
||||
}
|
||||
foreach($aRes as $sKey => $sValue)
|
||||
{
|
||||
$sQueryResult .= "'$sKey'='$sValue' ";
|
||||
}
|
||||
$sQueryResult .= '<br>';
|
||||
$oP->add('<div style="padding: 15px; background: #f1f1f1;">');
|
||||
$oP->add('<pre>'.$sQueryResult.'</pre>');
|
||||
$oP->add('</div>');
|
||||
}
|
||||
$oP->add('<div style="padding: 15px; background: #f1f1f1;">');
|
||||
$oP->add('<code>'.$sQueryResult.'</code>');
|
||||
$oP->add('</div>');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,14 +199,15 @@ function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppCon
|
||||
|
||||
/**
|
||||
* @param $aResults
|
||||
* @param bool $bVerbose
|
||||
*
|
||||
* @return mixed
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*/
|
||||
function DisplayInconsistenciesReport($aResults)
|
||||
function DisplayInconsistenciesReport($aResults, $bVerbose = false)
|
||||
{
|
||||
$sReportFile = DBAnalyzerUtils::GenerateReport($aResults);
|
||||
$sReportFile = DBAnalyzerUtils::GenerateReport($aResults, $bVerbose);
|
||||
|
||||
$sZipReport = "{$sReportFile}.zip";
|
||||
$oArchive = new ZipArchive();
|
||||
|
||||
@@ -28,6 +28,8 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'DBTools:Class' => 'Class',
|
||||
'DBTools:Title' => 'Database Maintenance Tools',
|
||||
'DBTools:ErrorsFound' => 'Errors Found',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES',
|
||||
'DBTools:Error' => 'Error',
|
||||
'DBTools:Count' => 'Count',
|
||||
'DBTools:SQLquery' => 'SQL query',
|
||||
|
||||
@@ -20,12 +20,19 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*/
|
||||
// Database inconsistencies
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
// Dictionary entries go here
|
||||
'Menu:DBToolsMenu' => 'Herramientas de bases de datos',
|
||||
'Menu:DBToolsMenu' => 'Integridad de Base de Datos',
|
||||
'DBTools:Class' => 'Clase',
|
||||
'DBTools:Title' => 'Herramientas de mantenimiento de base de datos',
|
||||
'DBTools:Title' => 'Herramientas de Mantenimiento de Base de Datos',
|
||||
'DBTools:ErrorsFound' => 'Errores encontrados',
|
||||
'DBTools:Error' => 'Error',
|
||||
'DBTools:Count' => 'Cantidad',
|
||||
@@ -37,45 +44,45 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'DBTools:ShowIds' => 'Vista detallada',
|
||||
'DBTools:ShowReport' => 'Reporte',
|
||||
'DBTools:IntegrityCheck' => 'Verificación de integridad',
|
||||
'DBTools:FetchCheck' => 'Fetch Check (long)~~',
|
||||
'DBTools:FetchCheck' => 'Verificación de búsqueda (larga)',
|
||||
|
||||
'DBTools:Analyze' => 'Analizar',
|
||||
'DBTools:Details' => 'Mostrar detalles',
|
||||
'DBTools:ShowAll' => 'Mostrar todos los errores',
|
||||
|
||||
'DBTools:Inconsistencies' => 'Inconsistencias de base de datos',
|
||||
'DBTools:Inconsistencies' => 'Inconsistencias de Base de Datos',
|
||||
|
||||
'DBAnalyzer-Integrity-OrphanRecord' => 'Registro huérfano en `%1$s`, debería tener su contraparte en la tabla `%2$s`',
|
||||
'DBAnalyzer-Integrity-InvalidExtKey' => 'Llave externa inválida %1$s (columna: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-MissingExtKey' => 'Llave externa perdida %1$s (columna: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-InvalidValue' => 'Valor inválido para %1$s (columna: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Algunas cuentas de usuario no tienen perfil asignado',
|
||||
'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~',
|
||||
'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~',
|
||||
'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~',
|
||||
'DBAnalyzer-Fetch-Count-Error' => 'Obtener cuenta de errores en `%1$s`, %2$d entradas recuperadas / %3$d contadas',
|
||||
'DBAnalyzer-Integrity-FinalClass' => 'Campo `%2$s`.`%1$s` debe tener los mismos valores que `%3$s`.`%1$s`',
|
||||
'DBAnalyzer-Integrity-RootFinalClass' => 'Campo `%2$s`.`%1$s` debe contener un caracter válido',
|
||||
));
|
||||
|
||||
// Database Info
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'DBTools:DatabaseInfo' => 'Información de base de datos',
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'DBTools:DatabaseInfo' => 'Información de Base de Datos',
|
||||
'DBTools:Base' => 'Base',
|
||||
'DBTools:Size' => 'Tamaño',
|
||||
));
|
||||
|
||||
// Lost attachments
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'DBTools:LostAttachments' => 'Adjuntos perdidos',
|
||||
'DBTools:LostAttachments:Disclaimer' => 'Aquí usted puede buscar adjuntos perdidos o desplazados. Esta NO es una herramienta de recuperación de datos, no obtiene datos borrados.',
|
||||
'DBTools:LostAttachments:Disclaimer' => 'Aquí usted puede buscar anexos perdidos o fuera de lugar. Esta NO es una herramienta de recuperación de datos, no obtiene datos borrados.',
|
||||
|
||||
'DBTools:LostAttachments:Button:Analyze' => 'Analizar',
|
||||
'DBTools:LostAttachments:Button:Restore' => 'Restaurar',
|
||||
'DBTools:LostAttachments:Button:Restore:Confirm' => 'Esta acción no se puede deshacer, por favor confirme que quiere restaurar los archivos seleccionados.',
|
||||
'DBTools:LostAttachments:Button:Busy' => 'Por favor espere...',
|
||||
|
||||
'DBTools:LostAttachments:Step:Analyze' => 'Primero, buscaremos adjuntos perdidos/desplazados analizando la base de datos.',
|
||||
'DBTools:LostAttachments:Step:Analyze' => 'Primero, buscar anexos perdidos o fuera de lugar analizando la base de datos.',
|
||||
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analizar resultados:',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Genial! Todo parece estar en el lugar correcto.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:None' => '¡Genial! Todo parece estar en el lugar correcto.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Algunos adjuntos (%1$d) parecen estar desplazados. Mire la siguiente lista y verifique los que quiera mover.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Nombre de archivo',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Ubicación actual',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'DBTools:Class' => 'Classe',
|
||||
'DBTools:Title' => 'Outils maintenance base de données',
|
||||
'DBTools:ErrorsFound' => 'Erreurs trouvées',
|
||||
'DBTools:Indication' => 'Important : après correction il est nécessaire de relancer l\'analyse car d\'autres inconsistances peuvent être générées par les modifications',
|
||||
'DBTools:Disclaimer' => 'ATTENTION : EFFECTUEZ UNE SAUVEGARDE DE LA BASE AVANT D\'APPLIQUER LES CORRECTIONS',
|
||||
'DBTools:Error' => 'Erreur',
|
||||
'DBTools:Count' => 'Nombre',
|
||||
'DBTools:SQLquery' => 'Requête SQL',
|
||||
@@ -41,7 +43,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
|
||||
'DBTools:Inconsistencies' => 'Incohérences de base de données',
|
||||
|
||||
'DBAnalyzer-Integrity-OrphanRecord' => 'Enregistrement orphelin dans `%1$s`, il devrait avoir son équivalent dans la tableit `%2$s`',
|
||||
'DBAnalyzer-Integrity-OrphanRecord' => 'Enregistrement orphelin dans `%1$s`, il devrait avoir son équivalent dans la table `%2$s`',
|
||||
'DBAnalyzer-Integrity-InvalidExtKey' => 'Clef externe invalide %1$s (colonne: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-MissingExtKey' => 'Clef externe manquante %1$s (colonne: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-InvalidValue' => 'Valeur invalide pour %1$s (colonne: `%2$s.%3$s`)',
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'combodo-db-tools/2.7.4',
|
||||
'combodo-db-tools/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace Combodo\iTop\DBTools\Service;
|
||||
|
||||
use CoreException;
|
||||
use Dict;
|
||||
use DictExceptionMissingString;
|
||||
use MetaModel;
|
||||
|
||||
@@ -20,29 +21,31 @@ class DBAnalyzerUtils
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
public static function GenerateReport($aResults)
|
||||
public static function GenerateReport($aResults, $bVerbose = false)
|
||||
{
|
||||
$sDBToolsFolder = str_replace("\\", '/', APPROOT.'log/');
|
||||
$sReportFile = 'dbtools-report';
|
||||
|
||||
$fReport = fopen($sDBToolsFolder.$sReportFile.'.log', 'w');
|
||||
fwrite($fReport, 'Database Maintenance tools: '.date('Y-m-d H:i:s')."\r\n");
|
||||
fwrite($fReport, '-- Database Maintenance tools: '.date('Y-m-d H:i:s')."\r\n");
|
||||
fwrite($fReport, "-- ".Dict::S('DBTools:Disclaimer')."\r\n");
|
||||
fwrite($fReport, "-- ".Dict::S('DBTools:Indication')."\r\n");
|
||||
foreach ($aResults as $sClass => $aErrorList)
|
||||
{
|
||||
fwrite($fReport, '');
|
||||
foreach ($aErrorList as $sErrorLabel => $aError)
|
||||
{
|
||||
fwrite($fReport, "\r\n----------\r\n");
|
||||
fwrite($fReport, 'Class: '.MetaModel::GetName($sClass).' ('.$sClass.")\r\n");
|
||||
fwrite($fReport, "\r\n-- \r\n");
|
||||
fwrite($fReport, '-- Class: '.MetaModel::GetName($sClass).' ('.$sClass.")\r\n");
|
||||
$iCount = $aError['count'];
|
||||
fwrite($fReport, 'Count: '.$iCount."\r\n");
|
||||
fwrite($fReport, 'Error: '.$sErrorLabel."\r\n");
|
||||
fwrite($fReport, '-- Count: '.$iCount."\r\n");
|
||||
fwrite($fReport, '-- Error: '.$sErrorLabel."\r\n");
|
||||
$sQuery = $aError['query'];
|
||||
fwrite($fReport, 'Query: '.$sQuery."\r\n");
|
||||
fwrite($fReport, '-- Query: '.$sQuery."\r\n");
|
||||
|
||||
if (isset($aError['fixit']))
|
||||
{
|
||||
fwrite($fReport, "\r\nFix it (indication):\r\n\r\n");
|
||||
fwrite($fReport, "\r\n-- Fix it (indication):\r\n\r\n");
|
||||
$aFixitQueries = $aError['fixit'];
|
||||
foreach ($aFixitQueries as $sFixitQuery)
|
||||
{
|
||||
@@ -51,31 +54,26 @@ class DBAnalyzerUtils
|
||||
fwrite($fReport, "\r\n");
|
||||
}
|
||||
|
||||
$sQueryResult = '';
|
||||
$aIdList = array();
|
||||
foreach ($aError['res'] as $aRes)
|
||||
{
|
||||
foreach ($aRes as $sKey => $sValue)
|
||||
{
|
||||
$sQueryResult .= "'$sKey'='$sValue' ";
|
||||
if ($sKey == 'id')
|
||||
{
|
||||
$aIdList[] = $sValue;
|
||||
if ($bVerbose) {
|
||||
$sQueryResult = '';
|
||||
$aIdList = [];
|
||||
foreach ($aError['res'] as $aRes) {
|
||||
$sQueryResult .= " - ";
|
||||
foreach ($aRes as $sKey => $sValue) {
|
||||
$sQueryResult .= "'$sKey'='$sValue' ";
|
||||
if ($sKey == 'id') {
|
||||
$aIdList[] = $sValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sQueryResult .= "\r\n";
|
||||
|
||||
fwrite($fReport, "-- Result: ".$sQueryResult);
|
||||
$sIdList = '('.implode(',', $aIdList).')';
|
||||
fwrite($fReport, "\r\n-- Ids: ".$sIdList."\r\n");
|
||||
}
|
||||
fwrite($fReport, "Result: \r\n".$sQueryResult);
|
||||
$sIdList = '('.implode(',', $aIdList).')';
|
||||
fwrite($fReport, 'Ids: '.$sIdList."\r\n");
|
||||
}
|
||||
}
|
||||
fclose($fReport);
|
||||
|
||||
|
||||
$sReportFile = $sDBToolsFolder.$sReportFile;
|
||||
|
||||
return $sReportFile;
|
||||
return $sDBToolsFolder.$sReportFile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,26 +64,26 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
|
||||
// Lost attachments
|
||||
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'DBTools:LostAttachments' => 'Lost attachments~~',
|
||||
'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~',
|
||||
'DBTools:LostAttachments' => '附件丢失',
|
||||
'DBTools:LostAttachments:Disclaimer' => '您可以在数据库里搜索附件是否有丢失或误挪动. 这不是数据恢复工具, 无法恢复已删除的数据.',
|
||||
|
||||
'DBTools:LostAttachments:Button:Analyze' => '分析',
|
||||
'DBTools:LostAttachments:Button:Restore' => '还原',
|
||||
'DBTools:LostAttachments:Button:Restore:Confirm' => '该操作无法回退, 请确认是否继续还原.',
|
||||
'DBTools:LostAttachments:Button:Busy' => '请稍后...',
|
||||
|
||||
'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~',
|
||||
'DBTools:LostAttachments:Step:Analyze' => '首先, 通过分析数据库来搜索丢失或误挪动的附件.',
|
||||
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults' => '分析结果:',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:None' => '非常好! 所有附件都是正常的.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Some' => '某些附件 (%1$d) 看起来放错了位置. 请检查下面的列表并选择要挪动的文件.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => '文件名',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => '当前位置',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => '移动到...',
|
||||
|
||||
'DBTools:LostAttachments:Step:RestoreResults' => '还原结果:',
|
||||
'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~',
|
||||
'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d 的附件已还原.',
|
||||
|
||||
'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~',
|
||||
'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~'
|
||||
'DBTools:LostAttachments:History' => 'Attachment "附件 %1$s" 已被数据库工具恢复'
|
||||
));
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
//
|
||||
// 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
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Attachments:TabTitle_Count' => 'Anexos (%1$d)',
|
||||
'Attachments:EmptyTabTitle' => 'Anexos',
|
||||
'Attachments:FieldsetTitle' => 'Anexos',
|
||||
@@ -36,53 +36,53 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Attachment:Max_Ko' => '(Tamaño Máximo de Archivo: %1$s Kb)',
|
||||
'Attachments:NoAttachment' => 'No hay Anexo. ',
|
||||
'Attachments:PreviewNotAvailable' => 'Vista preliminar no disponible para este tipo de Anexo.',
|
||||
'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~',
|
||||
'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached.
|
||||
Either you have pushed an empty file,
|
||||
or ask your iTop administrator if the iTop server disk is full.~~',
|
||||
'Attachments:Render:Icons' => 'Display as icons~~',
|
||||
'Attachments:Render:Table' => 'Display as list~~',
|
||||
'Attachments:Error:FileTooLarge' => 'El archivo es demasiado grande para ser cargado. %1$s',
|
||||
'Attachments:Error:UploadedFileEmpty' => 'El archivo recibido está vacío y no puede ser anexado.
|
||||
Puede ser que haya enviado un archivo vació,
|
||||
o pregunte al administador de iTop si el servidor que ha quedado sin espacio en disco.',
|
||||
'Attachments:Render:Icons' => 'Desplegar como icono',
|
||||
'Attachments:Render:Table' => 'Desplegar como lista',
|
||||
));
|
||||
|
||||
//
|
||||
// Class: Attachment
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Class:Attachment' => 'Attachment~~',
|
||||
'Class:Attachment+' => '~~',
|
||||
'Class:Attachment/Attribute:expire' => 'Expire~~',
|
||||
'Class:Attachment/Attribute:expire+' => '~~',
|
||||
'Class:Attachment/Attribute:temp_id' => 'Temporary id~~',
|
||||
'Class:Attachment/Attribute:temp_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_class' => 'Item class~~',
|
||||
'Class:Attachment/Attribute:item_class+' => '~~',
|
||||
'Class:Attachment/Attribute:item_id' => 'Item~~',
|
||||
'Class:Attachment/Attribute:item_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_org_id' => 'Item organization~~',
|
||||
'Class:Attachment/Attribute:item_org_id+' => '~~',
|
||||
'Class:Attachment/Attribute:contents' => 'Contents~~',
|
||||
'Class:Attachment/Attribute:contents+' => '~~',
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:Attachment' => 'Anexo',
|
||||
'Class:Attachment+' => 'Anexo',
|
||||
'Class:Attachment/Attribute:expire' => 'Expira',
|
||||
'Class:Attachment/Attribute:expire+' => '',
|
||||
'Class:Attachment/Attribute:temp_id' => 'Id Temporal',
|
||||
'Class:Attachment/Attribute:temp_id+' => '',
|
||||
'Class:Attachment/Attribute:item_class' => 'Clase de Elemento',
|
||||
'Class:Attachment/Attribute:item_class+' => '',
|
||||
'Class:Attachment/Attribute:item_id' => 'Elemento',
|
||||
'Class:Attachment/Attribute:item_id+' => '',
|
||||
'Class:Attachment/Attribute:item_org_id' => 'Organización de Elemento',
|
||||
'Class:Attachment/Attribute:item_org_id+' => '',
|
||||
'Class:Attachment/Attribute:contents' => 'Contenido',
|
||||
'Class:Attachment/Attribute:contents+' => '',
|
||||
));
|
||||
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Attachments:File:Thumbnail' => 'Icon~~',
|
||||
'Attachments:File:Name' => 'File name~~',
|
||||
'Attachments:File:Date' => 'Upload date~~',
|
||||
'Attachments:File:Uploader' => 'Uploaded by~~',
|
||||
'Attachments:File:Size' => 'Size~~',
|
||||
'Attachments:File:MimeType' => 'Type~~',
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Attachments:File:Thumbnail' => 'Ícono',
|
||||
'Attachments:File:Name' => 'Nombre de Archivo',
|
||||
'Attachments:File:Date' => 'Fecha de Carga',
|
||||
'Attachments:File:Uploader' => 'Cargado por',
|
||||
'Attachments:File:Size' => 'Tamaño',
|
||||
'Attachments:File:MimeType' => 'Tipo',
|
||||
));
|
||||
//
|
||||
// Class: Attachment
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Class:Attachment/Attribute:creation_date' => 'Creation date~~',
|
||||
'Class:Attachment/Attribute:creation_date+' => '~~',
|
||||
'Class:Attachment/Attribute:user_id' => 'User id~~',
|
||||
'Class:Attachment/Attribute:user_id+' => '~~',
|
||||
'Class:Attachment/Attribute:contact_id' => 'Contact id~~',
|
||||
'Class:Attachment/Attribute:contact_id+' => '~~',
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:Attachment/Attribute:creation_date' => 'Fecha de Creación',
|
||||
'Class:Attachment/Attribute:creation_date+' => '',
|
||||
'Class:Attachment/Attribute:user_id' => 'Id del Usuario',
|
||||
'Class:Attachment/Attribute:user_id+' => '',
|
||||
'Class:Attachment/Attribute:contact_id' => 'Id del Contacto',
|
||||
'Class:Attachment/Attribute:contact_id+' => '',
|
||||
));
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-attachments/2.7.4',
|
||||
'itop-attachments/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
@@ -177,6 +177,12 @@ SQL;
|
||||
SetupPage::log_info("Initializing attachment/item_org_id - zero to the container");
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_org_id = 0");
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$oSet->OptimizeColumnLoad([
|
||||
'Attachment' => [
|
||||
'item_class',
|
||||
'item_id',
|
||||
]
|
||||
]);
|
||||
$iUpdated = 0;
|
||||
while ($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
|
||||
@@ -551,7 +551,7 @@ JS
|
||||
<td role="filename"><a href="$sDocDownloadUrl" target="_blank" class="$sIconClass">$sFileName</a>$sAttachmentMeta</td>
|
||||
<td role="formatted-size" data-order="$iFileSize">$sFileFormattedSize</td>
|
||||
<td role="upload-date" data-order="$iAttachmentDateRaw">$sAttachmentDateFormatted</td>
|
||||
<td role="uploader">$sAttachmentUploader</td>
|
||||
<td role="uploader">$sAttachmentUploaderForHtml</td>
|
||||
<td role="type">$sFileType</td>
|
||||
$sDeleteColumn
|
||||
</tr>
|
||||
|
||||
@@ -30,10 +30,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Attachment:Max_Ko' => '(最大文件尺寸: %1$s KB)',
|
||||
'Attachments:NoAttachment' => '没有附件. ',
|
||||
'Attachments:PreviewNotAvailable' => '该附件类型不支持预览.',
|
||||
'Attachments:Error:FileTooLarge' => '上传的文件过大. %1$s~~',
|
||||
'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached.
|
||||
Either you have pushed an empty file,
|
||||
or ask your iTop administrator if the iTop server disk is full.~~',
|
||||
'Attachments:Error:FileTooLarge' => '上传的文件过大. %1$s',
|
||||
'Attachments:Error:UploadedFileEmpty' => '收到的文件为空,无法添加.
|
||||
可能是因为您上传了一个空文件,,
|
||||
或者咨询iTop 管理员确认iTop 服务器磁盘空间是否已满.',
|
||||
'Attachments:Render:Icons' => '显示为图标',
|
||||
'Attachments:Render:Table' => '显示为列表',
|
||||
));
|
||||
@@ -47,11 +47,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Attachment+' => '',
|
||||
'Class:Attachment/Attribute:expire' => '过期',
|
||||
'Class:Attachment/Attribute:expire+' => '~~',
|
||||
'Class:Attachment/Attribute:temp_id' => '临时 id',
|
||||
'Class:Attachment/Attribute:temp_id' => '临时id',
|
||||
'Class:Attachment/Attribute:temp_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_class' => 'Item class~~',
|
||||
'Class:Attachment/Attribute:item_class+' => '~~',
|
||||
'Class:Attachment/Attribute:item_id' => 'Item~~',
|
||||
'Class:Attachment/Attribute:item_id' => '项目',
|
||||
'Class:Attachment/Attribute:item_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_org_id' => 'Item organization~~',
|
||||
'Class:Attachment/Attribute:item_org_id+' => '',
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
@@ -20,7 +21,7 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
|
||||
'bkp-backup-running' => 'Un respaldo está en ejecuión. Por favor espere...',
|
||||
'bkp-restore-running' => 'Una restauración está en ejecución. Por favor espere...',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-backup/2.7.4',
|
||||
'itop-backup/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-bridge-virtualization-storage/2.7.4',
|
||||
'itop-bridge-virtualization-storage/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
//
|
||||
// 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
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Menu:ChangeManagement' => 'Administración de Cambios',
|
||||
'Menu:Change:Overview' => 'Resumen de Cambios',
|
||||
'Menu:Change:Overview+' => 'Resumen de Cambios',
|
||||
@@ -63,7 +63,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
// Class: Change
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:Change' => 'Cambio',
|
||||
'Class:Change+' => 'Cambio',
|
||||
'Class:Change/Attribute:status' => 'Estatus',
|
||||
@@ -166,7 +166,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
// Class: RoutineChange
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:RoutineChange' => 'Cambio Rutinario',
|
||||
'Class:RoutineChange+' => 'Cambio Rutinario',
|
||||
'Class:RoutineChange/Stimulus:ev_validate' => 'Validar',
|
||||
@@ -197,7 +197,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
// Class: ApprovedChange
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:ApprovedChange' => 'Cambios Aprobados',
|
||||
'Class:ApprovedChange+' => 'Cambios Aprobados',
|
||||
'Class:ApprovedChange/Attribute:approval_date' => 'Fecha de Aprobación',
|
||||
@@ -232,7 +232,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
// Class: NormalChange
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:NormalChange' => 'Cambio Normal',
|
||||
'Class:NormalChange+' => 'Cambio Normal',
|
||||
'Class:NormalChange/Attribute:acceptance_date' => 'Fecha de Aceptación',
|
||||
@@ -267,7 +267,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
// Class: EmergencyChange
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:EmergencyChange' => 'Cambio de Emergencia',
|
||||
'Class:EmergencyChange+' => 'Cambio de Emergencia',
|
||||
'Class:EmergencyChange/Stimulus:ev_validate' => 'Validar',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-change-mgmt-itil/2.7.4',
|
||||
'itop-change-mgmt-itil/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
//
|
||||
// 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
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Menu:ChangeManagement' => 'Administración de Cambios',
|
||||
'Menu:Change:Overview' => 'Resumen de Cambios',
|
||||
'Menu:Change:Overview+' => 'Resumen de Cambios',
|
||||
@@ -63,7 +63,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
// Class: Change
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:Change' => 'Cambio',
|
||||
'Class:Change+' => 'Cambio',
|
||||
'Class:Change/Attribute:status' => 'Estatus',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-change-mgmt/2.7.4',
|
||||
'itop-change-mgmt/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -86,8 +86,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Change/Attribute:category/Value:hardware+' => '硬件',
|
||||
'Class:Change/Attribute:category/Value:network' => '网络',
|
||||
'Class:Change/Attribute:category/Value:network+' => '网络',
|
||||
'Class:Change/Attribute:category/Value:other' => '其他',
|
||||
'Class:Change/Attribute:category/Value:other+' => '其他',
|
||||
'Class:Change/Attribute:category/Value:other' => '其它',
|
||||
'Class:Change/Attribute:category/Value:other+' => '其它',
|
||||
'Class:Change/Attribute:category/Value:software' => '软件',
|
||||
'Class:Change/Attribute:category/Value:software+' => '软件',
|
||||
'Class:Change/Attribute:category/Value:system' => '系统',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-config-mgmt/2.7.4',
|
||||
'itop-config-mgmt/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -365,8 +365,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:PhysicalDevice/Attribute:status/Value:obsolete+' => '废弃',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:production' => '生产',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:production+' => '生产',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:stock' => '闲置',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:stock+' => '闲置',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:stock' => '库存',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:stock+' => '库存',
|
||||
'Class:PhysicalDevice/Attribute:brand_id' => '品牌',
|
||||
'Class:PhysicalDevice/Attribute:brand_id+' => '',
|
||||
'Class:PhysicalDevice/Attribute:brand_name' => '品牌名称',
|
||||
@@ -868,8 +868,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:VirtualDevice/Attribute:status/Value:obsolete+' => '废弃',
|
||||
'Class:VirtualDevice/Attribute:status/Value:production' => '生产',
|
||||
'Class:VirtualDevice/Attribute:status/Value:production+' => '生产',
|
||||
'Class:VirtualDevice/Attribute:status/Value:stock' => '闲置',
|
||||
'Class:VirtualDevice/Attribute:status/Value:stock+' => '闲置',
|
||||
'Class:VirtualDevice/Attribute:status/Value:stock' => '库存',
|
||||
'Class:VirtualDevice/Attribute:status/Value:stock+' => '库存',
|
||||
'Class:VirtualDevice/Attribute:logicalvolumes_list' => '逻辑卷',
|
||||
'Class:VirtualDevice/Attribute:logicalvolumes_list+' => '该设备使用的所有逻辑卷',
|
||||
));
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
@@ -20,7 +21,7 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
|
||||
'Menu:ConfigEditor' => 'Configuración',
|
||||
'config-edit-title' => 'Editor de Archivo de Configuración',
|
||||
@@ -34,5 +35,5 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'config-reverted' => 'La configuración ha sido revertida.',
|
||||
'config-parse-error' => 'Línea %2$d: %1$s.<br/>El archivo NO ha sido actualizado.',
|
||||
'config-current-line' => 'Editando línea: %1$s',
|
||||
'config-saved-warning-db-password' => 'Successfully recorded, but the backup won\'t work due to unsupported characters in the database password.~~',
|
||||
'config-saved-warning-db-password' => 'Registrado correctamente, pero el respaldo NO funcionará debido a caracteres no admitidos en la contraseña de la base de datos.',
|
||||
));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-config/2.7.4',
|
||||
'itop-config/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
@@ -20,15 +21,15 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'iTopUpdate:UI:PageTitle' => 'Actualización de aplicación',
|
||||
'itop-core-update:UI:SelectUpdateFile' => 'Application Upgrade~~',
|
||||
'itop-core-update:UI:ConfirmUpdate' => 'Application Upgrade~~',
|
||||
'itop-core-update:UI:UpdateCoreFiles' => 'Application Upgrade~~',
|
||||
'itop-core-update:UI:SelectUpdateFile' => 'Seleccionar archivo',
|
||||
'itop-core-update:UI:ConfirmUpdate' => 'Confirmar actualización',
|
||||
'itop-core-update:UI:UpdateCoreFiles' => 'Actualizar aplicación',
|
||||
'iTopUpdate:UI:MaintenanceModeActive' => 'La aplicación está actualmente en mantenimiento, ningún usuario puede acceder. UStede debe ejecutar la instalación o restaturar la aplicación para regresar al modo normal.',
|
||||
'itop-core-update:UI:UpdateDone' => 'Application Upgrade~~',
|
||||
'itop-core-update:UI:UpdateDone' => 'Actualización realizada',
|
||||
|
||||
'itop-core-update/Operation:SelectUpdateFile/Title' => 'Application Upgrade~~',
|
||||
'itop-core-update/Operation:SelectUpdateFile/Title' => 'Seleccionar archivo',
|
||||
'itop-core-update/Operation:ConfirmUpdate/Title' => 'Confirmar actualización de la aplicación',
|
||||
'itop-core-update/Operation:UpdateCoreFiles/Title' => 'Actualizando aplicación',
|
||||
'itop-core-update/Operation:UpdateDone/Title' => 'Actualización de aplicación terminada',
|
||||
@@ -60,21 +61,21 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'iTopUpdate:UI:DoBackup:Label' => 'Respaldo de archivos y base de datos',
|
||||
'iTopUpdate:UI:DoBackup:Warning' => 'El respaldo no está recomendado por el limitado espacio en el dispositivo',
|
||||
|
||||
'iTopUpdate:UI:DiskFreeSpace' => 'Espaciolibre en el dispositivo',
|
||||
'iTopUpdate:UI:ItopDiskSpace' => 'Espacio en diso de iTop',
|
||||
'iTopUpdate:UI:DBDiskSpace' => 'Espacio en diso de base de datos',
|
||||
'iTopUpdate:UI:FileUploadMaxSize' => 'Máximo tamaño de subida de archivos',
|
||||
'iTopUpdate:UI:DiskFreeSpace' => 'Espacio libre en el dispositivo',
|
||||
'iTopUpdate:UI:ItopDiskSpace' => 'Espacio en disco de iTop',
|
||||
'iTopUpdate:UI:DBDiskSpace' => 'Espacio en disco de base de datos',
|
||||
'iTopUpdate:UI:FileUploadMaxSize' => 'Máximo tamaño de carga de archivos',
|
||||
|
||||
'iTopUpdate:UI:PostMaxSize' => 'Valor post_max_size en PHP ini: %1$s~~',
|
||||
'iTopUpdate:UI:UploadMaxFileSize' => 'Valor upload_max_filesize en PHP ini: %1$s~~',
|
||||
'iTopUpdate:UI:PostMaxSize' => 'Valor post_max_size en PHP ini: %1$s',
|
||||
'iTopUpdate:UI:UploadMaxFileSize' => 'Valor upload_max_filesize en PHP ini: %1$s',
|
||||
|
||||
'iTopUpdate:UI:CanCoreUpdate:Loading' => 'Revisando sistema de archivos',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Error' => 'La revisión del sistema de archivos falló (%1$s)',
|
||||
'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'La revisión del sistema de archivos falló (Archivo no existe %1$s)~~',
|
||||
'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'La revisión del sistema de archivos falló (Archivo no existe %1$s)',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Failed' => 'La revisión del sistema de archivos falló',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Yes' => 'La aplicación puede ser actualizada',
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'La aplicación no puede ser actualizada: %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Advertencia: la actualización de la aplicación puede fallar: %1$s',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Listo para empezar',
|
||||
@@ -90,12 +91,12 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
|
||||
// Errors
|
||||
'iTopUpdate:Error:MissingFunction' => 'Imposible comenzar actualización, función no disponible',
|
||||
'iTopUpdate:Error:MissingFile' => 'Archivo no encontrado: %1$s~~',
|
||||
'iTopUpdate:Error:MissingFile' => 'Archivo no encontrado: %1$s',
|
||||
'iTopUpdate:Error:CorruptedFile' => 'El archivo %1$s está corrupto',
|
||||
'iTopUpdate:Error:BadFileFormat' => 'El archivo de actualización no es un archivo zip',
|
||||
'iTopUpdate:Error:BadFileContent' => 'El archivo de actualización no es correcto',
|
||||
'iTopUpdate:Error:BadItopProduct' => 'El archivo de actualización no es compatible con su producto',
|
||||
'iTopUpdate:Error:Copy' => 'Error, no puedo copiar \'%1$s\' a \'%2$s\'~~',
|
||||
'iTopUpdate:Error:Copy' => 'Error, no puedo copiar \'%1$s\' a \'%2$s\'',
|
||||
'iTopUpdate:Error:FileNotFound' => 'Archivo no encontrado',
|
||||
'iTopUpdate:Error:NoFile' => 'Archivo no seleccionado',
|
||||
'iTopUpdate:Error:InvalidToken' => 'Token inválido',
|
||||
@@ -104,7 +105,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
|
||||
'iTopUpdate:UI:RestoreArchive' => 'Usted puede restaurar su aplicación desde el archivo \'%1$s\'',
|
||||
'iTopUpdate:UI:RestoreBackup' => 'Usted puede restaurar la base de datos desde \'%1$s\'',
|
||||
'iTopUpdate:UI:UpdateDone' => 'Actualización exitosa',
|
||||
'iTopUpdate:UI:UpdateDone' => 'Actualización éxitosa',
|
||||
'Menu:iTopUpdate' => 'Actualización de aplicación',
|
||||
'Menu:iTopUpdate+' => 'Actualización de aplicación',
|
||||
|
||||
@@ -112,7 +113,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Class:ModuleInstallation/Attribute:installed' => 'Instalado en',
|
||||
'Class:ModuleInstallation/Attribute:name' => 'Nombre',
|
||||
'Class:ModuleInstallation/Attribute:version' => 'Versión',
|
||||
'Class:ModuleInstallation/Attribute:comment' => 'Commentario',
|
||||
'Class:ModuleInstallation/Attribute:comment' => 'Comentario',
|
||||
));
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-core-update/2.7.4',
|
||||
'itop-core-update/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -24,33 +24,33 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'iTopUpdate:UI:PageTitle' => '应用升级',
|
||||
'itop-core-update:UI:SelectUpdateFile' => '应用升级',
|
||||
'itop-core-update:UI:ConfirmUpdate' => ' 升级',
|
||||
'itop-core-update:UI:UpdateCoreFiles' => 'Application Upgrade~~',
|
||||
'iTopUpdate:UI:MaintenanceModeActive' => 'The application is currently under maintenance, no user can access the application. You have to run a setup or restore the application archive to return in normal mode.~~',
|
||||
'itop-core-update:UI:UpdateDone' => 'Application Upgrade~~',
|
||||
'itop-core-update:UI:UpdateCoreFiles' => '应用升级',
|
||||
'iTopUpdate:UI:MaintenanceModeActive' => '应用正处于维护状态, 用户无法登录系统. 您必须运行安装向导或从备份中还原才能恢复到正常状态.',
|
||||
'itop-core-update:UI:UpdateDone' => '应用升级',
|
||||
|
||||
'itop-core-update/Operation:SelectUpdateFile/Title' => 'Application Upgrade~~',
|
||||
'itop-core-update/Operation:ConfirmUpdate/Title' => 'Confirm Application Upgrade~~',
|
||||
'itop-core-update/Operation:UpdateCoreFiles/Title' => 'Application Upgrading~~',
|
||||
'itop-core-update/Operation:UpdateDone/Title' => 'Application Upgrade Done~~',
|
||||
'itop-core-update/Operation:SelectUpdateFile/Title' => '应用升级',
|
||||
'itop-core-update/Operation:ConfirmUpdate/Title' => '确认升级',
|
||||
'itop-core-update/Operation:UpdateCoreFiles/Title' => '应用升级进行中',
|
||||
'itop-core-update/Operation:UpdateDone/Title' => '升级完毕',
|
||||
|
||||
'iTopUpdate:UI:SelectUpdateFile' => '请选择要上传的升级文件',
|
||||
'iTopUpdate:UI:CheckUpdate' => '校验升级文件',
|
||||
'iTopUpdate:UI:ConfirmInstallFile' => '即将安装 %1$s',
|
||||
'iTopUpdate:UI:DoUpdate' => '升级',
|
||||
'iTopUpdate:UI:CurrentVersion' => '当前已安装的版本',
|
||||
'iTopUpdate:UI:NewVersion' => 'Newly installed version~~',
|
||||
'iTopUpdate:UI:NewVersion' => '新安装的版本',
|
||||
'iTopUpdate:UI:Back' => '返回',
|
||||
'iTopUpdate:UI:Cancel' => '取消',
|
||||
'iTopUpdate:UI:Continue' => '继续',
|
||||
'iTopUpdate:UI:RunSetup' => 'Run Setup~~',
|
||||
'iTopUpdate:UI:RunSetup' => '运行安装向导',
|
||||
'iTopUpdate:UI:WithDBBackup' => '数据库备份',
|
||||
'iTopUpdate:UI:WithFilesBackup' => '应用文件备份',
|
||||
'iTopUpdate:UI:WithoutBackup' => 'No backup is planned~~',
|
||||
'iTopUpdate:UI:WithoutBackup' => '暂时没有备份计划',
|
||||
'iTopUpdate:UI:Backup' => '升级之前执行备份',
|
||||
'iTopUpdate:UI:DoFilesArchive' => '打包应用文件',
|
||||
'iTopUpdate:UI:UploadArchive' => 'Select a package to upload~~',
|
||||
'iTopUpdate:UI:ServerFile' => 'Path of a package already on the server~~',
|
||||
'iTopUpdate:UI:WarningReadOnlyDuringUpdate' => 'During the upgrade, the application will be read-only.~~',
|
||||
'iTopUpdate:UI:UploadArchive' => '请选择要上传的包',
|
||||
'iTopUpdate:UI:ServerFile' => '软件包已在服务器上',
|
||||
'iTopUpdate:UI:WarningReadOnlyDuringUpdate' => '在升级期间, 应用会变成只读状态.',
|
||||
|
||||
'iTopUpdate:UI:Status' => '状态',
|
||||
'iTopUpdate:UI:Action' => '升级',
|
||||
@@ -74,7 +74,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:Failed' => '文件系统检查失败',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Yes' => '应用无法升级',
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => '应用无法升级: %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => '警告: 应用升级可能会失败: %1$s',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => '准备开始',
|
||||
@@ -82,9 +82,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'iTopUpdate:UI:SetupMessage:Backup' => '数据库备份',
|
||||
'iTopUpdate:UI:SetupMessage:FilesArchive' => '打包应用文件',
|
||||
'iTopUpdate:UI:SetupMessage:CopyFiles' => '复制新文件',
|
||||
'iTopUpdate:UI:SetupMessage:CheckCompile' => 'Check application upgrade~~',
|
||||
'iTopUpdate:UI:SetupMessage:CheckCompile' => '检查更新',
|
||||
'iTopUpdate:UI:SetupMessage:Compile' => '升级应用程序和数据库',
|
||||
'iTopUpdate:UI:SetupMessage:UpdateDatabase' => 'Upgrade database~~',
|
||||
'iTopUpdate:UI:SetupMessage:UpdateDatabase' => '升级数据库',
|
||||
'iTopUpdate:UI:SetupMessage:ExitMaintenance' => '正在退出维护模式',
|
||||
'iTopUpdate:UI:SetupMessage:UpdateDone' => '升级完成',
|
||||
|
||||
@@ -109,7 +109,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Menu:iTopUpdate+' => '应用升级',
|
||||
|
||||
// Missing itop entries
|
||||
'Class:ModuleInstallation/Attribute:installed' => '安装在',
|
||||
'Class:ModuleInstallation/Attribute:installed' => '安装时间',
|
||||
'Class:ModuleInstallation/Attribute:name' => '名称',
|
||||
'Class:ModuleInstallation/Attribute:version' => '版本',
|
||||
'Class:ModuleInstallation/Attribute:comment' => '备注',
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
@@ -20,6 +21,6 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
// Dictionary entries go here
|
||||
));
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-datacenter-mgmt/2.7.4',
|
||||
'itop-datacenter-mgmt/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
@@ -20,6 +21,6 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
// Dictionary entries go here
|
||||
));
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-endusers-devices/2.7.4',
|
||||
'itop-endusers-devices/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
@@ -20,11 +21,11 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
// Errors
|
||||
'FilesInformation:Error:MissingFile' => 'Missing file: %1$s~~',
|
||||
'FilesInformation:Error:CorruptedFile' => 'File %1$s is corrupted~~',
|
||||
'FilesInformation:Error:CantWriteToFile' => 'Can not write to file %1$s~~',
|
||||
'FilesInformation:Error:MissingFile' => 'Archivo faltante: %1$s',
|
||||
'FilesInformation:Error:CorruptedFile' => 'El archivo %1$s está corrupto',
|
||||
'FilesInformation:Error:CantWriteToFile' => 'No se puede escribir al archivo %1$s',
|
||||
));
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-files-information/2.7.4',
|
||||
'itop-files-information/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-full-itil/2.7.4',
|
||||
'itop-full-itil/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
@@ -20,7 +21,7 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
// Dictionary entries go here
|
||||
'Menu:iTopHub' => 'iTop Hub',
|
||||
'Menu:iTopHub:Register' => 'Conectar a iTop Hub',
|
||||
@@ -47,7 +48,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'iTopHub:Landing:Install' => 'Instalando extensiones...',
|
||||
'iTopHub:CompiledOK' => 'Compilación éxitosa.',
|
||||
'iTopHub:ConfigurationSafelyReverted' => 'Error detectado durante la instalación!<br/>La configuración de iTop NO fue modificada.',
|
||||
'iTopHub:FailAuthent' => 'Authentication failed for this action.~~',
|
||||
'iTopHub:FailAuthent' => 'Autenticación fallida para esta acciónn.',
|
||||
|
||||
'iTopHub:InstalledExtensions' => 'Extensiones instaladas en esta instancia',
|
||||
'iTopHub:ExtensionCategory:Manual' => 'Extensiones instaladas manualmente',
|
||||
@@ -75,11 +76,11 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'iTopHub:InstallationProgress:ExtensionsInstallation' => 'Instalación de extensiones',
|
||||
'iTopHub:InstallationEffect:MissingDependencies' => 'Esta extensión no puede ser instalad porque no cumple con las dependencias.',
|
||||
'iTopHub:InstallationEffect:MissingDependencies_Details' => 'La extensión require el/los módulo(s): %1$s',
|
||||
'iTopHub:InstallationProgress:InstallationSuccessful' => 'Instalación éxitosa!',
|
||||
'iTopHub:InstallationProgress:InstallationSuccessful' => '¡Instalación éxitosa!',
|
||||
|
||||
'iTopHub:InstallationStatus:Installed_Version' => '%1$s version: %2$s.',
|
||||
'iTopHub:InstallationStatus:Installed_Version' => '%1$s versión: %2$s.',
|
||||
'iTopHub:InstallationStatus:Installed' => 'Instalada',
|
||||
'iTopHub:InstallationStatus:Version_NotInstalled' => 'Version %1$s <b>NO</b> está instalada.',
|
||||
'iTopHub:InstallationStatus:Version_NotInstalled' => 'Versión %1$s <b>NO</b> está instalada.',
|
||||
));
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-hub-connector/2.7.4',
|
||||
'itop-hub-connector/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
//
|
||||
// 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
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Menu:IncidentManagement' => 'Administración de Incidentes',
|
||||
'Menu:IncidentManagement+' => 'Administración de Incidentes',
|
||||
'Menu:Incident:Overview' => 'Resumen de Incidentes',
|
||||
@@ -61,7 +61,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
// Class: Incident
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:Incident' => 'Incidente',
|
||||
'Class:Incident+' => 'Incidente',
|
||||
'Class:Incident/Attribute:status' => 'Estatus',
|
||||
@@ -192,8 +192,8 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Class:Incident/Attribute:parent_change_ref+' => 'Ref. Cambio Padre',
|
||||
'Class:Incident/Attribute:parent_problem_id' => 'Problema Padre',
|
||||
'Class:Incident/Attribute:parent_problem_id+' => 'Problema Padre',
|
||||
'Class:Incident/Attribute:parent_problem_ref' => 'Parent problem ref~~',
|
||||
'Class:Incident/Attribute:parent_problem_ref+' => '~~',
|
||||
'Class:Incident/Attribute:parent_problem_ref' => 'Ref. Problema Padre',
|
||||
'Class:Incident/Attribute:parent_problem_ref+' => '',
|
||||
'Class:Incident/Attribute:related_request_list' => 'Requerimientos Relacionados',
|
||||
'Class:Incident/Attribute:related_request_list+' => 'Requerimientos Relacionados',
|
||||
'Class:Incident/Attribute:child_incidents_list' => 'Incidentes Hijos',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-incident-mgmt-itil/2.7.4',
|
||||
'itop-incident-mgmt-itil/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -144,9 +144,9 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Class:Incident/Attribute:cumulatedpending' => 'Pendências acumuladas',
|
||||
'Class:Incident/Attribute:cumulatedpending+' => '',
|
||||
'Class:Incident/Attribute:tto' => 'TTO',
|
||||
'Class:Incident/Attribute:tto+' => '',
|
||||
'Class:Incident/Attribute:tto+' => 'Tempo de atribuição',
|
||||
'Class:Incident/Attribute:ttr' => 'TTR',
|
||||
'Class:Incident/Attribute:ttr+' => '',
|
||||
'Class:Incident/Attribute:ttr+' => 'Tempo de resolução',
|
||||
'Class:Incident/Attribute:tto_escalation_deadline' => 'Prazo determinado TTO',
|
||||
'Class:Incident/Attribute:tto_escalation_deadline+' => '',
|
||||
'Class:Incident/Attribute:sla_tto_passed' => 'SLA TTO passou',
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
//
|
||||
// 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
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*/
|
||||
@@ -47,7 +47,7 @@
|
||||
//
|
||||
// Class: KnownError
|
||||
//
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:KnownError' => 'Error Conocido',
|
||||
'Class:KnownError+' => 'Documentación para un error conocido',
|
||||
'Class:KnownError/Attribute:name' => 'Nombre',
|
||||
@@ -96,7 +96,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
// Class: lnkErrorToFunctionalCI
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:lnkErrorToFunctionalCI' => 'Relación Error Conocido y EC Funcional',
|
||||
'Class:lnkErrorToFunctionalCI+' => 'Relación Error Conocido y EC Funcional',
|
||||
'Class:lnkErrorToFunctionalCI/Attribute:functionalci_id' => 'EC',
|
||||
@@ -115,7 +115,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
// Class: lnkDocumentToError
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:lnkDocumentToError' => 'Relación Documento y Error Conocido',
|
||||
'Class:lnkDocumentToError+' => 'Relación Documento y Error Conocido',
|
||||
'Class:lnkDocumentToError/Attribute:document_id' => 'Documento',
|
||||
@@ -134,7 +134,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
// Class: FAQ
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:FAQ' => 'Preguntas y Respuestas Frecuentes',
|
||||
'Class:FAQ+' => 'Preguntas y Respuestas Frecuentes',
|
||||
'Class:FAQ/Attribute:title' => 'Asunto',
|
||||
@@ -151,14 +151,14 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Class:FAQ/Attribute:error_code+' => 'Código de Error',
|
||||
'Class:FAQ/Attribute:key_words' => 'Palabras Clave',
|
||||
'Class:FAQ/Attribute:key_words+' => 'Palabras Clave',
|
||||
'Class:FAQ/Attribute:domains' => 'Domains~~',
|
||||
'Class:FAQ/Attribute:domains' => 'Ámbito',
|
||||
));
|
||||
|
||||
//
|
||||
// Class: FAQCategory
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:FAQCategory' => 'Categoría de Preguntas y Respuesta Frecuentes',
|
||||
'Class:FAQCategory+' => 'Categoría de Preguntas y Respuesta Frecuentes',
|
||||
'Class:FAQCategory/Attribute:name' => 'Nombre',
|
||||
@@ -166,7 +166,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Class:FAQCategory/Attribute:faq_list' => 'FAQs',
|
||||
'Class:FAQCategory/Attribute:faq_list+' => 'FAQs',
|
||||
));
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Menu:ProblemManagement' => 'Gestión de problemas',
|
||||
'Menu:ProblemManagement+' => 'Gestión de problemas',
|
||||
'Menu:Problem:Shortcuts' => 'Acceso Rápido',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-knownerror-mgmt/2.7.4',
|
||||
'itop-knownerror-mgmt/2.7.6',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -63,6 +63,8 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Portal:File:DisplayInfo+' => '%1$s (%2$s) <a href="%3$s" class="file_open_link" target="_blank">Open</a> / <a href="%4$s" class="file_download_link">Download</a>',
|
||||
'Portal:Calendar-FirstDayOfWeek' => 'en-us', //work with moment.js locales
|
||||
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost',
|
||||
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.',
|
||||
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.',
|
||||
));
|
||||
|
||||
// UserProfile brick
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2020 Combodo SARL
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
@@ -17,7 +21,7 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
// Portal
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Page:DefaultTitle' => '%1$s - Portal de Usuario',
|
||||
'Page:PleaseWait' => 'Por favor espere...',
|
||||
'Page:Home' => 'Inicio',
|
||||
@@ -33,7 +37,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Portal:Button:Delete' => 'Borrar',
|
||||
'Portal:EnvironmentBanner:Title' => 'Se encuentra en modo <strong>%1$s</strong>',
|
||||
'Portal:EnvironmentBanner:GoToProduction' => 'Regresar a modo PRODUCTION',
|
||||
'Error:HTTP:400' => 'Bad request~~',
|
||||
'Error:HTTP:400' => 'Solicitud Incorrecta',
|
||||
'Error:HTTP:401' => 'Autenticación',
|
||||
'Error:HTTP:404' => 'Página no encontrada',
|
||||
'Error:HTTP:500' => '¡Vaya! Ha ocurrido un error.',
|
||||
@@ -59,13 +63,13 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Portal:Attachments:DropZone:Message' => 'Agrega tus archivos para agregarlos como documentos adjuntos',
|
||||
'Portal:File:None' => 'No hay archivo',
|
||||
'Portal:File:DisplayInfo' => '<a href="%2$s" class="file_download_link">%1$s</a>',
|
||||
'Portal:File:DisplayInfo+' => '%1$s (%2$s) <a href="%3$s" class="file_open_link" target="_blank">Abierto</a> / <a href="%4$s" class="file_download_link">Download</a>',
|
||||
'Portal:File:DisplayInfo+' => '%1$s (%2$s) <a href="%3$s" class="file_open_link" target="_blank">Abierto</a> / <a href="%4$s" class="file_download_link">Descarga</a>',
|
||||
'Portal:Calendar-FirstDayOfWeek' => 'es', //work with moment.js locales
|
||||
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
||||
'Portal:Form:Close:Warning' => '¿Desea abandorar esta forma? Datos modificados se perderan',
|
||||
));
|
||||
|
||||
// UserProfile brick
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Brick:Portal:UserProfile:Name' => 'Perfil del usuario',
|
||||
'Brick:Portal:UserProfile:Navigation:Dropdown:MyProfil' => 'Mi perfil',
|
||||
'Brick:Portal:UserProfile:Navigation:Dropdown:Logout' => 'Cerrar Sesión',
|
||||
@@ -79,12 +83,12 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
));
|
||||
|
||||
// AggregatePageBrick
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Brick:Portal:AggregatePage:DefaultTitle' => 'Tablero de Control',
|
||||
));
|
||||
|
||||
// BrowseBrick brick
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Brick:Portal:Browse:Name' => 'Buscar en todos los elementos',
|
||||
'Brick:Portal:Browse:Mode:List' => 'Lista',
|
||||
'Brick:Portal:Browse:Mode:Tree' => 'Árbol',
|
||||
@@ -100,7 +104,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
));
|
||||
|
||||
// ManageBrick brick
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Brick:Portal:Manage:Name' => 'Administrar elementos',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Sin elementos',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Acciones',
|
||||
@@ -118,29 +122,29 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Brick:Portal:Object:Name' => 'Objecto',
|
||||
'Brick:Portal:Object:Form:Create:Title' => 'Nuevo %1$s',
|
||||
'Brick:Portal:Object:Form:Edit:Title' => 'Actualizando %2$s (%1$s)',
|
||||
'Brick:Portal:Object:Form:View:Title' => '%1$s : %2$s',
|
||||
'Brick:Portal:Object:Form:Stimulus:Title' => 'Por favor, proporcione la siguiente información:',
|
||||
'Brick:Portal:Object:Form:Message:Saved' => 'Guardado',
|
||||
'Brick:Portal:Object:Form:Message:ObjectSaved' => '%1$s guardado~~',
|
||||
'Brick:Portal:Object:Form:Message:ObjectSaved' => '%1$s guardado',
|
||||
'Brick:Portal:Object:Search:Regular:Title' => 'Selección %1$s (%2$s)',
|
||||
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Selección %1$s (%2$s)',
|
||||
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
|
||||
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
|
||||
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
|
||||
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s',
|
||||
'Brick:Portal:Object:Copy:Tooltip' => 'Copiar liga al objeto',
|
||||
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copiado'
|
||||
));
|
||||
|
||||
// CreateBrick brick
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Brick:Portal:Create:Name' => 'Creación rápida',
|
||||
'Brick:Portal:Create:ChooseType' => 'Por favor, seleccione un tipo',
|
||||
));
|
||||
|
||||
// Filter brick
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Brick:Portal:Filter:Name' => 'Prefiltre un bloquek',
|
||||
'Brick:Portal:Filter:SearchInput:Placeholder' => 'Ej.:. Conectar a WiFi',
|
||||
'Brick:Portal:Filter:SearchInput:Submit' => 'Buscar',
|
||||
|
||||
@@ -62,6 +62,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Portal:File:DisplayInfo+' => '%1$s (%2$s) <a href="%3$s" class="file_open_link" target="_blank">Ouvrir</a> / <a href="%4$s" class="file_download_link">Télécharger</a>',
|
||||
'Portal:Calendar-FirstDayOfWeek' => 'fr', //work with moment.js locales
|
||||
'Portal:Form:Close:Warning' => 'Voulez-vous quitter ce formulaire ? Les données saisies seront perdues',
|
||||
'Portal:Error:ObjectCannotBeCreated' => 'Erreur: L\'objet n\'a pas été créé. Vérifiez les objets liés et les attachements avant de soumettre à nouveau le formulaire.',
|
||||
'Portal:Error:ObjectCannotBeUpdated' => 'Erreur: L\'objet n\'a pas été modifié. Vérifiez les objets liés et les attachements avant de soumettre à nouveau le formulaire.',
|
||||
));
|
||||
|
||||
// UserProfile brick
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-portal-base/2.7.4', array(
|
||||
'itop-portal-base/2.7.6', array(
|
||||
// Identification
|
||||
'label' => 'Portal Development Library',
|
||||
'category' => 'Portal',
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,50 @@
|
||||
# FixedHeader for DataTables
|
||||
|
||||
This package contains distribution files for the [FixedHeader extension](https://datatables.net/extensions/fixedheader) for [DataTables](https://datatables.net/). Only the core software for this library is contained in this package - to be correctly styled, a styling package for FixedHeader must also be included. Styling options include DataTable's native styling, [Bootstrap](http://getbootstrap.com) and [Foundation](http://foundation.zurb.com/).
|
||||
|
||||
When displaying large amounts of data in a table, it can often be useful for the end user to have the column titles always visible. This is particularly true if using DataTables with pagination disabled, or the display length is set to a high value. The FixedHeader extension provides this ability.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
### Browser
|
||||
|
||||
For inclusion of this library using a standard `<script>` tag, rather than using this package, it is recommended that you use the [DataTables download builder](//datatables.net/download) which can create CDN or locally hosted packages for you, will all dependencies satisfied.
|
||||
|
||||
### npm
|
||||
|
||||
```
|
||||
npm install datatables.net-fixedheader
|
||||
```
|
||||
|
||||
```
|
||||
var $ = require( 'jquery' );
|
||||
require( 'datatables.net-fixedheader' )( window, $ );
|
||||
```
|
||||
|
||||
### bower
|
||||
|
||||
```
|
||||
bower install --save datatables.net-fixedheader
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
Full documentation of the DataTables options, API and plug-in interface are available on the DOCS_LINK. The site also contains information on the wide variety of plug-ins that are available for DataTables, which can be used to enhance and customise your table even further.
|
||||
|
||||
|
||||
## Bug / Support
|
||||
|
||||
Support for DataTables is available through the [DataTables forums](//datatables.net/forums) and [commercial support options](//datatables.net/support) are available.
|
||||
|
||||
|
||||
### Contributing
|
||||
|
||||
If you are thinking of contributing code to DataTables, first of all, thank you! All fixes, patches and enhancements to DataTables are very warmly welcomed. This repository is a distribution repo, so patches and issues sent to this repo will not be accepted. Instead, please direct pull requests to the [DataTables/FixedHeader](http://github.com/DataTables/FixedHeader). For issues / bugs, please direct your questions to the [DataTables forums](//datatables.net/forums).
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This software is released under the [MIT license](//datatables.net/license). You are free to use, modify and distribute this software, but all copyright information must remain.
|
||||
@@ -0,0 +1,736 @@
|
||||
/*! FixedHeader 3.1.8
|
||||
* ©2009-2021 SpryMedia Ltd - datatables.net/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @summary FixedHeader
|
||||
* @description Fix a table's header or footer, so it is always visible while
|
||||
* scrolling
|
||||
* @version 3.1.8
|
||||
* @file dataTables.fixedHeader.js
|
||||
* @author SpryMedia Ltd (www.sprymedia.co.uk)
|
||||
* @contact www.sprymedia.co.uk/contact
|
||||
* @copyright Copyright 2009-2021 SpryMedia Ltd.
|
||||
*
|
||||
* This source file is free software, available under the following license:
|
||||
* MIT license - http://datatables.net/license/mit
|
||||
*
|
||||
* This source file 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 license files for details.
|
||||
*
|
||||
* For details please refer to: http://www.datatables.net
|
||||
*/
|
||||
|
||||
(function( factory ){
|
||||
if ( typeof define === 'function' && define.amd ) {
|
||||
// AMD
|
||||
define( ['jquery', 'datatables.net'], function ( $ ) {
|
||||
return factory( $, window, document );
|
||||
} );
|
||||
}
|
||||
else if ( typeof exports === 'object' ) {
|
||||
// CommonJS
|
||||
module.exports = function (root, $) {
|
||||
if ( ! root ) {
|
||||
root = window;
|
||||
}
|
||||
|
||||
if ( ! $ || ! $.fn.dataTable ) {
|
||||
$ = require('datatables.net')(root, $).$;
|
||||
}
|
||||
|
||||
return factory( $, root, root.document );
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Browser
|
||||
factory( jQuery, window, document );
|
||||
}
|
||||
}(function( $, window, document, undefined ) {
|
||||
'use strict';
|
||||
var DataTable = $.fn.dataTable;
|
||||
|
||||
|
||||
var _instCounter = 0;
|
||||
|
||||
var FixedHeader = function ( dt, config ) {
|
||||
// Sanity check - you just know it will happen
|
||||
if ( ! (this instanceof FixedHeader) ) {
|
||||
throw "FixedHeader must be initialised with the 'new' keyword.";
|
||||
}
|
||||
|
||||
// Allow a boolean true for defaults
|
||||
if ( config === true ) {
|
||||
config = {};
|
||||
}
|
||||
|
||||
dt = new DataTable.Api( dt );
|
||||
|
||||
this.c = $.extend( true, {}, FixedHeader.defaults, config );
|
||||
|
||||
this.s = {
|
||||
dt: dt,
|
||||
position: {
|
||||
theadTop: 0,
|
||||
tbodyTop: 0,
|
||||
tfootTop: 0,
|
||||
tfootBottom: 0,
|
||||
width: 0,
|
||||
left: 0,
|
||||
tfootHeight: 0,
|
||||
theadHeight: 0,
|
||||
windowHeight: $(window).height(),
|
||||
visible: true
|
||||
},
|
||||
headerMode: null,
|
||||
footerMode: null,
|
||||
autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
|
||||
namespace: '.dtfc'+(_instCounter++),
|
||||
scrollLeft: {
|
||||
header: -1,
|
||||
footer: -1
|
||||
},
|
||||
enable: true
|
||||
};
|
||||
|
||||
this.dom = {
|
||||
floatingHeader: null,
|
||||
thead: $(dt.table().header()),
|
||||
tbody: $(dt.table().body()),
|
||||
tfoot: $(dt.table().footer()),
|
||||
header: {
|
||||
host: null,
|
||||
floating: null,
|
||||
placeholder: null
|
||||
},
|
||||
footer: {
|
||||
host: null,
|
||||
floating: null,
|
||||
placeholder: null
|
||||
}
|
||||
};
|
||||
|
||||
this.dom.header.host = this.dom.thead.parent();
|
||||
this.dom.footer.host = this.dom.tfoot.parent();
|
||||
|
||||
var dtSettings = dt.settings()[0];
|
||||
if ( dtSettings._fixedHeader ) {
|
||||
throw "FixedHeader already initialised on table "+dtSettings.nTable.id;
|
||||
}
|
||||
|
||||
dtSettings._fixedHeader = this;
|
||||
|
||||
this._constructor();
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Variable: FixedHeader
|
||||
* Purpose: Prototype for FixedHeader
|
||||
* Scope: global
|
||||
*/
|
||||
$.extend( FixedHeader.prototype, {
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* API methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* Kill off FH and any events
|
||||
*/
|
||||
destroy: function () {
|
||||
this.s.dt.off( '.dtfc' );
|
||||
$(window).off( this.s.namespace );
|
||||
|
||||
if ( this.c.header ) {
|
||||
this._modeChange( 'in-place', 'header', true );
|
||||
}
|
||||
|
||||
if ( this.c.footer && this.dom.tfoot.length ) {
|
||||
this._modeChange( 'in-place', 'footer', true );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable / disable the fixed elements
|
||||
*
|
||||
* @param {boolean} enable `true` to enable, `false` to disable
|
||||
*/
|
||||
enable: function ( enable, update )
|
||||
{
|
||||
this.s.enable = enable;
|
||||
|
||||
if ( update || update === undefined ) {
|
||||
this._positions();
|
||||
this._scroll( true );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get enabled status
|
||||
*/
|
||||
enabled: function ()
|
||||
{
|
||||
return this.s.enable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set header offset
|
||||
*
|
||||
* @param {int} new value for headerOffset
|
||||
*/
|
||||
headerOffset: function ( offset )
|
||||
{
|
||||
if ( offset !== undefined ) {
|
||||
this.c.headerOffset = offset;
|
||||
this.update();
|
||||
}
|
||||
|
||||
return this.c.headerOffset;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set footer offset
|
||||
*
|
||||
* @param {int} new value for footerOffset
|
||||
*/
|
||||
footerOffset: function ( offset )
|
||||
{
|
||||
if ( offset !== undefined ) {
|
||||
this.c.footerOffset = offset;
|
||||
this.update();
|
||||
}
|
||||
|
||||
return this.c.footerOffset;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Recalculate the position of the fixed elements and force them into place
|
||||
*/
|
||||
update: function ()
|
||||
{
|
||||
var table = this.s.dt.table().node();
|
||||
|
||||
if ( $(table).is(':visible') ) {
|
||||
this.enable( true, false );
|
||||
}
|
||||
else {
|
||||
this.enable( false, false );
|
||||
}
|
||||
|
||||
this._positions();
|
||||
this._scroll( true );
|
||||
},
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Constructor
|
||||
*/
|
||||
|
||||
/**
|
||||
* FixedHeader constructor - adding the required event listeners and
|
||||
* simple initialisation
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_constructor: function ()
|
||||
{
|
||||
var that = this;
|
||||
var dt = this.s.dt;
|
||||
|
||||
$(window)
|
||||
.on( 'scroll'+this.s.namespace, function () {
|
||||
that._scroll();
|
||||
} )
|
||||
.on( 'resize'+this.s.namespace, DataTable.util.throttle( function () {
|
||||
that.s.position.windowHeight = $(window).height();
|
||||
that.update();
|
||||
}, 50 ) );
|
||||
|
||||
var autoHeader = $('.fh-fixedHeader');
|
||||
if ( ! this.c.headerOffset && autoHeader.length ) {
|
||||
this.c.headerOffset = autoHeader.outerHeight();
|
||||
}
|
||||
|
||||
var autoFooter = $('.fh-fixedFooter');
|
||||
if ( ! this.c.footerOffset && autoFooter.length ) {
|
||||
this.c.footerOffset = autoFooter.outerHeight();
|
||||
}
|
||||
|
||||
dt.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc responsive-display.dt.dtfc', function () {
|
||||
that.update();
|
||||
} );
|
||||
|
||||
dt.on( 'destroy.dtfc', function () {
|
||||
that.destroy();
|
||||
} );
|
||||
|
||||
this._positions();
|
||||
this._scroll();
|
||||
},
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Private methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* Clone a fixed item to act as a place holder for the original element
|
||||
* which is moved into a clone of the table element, and moved around the
|
||||
* document to give the fixed effect.
|
||||
*
|
||||
* @param {string} item 'header' or 'footer'
|
||||
* @param {boolean} force Force the clone to happen, or allow automatic
|
||||
* decision (reuse existing if available)
|
||||
* @private
|
||||
*/
|
||||
_clone: function ( item, force )
|
||||
{
|
||||
var dt = this.s.dt;
|
||||
var itemDom = this.dom[ item ];
|
||||
var itemElement = item === 'header' ?
|
||||
this.dom.thead :
|
||||
this.dom.tfoot;
|
||||
|
||||
if ( ! force && itemDom.floating ) {
|
||||
// existing floating element - reuse it
|
||||
itemDom.floating.removeClass( 'fixedHeader-floating fixedHeader-locked' );
|
||||
}
|
||||
else {
|
||||
if ( itemDom.floating ) {
|
||||
itemDom.placeholder.remove();
|
||||
this._unsize( item );
|
||||
itemDom.floating.children().detach();
|
||||
itemDom.floating.remove();
|
||||
}
|
||||
|
||||
itemDom.floating = $( dt.table().node().cloneNode( false ) )
|
||||
.css( 'table-layout', 'fixed' )
|
||||
.attr( 'aria-hidden', 'true' )
|
||||
.removeAttr( 'id' )
|
||||
.append( itemElement )
|
||||
.appendTo( 'body' );
|
||||
|
||||
// Insert a fake thead/tfoot into the DataTable to stop it jumping around
|
||||
itemDom.placeholder = itemElement.clone( false );
|
||||
itemDom.placeholder
|
||||
.find( '*[id]' )
|
||||
.removeAttr( 'id' );
|
||||
|
||||
itemDom.host.prepend( itemDom.placeholder );
|
||||
|
||||
// Clone widths
|
||||
this._matchWidths( itemDom.placeholder, itemDom.floating );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy widths from the cells in one element to another. This is required
|
||||
* for the footer as the footer in the main table takes its sizes from the
|
||||
* header columns. That isn't present in the footer so to have it still
|
||||
* align correctly, the sizes need to be copied over. It is also required
|
||||
* for the header when auto width is not enabled
|
||||
*
|
||||
* @param {jQuery} from Copy widths from
|
||||
* @param {jQuery} to Copy widths to
|
||||
* @private
|
||||
*/
|
||||
_matchWidths: function ( from, to ) {
|
||||
var get = function ( name ) {
|
||||
return $(name, from)
|
||||
.map( function () {
|
||||
return $(this).css('width').replace(/[^\d\.]/g, '') * 1;
|
||||
} ).toArray();
|
||||
};
|
||||
|
||||
var set = function ( name, toWidths ) {
|
||||
$(name, to).each( function ( i ) {
|
||||
$(this).css( {
|
||||
width: toWidths[i],
|
||||
minWidth: toWidths[i]
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
var thWidths = get( 'th' );
|
||||
var tdWidths = get( 'td' );
|
||||
|
||||
set( 'th', thWidths );
|
||||
set( 'td', tdWidths );
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove assigned widths from the cells in an element. This is required
|
||||
* when inserting the footer back into the main table so the size is defined
|
||||
* by the header columns and also when auto width is disabled in the
|
||||
* DataTable.
|
||||
*
|
||||
* @param {string} item The `header` or `footer`
|
||||
* @private
|
||||
*/
|
||||
_unsize: function ( item ) {
|
||||
var el = this.dom[ item ].floating;
|
||||
|
||||
if ( el && (item === 'footer' || (item === 'header' && ! this.s.autoWidth)) ) {
|
||||
$('th, td', el).css( {
|
||||
width: '',
|
||||
minWidth: ''
|
||||
} );
|
||||
}
|
||||
else if ( el && item === 'header' ) {
|
||||
$('th, td', el).css( 'min-width', '' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reposition the floating elements to take account of horizontal page
|
||||
* scroll
|
||||
*
|
||||
* @param {string} item The `header` or `footer`
|
||||
* @param {int} scrollLeft Document scrollLeft
|
||||
* @private
|
||||
*/
|
||||
_horizontal: function ( item, scrollLeft )
|
||||
{
|
||||
var itemDom = this.dom[ item ];
|
||||
var position = this.s.position;
|
||||
var lastScrollLeft = this.s.scrollLeft;
|
||||
|
||||
if ( itemDom.floating && lastScrollLeft[ item ] !== scrollLeft ) {
|
||||
itemDom.floating.css( 'left', position.left - scrollLeft );
|
||||
|
||||
lastScrollLeft[ item ] = scrollLeft;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Change from one display mode to another. Each fixed item can be in one
|
||||
* of:
|
||||
*
|
||||
* * `in-place` - In the main DataTable
|
||||
* * `in` - Floating over the DataTable
|
||||
* * `below` - (Header only) Fixed to the bottom of the table body
|
||||
* * `above` - (Footer only) Fixed to the top of the table body
|
||||
*
|
||||
* @param {string} mode Mode that the item should be shown in
|
||||
* @param {string} item 'header' or 'footer'
|
||||
* @param {boolean} forceChange Force a redraw of the mode, even if already
|
||||
* in that mode.
|
||||
* @private
|
||||
*/
|
||||
_modeChange: function ( mode, item, forceChange )
|
||||
{
|
||||
var dt = this.s.dt;
|
||||
var itemDom = this.dom[ item ];
|
||||
var position = this.s.position;
|
||||
|
||||
// It isn't trivial to add a !important css attribute...
|
||||
var importantWidth = function (w) {
|
||||
itemDom.floating.attr('style', function(i,s) {
|
||||
return (s || '') + 'width: '+w+'px !important;';
|
||||
});
|
||||
};
|
||||
|
||||
// Record focus. Browser's will cause input elements to loose focus if
|
||||
// they are inserted else where in the doc
|
||||
var tablePart = this.dom[ item==='footer' ? 'tfoot' : 'thead' ];
|
||||
var focus = $.contains( tablePart[0], document.activeElement ) ?
|
||||
document.activeElement :
|
||||
null;
|
||||
|
||||
if ( focus ) {
|
||||
focus.blur();
|
||||
}
|
||||
|
||||
if ( mode === 'in-place' ) {
|
||||
// Insert the header back into the table's real header
|
||||
if ( itemDom.placeholder ) {
|
||||
itemDom.placeholder.remove();
|
||||
itemDom.placeholder = null;
|
||||
}
|
||||
|
||||
this._unsize( item );
|
||||
|
||||
if ( item === 'header' ) {
|
||||
itemDom.host.prepend( tablePart );
|
||||
}
|
||||
else {
|
||||
itemDom.host.append( tablePart );
|
||||
}
|
||||
|
||||
if ( itemDom.floating ) {
|
||||
itemDom.floating.remove();
|
||||
itemDom.floating = null;
|
||||
}
|
||||
}
|
||||
else if ( mode === 'in' ) {
|
||||
// Remove the header from the read header and insert into a fixed
|
||||
// positioned floating table clone
|
||||
this._clone( item, forceChange );
|
||||
|
||||
itemDom.floating
|
||||
.addClass( 'fixedHeader-floating' )
|
||||
.css( item === 'header' ? 'top' : 'bottom', this.c[item+'Offset'] )
|
||||
.css( 'left', position.left+'px' );
|
||||
|
||||
importantWidth(position.width);
|
||||
|
||||
if ( item === 'footer' ) {
|
||||
itemDom.floating.css( 'top', '' );
|
||||
}
|
||||
}
|
||||
else if ( mode === 'below' ) { // only used for the header
|
||||
// Fix the position of the floating header at base of the table body
|
||||
this._clone( item, forceChange );
|
||||
|
||||
itemDom.floating
|
||||
.addClass( 'fixedHeader-locked' )
|
||||
.css( 'top', position.tfootTop - position.theadHeight )
|
||||
.css( 'left', position.left+'px' );
|
||||
|
||||
importantWidth(position.width);
|
||||
}
|
||||
else if ( mode === 'above' ) { // only used for the footer
|
||||
// Fix the position of the floating footer at top of the table body
|
||||
this._clone( item, forceChange );
|
||||
|
||||
itemDom.floating
|
||||
.addClass( 'fixedHeader-locked' )
|
||||
.css( 'top', position.tbodyTop )
|
||||
.css( 'left', position.left+'px' );
|
||||
|
||||
importantWidth(position.width);
|
||||
}
|
||||
|
||||
// Restore focus if it was lost
|
||||
if ( focus && focus !== document.activeElement ) {
|
||||
setTimeout( function () {
|
||||
focus.focus();
|
||||
}, 10 );
|
||||
}
|
||||
|
||||
this.s.scrollLeft.header = -1;
|
||||
this.s.scrollLeft.footer = -1;
|
||||
this.s[item+'Mode'] = mode;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cache the positional information that is required for the mode
|
||||
* calculations that FixedHeader performs.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_positions: function ()
|
||||
{
|
||||
var dt = this.s.dt;
|
||||
var table = dt.table();
|
||||
var position = this.s.position;
|
||||
var dom = this.dom;
|
||||
var tableNode = $(table.node());
|
||||
|
||||
// Need to use the header and footer that are in the main table,
|
||||
// regardless of if they are clones, since they hold the positions we
|
||||
// want to measure from
|
||||
var thead = tableNode.children('thead');
|
||||
var tfoot = tableNode.children('tfoot');
|
||||
var tbody = dom.tbody;
|
||||
|
||||
position.visible = tableNode.is(':visible');
|
||||
position.width = tableNode.outerWidth();
|
||||
position.left = tableNode.offset().left;
|
||||
position.theadTop = thead.offset().top;
|
||||
position.tbodyTop = tbody.offset().top;
|
||||
position.tbodyHeight = tbody.outerHeight();
|
||||
position.theadHeight = position.tbodyTop - position.theadTop;
|
||||
|
||||
if ( tfoot.length ) {
|
||||
position.tfootTop = tfoot.offset().top;
|
||||
position.tfootBottom = position.tfootTop + tfoot.outerHeight();
|
||||
position.tfootHeight = position.tfootBottom - position.tfootTop;
|
||||
}
|
||||
else {
|
||||
position.tfootTop = position.tbodyTop + tbody.outerHeight();
|
||||
position.tfootBottom = position.tfootTop;
|
||||
position.tfootHeight = position.tfootTop;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Mode calculation - determine what mode the fixed items should be placed
|
||||
* into.
|
||||
*
|
||||
* @param {boolean} forceChange Force a redraw of the mode, even if already
|
||||
* in that mode.
|
||||
* @private
|
||||
*/
|
||||
_scroll: function ( forceChange )
|
||||
{
|
||||
var windowTop = $(document).scrollTop();
|
||||
var windowLeft = $(document).scrollLeft();
|
||||
var position = this.s.position;
|
||||
var headerMode, footerMode;
|
||||
|
||||
if ( this.c.header ) {
|
||||
if ( ! this.s.enable ) {
|
||||
headerMode = 'in-place';
|
||||
}
|
||||
else if ( ! position.visible || windowTop <= position.theadTop - this.c.headerOffset ) {
|
||||
headerMode = 'in-place';
|
||||
}
|
||||
else if ( windowTop <= position.tfootTop - position.theadHeight - this.c.headerOffset ) {
|
||||
headerMode = 'in';
|
||||
}
|
||||
else {
|
||||
headerMode = 'below';
|
||||
}
|
||||
|
||||
if ( forceChange || headerMode !== this.s.headerMode ) {
|
||||
this._modeChange( headerMode, 'header', forceChange );
|
||||
}
|
||||
|
||||
this._horizontal( 'header', windowLeft );
|
||||
}
|
||||
|
||||
if ( this.c.footer && this.dom.tfoot.length ) {
|
||||
if ( ! this.s.enable ) {
|
||||
footerMode = 'in-place';
|
||||
}
|
||||
else if ( ! position.visible || windowTop + position.windowHeight >= position.tfootBottom + this.c.footerOffset ) {
|
||||
footerMode = 'in-place';
|
||||
}
|
||||
else if ( position.windowHeight + windowTop > position.tbodyTop + position.tfootHeight + this.c.footerOffset ) {
|
||||
footerMode = 'in';
|
||||
}
|
||||
else {
|
||||
footerMode = 'above';
|
||||
}
|
||||
|
||||
if ( forceChange || footerMode !== this.s.footerMode ) {
|
||||
this._modeChange( footerMode, 'footer', forceChange );
|
||||
}
|
||||
|
||||
this._horizontal( 'footer', windowLeft );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
|
||||
/**
|
||||
* Version
|
||||
* @type {String}
|
||||
* @static
|
||||
*/
|
||||
FixedHeader.version = "3.1.8";
|
||||
|
||||
/**
|
||||
* Defaults
|
||||
* @type {Object}
|
||||
* @static
|
||||
*/
|
||||
FixedHeader.defaults = {
|
||||
header: true,
|
||||
footer: false,
|
||||
headerOffset: 0,
|
||||
footerOffset: 0
|
||||
};
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* DataTables interfaces
|
||||
*/
|
||||
|
||||
// Attach for constructor access
|
||||
$.fn.dataTable.FixedHeader = FixedHeader;
|
||||
$.fn.DataTable.FixedHeader = FixedHeader;
|
||||
|
||||
|
||||
// DataTables creation - check if the FixedHeader option has been defined on the
|
||||
// table and if so, initialise
|
||||
$(document).on( 'init.dt.dtfh', function (e, settings, json) {
|
||||
if ( e.namespace !== 'dt' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var init = settings.oInit.fixedHeader;
|
||||
var defaults = DataTable.defaults.fixedHeader;
|
||||
|
||||
if ( (init || defaults) && ! settings._fixedHeader ) {
|
||||
var opts = $.extend( {}, defaults, init );
|
||||
|
||||
if ( init !== false ) {
|
||||
new FixedHeader( settings, opts );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
// DataTables API methods
|
||||
DataTable.Api.register( 'fixedHeader()', function () {} );
|
||||
|
||||
DataTable.Api.register( 'fixedHeader.adjust()', function () {
|
||||
return this.iterator( 'table', function ( ctx ) {
|
||||
var fh = ctx._fixedHeader;
|
||||
|
||||
if ( fh ) {
|
||||
fh.update();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
DataTable.Api.register( 'fixedHeader.enable()', function ( flag ) {
|
||||
return this.iterator( 'table', function ( ctx ) {
|
||||
var fh = ctx._fixedHeader;
|
||||
|
||||
flag = ( flag !== undefined ? flag : true );
|
||||
if ( fh && flag !== fh.enabled() ) {
|
||||
fh.enable( flag );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
DataTable.Api.register( 'fixedHeader.enabled()', function () {
|
||||
if ( this.context.length ) {
|
||||
var fh = this.context[0]._fixedHeader;
|
||||
|
||||
if ( fh ) {
|
||||
return fh.enabled();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} );
|
||||
|
||||
DataTable.Api.register( 'fixedHeader.disable()', function ( ) {
|
||||
return this.iterator( 'table', function ( ctx ) {
|
||||
var fh = ctx._fixedHeader;
|
||||
|
||||
if ( fh && fh.enabled() ) {
|
||||
fh.enable( false );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
$.each( ['header', 'footer'], function ( i, el ) {
|
||||
DataTable.Api.register( 'fixedHeader.'+el+'Offset()', function ( offset ) {
|
||||
var ctx = this.context;
|
||||
|
||||
if ( offset === undefined ) {
|
||||
return ctx.length && ctx[0]._fixedHeader ?
|
||||
ctx[0]._fixedHeader[el +'Offset']() :
|
||||
undefined;
|
||||
}
|
||||
|
||||
return this.iterator( 'table', function ( ctx ) {
|
||||
var fh = ctx._fixedHeader;
|
||||
|
||||
if ( fh ) {
|
||||
fh[ el +'Offset' ]( offset );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
||||
return FixedHeader;
|
||||
}));
|
||||
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
FixedHeader 3.1.8
|
||||
©2009-2021 SpryMedia Ltd - datatables.net/license
|
||||
*/
|
||||
(function(d){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(g){return d(g,window,document)}):"object"===typeof exports?module.exports=function(g,j){g||(g=window);if(!j||!j.fn.dataTable)j=require("datatables.net")(g,j).$;return d(j,g,g.document)}:d(jQuery,window,document)})(function(d,g,j,k){var i=d.fn.dataTable,l=0,h=function(a,b){if(!(this instanceof h))throw"FixedHeader must be initialised with the 'new' keyword.";!0===b&&(b={});a=new i.Api(a);this.c=d.extend(!0,
|
||||
{},h.defaults,b);this.s={dt:a,position:{theadTop:0,tbodyTop:0,tfootTop:0,tfootBottom:0,width:0,left:0,tfootHeight:0,theadHeight:0,windowHeight:d(g).height(),visible:!0},headerMode:null,footerMode:null,autoWidth:a.settings()[0].oFeatures.bAutoWidth,namespace:".dtfc"+l++,scrollLeft:{header:-1,footer:-1},enable:!0};this.dom={floatingHeader:null,thead:d(a.table().header()),tbody:d(a.table().body()),tfoot:d(a.table().footer()),header:{host:null,floating:null,placeholder:null},footer:{host:null,floating:null,
|
||||
placeholder:null}};this.dom.header.host=this.dom.thead.parent();this.dom.footer.host=this.dom.tfoot.parent();var e=a.settings()[0];if(e._fixedHeader)throw"FixedHeader already initialised on table "+e.nTable.id;e._fixedHeader=this;this._constructor()};d.extend(h.prototype,{destroy:function(){this.s.dt.off(".dtfc");d(g).off(this.s.namespace);this.c.header&&this._modeChange("in-place","header",!0);this.c.footer&&this.dom.tfoot.length&&this._modeChange("in-place","footer",!0)},enable:function(a,b){this.s.enable=
|
||||
a;if(b||b===k)this._positions(),this._scroll(!0)},enabled:function(){return this.s.enable},headerOffset:function(a){a!==k&&(this.c.headerOffset=a,this.update());return this.c.headerOffset},footerOffset:function(a){a!==k&&(this.c.footerOffset=a,this.update());return this.c.footerOffset},update:function(){var a=this.s.dt.table().node();d(a).is(":visible")?this.enable(!0,!1):this.enable(!1,!1);this._positions();this._scroll(!0)},_constructor:function(){var a=this,b=this.s.dt;d(g).on("scroll"+this.s.namespace,
|
||||
function(){a._scroll()}).on("resize"+this.s.namespace,i.util.throttle(function(){a.s.position.windowHeight=d(g).height();a.update()},50));var e=d(".fh-fixedHeader");!this.c.headerOffset&&e.length&&(this.c.headerOffset=e.outerHeight());e=d(".fh-fixedFooter");!this.c.footerOffset&&e.length&&(this.c.footerOffset=e.outerHeight());b.on("column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc responsive-display.dt.dtfc",function(){a.update()});b.on("destroy.dtfc",function(){a.destroy()});
|
||||
this._positions();this._scroll()},_clone:function(a,b){var e=this.s.dt,c=this.dom[a],f="header"===a?this.dom.thead:this.dom.tfoot;!b&&c.floating?c.floating.removeClass("fixedHeader-floating fixedHeader-locked"):(c.floating&&(c.placeholder.remove(),this._unsize(a),c.floating.children().detach(),c.floating.remove()),c.floating=d(e.table().node().cloneNode(!1)).css("table-layout","fixed").attr("aria-hidden","true").removeAttr("id").append(f).appendTo("body"),c.placeholder=f.clone(!1),c.placeholder.find("*[id]").removeAttr("id"),
|
||||
c.host.prepend(c.placeholder),this._matchWidths(c.placeholder,c.floating))},_matchWidths:function(a,b){var e=function(b){return d(b,a).map(function(){return 1*d(this).css("width").replace(/[^\d\.]/g,"")}).toArray()},c=function(a,c){d(a,b).each(function(a){d(this).css({width:c[a],minWidth:c[a]})})},f=e("th"),e=e("td");c("th",f);c("td",e)},_unsize:function(a){var b=this.dom[a].floating;b&&("footer"===a||"header"===a&&!this.s.autoWidth)?d("th, td",b).css({width:"",minWidth:""}):b&&"header"===a&&d("th, td",
|
||||
b).css("min-width","")},_horizontal:function(a,b){var e=this.dom[a],c=this.s.position,d=this.s.scrollLeft;e.floating&&d[a]!==b&&(e.floating.css("left",c.left-b),d[a]=b)},_modeChange:function(a,b,e){var c=this.dom[b],f=this.s.position,g=function(a){c.floating.attr("style",function(b,c){return(c||"")+"width: "+a+"px !important;"})},i=this.dom["footer"===b?"tfoot":"thead"],h=d.contains(i[0],j.activeElement)?j.activeElement:null;h&&h.blur();if("in-place"===a){if(c.placeholder&&(c.placeholder.remove(),
|
||||
c.placeholder=null),this._unsize(b),"header"===b?c.host.prepend(i):c.host.append(i),c.floating)c.floating.remove(),c.floating=null}else"in"===a?(this._clone(b,e),c.floating.addClass("fixedHeader-floating").css("header"===b?"top":"bottom",this.c[b+"Offset"]).css("left",f.left+"px"),g(f.width),"footer"===b&&c.floating.css("top","")):"below"===a?(this._clone(b,e),c.floating.addClass("fixedHeader-locked").css("top",f.tfootTop-f.theadHeight).css("left",f.left+"px"),g(f.width)):"above"===a&&(this._clone(b,
|
||||
e),c.floating.addClass("fixedHeader-locked").css("top",f.tbodyTop).css("left",f.left+"px"),g(f.width));h&&h!==j.activeElement&&setTimeout(function(){h.focus()},10);this.s.scrollLeft.header=-1;this.s.scrollLeft.footer=-1;this.s[b+"Mode"]=a},_positions:function(){var a=this.s.dt.table(),b=this.s.position,e=this.dom,a=d(a.node()),c=a.children("thead"),f=a.children("tfoot"),e=e.tbody;b.visible=a.is(":visible");b.width=a.outerWidth();b.left=a.offset().left;b.theadTop=c.offset().top;b.tbodyTop=e.offset().top;
|
||||
b.tbodyHeight=e.outerHeight();b.theadHeight=b.tbodyTop-b.theadTop;f.length?(b.tfootTop=f.offset().top,b.tfootBottom=b.tfootTop+f.outerHeight(),b.tfootHeight=b.tfootBottom-b.tfootTop):(b.tfootTop=b.tbodyTop+e.outerHeight(),b.tfootBottom=b.tfootTop,b.tfootHeight=b.tfootTop)},_scroll:function(a){var b=d(j).scrollTop(),e=d(j).scrollLeft(),c=this.s.position,f;this.c.header&&(f=this.s.enable?!c.visible||b<=c.theadTop-this.c.headerOffset?"in-place":b<=c.tfootTop-c.theadHeight-this.c.headerOffset?"in":"below":
|
||||
"in-place",(a||f!==this.s.headerMode)&&this._modeChange(f,"header",a),this._horizontal("header",e));this.c.footer&&this.dom.tfoot.length&&(b=this.s.enable?!c.visible||b+c.windowHeight>=c.tfootBottom+this.c.footerOffset?"in-place":c.windowHeight+b>c.tbodyTop+c.tfootHeight+this.c.footerOffset?"in":"above":"in-place",(a||b!==this.s.footerMode)&&this._modeChange(b,"footer",a),this._horizontal("footer",e))}});h.version="3.1.8";h.defaults={header:!0,footer:!1,headerOffset:0,footerOffset:0};d.fn.dataTable.FixedHeader=
|
||||
h;d.fn.DataTable.FixedHeader=h;d(j).on("init.dt.dtfh",function(a,b){if("dt"===a.namespace){var e=b.oInit.fixedHeader,c=i.defaults.fixedHeader;if((e||c)&&!b._fixedHeader)c=d.extend({},c,e),!1!==e&&new h(b,c)}});i.Api.register("fixedHeader()",function(){});i.Api.register("fixedHeader.adjust()",function(){return this.iterator("table",function(a){(a=a._fixedHeader)&&a.update()})});i.Api.register("fixedHeader.enable()",function(a){return this.iterator("table",function(b){b=b._fixedHeader;a=a!==k?a:!0;
|
||||
b&&a!==b.enabled()&&b.enable(a)})});i.Api.register("fixedHeader.enabled()",function(){if(this.context.length){var a=this.context[0]._fixedHeader;if(a)return a.enabled()}return!1});i.Api.register("fixedHeader.disable()",function(){return this.iterator("table",function(a){(a=a._fixedHeader)&&a.enabled()&&a.enable(!1)})});d.each(["header","footer"],function(a,b){i.Api.register("fixedHeader."+b+"Offset()",function(a){var c=this.context;return a===k?c.length&&c[0]._fixedHeader?c[0]._fixedHeader[b+"Offset"]():
|
||||
k:this.iterator("table",function(c){if(c=c._fixedHeader)c[b+"Offset"](a)})})});return h});
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "datatables.net-fixedheader",
|
||||
"version": "3.1.8",
|
||||
"description": "FixedHeader for DataTables ",
|
||||
"files": [
|
||||
"js/**/*.js"
|
||||
],
|
||||
"main": "js/dataTables.fixedHeader.js",
|
||||
"type": "types.d.ts",
|
||||
"keywords": [
|
||||
"fixed headers",
|
||||
"sticky",
|
||||
"DataTables",
|
||||
"jQuery",
|
||||
"table",
|
||||
"DataTables"
|
||||
],
|
||||
"homepage": "https://datatables.net",
|
||||
"bugs": "https://datatables.net/forums",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
"name": "SpryMedia Ltd",
|
||||
"url": "http://datatables.net"
|
||||
},
|
||||
"dependencies": {
|
||||
"jquery": ">=1.7",
|
||||
"datatables.net": "^1.10.15"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DataTables/Dist-DataTables-FixedHeader.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jquery": "^3.5.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Responsive for DataTables
|
||||
|
||||
This package contains distribution files for the [Responsive extension](https://datatables.net/extensions/responsive) for [DataTables](https://datatables.net/). Only the core software for this library is contained in this package - to be correctly styled, a styling package for Responsive must also be included. Styling options include DataTable's native styling, [Bootstrap](http://getbootstrap.com) and [Foundation](http://foundation.zurb.com/).
|
||||
|
||||
In the modern world of responsive web design tables can often cause a particular problem for designers due to their row based layout. Responsive is an extension for DataTables that resolves that problem by optimising the table's layout for different screen sizes through the dynamic insertion and removal of columns from the table.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
### Browser
|
||||
|
||||
For inclusion of this library using a standard `<script>` tag, rather than using this package, it is recommended that you use the [DataTables download builder](//datatables.net/download) which can create CDN or locally hosted packages for you, will all dependencies satisfied.
|
||||
|
||||
### npm
|
||||
|
||||
```
|
||||
npm install datatables.net-responsive
|
||||
```
|
||||
|
||||
```
|
||||
var $ = require( 'jquery' );
|
||||
require( 'datatables.net-responsive' )( window, $ );
|
||||
```
|
||||
|
||||
### bower
|
||||
|
||||
```
|
||||
bower install --save datatables.net-responsive
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
Full documentation of the DataTables options, API and plug-in interface are available on the DOCS_LINK. The site also contains information on the wide variety of plug-ins that are available for DataTables, which can be used to enhance and customise your table even further.
|
||||
|
||||
|
||||
## Bug / Support
|
||||
|
||||
Support for DataTables is available through the [DataTables forums](//datatables.net/forums) and [commercial support options](//datatables.net/support) are available.
|
||||
|
||||
|
||||
### Contributing
|
||||
|
||||
If you are thinking of contributing code to DataTables, first of all, thank you! All fixes, patches and enhancements to DataTables are very warmly welcomed. This repository is a distribution repo, so patches and issues sent to this repo will not be accepted. Instead, please direct pull requests to the [DataTables/Responsive](http://github.com/DataTables/Responsive). For issues / bugs, please direct your questions to the [DataTables forums](//datatables.net/forums).
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This software is released under the [MIT license](//datatables.net/license). You are free to use, modify and distribute this software, but all copyright information must remain.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
Responsive 2.2.7
|
||||
2014-2021 SpryMedia Ltd - datatables.net/license
|
||||
*/
|
||||
(function(d){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(m){return d(m,window,document)}):"object"===typeof exports?module.exports=function(m,l){m||(m=window);if(!l||!l.fn.dataTable)l=require("datatables.net")(m,l).$;return d(l,m,m.document)}:d(jQuery,window,document)})(function(d,m,l,q){function t(a,b,c){var e=b+"-"+c;if(n[e])return n[e];for(var d=[],a=a.cell(b,c).node().childNodes,b=0,c=a.length;b<c;b++)d.push(a[b]);return n[e]=d}function r(a,b,c){var e=b+
|
||||
"-"+c;if(n[e]){for(var a=a.cell(b,c).node(),c=n[e][0].parentNode.childNodes,b=[],d=0,g=c.length;d<g;d++)b.push(c[d]);c=0;for(d=b.length;c<d;c++)a.appendChild(b[c]);n[e]=q}}var o=d.fn.dataTable,i=function(a,b){if(!o.versionCheck||!o.versionCheck("1.10.10"))throw"DataTables Responsive requires DataTables 1.10.10 or newer";this.s={dt:new o.Api(a),columns:[],current:[]};this.s.dt.settings()[0].responsive||(b&&"string"===typeof b.details?b.details={type:b.details}:b&&!1===b.details?b.details={type:!1}:
|
||||
b&&!0===b.details&&(b.details={type:"inline"}),this.c=d.extend(!0,{},i.defaults,o.defaults.responsive,b),a.responsive=this,this._constructor())};d.extend(i.prototype,{_constructor:function(){var a=this,b=this.s.dt,c=b.settings()[0],e=d(m).innerWidth();b.settings()[0]._responsive=this;d(m).on("resize.dtr orientationchange.dtr",o.util.throttle(function(){var b=d(m).innerWidth();b!==e&&(a._resize(),e=b)}));c.oApi._fnCallbackReg(c,"aoRowCreatedCallback",function(c){-1!==d.inArray(!1,a.s.current)&&d(">td, >th",
|
||||
c).each(function(c){c=b.column.index("toData",c);!1===a.s.current[c]&&d(this).css("display","none")})});b.on("destroy.dtr",function(){b.off(".dtr");d(b.table().body()).off(".dtr");d(m).off("resize.dtr orientationchange.dtr");b.cells(".dtr-control").nodes().to$().removeClass("dtr-control");d.each(a.s.current,function(b,c){!1===c&&a._setColumnVis(b,!0)})});this.c.breakpoints.sort(function(a,b){return a.width<b.width?1:a.width>b.width?-1:0});this._classLogic();this._resizeAuto();c=this.c.details;!1!==
|
||||
c.type&&(a._detailsInit(),b.on("column-visibility.dtr",function(){a._timer&&clearTimeout(a._timer);a._timer=setTimeout(function(){a._timer=null;a._classLogic();a._resizeAuto();a._resize(!0);a._redrawChildren()},100)}),b.on("draw.dtr",function(){a._redrawChildren()}),d(b.table().node()).addClass("dtr-"+c.type));b.on("column-reorder.dtr",function(){a._classLogic();a._resizeAuto();a._resize(true)});b.on("column-sizing.dtr",function(){a._resizeAuto();a._resize()});b.on("preXhr.dtr",function(){var c=[];
|
||||
b.rows().every(function(){this.child.isShown()&&c.push(this.id(true))});b.one("draw.dtr",function(){a._resizeAuto();a._resize();b.rows(c).every(function(){a._detailsDisplay(this,false)})})});b.on("draw.dtr",function(){a._controlClass()}).on("init.dtr",function(c){if(c.namespace==="dt"){a._resizeAuto();a._resize();d.inArray(false,a.s.current)&&b.columns.adjust()}});this._resize()},_columnsVisiblity:function(a){var b=this.s.dt,c=this.s.columns,e,f,g=c.map(function(a,b){return{columnIdx:b,priority:a.priority}}).sort(function(a,
|
||||
b){return a.priority!==b.priority?a.priority-b.priority:a.columnIdx-b.columnIdx}),j=d.map(c,function(c,e){return!1===b.column(e).visible()?"not-visible":c.auto&&null===c.minWidth?!1:!0===c.auto?"-":-1!==d.inArray(a,c.includeIn)}),h=0;e=0;for(f=j.length;e<f;e++)!0===j[e]&&(h+=c[e].minWidth);e=b.settings()[0].oScroll;e=e.sY||e.sX?e.iBarWidth:0;h=b.table().container().offsetWidth-e-h;e=0;for(f=j.length;e<f;e++)c[e].control&&(h-=c[e].minWidth);var s=!1;e=0;for(f=g.length;e<f;e++){var k=g[e].columnIdx;
|
||||
"-"===j[k]&&(!c[k].control&&c[k].minWidth)&&(s||0>h-c[k].minWidth?(s=!0,j[k]=!1):j[k]=!0,h-=c[k].minWidth)}g=!1;e=0;for(f=c.length;e<f;e++)if(!c[e].control&&!c[e].never&&!1===j[e]){g=!0;break}e=0;for(f=c.length;e<f;e++)c[e].control&&(j[e]=g),"not-visible"===j[e]&&(j[e]=!1);-1===d.inArray(!0,j)&&(j[0]=!0);return j},_classLogic:function(){var a=this,b=this.c.breakpoints,c=this.s.dt,e=c.columns().eq(0).map(function(a){var b=this.column(a),e=b.header().className,a=c.settings()[0].aoColumns[a].responsivePriority,
|
||||
b=b.header().getAttribute("data-priority");a===q&&(a=b===q||null===b?1E4:1*b);return{className:e,includeIn:[],auto:!1,control:!1,never:e.match(/\bnever\b/)?!0:!1,priority:a}}),f=function(a,b){var c=e[a].includeIn;-1===d.inArray(b,c)&&c.push(b)},g=function(c,d,g,k){if(g)if("max-"===g){k=a._find(d).width;d=0;for(g=b.length;d<g;d++)b[d].width<=k&&f(c,b[d].name)}else if("min-"===g){k=a._find(d).width;d=0;for(g=b.length;d<g;d++)b[d].width>=k&&f(c,b[d].name)}else{if("not-"===g){d=0;for(g=b.length;d<g;d++)-1===
|
||||
b[d].name.indexOf(k)&&f(c,b[d].name)}}else e[c].includeIn.push(d)};e.each(function(a,c){for(var e=a.className.split(" "),f=!1,i=0,m=e.length;i<m;i++){var l=e[i].trim();if("all"===l){f=!0;a.includeIn=d.map(b,function(a){return a.name});return}if("none"===l||a.never){f=!0;return}if("control"===l||"dtr-control"===l){f=!0;a.control=!0;return}d.each(b,function(a,b){var d=b.name.split("-"),e=l.match(RegExp("(min\\-|max\\-|not\\-)?("+d[0]+")(\\-[_a-zA-Z0-9])?"));e&&(f=!0,e[2]===d[0]&&e[3]==="-"+d[1]?g(c,
|
||||
b.name,e[1],e[2]+e[3]):e[2]===d[0]&&!e[3]&&g(c,b.name,e[1],e[2]))})}f||(a.auto=!0)});this.s.columns=e},_controlClass:function(){if("inline"===this.c.details.type){var a=this.s.dt,b=d.inArray(!0,this.s.current);a.cells(null,function(a){return a!==b},{page:"current"}).nodes().to$().filter(".dtr-control").removeClass("dtr-control");a.cells(null,b,{page:"current"}).nodes().to$().addClass("dtr-control")}},_detailsDisplay:function(a,b){var c=this,e=this.s.dt,f=this.c.details;if(f&&!1!==f.type){var g=f.display(a,
|
||||
b,function(){return f.renderer(e,a[0],c._detailsObj(a[0]))});(!0===g||!1===g)&&d(e.table().node()).triggerHandler("responsive-display.dt",[e,a,g,b])}},_detailsInit:function(){var a=this,b=this.s.dt,c=this.c.details;"inline"===c.type&&(c.target="td.dtr-control, th.dtr-control");b.on("draw.dtr",function(){a._tabIndexes()});a._tabIndexes();d(b.table().body()).on("keyup.dtr","td, th",function(a){a.keyCode===13&&d(this).data("dtr-keyboard")&&d(this).click()});var e=c.target;if(e!==q||null!==e)d(b.table().body()).on("click.dtr mousedown.dtr mouseup.dtr",
|
||||
"string"===typeof e?e:"td, th",function(c){if(d(b.table().node()).hasClass("collapsed")&&d.inArray(d(this).closest("tr").get(0),b.rows().nodes().toArray())!==-1){if(typeof e==="number"){var g=e<0?b.columns().eq(0).length+e:e;if(b.cell(this).index().column!==g)return}g=b.row(d(this).closest("tr"));c.type==="click"?a._detailsDisplay(g,false):c.type==="mousedown"?d(this).css("outline","none"):c.type==="mouseup"&&d(this).trigger("blur").css("outline","")}})},_detailsObj:function(a){var b=this,c=this.s.dt;
|
||||
return d.map(this.s.columns,function(e,f){if(!e.never&&!e.control){var g=c.settings()[0].aoColumns[f];return{className:g.sClass,columnIndex:f,data:c.cell(a,f).render(b.c.orthogonal),hidden:c.column(f).visible()&&!b.s.current[f],rowIndex:a,title:null!==g.sTitle?g.sTitle:d(c.column(f).header()).text()}}})},_find:function(a){for(var b=this.c.breakpoints,c=0,d=b.length;c<d;c++)if(b[c].name===a)return b[c]},_redrawChildren:function(){var a=this,b=this.s.dt;b.rows({page:"current"}).iterator("row",function(c,
|
||||
d){b.row(d);a._detailsDisplay(b.row(d),!0)})},_resize:function(a){var b=this,c=this.s.dt,e=d(m).innerWidth(),f=this.c.breakpoints,g=f[0].name,j=this.s.columns,h,i=this.s.current.slice();for(h=f.length-1;0<=h;h--)if(e<=f[h].width){g=f[h].name;break}var k=this._columnsVisiblity(g);this.s.current=k;f=!1;h=0;for(e=j.length;h<e;h++)if(!1===k[h]&&!j[h].never&&!j[h].control&&!1===!c.column(h).visible()){f=!0;break}d(c.table().node()).toggleClass("collapsed",f);var l=!1,n=0;c.columns().eq(0).each(function(c,
|
||||
d){!0===k[d]&&n++;if(a||k[d]!==i[d])l=!0,b._setColumnVis(c,k[d])});l&&(this._redrawChildren(),d(c.table().node()).trigger("responsive-resize.dt",[c,this.s.current]),0===c.page.info().recordsDisplay&&d("td",c.table().body()).eq(0).attr("colspan",n));b._controlClass()},_resizeAuto:function(){var a=this.s.dt,b=this.s.columns;if(this.c.auto&&-1!==d.inArray(!0,d.map(b,function(a){return a.auto}))){d.isEmptyObject(n)||d.each(n,function(b){b=b.split("-");r(a,1*b[0],1*b[1])});a.table().node();var c=a.table().node().cloneNode(!1),
|
||||
e=d(a.table().header().cloneNode(!1)).appendTo(c),f=d(a.table().body()).clone(!1,!1).empty().appendTo(c);c.style.width="auto";var g=a.columns().header().filter(function(b){return a.column(b).visible()}).to$().clone(!1).css("display","table-cell").css("width","auto").css("min-width",0);d(f).append(d(a.rows({page:"current"}).nodes()).clone(!1)).find("th, td").css("display","");if(f=a.table().footer()){var f=d(f.cloneNode(!1)).appendTo(c),j=a.columns().footer().filter(function(b){return a.column(b).visible()}).to$().clone(!1).css("display",
|
||||
"table-cell");d("<tr/>").append(j).appendTo(f)}d("<tr/>").append(g).appendTo(e);"inline"===this.c.details.type&&d(c).addClass("dtr-inline collapsed");d(c).find("[name]").removeAttr("name");d(c).css("position","relative");c=d("<div/>").css({width:1,height:1,overflow:"hidden",clear:"both"}).append(c);c.insertBefore(a.table().node());g.each(function(d){d=a.column.index("fromVisible",d);b[d].minWidth=this.offsetWidth||0});c.remove()}},_responsiveOnlyHidden:function(){var a=this.s.dt;return d.map(this.s.current,
|
||||
function(b,d){return!1===a.column(d).visible()?!0:b})},_setColumnVis:function(a,b){var c=this.s.dt,e=b?"":"none";d(c.column(a).header()).css("display",e);d(c.column(a).footer()).css("display",e);c.column(a).nodes().to$().css("display",e);d.isEmptyObject(n)||c.cells(null,a).indexes().each(function(a){r(c,a.row,a.column)})},_tabIndexes:function(){var a=this.s.dt,b=a.cells({page:"current"}).nodes().to$(),c=a.settings()[0],e=this.c.details.target;b.filter("[data-dtr-keyboard]").removeData("[data-dtr-keyboard]");
|
||||
"number"===typeof e?a.cells(null,e,{page:"current"}).nodes().to$().attr("tabIndex",c.iTabIndex).data("dtr-keyboard",1):("td:first-child, th:first-child"===e&&(e=">td:first-child, >th:first-child"),d(e,a.rows({page:"current"}).nodes()).attr("tabIndex",c.iTabIndex).data("dtr-keyboard",1))}});i.breakpoints=[{name:"desktop",width:Infinity},{name:"tablet-l",width:1024},{name:"tablet-p",width:768},{name:"mobile-l",width:480},{name:"mobile-p",width:320}];i.display={childRow:function(a,b,c){if(b){if(d(a.node()).hasClass("parent"))return a.child(c(),
|
||||
"child").show(),!0}else{if(a.child.isShown())return a.child(!1),d(a.node()).removeClass("parent"),!1;a.child(c(),"child").show();d(a.node()).addClass("parent");return!0}},childRowImmediate:function(a,b,c){if(!b&&a.child.isShown()||!a.responsive.hasHidden())return a.child(!1),d(a.node()).removeClass("parent"),!1;a.child(c(),"child").show();d(a.node()).addClass("parent");return!0},modal:function(a){return function(b,c,e){if(c)d("div.dtr-modal-content").empty().append(e());else{var f=function(){g.remove();
|
||||
d(l).off("keypress.dtr")},g=d('<div class="dtr-modal"/>').append(d('<div class="dtr-modal-display"/>').append(d('<div class="dtr-modal-content"/>').append(e())).append(d('<div class="dtr-modal-close">×</div>').click(function(){f()}))).append(d('<div class="dtr-modal-background"/>').click(function(){f()})).appendTo("body");d(l).on("keyup.dtr",function(a){27===a.keyCode&&(a.stopPropagation(),f())})}a&&a.header&&d("div.dtr-modal-content").prepend("<h2>"+a.header(b)+"</h2>")}}};var n={};i.renderer=
|
||||
{listHiddenNodes:function(){return function(a,b,c){var e=d('<ul data-dtr-index="'+b+'" class="dtr-details"/>'),f=!1;d.each(c,function(b,c){c.hidden&&(d("<li "+(c.className?'class="'+c.className+'"':"")+' data-dtr-index="'+c.columnIndex+'" data-dt-row="'+c.rowIndex+'" data-dt-column="'+c.columnIndex+'"><span class="dtr-title">'+c.title+"</span> </li>").append(d('<span class="dtr-data"/>').append(t(a,c.rowIndex,c.columnIndex))).appendTo(e),f=!0)});return f?e:!1}},listHidden:function(){return function(a,
|
||||
b,c){return(a=d.map(c,function(a){var b=a.className?'class="'+a.className+'"':"";return a.hidden?"<li "+b+' data-dtr-index="'+a.columnIndex+'" data-dt-row="'+a.rowIndex+'" data-dt-column="'+a.columnIndex+'"><span class="dtr-title">'+a.title+'</span> <span class="dtr-data">'+a.data+"</span></li>":""}).join(""))?d('<ul data-dtr-index="'+b+'" class="dtr-details"/>').append(a):!1}},tableAll:function(a){a=d.extend({tableClass:""},a);return function(b,c,e){b=d.map(e,function(a){return"<tr "+(a.className?
|
||||
'class="'+a.className+'"':"")+' data-dt-row="'+a.rowIndex+'" data-dt-column="'+a.columnIndex+'"><td>'+a.title+":</td> <td>"+a.data+"</td></tr>"}).join("");return d('<table class="'+a.tableClass+' dtr-details" width="100%"/>').append(b)}}};i.defaults={breakpoints:i.breakpoints,auto:!0,details:{display:i.display.childRow,renderer:i.renderer.listHidden(),target:0,type:"inline"},orthogonal:"display"};var p=d.fn.dataTable.Api;p.register("responsive()",function(){return this});p.register("responsive.index()",
|
||||
function(a){a=d(a);return{column:a.data("dtr-index"),row:a.parent().data("dtr-index")}});p.register("responsive.rebuild()",function(){return this.iterator("table",function(a){a._responsive&&a._responsive._classLogic()})});p.register("responsive.recalc()",function(){return this.iterator("table",function(a){a._responsive&&(a._responsive._resizeAuto(),a._responsive._resize())})});p.register("responsive.hasHidden()",function(){var a=this.context[0];return a._responsive?-1!==d.inArray(!1,a._responsive._responsiveOnlyHidden()):
|
||||
!1});p.registerPlural("columns().responsiveHidden()","column().responsiveHidden()",function(){return this.iterator("column",function(a,b){return a._responsive?a._responsive._responsiveOnlyHidden()[b]:!1},1)});i.version="2.2.7";d.fn.dataTable.Responsive=i;d.fn.DataTable.Responsive=i;d(l).on("preInit.dt.dtr",function(a,b){if("dt"===a.namespace&&(d(b.nTable).hasClass("responsive")||d(b.nTable).hasClass("dt-responsive")||b.oInit.responsive||o.defaults.responsive)){var c=b.oInit.responsive;!1!==c&&new i(b,
|
||||
d.isPlainObject(c)?c:{})}});return i});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user