Compare commits

...

147 Commits

Author SHA1 Message Date
Benjamin Dalsass
f70f95c119 Update community licence 2.7.7 (script from git bash, not php execution) 2022-07-11 15:20:36 +02:00
Benjamin Dalsass
53c50cf6fc Update community licence 2.7.7 2022-07-11 08:24:20 +02:00
Vincent Dumas
f19d1472c5 N°5102 - OAuth - replace double quote char EN
Use a special double quote characters so it is correctly handled in HTML
2022-07-08 13:44:00 +02:00
Vincent Dumas
eef00502cd N°5102 - OAuth - remove unsupported quote FR 2022-07-08 13:38:37 +02:00
Molkobain
0b1caac195 N°4867 - Restore datamodel node to avoid minor version migration crash
Will be properly removed in 3.1.0
2022-07-08 13:22:27 +02:00
Vincent Dumas
e900a44d47 N°5102 - OAuth client - FR tooltips 2022-07-08 12:22:52 +02:00
Vincent Dumas
a3de9fa898 N°5102 - OAuth client - Add EN tooltips 2022-07-08 12:06:47 +02:00
bdalsass
8b0154cc62 N°5168 - Access to unauthorized contact information on Portal (#305)
GlobalRequestMgmt issue
2022-07-08 09:51:20 +02:00
Eric Espie
1a225bf55b N°5102 - Allow to send emails using GSuite SMTP and OAuth - Access rights 2022-07-07 17:57:24 +02:00
Eric Espie
24d19cd8d6 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Add a flag to select OAuth client for SMTP usage 2022-07-07 16:34:19 +02:00
Eric Espie
c25a4a7346 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Access rights 2022-07-07 14:17:09 +02:00
Eric Espie
20fb7b241f N°5102 - Allow to send emails using GSuite SMTP and OAuth - Highlight classes 2022-07-06 17:23:44 +02:00
Eric Espie
a0553e1195 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Highlight classes 2022-07-06 17:10:59 +02:00
Eric Espie
f40141072a N°5102 - Allow to send emails using GSuite SMTP and OAuth - Fix errors on vendor name 2022-07-06 14:10:01 +02:00
Eric Espie
c759856a61 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Highlight classes 2022-07-06 10:10:42 +02:00
Pierre Goiffon
237b181eec 💡 Fix \SetupUtils::GetTmpDir PHPDoc 2022-07-05 18:09:59 +02:00
Eric Espie
48957fd2f0 N°5102 - Allow to send emails using GSuite SMTP and OAuth - refactor scopes 2022-07-05 17:54:43 +02:00
Pierre Goiffon
8a99c37200 N°5287 Fix composer.json errors
See https://getcomposer.org/doc/03-cli.md#validate
2022-07-05 15:08:29 +02:00
Eric Espie
d388c3fd3d N°5102 - Allow to send emails using GSuite SMTP and OAuth - Limit error size 2022-07-04 16:48:56 +02:00
Eric Espie
1b8e48539d N°5102 - Allow to send emails using GSuite SMTP and OAuth - Add comment in configuration 2022-07-01 14:32:44 +02:00
Eric Espie
104beff158 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Fix log 2022-06-29 15:09:17 +02:00
Pierre Goiffon
4712569a36 📝 CONTRIBUTING : fix GitMoji link 2022-06-29 10:56:21 +02:00
Pierre Goiffon
2392f4a902 🔒 Update guzzlehttp/guzzle 2022-06-28 15:13:04 +02:00
Eric Espie
a0f28a9098 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Rework 2022-06-28 10:47:20 +02:00
Eric Espie
6df622e8ed N°5102 - Allow to send emails using GSuite SMTP and OAuth - Rework 2022-06-23 14:05:17 +02:00
Eric Espie
54eb9d081b N°5102 - Allow to send emails using GSuite SMTP and OAuth - Rework 2022-06-23 12:20:53 +02:00
Eric Espie
9f60f27636 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Rework 2022-06-22 16:41:45 +02:00
Eric Espie
ba59643f52 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Rework 2022-06-21 16:47:46 +02:00
Eric Espie
01c02a75a8 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Rework 2022-06-21 16:40:43 +02:00
Eric Espie
f5b3e5f341 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Rework 2022-06-21 16:10:34 +02:00
Eric Espie
9b825cb529 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Rework 2022-06-21 16:05:58 +02:00
Eric Espie
3f326f0913 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Rework 2022-06-21 16:05:58 +02:00
acognet
ec86bd246a N°5129 - Unwanted popup during a transition with an AttributeExternalField 2022-06-21 15:24:25 +02:00
Eric Espie
aa90d5b6ab N°5102 - Allow to send emails using GSuite SMTP and OAuth - Rework 2022-06-21 13:51:08 +02:00
acognet
53d2129bd1 N°5129 - Unwanted popup during a transition with an AttributeExternalField 2022-06-16 17:58:19 +02:00
Benjamin Dalsass
00e8c11ec2 N°5037 - Setup: Add disclaimer about collected data
change ui organization
2022-06-14 17:18:34 +02:00
Benjamin Dalsass
617b6b991f N°5037 - Setup: Add disclaimer about collected data
flip modules array
2022-06-14 14:33:35 +02:00
Benjamin Dalsass
b3ea1050eb N°5037 - Setup: Add disclaimer about collected data 2022-06-14 12:40:36 +02:00
Benjamin Dalsass
ca98066d68 N°5037 - Setup: Add disclaimer about collected data 2022-06-14 10:54:25 +02:00
Pierre Goiffon
352f7c8675 Update guzzlehttp/guzzle 2022-06-14 09:47:13 +02:00
Eric Espie
df5d514c28 N°4642 - Core Update : limit the usage of this function to minor version - fix error message (revert) 2022-06-13 15:59:25 +02:00
Eric Espie
16663797b2 N°4642 - Core Update : limit the usage of this function to minor version - fix error message 2022-06-13 15:51:36 +02:00
Eric Espie
4099376472 N°5102 - Allow to send emails using GSuite SMTP and OAuth - Defer the deletion (expunge) to the end of connection 2022-06-09 11:40:13 +02:00
Eric Espie
6d3118d9e9 N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth (fix config error message) 2022-06-08 13:24:29 +02:00
odain
4c585614cd ease testing: CreateTestOrganization returns org object 2022-06-08 11:07:31 +02:00
Eric Espie
9674378c56 N°5211 - Core update not working with auto-selected modules (comments) 2022-06-08 10:36:55 +02:00
Eric Espie
9e314ba77b N°5211 - Core update not working with auto-selected modules 2022-06-08 10:24:03 +02:00
Eric Espie
cdd7dcdc5c N°5211 - Core update not working with auto-selected modules 2022-06-08 10:12:19 +02:00
Benjamin Dalsass
34bed5ec4f N°5215 - Portal insufficient access control for ajax search form 2022-06-07 11:14:43 +02:00
Pierre Goiffon
3ea82e37d5 N°4635 Report \LogChannels::NOTIFICATIONS 2022-06-03 18:00:29 +02:00
Pierre Goiffon
596c62aec8 💡 N°4867 Add bug reference in phpdoc 2022-06-03 09:54:29 +02:00
acognet
265415030e N°4867 - "Twig content not allowed" error when use the extkey widget search icon in the user portal - Add comment 2022-06-02 12:35:42 +02:00
Eric Espie
3d26f28f9b N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
* Add icons to wizard
2022-06-01 11:44:02 +02:00
Eric Espie
0abec767e3 Dictionaries 2022-06-01 10:45:53 +02:00
Benjamin Dalsass
9fd10bd73e N°5168 - Security hardening 2022-05-31 16:28:02 +02:00
acognet
95dafc87c0 N°4867 - "Twig content not allowed" error when use the extkey widget search icon in the user portal - Add tests 2022-05-30 15:10:50 +02:00
acognet
fe1790793e N°4898 - security hardening 2022-05-30 15:10:49 +02:00
Eric Espie
ddb95dc64e Removed laminas service manager test folder 2022-05-27 09:32:16 +02:00
Eric Espie
f6f9ee26e1 Removed laminas service manager test folder 2022-05-27 09:29:00 +02:00
Eric Espie
21faa92904 Merge branch 'support/2.7' into feature/OAuthMail 2022-05-27 09:06:28 +02:00
Eric Espie
622f40c06c N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
 * Fix legacy mailboxes compatibility
2022-05-25 08:21:16 +02:00
acognet
964134cb60 N°4867 - "Twig content not allowed" error when use the extkey widget search icon in the user portal - Remove useless code 2022-05-24 18:20:18 +02:00
acognet
72f498a63b N°5168 - Fix error message "Call to a member function GetKey() on null" 2022-05-24 10:50:56 +02:00
Pierre Goiffon
f9a1f68295 N°4655 Remove OQL tests
Were only here to check legacy OQL engine, but since 2.7.0 we fixed couple of bugs in the current OQL engine : we can't keep same functionalities in both engines :/.

Plus now we are working on 2.7.7 and we're not aware of any use of this legacy engine...

Note that it will be deprecated (N°3141) and removed (N°4715) very soon.
2022-05-24 10:38:59 +02:00
Pierre Goiffon
9b67b0b9d5 Same options in phpunit config files 2022-05-23 14:53:13 +02:00
acognet
f798ef1d76 N°4538 - Dashlet Groupby on ExternalKey with special character, bad display - remove useless test 2022-05-23 14:21:03 +02:00
Eric Espie
754946bf62 N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
 * Fix legacy mailboxes compatibility
2022-05-23 12:09:40 +02:00
Eric Espie
a6580e3cd8 Merge branch 'support/2.7' into feature/OAuthMail 2022-05-23 10:56:32 +02:00
Pierre Goiffon
da6621f2ff Fix include warning in unittestautoload.php on Windows 2022-05-23 09:53:30 +02:00
Molkobain
f2d42a7e56 N°5002 - Simplify code 2022-05-20 18:41:39 +02:00
Benjamin Dalsass
d01e4b4a85 N°5168 - Security hardening 2022-05-20 16:08:25 +02:00
Pierre Goiffon
f57d1f1de3 Fix PHPunit errors with InlineImageMock.php and UtilsTest
HTMLDOMSanitizerTest : fix "Fatal error: Cannot declare class InlineImage, because the name is already in use in /var/www/html/iTop/test/core/sanitizer/InlineImageMock.php"
We are now injecting the class to mock, instead of declaring another class with the same name (was working before but why ?!???)

\UtilsTest::testSanitizer : no more testing the "class" filter, because it is a simple indirection, and we need to load datamodel which is causing multiple problems (see the comment in the test method dataprovider)
2022-05-20 10:48:05 +02:00
Eric Espie
a3f122184c N°4642 - Core Update : limit the usage of this function - revert due to N°4666 fix 2022-05-20 10:20:47 +02:00
acognet
16fcddc249 N°4867 - "Twig content not allowed" error when use the extkey widget search icon in the user portal (regression of N°4384 ) 2022-05-20 09:52:25 +02:00
Eric Espie
2a9c9be36a N°4666 - Core Update : handle modules 2022-05-20 09:42:14 +02:00
Eric Espie
ca3aae23a1 N°4666 - Core Update : handle modules 2022-05-20 09:33:41 +02:00
bdalsass
4dd384e418 N°4872 - Create a ticket in resolved statut Inlineimage disappear (#294) 2022-05-20 09:26:06 +02:00
Molkobain
80e7313b24 PHPDoc 2022-05-19 17:40:14 +02:00
Eric Espie
183c3c1baf N°4666 - Core Update : handle modules 2022-05-19 16:30:06 +02:00
Eric Espie
160c52fe81 Merge branch 'support/2.7' into feature/OAuthMail 2022-05-19 14:49:48 +02:00
Benjamin Dalsass
5f0a820b4a N°4899 - add sanitizer url since annotation and tests for sanitizer function 2022-05-19 08:36:42 +02:00
Benjamin Dalsass
03ef4246bf N°4899 - add sanitizer url since annotation and tests for sanitizer function 2022-05-18 12:03:07 +02:00
Pierre Goiffon
534e7cf59d N°4655 New nightly PHPUnit file containing OQL tests
Those tests were removed in 72af2b7c as they took too much time to run.
We are re-enabling them but only for nightly builds !
2022-05-18 09:35:13 +02:00
Eric Espie
e1645f6903 N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
 * Config messages
 * Fix unit tests
2022-05-18 08:41:58 +02:00
Benjamin Dalsass
61a2d200b4 N°4900 - Stored XSS in dashlets failed OQL query 2022-05-18 08:10:01 +02:00
Benjamin Dalsass
3d6bbe4029 Revert "N°4900 - Stored XSS in dashlets failed OQL query"
This reverts commit 562dd8fc21.
2022-05-18 08:05:19 +02:00
Eric Espie
44eda676a3 N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
 * 2.7 migration
2022-05-17 16:56:43 +02:00
Molkobain
eac6f07823 N°4985 - PHP 8.0: Fix optional parameter before mandatory parameter
* Method is always (once) called with the value defined in iTop
  * No Combodo extension call the method
  * No customization in the ITSM Designer (snippets / extensions) call the method
  * Calling method with only the first parameter would crash anyway
2022-05-17 16:51:50 +02:00
Pierre Goiffon
424e2a5745 💡 Fix PHPDoc for \DBObject::CheckConsistency 2022-05-17 15:52:43 +02:00
Molkobain
0ef4fee0b4 N°4985 - PHP 8.0: Fix usort callback return type 2022-05-17 15:28:04 +02:00
Eric Espie
1d45eff9b0 N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
 * 2.7 migration
2022-05-17 10:11:15 +02:00
Benjamin Dalsass
8e97279401 N°4899 - Reflected XSS on revert_dashboard operation 2022-05-17 09:27:06 +02:00
Eric Espie
932ef780fd N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
 * 2.7 migration
2022-05-17 09:06:49 +02:00
Benjamin Dalsass
59424c3126 N°4976 - CSRF in import page 2022-05-17 09:02:06 +02:00
Benjamin Dalsass
562dd8fc21 N°4900 - Stored XSS in dashlets failed OQL query 2022-05-17 08:20:26 +02:00
Molkobain
cf745554fb N°4985 - PHP 8.0: Fix strlen() test condition that needs to be more strict 2022-05-16 18:04:29 +02:00
Molkobain
e909eac98e N°4985 - PHP 8.0: Fix is_callable() first param syntax in ObjectFormManager 2022-05-16 17:44:34 +02:00
Molkobain
5e42efc3ec N°4985 - PHP 8.0: Fix usort callback return type in portal's lists initialization 2022-05-16 17:44:33 +02:00
Eric Espie
eb1d56f439 N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
 * 2.7 migration
2022-05-16 14:51:12 +02:00
Eric Espie
644e1ac4f6 N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
 * 2.7 migration (wip)
2022-05-13 16:27:56 +02:00
Stephen Abello
4c88dbd9ac N°2504 N°3169 N°5102 Add libraries 2022-05-13 14:39:19 +02:00
Stephen Abello
11d2e62e67 N°2504 N°3169 N°5102 Correctly disable authentication button for 2.7 2022-05-13 14:38:55 +02:00
Stephen Abello
58b27a9daa N°2504 N°3169 N°5102 Handle result display 2022-05-13 14:28:38 +02:00
Stephen Abello
caf939bf58 N°2504 N°3169 N°5102 Add dictionaries 2022-05-13 14:06:11 +02:00
Eric Espie
8c217fdac9 N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
 * 2.7 migration (wip)
2022-05-13 12:07:27 +02:00
Eric Espie
6b80bbeaa2 N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
 * 2.7 migration (wip)
2022-05-13 11:45:42 +02:00
Eric Espie
134736dce5 N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
 * 2.7 migration (wip)
2022-05-13 11:37:09 +02:00
Stephen Abello
4b870bcf1e N°2504 N°3169 N°5102 Add js template 2022-05-12 17:38:38 +02:00
Eric Espie
dd8a4a0082 N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph
N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
2022-05-12 14:40:55 +02:00
Molkobain
c2607c4223 N°5035 - Setup: Remove tracking image at the end of the setup 2022-05-09 13:55:06 +02:00
Pierre Goiffon
1fb0911710 🔧 N°3091 postbuild PHPunit XML : change html_errors PHP setting 2022-05-03 10:53:32 +02:00
Pierre Goiffon
b348e0ff27 🔧 N°3091 PHPunit XML : change html_errors PHP setting
We are outputting to console, and will get results in Jenkins or terminal, so no HTML please :o)
2022-05-03 10:51:48 +02:00
Pierre Goiffon
4646a05c7a N°4824 Update consumers after swiftmailer/swiftmailer
Multiple things to do as there were some changes in 6.0
Reference : https://github.com/swiftmailer/swiftmailer/blob/master/CHANGES#L107

* Fix "Call to undefined method Swift_Message::newInstance()" exception in notifications
* Fix removed Swift_MailTransport
2022-05-03 09:35:48 +02:00
Pierre Goiffon
c5527c106c 🔧 N°3091 PHPunit XML : set columns 2022-05-02 15:33:39 +02:00
Pierre Goiffon
5eac1b8730 🔧 N°3091 PHPunit XML : fix correct PHP INI settings
see https://phpunit.readthedocs.io/en/8.5/configuration.html#the-php-element
2022-05-02 15:25:59 +02:00
Pierre Goiffon
0de15d040f ⬇️ N°4824 rollback scssphp/scssphp update (won't be done in this branch !) 2022-05-02 09:15:48 +02:00
Pierre Goiffon
c4ae94fd4c Update denied test dirs 2022-04-29 17:15:50 +02:00
Pierre Goiffon
1e8818984e 📄 N°4284 Update licenses 2022-04-29 15:53:03 +02:00
Pierre Goiffon
a023f73509 N°4284 Update jquery UI SCSS to remove SCSSPHP warnings
Replace "Alpha(..." with "alpha(..."

Example of warning at compilation with SCSSPHP :

DEPRECATION WARNING: Calling built-in functions with a non-standard name is deprecated since Scssphp 1.8.0 and will not work anymore in 2.0 (they will be treated as CSS function calls instead).
Use "alpha" instead of "Alpha".
         on line 55 of /var/www/html/iTop/css/../css/ui-lightness/jqueryui.scss
2022-04-29 15:39:26 +02:00
Pierre Goiffon
6f0e1a7f47 N°4824 Update consumers after swiftmailer/swiftmailer update
Also remove new Doctrine test dir (iTopComposerTest feedback)
2022-04-29 15:24:56 +02:00
Pierre Goiffon
0ef9bb1a47 ⬆️ N°4824 Composer libs : update swiftmailer/swiftmailer 2022-04-29 15:24:56 +02:00
Pierre Goiffon
71ceedc4bb 🔨 N°4284 Detect new test dirs on composer update 2022-04-29 15:24:56 +02:00
Pierre Goiffon
73c3c1249f ⬆️ N°4824 Composer libs : update scssphp/scssphp 2022-04-29 15:24:56 +02:00
Pierre Goiffon
88a10dba28 N°4824 Update consumers after pelago/emogrifier update 2022-04-29 15:24:56 +02:00
Pierre Goiffon
001e222f67 ⬆️ N°4824 Composer libs : update pelago/emogrifier 2022-04-29 15:24:56 +02:00
Pierre Goiffon
af8bcdc242 ⬆️ N°4824 Composer libs : update pear/archive_tar 2022-04-29 15:24:56 +02:00
Pierre Goiffon
f4c7afc148 N°4824 Update consumers & tests after nikic/php-parser update
Was done in 3.0.0 with N°3867
(cherry picked from commit cd1ba097cb)
(cherry picked from commit 5b42f67a99)
(cherry picked from commit 2d98ca2318)
(cherry picked from commit ddc5bbd1bb)
2022-04-29 15:24:56 +02:00
Pierre Goiffon
b19c73a36e ⬆️ N°4284 Composer libs : update nikic/php-parser
Was done in 3.0.0 with N°3867
2022-04-29 15:24:55 +02:00
Pierre Goiffon
5fe0d0b94f ⬆️ N°4284 Composer libs : update combodo/tcpdf 2022-04-29 15:18:41 +02:00
Pierre Goiffon
f8d435d5f3 N°4284 Composer libs : refresh symfony 2022-04-29 15:18:41 +02:00
Pierre Goiffon
f15ef36fd1 N°4284 Composer libs : remove symfony/polyfill-php70
Though it is still downloaded because asked by symfony framework, but as we don't need it in our code no need to specify it here !
2022-04-29 15:18:41 +02:00
Pierre Goiffon
64b25c4daa 📌 N°4284 Composer libs : fix twig/twig to ~1.42.5
Without specifying explicitly the Twig version, since the update of require php from 5.6 to 7.0 we are getting Twig 2.12.5 !
We don't want Twig 2 as this version changes the macro scope and causes massive changes in our code... This update will be done later in other branches.
2022-04-29 15:15:32 +02:00
Pierre Goiffon
d0ba0d193b N°3091 iTopComposerTest : change deprecated PHPUnit method call 2022-04-22 14:42:12 +02:00
Pierre Goiffon
8e6e2432d3 Extensibility : iPortalUIExtension and AbstractPortalUIExtension aren't experimental anymore 2022-04-21 17:29:40 +02:00
Molkobain
83ec19dfca Remove duplicated lines in .gitignore 2022-04-21 14:26:56 +02:00
Pierre Goiffon
6e619f2c35 Fix \iTopConfigParserTest::testConfigWriteToFile_FromScratchInstallation throwing error 2022-04-21 12:02:35 +02:00
Pierre Goiffon
163ba41e8d N°3091 Update PHPUnit to 8.5 : remove doesNotPerformAssertions annotation
Modified tests :
- iTopConfigParsertest
- DBSearchIntersectTest::testIntersectOptimization

As expected this isn't working with PHPUnit 8.5
Why does it worked with previous PHPUnit 6 version ? Maybe this annotation wasn't handled yet ? The corresponding PHPUnit doc isn't available anymore...

Annotations doc for PHP 8.5 : https://phpunit.readthedocs.io/en/8.5/annotations.html#doesnotperformassertions
2022-04-21 12:02:35 +02:00
Pierre Goiffon
ec143c43db N°3091 Update PHPUnit to 8.5 : fix setUp and teardDown methods signatures
"Return type declaration must be compatible with parent"
See https://phpunit.de/announcements/phpunit-8.html "Return Type of Template Methods"
2022-04-21 12:02:35 +02:00
Pierre Goiffon
cacf0004a5 🙈 N°3091 Update PHPUnit to 8.5 : add PHPunit cache file to ignore
We don't want to disable the feature, nor we want this file to be versionned
https://phpunit.readthedocs.io/en/8.5/configuration.html#the-cacheresult-attribute
2022-04-21 12:02:35 +02:00
Pierre Goiffon
cb39541e2a N°3091 Update PHPUnit to 8.5 : composer and base files
Autoload wasn't working anymore, easy to see : just launch `php unittestautoload.php` (or see fatal errors when launching tests with your IDE)
2022-04-21 10:47:30 +02:00
Pierre Goiffon
b9ddadeb44 N°5109 update PHP requirements from 5.6 to 7.0
No embedded libs supports all versions from PHP 5.6 to 8.0 included :/
7.0.8 is required for our Symfony version (updated with N°4770)
2022-04-20 17:29:20 +02:00
Pierre Goiffon
11e811cc4b N°3717 Improve iTop object history API (#192)
This fixes a major flaw in the history API that was causing "phantom" CMDBChange records (without any CMDBChangeOp attached). That was happening especially in iProcess impl.
For example this lead to the creation of the combodo-cmdbchange-cleaner module in the Mail To Ticket extension.

The modifications in detail : 
- We can now pass a non persisted CMDBChange instance to \CMDBObject::SetCurrentChange
- No persistence done in \CMDBObject::CreateChange anymore
- Persistence of the attached CMDChange will be done if necessary in CMDBChangeOp::OnInsert
- New CMDBObject::SetCurrentChangeFromParams helper method to ease resetting the current change
2022-04-19 17:13:18 +02:00
Pierre Goiffon
e422adb0d0 N°4998 Fix CSS for AttributeDuration in transition form (#281) 2022-04-19 12:25:15 +02:00
Pierre Goiffon
e02d9f3f0e 💡 N°5090 Improve phpdoc using list array shape 2022-04-15 17:43:20 +02:00
Pierre Goiffon
e831d66b76 N°5090 Setup : improve missing dependencies message (#280)
The setup now relies on the new method MissingDependencyException::getHtmlDesc to get the message to display
MissingDependencyException is also now a CoreException child.

Note that previous behavior (MissingDependencyException instantiator setting message) is kept, as some consumer still do $e->getMessage() (like unattended install)
2022-04-15 17:30:05 +02:00
acognet
6fa2d47780 N°4538 - Dashlet Groupby on ExternalKey with special character, bad display 2022-04-15 10:03:04 +02:00
acognet
e691454339 N°5002 - memory leak after object creation in popup 2022-04-15 10:00:08 +02:00
1466 changed files with 160716 additions and 26034 deletions

10
.gitignore vendored
View File

@@ -6,12 +6,6 @@
# maintenance mode (N°2240)
/.maintenance
# listing prevention in conf directory
/conf/**
!/conf/.htaccess
!/conf/index.php
!/conf/web.config
# composer reserver directory, from sources, populate/update using "composer install"
vendor/*
test/vendor/*
@@ -19,6 +13,7 @@ test/vendor/*
# all conf but listing prevention
/conf/**
!/conf/.htaccess
!/conf/index.php
!/conf/web.config
# all datas but listing prevention
@@ -37,6 +32,9 @@ test/vendor/*
!/log/index.php
!/log/web.config
# PHPUnit cache file
/test/.phpunit.result.cache
# Jetbrains
/.idea/**

View File

@@ -50,14 +50,24 @@ foreach ($aDeniedButStillPresent as $sDir)
continue;
}
try
{
try {
SetupUtils::rrmdir($sDir);
echo "OK Remove denied test dir: '$sDir'\n";
}
catch (\Exception $e)
{
catch (\Exception $e) {
echo "\nFAILED to remove denied test dir: '$sDir'\n";
}
}
$aAllowedAndDeniedDirs = array_merge(
$oiTopComposer->ListAllowedTestDir(),
$oiTopComposer->ListDeniedTestDir()
);
$aExistingDirs = $oiTopComposer->ListAllTestDir();
$aMissing = array_diff($aExistingDirs, $aAllowedAndDeniedDirs);
if (false === empty($aMissing)) {
echo "Some new tests dirs exists !\n"
.' They must be declared either in the allowed or denied list in '.iTopComposer::class." (see N°2651).\n"
.' List of dirs:'."\n".var_export($aMissing, true);
}

View File

@@ -111,9 +111,9 @@ Our tests are located in the `test/` directory, containing a PHPUnit config file
* Use the present tense ("Add feature" not "Added feature")
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
* Limit the first line to 72 characters or less
* Please start the commit message with an applicable emoji code (following the [Gitmoji guide](https://gitmoji.carloscuesta.me/)).
Beware to use the code (for example `:bug:`) and not the character (🐛) as Unicode support in git clients is very poor for now...
Emoji examples :
* Please start the commit message with an applicable emoji code (following the [Gitmoji guide](https://gitmoji.dev/)).
Beware to use the code (for example `:bug:`) and not the character (🐛) as Unicode support in git clients is very poor for now...
Emoji examples :
* 🌐 `:globe_with_meridians:` for translations
* 🎨 `:art:` when improving the format/structure of the code
* ⚡️ `:zap:` when improving performance

View File

@@ -1083,11 +1083,11 @@ abstract class AbstractPageUIExtension implements iPageUIExtension
/**
* Implement this interface to add content to any enhanced portal page
*
* IMPORTANT! Experimental API, may be removed at anytime, we don't recommend to use it just now!
*
* @api
* @package Extensibility
* @since 2.4.0
*
* @since 2.4.0 interface creation
* @since 2.7.0 change method signatures due to Silex to Symfony migration
*/
interface iPortalUIExtension
{
@@ -1160,7 +1160,11 @@ interface iPortalUIExtension
}
/**
* IMPORTANT! Experimental API, may be removed at anytime, we don't recommend to use it just now!
* Extend this class instead of iPortalUIExtension if you don't need to overload all methods
*
* @api
* @package Extensibility
* @since 2.4.0
*/
abstract class AbstractPortalUIExtension implements iPortalUIExtension
{

View File

@@ -255,7 +255,7 @@ abstract class Dashlet
catch(OqlException $e)
{
$oPage->add('<div class="dashlet-content">');
$oPage->p($e->GetUserFriendlyDescription());
$oPage->p(utils::HtmlEntities($e->GetUserFriendlyDescription()));
$oPage->add('</div>');
}
catch(Exception $e)

View File

@@ -1223,7 +1223,7 @@ class DesignerComboField extends DesignerFormField
$sChecked = $this->defaultValue ? 'checked' : '';
$sMandatory = $this->bMandatory ? 'true' : 'false';
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
if ($this->IsSorted())
if ($this->IsSorted() )
{
asort($this->aAllowedValues);
}
@@ -1271,18 +1271,14 @@ class DesignerComboField extends DesignerFormField
$sHtml .= "<option value=\"\">".$this->sNullLabel."</option>";
}
}
foreach($this->aAllowedValues as $sKey => $sDisplayValue)
{
if ($this->bMultipleSelection)
{
foreach ($this->aAllowedValues as $sKey => $sDisplayValue) {
if ($this->bMultipleSelection) {
$sSelected = in_array($sKey, $this->defaultValue) ? 'selected' : '';
}
else
{
} else {
$sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
}
// Quick and dirty: display the menu parents as a tree
$sHtmlValue = str_replace(' ', '&nbsp;', htmlentities($sDisplayValue, ENT_QUOTES, 'UTF-8'));
$sHtmlValue = str_replace(' ', '&nbsp;', $sDisplayValue);
$sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>$sHtmlValue</option>";
}
$sHtml .= "</select>";

View File

@@ -283,6 +283,7 @@ class utils
*
* @since 2.5.2 2.6.0 new 'transaction_id' filter
* @since 2.7.0 new 'element_identifier' filter
* @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new 'url' filter
*/
protected static function Sanitize_Internal($value, $sSanitizationFilter)
{
@@ -358,6 +359,11 @@ class utils
$retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
break;
// For URL
case 'url':
$retValue = filter_var($value, FILTER_SANITIZE_URL);
break;
default:
case 'raw_data':
$retValue = $value;

View File

@@ -1,8 +1,10 @@
{
"name": "combodo/itop",
"description": "IT Operations Portal",
"type": "project",
"license": "AGPLv3",
"license": "AGPL-3.0-or-later",
"require": {
"php": ">=5.6.0",
"php": ">=7.0.8",
"ext-ctype": "*",
"ext-dom": "*",
"ext-gd": "*",
@@ -10,18 +12,23 @@
"ext-json": "*",
"ext-mysqli": "*",
"ext-soap": "*",
"combodo/tcpdf": "6.3.5",
"nikic/php-parser": "^3.1",
"pear/archive_tar": "1.4.14",
"pelago/emogrifier": "2.1.0",
"combodo/tcpdf": "~6.4.4",
"guzzlehttp/guzzle": "^6.5.8",
"laminas/laminas-mail": "^2.11",
"laminas/laminas-servicemanager": "^3.5",
"league/oauth2-google": "^3.0",
"nikic/php-parser": "~4.13.2",
"pear/archive_tar": "~1.4.14",
"pelago/emogrifier": "~3.1.0",
"scssphp/scssphp": "1.0.6",
"swiftmailer/swiftmailer": "5.4.12",
"swiftmailer/swiftmailer": "~6.3.0",
"symfony/console": "~3.4.47",
"symfony/dotenv": "~3.4.47",
"symfony/framework-bundle": "~3.4.47",
"symfony/polyfill-php70": "1.*",
"symfony/twig-bundle": "~3.4.47",
"symfony/yaml": "~3.4.47"
"symfony/yaml": "~3.4.47",
"thenetworg/oauth2-azure": "^2.0",
"twig/twig": "~1.42.5"
},
"require-dev": {
"symfony/stopwatch": "~3.4.47",
@@ -37,7 +44,7 @@
},
"config": {
"platform": {
"php": "5.6.0"
"php": "7.0.8"
},
"vendor-dir": "lib",
"preferred-install": {
@@ -52,7 +59,8 @@
"application",
"sources/application",
"sources/Composer",
"sources/Controller"
"sources/Controller",
"sources/Core"
],
"exclude-from-classmap": [
"core/dbobjectsearch.class.php",

1915
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -179,7 +179,7 @@ class ActionEmail extends ActionNotification
protected function FindRecipients($sRecipAttCode, $aArgs)
{
$sOQL = $this->Get($sRecipAttCode);
if (strlen($sOQL) == '') return '';
if (strlen($sOQL) === 0) return '';
try
{

View File

@@ -230,7 +230,7 @@ abstract class AsyncTask extends DBObject
$this->Set('remaining_retries', $this->GetMaxRetries($iErrorCode));
}
$this->Set('last_error', $sErrorMessage);
$this->SetTrim('last_error', $sErrorMessage);
$this->Set('last_error_code', $iErrorCode); // Note: can be ZERO !!!
$this->Set('last_attempt', time());

View File

@@ -63,22 +63,30 @@ class CMDBChangeOp extends DBObject
/**
* Describe (as a text string) the modifications corresponding to this change
*/
*/
public function GetDescription()
{
return '';
}
/**
* Safety net: in case the change is not given, let's guarantee that it will
* be set to the current ongoing change (or create a new one)
*/
* Safety net:
* * if change isn't persisted yet, use the current change and persist it if needed
* * in case the change is not given, let's guarantee that it will be set to the current ongoing change (or create a new one)
*
* @since 2.7.7 3.0.2 3.1.0 N°3717 do persist the current change if needed
*/
protected function OnInsert()
{
if ($this->Get('change') <= 0)
{
$this->Set('change', CMDBObject::GetCurrentChange());
$iChange = $this->Get('change');
if (($iChange <= 0) || (is_null($iChange))) {
$oChange = CMDBObject::GetCurrentChange();
if ($oChange->IsNew()) {
$oChange->DBWrite();
}
$this->Set('change', $oChange);
}
parent::OnInsert();
}
}

View File

@@ -114,6 +114,26 @@ abstract class CMDBObject extends DBObject
self::$m_oCurrChange = $oChange;
}
/**
* @param string $sUserInfo
* @param string $sOrigin
* @param \DateTime $oDate
*
* @throws \CoreException
*
* @since 2.7.7 3.0.2 3.1.0 N°3717 new method to reset current change
*/
public static function SetCurrentChangeFromParams($sUserInfo, $sOrigin = null, $oDate = null)
{
static::SetTrackInfo($sUserInfo);
static::SetTrackOrigin($sOrigin);
static::CreateChange();
if (!is_null($oDate)) {
static::$m_oCurrChange->Set("date", $oDate);
}
}
//
// Todo: simplify the APIs and do not pass the current change as an argument anymore
// SetTrackInfo to be invoked in very few cases (UI.php, CSV import, Data synchro)
@@ -145,6 +165,8 @@ abstract class CMDBObject extends DBObject
* $oMyChange->Set("userinfo", 'this is done by ... for ...');
* $iChangeId = $oMyChange->DBInsert();
*
* **warning** : this will do nothing if current change already exists !
*
* @see SetCurrentChange to specify a CMDBObject instance instead
*
* @param string $sInfo
@@ -157,6 +179,8 @@ abstract class CMDBObject extends DBObject
/**
* Provides information about the origin of the change
*
* **warning** : this will do nothing if current change already exists !
*
* @see SetTrackInfo
* @see SetCurrentChange to specify a CMDBObject instance instead
*
@@ -167,18 +191,15 @@ abstract class CMDBObject extends DBObject
{
self::$m_sOrigin = $sOrigin;
}
/**
* Get the additional information (defaulting to user name)
*/
protected static function GetTrackInfo()
*/
public static function GetTrackInfo()
{
if (is_null(self::$m_sInfo))
{
if (is_null(self::$m_sInfo)) {
return CMDBChange::GetCurrentUserName();
}
else
{
} else {
return self::$m_sInfo;
}
}
@@ -201,7 +222,8 @@ abstract class CMDBObject extends DBObject
/**
* Set to {@link $m_oCurrChange} a standard change record (done here 99% of the time, and nearly once per page)
*
* The CMDBChange is persisted so that it has a key > 0, and any new CMDBChangeOp can link to it
* @since 2.7.7 3.0.2 3.1.0 N°3717 {@see CMDBChange} **will be persisted later** in {@see \CMDBChangeOp::OnInsert} (was done previously directly here)
* This will avoid creating in DB CMDBChange lines without any corresponding CMDBChangeOp
*/
protected static function CreateChange()
{
@@ -209,7 +231,6 @@ abstract class CMDBObject extends DBObject
self::$m_oCurrChange->Set("date", time());
self::$m_oCurrChange->Set("userinfo", self::GetTrackInfo());
self::$m_oCurrChange->Set("origin", self::GetTrackOrigin());
self::$m_oCurrChange->DBInsert();
}
/**

View File

@@ -509,7 +509,7 @@ class Config
),
'email_transport' => array(
'type' => 'string',
'description' => 'Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocol)',
'description' => 'Mean to send emails: PHPMail (uses the function mail()), SMTP (implements the client protocol) or SMTP_OAuth (connect to the server using OAuth 2.0)',
'default' => "PHPMail",
'value' => "PHPMail",
'source_of_value' => '',

View File

@@ -1880,7 +1880,7 @@ abstract class DBObject implements iDisplay
$oTargetObj = MetaModel::GetObject($sTargetClass, $toCheck, false /*must be found*/, true /*allow all data*/);
if (is_null($oTargetObj))
{
return "Target object not found ($sTargetClass::$toCheck)";
return "Target object not found (".utils::HtmlEntities($sTargetClass).".::".utils::HtmlEntities($toCheck).")";
}
}
if ($oAtt->IsHierarchicalKey())
@@ -1889,7 +1889,7 @@ abstract class DBObject implements iDisplay
$aValues = $oAtt->GetAllowedValues(array('this' => $this));
if (!array_key_exists($toCheck, $aValues))
{
return "Value not allowed [$toCheck]";
return "Value not allowed [". utils::HtmlEntities($toCheck)."]";
}
}
}
@@ -1903,7 +1903,7 @@ abstract class DBObject implements iDisplay
$oTag->SetValues(explode(' ', $toCheck));
} catch (Exception $e)
{
return "Tag value '$toCheck' is not a valid tag list";
return "Tag value [". utils::HtmlEntities($toCheck)."] is not a valid tag list";
}
return true;
@@ -1931,7 +1931,7 @@ abstract class DBObject implements iDisplay
$oTag->SetValues($aValues);
} catch (Exception $e)
{
return "Set value '$toCheck' is not a valid set";
return "Set value[". utils::HtmlEntities($toCheck)."] is not a valid set";
}
return true;
@@ -1951,7 +1951,7 @@ abstract class DBObject implements iDisplay
{
if (!array_key_exists($toCheck, $aValues))
{
return "Value not allowed [$toCheck]";
return "Value not allowed [". utils::HtmlEntities($toCheck)."]";
}
}
if (!is_null($iMaxSize = $oAtt->GetMaxSize()))
@@ -1964,7 +1964,7 @@ abstract class DBObject implements iDisplay
}
if (!$oAtt->CheckFormat($toCheck))
{
return "Wrong format [$toCheck]";
return "Wrong format [". utils::HtmlEntities($toCheck)."]";
}
}
else
@@ -1977,9 +1977,9 @@ abstract class DBObject implements iDisplay
/**
* check attributes together
*
* @overwritable-hook You can extend this method in order to provide your own logic.
*
* @return bool
* @overwritable-hook You can extend this method in order to provide your own logic.
*
* @return true|string true if successful, the error description otherwise
*/
public function CheckConsistency()
{

View File

@@ -1203,8 +1203,10 @@ class DisplayableGraph extends SimpleGraph
* @param float $xMax Right coordinate of the bounding box to display the graph
* @param float $yMin Top coordinate of the bounding box to display the graph
* @param float $yMax Bottom coordinate of the bounding box to display the graph
*
* @since 2.7.7 3.0.2 3.1.0 N°4985 $sComments param is no longer optional
*/
function RenderAsPDF(PDFPage $oPage, $sComments = '', $sContextKey, $xMin = -1, $xMax = -1, $yMin = -1, $yMax = -1)
function RenderAsPDF(PDFPage $oPage, $sComments, $sContextKey, $xMin = -1, $xMax = -1, $yMin = -1, $yMax = -1)
{
$aContextDefs = static::GetContextDefinitions($sContextKey, false); // No need to develop the parameters
$oPdf = $oPage->get_tcpdf();

View File

@@ -24,38 +24,26 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
Swift_Preferences::getInstance()->setCharset('UTF-8');
use Combodo\iTop\Core\Email\EmailFactory;
use Combodo\iTop\Core\Email\iEMail;
define ('EMAIL_SEND_OK', 0);
define ('EMAIL_SEND_PENDING', 1);
define ('EMAIL_SEND_ERROR', 2);
class EMail
class EMail implements iEMail
{
protected $oMailer;
// Serialization formats
const ORIGINAL_FORMAT = 1; // Original format, consisting in serializing the whole object, inculding the Swift Mailer's object.
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
const FORMAT_V2 = 2; // New format, only the raw data are serialized (base64 encoded if needed)
protected static $m_oConfig = null;
protected $m_aData; // For storing data to serialize
public function LoadConfig($sConfigFile = ITOP_DEFAULT_CONFIG_FILE)
{
if (is_null(self::$m_oConfig))
{
self::$m_oConfig = new Config($sConfigFile);
}
}
protected $m_oMessage;
public function __construct()
{
$this->m_aData = array();
$this->m_oMessage = Swift_Message::newInstance();
$this->SetRecipientFrom(MetaModel::GetConfig()->Get('email_default_sender_address'), MetaModel::GetConfig()->Get('email_default_sender_label'));
$this->oMailer = EmailFactory::GetMailer();
}
/**
@@ -66,485 +54,97 @@ class EMail
*/
public function SerializeV2()
{
return serialize($this->m_aData);
return $this->oMailer->SerializeV2();
}
/**
* Custom de-serialization method
*
* @param string $sSerializedMessage The serialized representation of the message
*
* @return \Email
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \Symfony\Component\CssSelector\Exception\SyntaxErrorException
*/
static public function UnSerializeV2($sSerializedMessage)
{
$aData = unserialize($sSerializedMessage);
$oMessage = new Email();
if (array_key_exists('body', $aData))
{
$oMessage->SetBody($aData['body']['body'], $aData['body']['mimeType']);
}
if (array_key_exists('message_id', $aData))
{
$oMessage->SetMessageId($aData['message_id']);
}
if (array_key_exists('bcc', $aData))
{
$oMessage->SetRecipientBCC($aData['bcc']);
}
if (array_key_exists('cc', $aData))
{
$oMessage->SetRecipientCC($aData['cc']);
}
if (array_key_exists('from', $aData))
{
$oMessage->SetRecipientFrom($aData['from']['address'], $aData['from']['label']);
}
if (array_key_exists('reply_to', $aData))
{
$oMessage->SetRecipientReplyTo($aData['reply_to']);
}
if (array_key_exists('to', $aData))
{
$oMessage->SetRecipientTO($aData['to']);
}
if (array_key_exists('subject', $aData))
{
$oMessage->SetSubject($aData['subject']);
}
if (array_key_exists('headers', $aData))
{
foreach($aData['headers'] as $sKey => $sValue)
{
$oMessage->AddToHeader($sKey, $sValue);
}
}
if (array_key_exists('parts', $aData))
{
foreach($aData['parts'] as $aPart)
{
$oMessage->AddPart($aPart['text'], $aPart['mimeType']);
}
}
if (array_key_exists('attachments', $aData))
{
foreach($aData['attachments'] as $aAttachment)
{
$oMessage->AddAttachment(base64_decode($aAttachment['data']), $aAttachment['filename'], $aAttachment['mimeType']);
}
}
return $oMessage;
}
protected function SendAsynchronous(&$aIssues, $oLog = null)
{
try
{
AsyncSendEmail::AddToQueue($this, $oLog);
}
catch(Exception $e)
{
$aIssues = array($e->GetMessage());
return EMAIL_SEND_ERROR;
}
$aIssues = array();
return EMAIL_SEND_PENDING;
}
protected function SendSynchronous(&$aIssues, $oLog = null)
{
// If the body of the message is in HTML, embed all images based on attachments
$this->EmbedInlineImages();
$this->LoadConfig();
$sTransport = self::$m_oConfig->Get('email_transport');
switch ($sTransport)
{
case 'SMTP':
$sHost = self::$m_oConfig->Get('email_transport_smtp.host');
$sPort = self::$m_oConfig->Get('email_transport_smtp.port');
$sEncryption = self::$m_oConfig->Get('email_transport_smtp.encryption');
$sUserName = self::$m_oConfig->Get('email_transport_smtp.username');
$sPassword = self::$m_oConfig->Get('email_transport_smtp.password');
$oTransport = Swift_SmtpTransport::newInstance($sHost, $sPort, $sEncryption);
if (strlen($sUserName) > 0)
{
$oTransport->setUsername($sUserName);
$oTransport->setPassword($sPassword);
}
break;
case 'Null':
$oTransport = Swift_NullTransport::newInstance();
break;
case 'LogFile':
$oTransport = Swift_LogFileTransport::newInstance();
$oTransport->setLogFile(APPROOT.'log/mail.log');
break;
case 'PHPMail':
default:
$oTransport = Swift_MailTransport::newInstance();
}
$oMailer = Swift_Mailer::newInstance($oTransport);
$aFailedRecipients = array();
$this->m_oMessage->setMaxLineLength(0);
$oKPI = new ExecutionKPI();
try
{
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
if ($iSent === 0)
{
// Beware: it seems that $aFailedRecipients sometimes contains the recipients that actually received the message !!!
IssueLog::Warning('Email sending failed: Some recipients were invalid, aFailedRecipients contains: '.implode(', ', $aFailedRecipients));
$aIssues = array('Some recipients were invalid.');
$oKPI->ComputeStats('Email Sent', 'Error received');
return EMAIL_SEND_ERROR;
}
else
{
$aIssues = array();
$oKPI->ComputeStats('Email Sent', 'Succeded');
return EMAIL_SEND_OK;
}
}
catch (Exception $e)
{
$oKPI->ComputeStats('Email Sent', 'Error received');
throw $e;
}
}
/**
* Reprocess the body of the message (if it is an HTML message)
* to replace the URL of images based on attachments by a link
* to an embedded image (i.e. cid:....)
*/
protected function EmbedInlineImages()
{
if ($this->m_aData['body']['mimeType'] == 'text/html')
{
$oDOMDoc = new DOMDocument();
$oDOMDoc->preserveWhitespace = true;
@$oDOMDoc->loadHTML('<?xml encoding="UTF-8"?>'.$this->m_aData['body']['body']); // For loading HTML chunks where the character set is not specified
$oXPath = new DOMXPath($oDOMDoc);
$sXPath = '//img[@'.InlineImage::DOM_ATTR_ID.']';
$oImagesList = $oXPath->query($sXPath);
if ($oImagesList->length != 0)
{
foreach($oImagesList as $oImg)
{
$iAttId = $oImg->getAttribute(InlineImage::DOM_ATTR_ID);
$oAttachment = MetaModel::GetObject('InlineImage', $iAttId, false, true /* Allow All Data */);
if ($oAttachment)
{
$sImageSecret = $oImg->getAttribute('data-img-secret');
$sAttachmentSecret = $oAttachment->Get('secret');
if ($sImageSecret !== $sAttachmentSecret)
{
// @see N°1921
// If copying from another iTop we could get an IMG pointing to an InlineImage with wrong secret
continue;
}
$oDoc = $oAttachment->Get('contents');
$oSwiftImage = new Swift_Image($oDoc->GetData(), $oDoc->GetFileName(), $oDoc->GetMimeType());
$sCid = $this->m_oMessage->embed($oSwiftImage);
$oImg->setAttribute('src', $sCid);
}
}
}
$sHtmlBody = $oDOMDoc->saveHTML();
$this->m_oMessage->setBody($sHtmlBody, 'text/html', 'UTF-8');
}
return EmailFactory::GetMailer()::UnSerializeV2($sSerializedMessage);
}
public function Send(&$aIssues, $bForceSynchronous = false, $oLog = null)
{
//select a default sender if none is provided.
if(empty($this->m_aData['from']['address']) && !empty($this->m_aData['to'])){
$this->SetRecipientFrom($this->m_aData['to']);
}
if ($bForceSynchronous)
{
return $this->SendSynchronous($aIssues, $oLog);
}
else
{
$bConfigASYNC = MetaModel::GetConfig()->Get('email_asynchronous');
if ($bConfigASYNC)
{
return $this->SendAsynchronous($aIssues, $oLog);
}
else
{
return $this->SendSynchronous($aIssues, $oLog);
}
}
return $this->oMailer->Send($aIssues, $bForceSynchronous, $oLog);
}
public function AddToHeader($sKey, $sValue)
{
if (!array_key_exists('headers', $this->m_aData))
{
$this->m_aData['headers'] = array();
}
$this->m_aData['headers'][$sKey] = $sValue;
if (strlen($sValue) > 0)
{
$oHeaders = $this->m_oMessage->getHeaders();
switch(strtolower($sKey))
{
case 'return-path':
$this->m_oMessage->setReturnPath($sValue);
break;
default:
$oHeaders->addTextHeader($sKey, $sValue);
}
}
$this->oMailer->AddToHeader($sKey, $sValue);
}
public function SetMessageId($sId)
{
$this->m_aData['message_id'] = $sId;
// Note: Swift will add the angle brackets for you
// so let's remove the angle brackets if present, for historical reasons
$sId = str_replace(array('<', '>'), '', $sId);
$oMsgId = $this->m_oMessage->getHeaders()->get('Message-ID');
$oMsgId->SetId($sId);
$this->oMailer->SetMessageId($sId);
}
public function SetReferences($sReferences)
{
$this->AddToHeader('References', $sReferences);
$this->oMailer->SetReferences($sReferences);
}
public function SetBody($sBody, $sMimeType = 'text/html', $sCustomStyles = null)
{
if (($sMimeType === 'text/html') && ($sCustomStyles !== null))
{
$emogrifier = new \Pelago\Emogrifier($sBody, $sCustomStyles);
$sBody = $emogrifier->emogrify(); // Adds html/body tags if not already present
}
$this->m_aData['body'] = array('body' => $sBody, 'mimeType' => $sMimeType);
$this->m_oMessage->setBody($sBody, $sMimeType);
$this->oMailer->SetBody($sBody, $sMimeType, $sCustomStyles);
}
public function AddPart($sText, $sMimeType = 'text/html')
{
if (!array_key_exists('parts', $this->m_aData))
{
$this->m_aData['parts'] = array();
}
$this->m_aData['parts'][] = array('text' => $sText, 'mimeType' => $sMimeType);
$this->m_oMessage->addPart($sText, $sMimeType);
$this->oMailer->AddPart($sText, $sMimeType);
}
public function AddAttachment($data, $sFileName, $sMimeType)
{
if (!array_key_exists('attachments', $this->m_aData))
{
$this->m_aData['attachments'] = array();
}
$this->m_aData['attachments'][] = array('data' => base64_encode($data), 'filename' => $sFileName, 'mimeType' => $sMimeType);
$this->m_oMessage->attach(Swift_Attachment::newInstance($data, $sFileName, $sMimeType));
$this->oMailer->AddAttachment($data, $sFileName, $sMimeType);
}
public function SetSubject($sSubject)
{
$this->m_aData['subject'] = $sSubject;
$this->m_oMessage->setSubject($sSubject);
$this->oMailer->SetSubject($sSubject);
}
public function GetSubject()
{
return $this->m_oMessage->getSubject();
return $this->oMailer->GetSubject();
}
/**
* Helper to transform and sanitize addresses
* - get rid of empty addresses
*/
protected function AddressStringToArray($sAddressCSVList)
{
$aAddresses = array();
foreach(explode(',', $sAddressCSVList) as $sAddress)
{
$sAddress = trim($sAddress);
if (strlen($sAddress) > 0)
{
$aAddresses[] = $sAddress;
}
}
return $aAddresses;
}
public function SetRecipientTO($sAddress)
{
$this->m_aData['to'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setTo($aAddresses);
}
$this->oMailer->SetRecipientTO($sAddress);
}
public function GetRecipientTO($bAsString = false)
{
$aRes = $this->m_oMessage->getTo();
if ($aRes === null)
{
// There is no "To" header field
$aRes = array();
}
if ($bAsString)
{
$aStrings = array();
foreach ($aRes as $sEmail => $sName)
{
if (is_null($sName))
{
$aStrings[] = $sEmail;
}
else
{
$sName = str_replace(array('<', '>'), '', $sName);
$aStrings[] = "$sName <$sEmail>";
}
}
return implode(', ', $aStrings);
}
else
{
return $aRes;
}
return $this->oMailer->GetRecipientTO($bAsString);
}
public function SetRecipientCC($sAddress)
{
$this->m_aData['cc'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setCc($aAddresses);
}
$this->oMailer->SetRecipientCC($sAddress);
}
public function SetRecipientBCC($sAddress)
{
$this->m_aData['bcc'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setBcc($aAddresses);
}
$this->oMailer->SetRecipientBCC($sAddress);
}
public function SetRecipientFrom($sAddress, $sLabel = '')
{
$this->m_aData['from'] = array('address' => $sAddress, 'label' => $sLabel);
if ($sLabel != '')
{
$this->m_oMessage->setFrom(array($sAddress => $sLabel));
}
else if (!empty($sAddress))
{
$this->m_oMessage->setFrom($sAddress);
}
$this->oMailer->SetRecipientFrom($sAddress, $sLabel);
}
public function SetRecipientReplyTo($sAddress)
{
$this->m_aData['reply_to'] = $sAddress;
if (!empty($sAddress))
{
$this->m_oMessage->setReplyTo($sAddress);
}
$this->oMailer->SetRecipientReplyTo($sAddress);
}
}
/////////////////////////////////////////////////////////////////////////////////////
/**
* Extension to SwiftMailer: "debug" transport that pretends messages have been sent,
* but just log them to a file.
*
* @package Swift
* @author Denis Flaven
*/
class Swift_Transport_LogFileTransport extends Swift_Transport_NullTransport
{
protected $sLogFile;
/**
* Sends the given message.
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int The number of sent emails
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
$hFile = @fopen($this->sLogFile, 'a');
if ($hFile)
{
$sTxt = "================== ".date('Y-m-d H:i:s')." ==================\n";
$sTxt .= $message->toString()."\n";
@fwrite($hFile, $sTxt);
@fclose($hFile);
}
return parent::send($message, $failedRecipients);
}
public function setLogFile($sFilename)
{
$this->sLogFile = $sFilename;
}
}
/**
* Pretends messages have been sent, but just log them to a file.
*
* @package Swift
* @author Denis Flaven
*/
class Swift_LogFileTransport extends Swift_Transport_LogFileTransport
{
/**
* Create a new LogFileTransport.
*/
public function __construct()
{
call_user_func_array(
array($this, 'Swift_Transport_LogFileTransport::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('transport.null')
);
}
/**
* Create a new LogFileTransport instance.
*
* @return Swift_LogFileTransport
*/
public static function newInstance()
{
return new self();
}
}

View File

@@ -116,6 +116,18 @@ abstract class DOMSanitizer extends HTMLSanitizer
{
/** @var DOMDocument */
protected $oDoc;
/**
* @var string Class to use for InlineImage static method calls
* @used-by \Combodo\iTop\Test\UnitTest\Core\Sanitizer\HTMLDOMSanitizerTest::testDoSanitizeCallInlineImageProcessImageTag
*/
protected $sInlineImageClassName;
public function __construct($sInlineImageClassName = InlineImage::class)
{
parent::__construct();
$this->sInlineImageClassName = $sInlineImageClassName;
}
abstract public function GetTagsWhiteList();
@@ -211,7 +223,7 @@ abstract class DOMSanitizer extends HTMLSanitizer
// Recurse
$this->CleanNode($oNode);
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img')) {
InlineImage::ProcessImageTag($oNode);
$this->sInlineImageClassName::ProcessImageTag($oNode);
}
}
}
@@ -347,6 +359,30 @@ class HTMLDOMSanitizer extends DOMSanitizer
'white-space',
);
public function __construct($sInlineImageClassName = InlineImage::class)
{
parent::__construct($sInlineImageClassName);
// 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)) {
// 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+\$_.-]*))?)?)';
// 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 = '/'.str_replace('/', '\/', $sPattern).'/i';
self::$aAttrsWhiteList['href'] = $sPattern;
}
}
public function GetTagsWhiteList()
{
return static::$aTagsWhiteList;
@@ -372,30 +408,6 @@ class HTMLDOMSanitizer extends DOMSanitizer
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)) {
// 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+\$_.-]*))?)?)';
// 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 = '/'.str_replace('/', '\/', $sPattern).'/i';
self::$aAttrsWhiteList['href'] = $sPattern;
}
}
public function LoadDoc($sHTML)
{
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified

View File

@@ -6,6 +6,8 @@
*/
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Variable;
use PhpParser\Parser;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter\Standard;
@@ -80,38 +82,49 @@ class iTopConfigParser
* @param \PhpParser\Parser $oParser
* @param $sConfig
*
* @return \Combodo\iTop\Config\Validator\ConfigNodesVisitor
* @return void
*/
private function BrowseFile(\PhpParser\Parser $oParser, $sConfig)
private function BrowseFile(Parser $oParser, $sConfig)
{
$prettyPrinter = new Standard();
try
{
try {
$aNodes = $oParser->parse($sConfig);
}
catch (\Error $e)
{
catch (\Error $e) {
$sMessage = Dict::Format('config-parse-error', $e->getMessage(), $e->getLine());
$this->oException = new \Exception($sMessage, 0, $e);
}
foreach ($aNodes as $oAssignation)
{
if (! $oAssignation instanceof Assign)
{
foreach ($aNodes as $sKey => $oNode) {
// With PhpParser 3 we had an Assign node at root
// In PhpParser 4 the root node is now an Expression
if (false === ($oNode instanceof \PhpParser\Node\Stmt\Expression)) {
continue;
}
/** @var \PhpParser\Node\Stmt\Expression $oNode */
if (false === ($oNode->expr instanceof Assign)) {
continue;
}
/** @var Assign $oAssignation */
$oAssignation = $oNode->expr;
if (false === ($oAssignation->var instanceof Variable)) {
continue;
}
if (false === ($oAssignation->expr instanceof PhpParser\Node\Expr\Array_)) {
continue;
}
$sCurrentRootVar = $oAssignation->var->name;
if (!array_key_exists($sCurrentRootVar, $this->aVarsMap))
{
if (!array_key_exists($sCurrentRootVar, $this->aVarsMap)) {
continue;
}
$aCurrentRootVarMap =& $this->aVarsMap[$sCurrentRootVar];
foreach ($oAssignation->expr->items as $oItem)
{
foreach ($oAssignation->expr->items as $oItem) {
$sValue = $prettyPrinter->prettyPrintExpr($oItem->value);
$aCurrentRootVarMap[$oItem->key->value] = $sValue;
}

View File

@@ -551,6 +551,13 @@ class LogChannels
const INLINE_IMAGE = 'InlineImage';
/**
* @var string
* @since 3.0.1 N°4849
* @since 2.7.7 N°4635
*/
const NOTIFICATIONS = 'notifications';
const PORTAL = 'portal';
}
@@ -691,7 +698,7 @@ abstract class LogAPI
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT]))
{
return $sLogLevelMin[$sChannel];
return $sLogLevelMin[static::CHANNEL_DEFAULT];
}
return static::LEVEL_DEFAULT;

View File

@@ -2424,26 +2424,33 @@ fieldset .details>.field_container {
.selectize-dropdown,
.selectize-input,
.selectize-input input{
font-size: 12px;
}
.selectize-input{
padding: 2px 2px 0px 2px; /* padding-bottom = padding-top - item margin-bottom */
border: 1px solid #ABABAB;
border-radius: 0;
.selectize-input input {
font-size: 12px;
}
.attribute-set-item.partial-code{
color: transparentize($gray-darker, 0.4);
background-color: lighten($gray-lighter, 5%);
}
}
}
}
}
}
}
}
}
.selectize-input {
padding: 2px 2px 0px 2px; /* padding-bottom = padding-top - item margin-bottom */
border: 1px solid #ABABAB;
border-radius: 0;
.attribute-set-item.partial-code {
color: transparentize($gray-darker, 0.4);
background-color: lighten($gray-lighter, 5%);
}
}
}
}
}
}
}
}
}
&[data-attribute-type="AttributeDuration"] {
.field_value_container {
white-space: nowrap;
}
}
}
.one-col-details .details .field_container.field_small {
div.field_label {

View File

@@ -4,6 +4,8 @@
* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=custom-theme&bgImgOpacityError=18&bgImgOpacityHighlight=75&bgImgOpacityActive=65&bgImgOpacityHover=100&bgImgOpacityDefault=100&bgImgOpacityContent=100&bgImgOpacityHeader=35&cornerRadiusShadow=5px&offsetLeftShadow=-5px&offsetTopShadow=-5px&thicknessShadow=5px&opacityShadow=20&bgImgOpacityShadow=10&bgTextureShadow=flat&bgColorShadow=%23000000&opacityOverlay=50&bgImgOpacityOverlay=20&bgTextureOverlay=diagonals_thick&bgColorOverlay=%23666666&iconColorError=%23ffd27a&fcError=%23ffffff&borderColorError=%23cd0a0a&bgTextureError=diagonals_thick&bgColorError=%23b81900&iconColorHighlight=%231c94c4&fcHighlight=%23363636&borderColorHighlight=%23fed22f&bgTextureHighlight=flat&bgColorHighlight=%23ffe45c&iconColorActive=%23E87C1E&fcActive=%23E87C1E&borderColorActive=%23E87C1E&bgTextureActive=flat&bgColorActive=%23ffffff&iconColorHover=%23E87C1E&fcHover=%23E87C1E&borderColorHover=%23E87C1E&bgTextureHover=flat&bgColorHover=%23fde17c&iconColorDefault=%23F26522&fcDefault=%23555555&borderColorDefault=%23cccccc&bgTextureDefault=flat&bgColorDefault=%23f1f1f1&iconColorContent=%23222222&fcContent=%23333333&borderColorContent=%23dddddd&bgTextureContent=flat&bgColorContent=%23eeeeee&iconColorHeader=%23ffffff&fcHeader=%23ffffff&borderColorHeader=%23F26522&bgTextureHeader=flat&bgColorHeader=%23E87C1E&cornerRadius=0&fwDefault=bold&fsDefault=1.1em&ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif
* Copyright jQuery Foundation and other contributors; Licensed MIT
* The original css file has been scssized (through www.css2scss.com)
*
* Other modification done : replaced the `Alpha(` by `alpha(` to avoid warnings generated by SCSSPHP
*/
.ui-draggable-handle {
-ms-touch-action: none;
@@ -46,26 +48,27 @@
}
}
.ui-helper-zfix {
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
opacity: 0;
filter: Alpha(Opacity=0);
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
opacity: 0;
filter: alpha(Opacity=0);
}
.ui-front {
z-index: 100;
}
.ui-state-disabled {
cursor: default !important;
pointer-events: none;
opacity: .35;
filter: Alpha(Opacity=35);
background-image: none;
.ui-icon {
filter: Alpha(Opacity=35);
}
cursor: default !important;
pointer-events: none;
opacity: .35;
filter: alpha(Opacity=35);
background-image: none;
.ui-icon {
filter: alpha(Opacity=35);
}
}
.ui-icon {
display: inline-block;
@@ -86,14 +89,14 @@
display: block;
}
.ui-widget-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #666666 url($approot-relative + "css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=" + $version) 50% 50% repeat;
opacity: .5;
filter: Alpha(Opacity=50);
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #666666 url($approot-relative + "css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=" + $version) 50% 50% repeat;
opacity: .5;
filter: alpha(Opacity=50);
}
.ui-resizable {
position: relative;
@@ -1069,14 +1072,14 @@ body {
font-weight: bold;
}
.ui-priority-secondary {
opacity: .7;
filter: Alpha(Opacity=70);
font-weight: normal;
opacity: .7;
filter: alpha(Opacity=70);
font-weight: normal;
}
.ui-state-disabled {
opacity: .35;
filter: Alpha(Opacity=35);
background-image: none;
opacity: .35;
filter: alpha(Opacity=35);
background-image: none;
}
.ui-icon {
background-image: url($approot-relative + "css/ui-lightness/images/ui-icons_222222_256x240.png?v=" + $version);
@@ -1137,14 +1140,14 @@ body {
font-weight: bold;
}
.ui-priority-secondary {
opacity: .7;
filter: Alpha(Opacity=70);
font-weight: normal;
opacity: .7;
filter: alpha(Opacity=70);
font-weight: normal;
}
.ui-state-disabled {
opacity: .35;
filter: Alpha(Opacity=35);
background-image: none;
opacity: .35;
filter: alpha(Opacity=35);
background-image: none;
}
.ui-icon {
background-image: url($approot-relative + "css/ui-lightness/images/ui-icons_ffffff_256x240.png?v=" + $version);
@@ -1341,9 +1344,9 @@ a {
font-weight: bold;
}
.ui-priority-secondary {
opacity: .7;
filter: Alpha(Opacity=70);
font-weight: normal;
opacity: .7;
filter: alpha(Opacity=70);
font-weight: normal;
}
.ui-icon-blank {
background-position: 16px 16px;

View File

@@ -301,9 +301,7 @@ function DisplayLostAttachments(iTopWebPage &$oP, ApplicationContext &$oAppConte
$sHistoryEntry = Dict::Format('DBTools:LostAttachments:History', $oOrmDocument->GetFileName());
CMDBObject::SetTrackInfo(UserRights::GetUserFriendlyName());
$oChangeOp = MetaModel::NewObject('CMDBChangeOpPlugin');
/** @var \Change $oChange */
$oChange = CMDBObject::GetCurrentChange();
$oChangeOp->Set('change', $oChange->GetKey());
// CMDBChangeOp.change will be automatically filled
$oChangeOp->Set('objclass', $sTargetClass);
$oChangeOp->Set('objkey', $sTargetId);
$oChangeOp->Set('description', $sHistoryEntry);

View File

@@ -20,6 +20,7 @@
<module>combodo-db-tools</module>
<module>itop-core-update</module>
<module>itop-hub-connector</module>
<module>itop-oauth-client</module>
</modules>
<mandatory>true</mandatory>
</choice>

View File

@@ -23,6 +23,7 @@ class ConfigNodesVisitor extends NodeVisitorAbstract
Node\Name::class,
Node\Const_::class,
Node\Identifier::class,
Node\Expr\Array_::class,
Node\Expr\ArrayDimFetch::class,
@@ -44,6 +45,7 @@ class ConfigNodesVisitor extends NodeVisitorAbstract
Node\Expr\PreDec::class,
Node\Expr\PreInc::class,
Node\Expr\Print_::class,
Node\Stmt\Expression::class,
Node\Expr\Ternary::class,
Node\Expr\UnaryMinus::class,
Node\Expr\UnaryPlus::class,

View File

@@ -76,7 +76,6 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -76,7 +76,6 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -76,7 +76,6 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Anwendungsupgrade nicht möglich: %1$s',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Einige angepasste Dateien wurden erkannt</b>, eine Teil-Update kann nicht ausgeführt werden.<br/>Befolgen Sie das <a target="_blank" href="%2$s">Verfahren</a>, um Ihr iTop manuell zu aktualisieren. Sie müssen das <a href="%1$s">Setup</a> benutzen, um Ihre Applikation zu aktualisieren.<br />',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Einige neue Module wurden erkannt</b>, eine Teil-Update kann nicht ausgeführt werden.<br/>Befolgen Sie das <a target="_blank" href="%2$s">Verfahren</a>, um Ihr iTop manuell zu aktualisieren. Sie müssen das <a href="%1$s">Setup</a> benutzen, um Ihre Applikation zu aktualisieren.<br />',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -76,7 +76,6 @@ Dict::Add('EN US', 'English', 'English', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check',
// Setup Messages

View File

@@ -77,7 +77,6 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'La aplicación no puede ser actualizada: %1$s',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Advertencia: la actualización de la aplicación puede fallar: %1$s',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -73,10 +73,9 @@ Dict::Add('FR FR', 'French', 'Français', array(
'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Échec de la vérification des fichiers (Fichier manquant %1$s)',
'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Échec de la vérification des fichiers',
'iTopUpdate:UI:CanCoreUpdate:Yes' => 'L\'application peut être mise à jour',
'iTopUpdate:UI:CanCoreUpdate:No' => 'L\'application ne peut pas être mise à jour : %1$s',
'iTopUpdate:UI:CanCoreUpdate:No' => 'L\'application ne peut pas être mise à jour : %1$s',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Attention : la mise à jour de l\'application peut échouer : %1$s',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Des fichiers modifiés ont été détectés</b>, une mise à jour partielle ne peut pas être effectuée.<br />Suivez la <a target="_blank" href="%2$s"> procedure</a> pour mettre à jour manuellement votre iTop. Vous devez utiliser la page <a href="%1$s">d\'installation</a> pour mettre à jour l\'application.',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>De nouveaux modules ont été détectés</b>, une mise à jour partielle ne peut pas être effectuée.<br />Suivez la <a target="_blank" href="%2$s"> procedure</a> pour mettre à jour manuellement votre iTop. Vous devez utiliser la page <a href="%1$s">d\'installation</a> pour mettre à jour l\'application.',
'iTopUpdate:UI:CheckInProgress'=>'Veuillez patienter pendant la vérification d\'intégrité',
// Setup Messages

View File

@@ -76,7 +76,6 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -76,7 +76,6 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -76,7 +76,6 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -78,7 +78,6 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Updaten van de toepassing is niet mogelijk: %1$s',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -76,7 +76,6 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -64,7 +64,6 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Приложение не может быть обновлено: %1$s',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -76,7 +76,6 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -9,7 +9,6 @@
namespace Combodo\iTop\CoreUpdate\Service;
use Combodo\iTop\FilesInformation\Service\FileIntegrityException;
use Combodo\iTop\FilesInformation\Service\FilesIntegrity;
use DBBackup;
use Dict;
@@ -539,9 +538,6 @@ final class CoreUpdater
$sRootPath = self::UPDATE_DIR.'web/';
FilesIntegrity::CheckInstallationIntegrity($sRootPath);
///Check new modules
self::CheckNewModules($sRootPath);
SetupLog::Info('itop-core-update: Files integrity OK');
} catch (Exception $e)
{
@@ -609,38 +605,4 @@ final class CoreUpdater
throw $e;
}
}
/**
* Check if new modules (not already installed) are present, and throw an exception if that is the case as core update doesn't know how to install them automatically for know
*
* @param string $sRootPath
*
* @throws \ApplicationException
* @since 2.7.7 3.0.1
*/
private static function CheckNewModules($sRootPath)
{
$aFilesInfo = FilesIntegrity::GetInstalledFiles($sRootPath.'manifest.xml');
if ($aFilesInfo === false) {
throw new FileIntegrityException(Dict::Format('FilesInformation:Error:MissingFile', 'manifest.xml'));
}
@clearstatcache();
$sSourceDir = MetaModel::GetConfig()->Get('source_dir');
foreach ($aFilesInfo as $aFileInfo) {
if (strpos($aFileInfo['path'], $sSourceDir) === 0) {
$aFilePath = explode('/', $aFileInfo['path']);
$sFolderPath = $aFilePath[0].'/'.$aFilePath[1].'/'.$aFilePath[2];
//if module don't already exist in itop and if module listed in manifest.xml is included in zip
if (!is_dir(APPROOT.'/'.$sFolderPath) && !is_file(APPROOT.'/'.$sFolderPath)
&& is_dir($sRootPath.'/'.$sFolderPath)) {
$sLink = utils::GetAbsoluteUrlAppRoot().'setup/';
$sLinkManualUpdate = 'https://www.itophub.io/wiki/page?id='.utils::GetItopVersionWikiSyntax().'%3Ainstall%3Aupgrading_itop#manually';
throw new FileIntegrityException(Dict::Format('iTopUpdate:UI:CannotUpdateNewModules' , $sLink, $sLinkManualUpdate));
}
}
// Packed with missing files...
}
}
}

View File

@@ -11,6 +11,7 @@ require_once(APPROOT."setup/runtimeenv.class.inc.php");
use Config;
use Exception;
use ModelFactory;
use RunTimeEnvironment;
use SetupUtils;
@@ -126,4 +127,38 @@ class RunTimeEnvironmentCoreUpdater extends RunTimeEnvironment
}
throw new Exception('No configuration file available');
}
protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir)
{
$aRet = parent::GetMFModulesToCompile($sSourceEnv, $sSourceDir);
// Add new mandatory modules from datamodel 2.x only
$sSourceDirFull = APPROOT.$sSourceDir;
if (!is_dir($sSourceDirFull))
{
throw new Exception("The source directory '$sSourceDirFull' does not exist (or could not be read)");
}
$aDirsToCompile = [$sSourceDirFull];
$oFactory = new ModelFactory($aDirsToCompile);
$aModules = $oFactory->FindModules();
$aAvailableModules = [];
/** @var \MFModule $oModule */
foreach ($aModules as $oModule) {
$aAvailableModules[$oModule->GetName()] = $oModule;
}
// TODO check the auto-selected modules here
foreach($this->oExtensionsMap->GetAllExtensions() as $oExtension) {
if ($oExtension->bMarkedAsChosen) {
foreach ($oExtension->aModules as $sModuleName) {
if (!isset($aRet[$sModuleName]) && isset($aAvailableModules[$sModuleName])) {
$aRet[$sModuleName] = $aAvailableModules[$sModuleName];
}
}
}
}
return $aRet;
}
}

View File

@@ -76,7 +76,6 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s~~',
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s~~',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -64,7 +64,7 @@
<fieldset>
<div class="message message-error">
<div>
<span>{{ sError|raw }}</span>
<span>{{ sError }}</span>
</div>
</div>

View File

@@ -76,7 +76,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'iTopUpdate:UI:CanCoreUpdate:No' => '应用无法升级: %1$s',
'iTopUpdate:UI:CanCoreUpdate:Warning' => '警告: 应用升级可能会失败: %1$s',
'iTopUpdate:UI:CannotUpdateUseSetup' => '<b>Some modified files were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CannotUpdateNewModules' => '<b>Some new modules were detected</b>, a partial update cannot be executed.</br>Follow the <a target="_blank" href="%2$s"> procedure</a> in order to manually upgrade your iTop. You must use the <a href="%1$s">setup</a> to update the application.~~',
'iTopUpdate:UI:CheckInProgress'=>'Please wait during integrity check~~',
// Setup Messages

View File

@@ -15,12 +15,13 @@
<module_parameters>
<parameters id="itop-hub-connector" _delta="define">
<url>https://www.itophub.io</url>
<route_landing>/my-instances/landing-from-remote</route_landing>
<route_landing_stateless>/stateless-remote-itop/landing-from-remote-stateless</route_landing_stateless>
<route_fetch_unread_messages>/api/messages</route_fetch_unread_messages>
<route_mark_all_messages_as_read>/api/messages/mark-all-as-read</route_mark_all_messages_as_read>
<route_view_all_messages>/messages</route_view_all_messages>
<route_landing>/my-instances/landing-from-remote</route_landing>
<route_landing_stateless>/stateless-remote-itop/landing-from-remote-stateless</route_landing_stateless>
<route_fetch_unread_messages>/api/messages</route_fetch_unread_messages>
<route_mark_all_messages_as_read>/api/messages/mark-all-as-read</route_mark_all_messages_as_read>
<route_view_all_messages>/messages</route_view_all_messages>
<setup_url>../pages/exec.php?exec_module=itop-hub-connector&amp;exec_page=launch.php&amp;target=inform_after_setup</setup_url>
<rgpd_url>https://www.itophub.io/page/data-privacy</rgpd_url>
</parameters>
</module_parameters>
</itop_design>

View File

@@ -0,0 +1,5 @@
# Extension OAuth 2.0 client
## GMail
If the account is in test, after 7 days the tokens are no longer valid, you have to renew the tokens manually.

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\OAuthClient;
use Combodo\iTop\OAuthClient\Controller\AjaxOauthClientController;
require_once(APPROOT.'application/startup.inc.php');
if (version_compare(ITOP_DESIGN_LATEST_VERSION , '3.0') >= 0) {
$sTemplates = MODULESROOT.'itop-oauth-client/templates';
} else {
$sTemplates = MODULESROOT.'itop-oauth-client/templates/legacy';
}
$oUpdateController = new AjaxOauthClientController($sTemplates, 'itop-oauth-client');
$oUpdateController->SetMenuId('OAuthClient');
$oUpdateController->HandleOperation();

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><linearGradient id="k8yl7~hDat~FaoWq8WjN6a" x1="-1254.397" x2="-1261.911" y1="877.268" y2="899.466" gradientTransform="translate(1981.75 -1362.063) scale(1.5625)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#114a8b"/><stop offset="1" stop-color="#0669bc"/></linearGradient><path fill="url(#k8yl7~hDat~FaoWq8WjN6a)" d="M17.634,6h11.305L17.203,40.773c-0.247,0.733-0.934,1.226-1.708,1.226H6.697 c-0.994,0-1.8-0.806-1.8-1.8c0-0.196,0.032-0.39,0.094-0.576L15.926,7.227C16.173,6.494,16.86,6,17.634,6L17.634,6z"/><path fill="#0078d4" d="M34.062,29.324H16.135c-0.458-0.001-0.83,0.371-0.831,0.829c0,0.231,0.095,0.451,0.264,0.608 l11.52,10.752C27.423,41.826,27.865,42,28.324,42h10.151L34.062,29.324z"/><linearGradient id="k8yl7~hDat~FaoWq8WjN6b" x1="-1252.05" x2="-1253.788" y1="887.612" y2="888.2" gradientTransform="translate(1981.75 -1362.063) scale(1.5625)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-opacity=".3"/><stop offset=".071" stop-opacity=".2"/><stop offset=".321" stop-opacity=".1"/><stop offset=".623" stop-opacity=".05"/><stop offset="1" stop-opacity="0"/></linearGradient><path fill="url(#k8yl7~hDat~FaoWq8WjN6b)" d="M17.634,6c-0.783-0.003-1.476,0.504-1.712,1.25L5.005,39.595 c-0.335,0.934,0.151,1.964,1.085,2.299C6.286,41.964,6.493,42,6.702,42h9.026c0.684-0.122,1.25-0.603,1.481-1.259l2.177-6.416 l7.776,7.253c0.326,0.27,0.735,0.419,1.158,0.422h10.114l-4.436-12.676l-12.931,0.003L28.98,6H17.634z"/><linearGradient id="k8yl7~hDat~FaoWq8WjN6c" x1="-1252.952" x2="-1244.704" y1="876.6" y2="898.575" gradientTransform="translate(1981.75 -1362.063) scale(1.5625)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#3ccbf4"/><stop offset="1" stop-color="#2892df"/></linearGradient><path fill="url(#k8yl7~hDat~FaoWq8WjN6c)" d="M32.074,7.225C31.827,6.493,31.141,6,30.368,6h-12.6c0.772,0,1.459,0.493,1.705,1.224 l10.935,32.399c0.318,0.942-0.188,1.963-1.13,2.281C29.093,41.968,28.899,42,28.703,42h12.6c0.994,0,1.8-0.806,1.8-1.801 c0-0.196-0.032-0.39-0.095-0.575L32.074,7.225z"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><path fill="#fbc02d" d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12 s5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24s8.955,20,20,20 s20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"/><path fill="#e53935" d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039 l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"/><path fill="#4caf50" d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36 c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"/><path fill="#1565c0" d="M43.611,20.083L43.595,20L42,20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571 c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"/></svg>

After

Width:  |  Height:  |  Size: 977 B

View File

@@ -0,0 +1,99 @@
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
// Function used to open OAuth popup
var oWindowObjectReference = null;
var sPreviousUrl = null;
var oListener = null;
var sOAuthAjaxURI = null;
var sOAuthObjClass = null;
var sOAuthObjKey = null;
var sOAuthReturnURI = null;
const oOnOauthSuccess = function (event) {
if (oListener !== null) {
clearInterval(oListener);
}
$.post(
sOAuthAjaxURI,
{
operation: 'GetDisplayAuthenticationResults',
class: sOAuthObjClass,
id: sOAuthObjKey,
redirect_url: event.data
},
function (oData) {
window.location = oData.data;
}
);
}
const oOpenSignInWindow = function (url, name) {
// Remove any existing event listener
window.removeEventListener('message', oOnOauthSuccess);
if (oListener !== null) {
clearInterval(oListener);
}
// Window features
const sWindowFeatures = 'toolbar=no, menubar=no, width=600, height=700, top=100, left=100';
if (oWindowObjectReference === null || oWindowObjectReference.closed) {
/* If the pointer to the window object in memory does not exist
or if such pointer exists but the window was closed */
oWindowObjectReference = window.open(url, name, sWindowFeatures);
} else if (sPreviousUrl !== url) {
/* If the resource to load is different,
then we load it in the already opened secondary window, and then
we bring such window back on top/in front of its parent window. */
oWindowObjectReference = window.open(url, name, sWindowFeatures);
oWindowObjectReference.focus();
} else {
/* Else the window reference must exist and the window
is not closed; therefore, we can bring it back on top of any other
window with the focus() method. There would be no need to re-create
the window or to reload the referenced resource. */
oWindowObjectReference.focus();
}
/* Let know every second our child window that we're waiting for it to complete,
once we reach our landing page, it'll send us a reply
*/
oListener = window.setInterval(function () {
if (oWindowObjectReference.closed) {
clearInterval(oListener);
}
oWindowObjectReference.postMessage('anyone', sOAuthReturnURI);
}, 1000);
/* Once we receive a response, transmit it to the server to get authenticate and display
results
*/
window.addEventListener('message', oOnOauthSuccess, false);
// Assign the previous URL
sPreviousUrl = url;
};
const OAuthConnect = function(sClass, sId, sAjaxUri, sReturnUri) {
sOAuthAjaxURI = sAjaxUri;
sOAuthObjClass = sClass;
sOAuthObjKey = sId;
sOAuthReturnURI = sReturnUri;
$.post(
sOAuthAjaxURI,
{
operation: 'GetOAuthAuthorizationUrl',
class: sOAuthObjClass,
id: sOAuthObjKey
},
function (oData) {
if (oData.status === 'success') {
oOpenSignInWindow(oData.data.authorization_url, 'OAuth authorization')
}
}
);
}

View File

@@ -0,0 +1,16 @@
{
"config": {
"classmap-authoritative": true
},
"autoload": {
"psr-4": {
"Combodo\\iTop\\OAuthClient\\": "src"
}
},
"name": "combodo/itop-oauth-client",
"type": "itop-extension",
"description": "Remote authentication for OAuth 2.0",
"require": {
"composer-runtime-api": "^2.0"
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('CS CZ', 'Czech', 'Čeština', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('CS CZ', 'Czech', 'Čeština', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('CS CZ', 'Czech', 'Čeština', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('CS CZ', 'Czech', 'Čeština', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('DA DA', 'Danish', 'Dansk', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('DA DA', 'Danish', 'Dansk', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('DA DA', 'Danish', 'Dansk', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('DA DA', 'Danish', 'Dansk', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,891 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.7">
<constants/>
<classes>
<class id="OAuthClient" _delta="define">
<parent>cmdbAbstractObject</parent>
<properties>
<category>grant_by_profile,application</category>
<abstract>true</abstract>
<key_type>autoincrement</key_type>
<db_table>priv_oauth_client</db_table>
<db_key_field>id</db_key_field>
<db_final_class_field/>
<naming>
<attributes>
<attribute id="provider"/>
<attribute id="name"/>
</attributes>
</naming>
<display_template/>
<icon/>
<reconciliation>
<attributes>
<attribute id="provider"/>
<attribute id="name"/>
</attributes>
</reconciliation>
</properties>
<fields>
<field id="provider" xsi:type="AttributeString">
<sql>provider</sql>
<default_value/>
<is_null_allowed>false</is_null_allowed>
</field>
<field id="name" xsi:type="AttributeString">
<sql>name</sql>
<default_value/>
<is_null_allowed>false</is_null_allowed>
</field>
<field id="status" xsi:type="AttributeEnum">
<always_load_in_tables>true</always_load_in_tables>
<values>
<value id="active">active</value>
<value id="inactive">inactive</value>
</values>
<sql>status</sql>
<default_value>inactive</default_value>
<is_null_allowed>false</is_null_allowed>
</field>
<field id="description" xsi:type="AttributeText">
<sql>description</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="client_id" xsi:type="AttributeText">
<sql>client_id</sql>
<default_value/>
<is_null_allowed>false</is_null_allowed>
</field>
<field id="client_secret" xsi:type="AttributeText">
<sql>client_secret</sql>
<default_value/>
<is_null_allowed>false</is_null_allowed>
</field>
<field id="refresh_token" xsi:type="AttributeText">
<sql>refresh_token</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
<tracking_level>none</tracking_level>
</field>
<field id="refresh_token_expiration" xsi:type="AttributeDateTime">
<sql>refresh_token_expiration</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
<tracking_level>none</tracking_level>
</field>
<field id="token" xsi:type="AttributeText">
<sql>token</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
<tracking_level>none</tracking_level>
</field>
<field id="token_expiration" xsi:type="AttributeDateTime">
<sql>token_expiration</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
<tracking_level>none</tracking_level>
</field>
<field id="redirect_url" xsi:type="AttributeURL">
<sql>redirect_url</sql>
<default_value/>
<target>_blank</target>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="mailbox_list" xsi:type="AttributeLinkedSet">
<linked_class>MailInboxOAuth</linked_class>
<ext_key_to_me>oauth_client_id</ext_key_to_me>
<count_min>0</count_min>
<count_max>0</count_max>
</field>
</fields>
<methods>
<method id="DisplayBareHeader">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
{
parent::DisplayBareHeader($oPage, $bEditMode);
if (!$bEditMode) {
$oConfig = utils::GetConfig();
if ($this->Get('status') == 'inactive') {
$oPage->p('<b>'.Dict::S('itop-oauth-client:Message:MissingToken').'</b>');
} elseif ($this->Get('used_for_smtp') == 'yes' && $oConfig->Get('email_transport_smtp.username') == $this->Get('name')) {
$sLabel = Dict::S('itop-oauth-client:UsedForSMTP');
$sTestLabel = Dict::S('itop-oauth-client:TestSMTP');
$sTestURL = utils::GetAbsoluteUrlAppRoot().'setup/email.test.php';
$oPage->p("<b>$sLabel</b>&nbsp;<a href='$sTestURL' target='_blank'>$sTestLabel</a>");
}
}
}
]]></code>
</method>
<method id="GetAttributeFlags">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
if ($sAttCode == 'status') {
return OPT_ATT_READONLY;
}
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
}
]]></code>
</method>
<method id="GetInitialStateAttributeFlags">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function GetInitialStateAttributeFlags($sAttCode, &$aReasons = array())
{
if ($sAttCode == 'status') {
return OPT_ATT_READONLY;
}
return parent::GetInitialStateAttributeFlags($sAttCode, $aReasons);
}
]]></code>
</method>
<method id="GetDefaultMailServer">
<static>false</static>
<access>public</access>
<code><![CDATA[
public function GetDefaultMailServer()
{
return 'imap.'.$this->Get('provider').'.com';
}
]]></code>
</method>
<method id="GetDefaultMailServerPort">
<static>false</static>
<access>public</access>
<code><![CDATA[
public function GetDefaultMailServerPort()
{
return 993;
}
]]></code>
</method>
<method id="GetAccessToken">
<static>false</static>
<access>public</access>
<code><![CDATA[
public function GetAccessToken()
{
if ($this->Get('status') == 'active') {
return new \League\OAuth2\Client\Token\AccessToken([
'access_token' => $this->Get('token'),
'expires_in' => date_format(new DateTime($this->Get('token_expiration')), 'U') - time(),
'refresh_token' => $this->Get('refresh_token'),
'token_type' => 'Bearer',
]);
}
return null;
}
]]></code>
</method>
<method id="SetAccessToken">
<static>false</static>
<access>public</access>
<code><![CDATA[
public function SetAccessToken(\League\OAuth2\Client\Token\AccessTokenInterface $oAccessToken)
{
$this->Set('token', $oAccessToken->getToken());
$this->Set('token_expiration', date(AttributeDateTime::GetSQLFormat(), $oAccessToken->getExpires()));
if (!empty($oAccessToken->getRefreshToken())) {
$this->Set('refresh_token', $oAccessToken->getRefreshToken());
}
$this->Set('status', 'active');
$this->DBUpdate();
}
]]></code>
</method>
</methods>
<presentation>
<details>
<items>
<item id="name">
<rank>1</rank>
</item>
<item id="description">
<rank>2</rank>
</item>
<item id="provider">
<rank>3</rank>
</item>
<item id="redirect_url">
<rank>5</rank>
</item>
<item id="client_id">
<rank>6</rank>
</item>
<item id="client_secret">
<rank>7</rank>
</item>
<item id="mailbox_list">
<rank>8</rank>
</item>
</items>
</details>
<list>
<items>
<item id="status">
<rank>1</rank>
</item>
<item id="provider">
<rank>3</rank>
</item>
</items>
</list>
<search>
<items>
<item id="name">
<rank>1</rank>
</item>
<item id="provider">
<rank>2</rank>
</item>
</items>
</search>
<default_search>
<items>
<item id="name">
<rank>1</rank>
</item>
<item id="provider">
<rank>2</rank>
</item>
</items>
</default_search>
</presentation>
</class>
<class id="OAuthClientAzure" _delta="define">
<parent>OAuthClient</parent>
<properties>
<category>grant_by_profile,application</category>
<abstract>false</abstract>
<key_type>autoincrement</key_type>
<db_table>priv_oauth_client_azure</db_table>
<db_key_field>id</db_key_field>
<db_final_class_field/>
<naming>
<attributes>
<attribute id="provider"/>
<attribute id="name"/>
</attributes>
</naming>
<display_template/>
<icon/>
<reconciliation>
<attributes>
<attribute id="provider"/>
<attribute id="name"/>
</attributes>
</reconciliation>
<uniqueness_rules>
<rule id="server">
<attributes>
<attribute id="provider"/>
<attribute id="client_id"/>
<attribute id="client_secret"/>
</attributes>
<is_blocking>true</is_blocking>
</rule>
</uniqueness_rules>
</properties>
<fields>
<field id="scope" xsi:type="AttributeEnumSet">
<always_load_in_tables>true</always_load_in_tables>
<values>
<value id="SMTP">SMTP</value>
<value id="IMAP">IMAP</value>
</values>
<sql>scope</sql>
<default_value>SMTP,IMAP</default_value>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="advanced_scope" xsi:type="AttributeString">
<sql>advanced_scope</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="used_scope" xsi:type="AttributeEnum">
<always_load_in_tables>true</always_load_in_tables>
<values>
<value id="simple">simple</value>
<value id="advanced">advanced</value>
</values>
<sql>used_scope</sql>
<default_value>simple</default_value>
<is_null_allowed>false</is_null_allowed>
<dependencies>
<attribute id="scope"/>
<attribute id="advanced_scope"/>
</dependencies>
</field>
<field id="used_for_smtp" xsi:type="AttributeEnum">
<always_load_in_tables>true</always_load_in_tables>
<values>
<value id="yes">yes</value>
<value id="no">no</value>
</values>
<sql>used_for_smtp</sql>
<default_value>no</default_value>
<is_null_allowed>true</is_null_allowed>
</field>
</fields>
<presentation>
<details>
<items>
<item id="col:col1">
<rank>10</rank>
<items>
<item id="fieldset:OAuthClient:baseinfo">
<rank>10</rank>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="status">
<rank>20</rank>
</item>
<item id="description">
<rank>30</rank>
</item>
<item id="provider">
<rank>40</rank>
</item>
<item id="redirect_url">
<rank>50</rank>
</item>
<item id="client_id">
<rank>60</rank>
</item>
<item id="client_secret">
<rank>70</rank>
</item>
<item id="mailbox_list">
<rank>80</rank>
</item>
</items>
</item>
</items>
</item>
<item id="col:col2">
<rank>20</rank>
<items>
<item id="fieldset:OAuthClient:scope">
<rank>10</rank>
<items>
<item id="used_scope">
<rank>10</rank>
</item>
<item id="scope">
<rank>20</rank>
</item>
<item id="advanced_scope">
<rank>30</rank>
</item>
<item id="used_for_smtp">
<rank>40</rank>
</item>
</items>
</item>
</items>
</item>
</items>
</details>
<list>
<items>
<item id="provider">
<rank>10</rank>
</item>
<item id="status">
<rank>10</rank>
</item>
</items>
</list>
<standard_search>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="provider">
<rank>10</rank>
</item>
<item id="status">
<rank>10</rank>
</item>
</items>
</standard_search>
</presentation>
<methods>
<method id="PrefillCreationForm">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function PrefillCreationForm(&$aContextParam)
{
$this->Set('provider', 'Azure');
$this->Set('redirect_url', Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory::GetRedirectUri());
$this->Set('scope', 'SMTP, IMAP');
parent::PrefillCreationForm($aContextParam);
}
]]></code>
</method>
<method id="DoCheckToWrite">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[ public function DoCheckToWrite()
{
parent::DoCheckToWrite();
$aChanges = $this->ListChanges();
if (array_key_exists('name', $aChanges) || array_key_exists('used_for_smtp', $aChanges))
{
$sNewName = $this->Get('name');
$sNewUseForSMTP = $this->Get('used_for_smtp');
if ($sNewUseForSMTP == 'yes') {
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT OAuthClientGoogle WHERE name = :newname AND used_for_smtp = :newuseforsmtp AND id != :id UNION SELECT OAuthClientAzure WHERE name = :newname AND used_for_smtp = :newuseforsmtp AND id != :id");
$oSet = new DBObjectSet($oSearch, array(), ['id' => $this->GetKey(), 'newname' => $sNewName, 'newuseforsmtp' => $sNewUseForSMTP]);
if ($oSet->Count() > 0)
{
$this->m_aCheckIssues[] = Dict::Format('OAuthClient:Name/UseForSMTPMustBeUnique', $sNewName, $sNewUseForSMTP);
}
}
}
} ]]></code>
</method>
<method id="ComputeValues">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function ComputeValues()
{
parent::ComputeValues();
if (empty($this->Get('provider'))) {
$this->Set('provider', 'Azure');
}
if (empty($this->Get('redirect_url'))) {
$this->Set('redirect_url', Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory::GetRedirectUri());
}
if (empty($this->Get('advanced_scope'))) {
$this->Set('used_scope', 'simple');
if (count($this->Get('scope')->GetValues()) == 0) {
$this->Set('scope', 'SMTP, IMAP');
}
} else {
$this->Set('used_scope', 'advanced');
$this->Set('scope', '');
}
}
]]></code>
</method>
<method id="GetAttributeFlags">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
switch ($sAttCode) {
case 'provider':
case 'redirect_url':
case 'used_scope':
return OPT_ATT_READONLY;
}
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
}
]]></code>
</method>
<method id="GetInitialStateAttributeFlags">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function GetInitialStateAttributeFlags($sAttCode, &$aReasons = array())
{
switch ($sAttCode) {
case 'provider':
case 'redirect_url':
case 'used_scope':
return OPT_ATT_READONLY;
}
return parent::GetInitialStateAttributeFlags($sAttCode, $aReasons);
}
]]></code>
</method>
<method id="GetDefaultMailServer">
<static>false</static>
<access>public</access>
<code><![CDATA[
public function GetDefaultMailServer()
{
return 'outlook.office365.com';
}
]]></code>
</method>
<method id="GetScope">
<static>false</static>
<access>public</access>
<code><![CDATA[
public function GetScope()
{
if (!empty($this->Get('advanced_scope'))) {
return $this->Get('advanced_scope');
}
$aScopes = $this->Get('scope')->GetValues();
$aRawScopes = ['offline_access'];
foreach ($aScopes as $sScope) {
switch ($sScope) {
case 'SMTP':
$aRawScopes[] = 'https://outlook.office.com/SMTP.Send';
break;
case 'IMAP':
$aRawScopes[] = 'https://outlook.office.com/IMAP.AccessAsUser.All';
break;
}
}
return implode(' ', $aRawScopes);
}
]]></code>
</method>
</methods>
</class>
<class id="OAuthClientGoogle" _delta="define">
<parent>OAuthClient</parent>
<properties>
<category>grant_by_profile,application</category>
<abstract>false</abstract>
<key_type>autoincrement</key_type>
<db_table>priv_oauth_client_google</db_table>
<db_key_field>id</db_key_field>
<db_final_class_field/>
<naming>
<attributes>
<attribute id="provider"/>
<attribute id="name"/>
</attributes>
</naming>
<display_template/>
<icon/>
<reconciliation>
<attributes>
<attribute id="provider"/>
<attribute id="name"/>
</attributes>
</reconciliation>
<uniqueness_rules>
<rule id="server">
<attributes>
<attribute id="provider"/>
<attribute id="client_id"/>
<attribute id="client_secret"/>
</attributes>
<is_blocking>true</is_blocking>
</rule>
</uniqueness_rules>
</properties>
<fields>
<field id="scope" xsi:type="AttributeEnumSet">
<always_load_in_tables>true</always_load_in_tables>
<values>
<value id="SMTP">SMTP</value>
<value id="IMAP">IMAP</value>
</values>
<sql>scope</sql>
<default_value>SMTP,IMAP</default_value>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="advanced_scope" xsi:type="AttributeString">
<sql>advanced_scope</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="used_scope" xsi:type="AttributeEnum">
<always_load_in_tables>true</always_load_in_tables>
<values>
<value id="simple">simple</value>
<value id="advanced">advanced</value>
</values>
<sql>used_scope</sql>
<default_value>simple</default_value>
<is_null_allowed>false</is_null_allowed>
<dependencies>
<attribute id="scope"/>
<attribute id="advanced_scope"/>
</dependencies>
</field>
<field id="used_for_smtp" xsi:type="AttributeEnum">
<always_load_in_tables>true</always_load_in_tables>
<values>
<value id="yes">yes</value>
<value id="no">no</value>
</values>
<sql>used_for_smtp</sql>
<default_value>no</default_value>
<is_null_allowed>true</is_null_allowed>
</field>
</fields>
<presentation>
<details>
<items>
<item id="col:col1">
<rank>10</rank>
<items>
<item id="fieldset:OAuthClient:baseinfo">
<rank>10</rank>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="status">
<rank>20</rank>
</item>
<item id="description">
<rank>30</rank>
</item>
<item id="provider">
<rank>40</rank>
</item>
<item id="redirect_url">
<rank>50</rank>
</item>
<item id="client_id">
<rank>60</rank>
</item>
<item id="client_secret">
<rank>70</rank>
</item>
<item id="mailbox_list">
<rank>80</rank>
</item>
</items>
</item>
</items>
</item>
<item id="col:col2">
<rank>20</rank>
<items>
<item id="fieldset:OAuthClient:scope">
<rank>10</rank>
<items>
<item id="used_scope">
<rank>10</rank>
</item>
<item id="scope">
<rank>20</rank>
</item>
<item id="advanced_scope">
<rank>30</rank>
</item>
<item id="used_for_smtp">
<rank>40</rank>
</item>
</items>
</item>
</items>
</item>
</items>
</details>
<list>
<items>
<item id="provider">
<rank>10</rank>
</item>
<item id="status">
<rank>10</rank>
</item>
</items>
</list>
<standard_search>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="provider">
<rank>10</rank>
</item>
<item id="status">
<rank>10</rank>
</item>
</items>
</standard_search>
</presentation>
<methods>
<method id="PrefillCreationForm">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function PrefillCreationForm(&$aContextParam)
{
$this->Set('provider', 'Google');
$this->Set('redirect_url', Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory::GetRedirectUri());
$this->Set('scope', 'SMTP, IMAP');
parent::PrefillCreationForm($aContextParam);
}
]]></code>
</method>
<method id="DoCheckToWrite">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[ public function DoCheckToWrite()
{
parent::DoCheckToWrite();
$aChanges = $this->ListChanges();
if (array_key_exists('name', $aChanges) || array_key_exists('used_for_smtp', $aChanges))
{
$sNewName = $this->Get('name');
$sNewUseForSMTP = $this->Get('used_for_smtp');
if ($sNewUseForSMTP == 'yes') {
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT OAuthClientGoogle WHERE name = :newname AND used_for_smtp = :newuseforsmtp AND id != :id UNION SELECT OAuthClientAzure WHERE name = :newname AND used_for_smtp = :newuseforsmtp AND id != :id");
$oSet = new DBObjectSet($oSearch, array(), ['id' => $this->GetKey(), 'newname' => $sNewName, 'newuseforsmtp' => $sNewUseForSMTP]);
if ($oSet->Count() > 0)
{
$this->m_aCheckIssues[] = Dict::Format('OAuthClient:Name/UseForSMTPMustBeUnique', $sNewName, $sNewUseForSMTP);
}
}
}
} ]]></code>
</method>
<method id="ComputeValues">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function ComputeValues()
{
parent::ComputeValues();
if (empty($this->Get('provider'))) {
$this->Set('provider', 'Google');
}
if (empty($this->Get('redirect_url'))) {
$this->Set('redirect_url', Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory::GetRedirectUri());
}
if (empty($this->Get('advanced_scope'))) {
$this->Set('used_scope', 'simple');
if (count($this->Get('scope')->GetValues()) == 0) {
$this->Set('scope', 'SMTP, IMAP');
}
} else {
$this->Set('used_scope', 'advanced');
$this->Set('scope', '');
}
}
]]></code>
</method>
<method id="GetAttributeFlags">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
switch ($sAttCode) {
case 'provider':
case 'redirect_url':
case 'used_scope':
return OPT_ATT_READONLY;
}
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
}
]]></code>
</method>
<method id="GetInitialStateAttributeFlags">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[
public function GetInitialStateAttributeFlags($sAttCode, &$aReasons = array())
{
switch ($sAttCode) {
case 'provider':
case 'redirect_url':
case 'used_scope':
return OPT_ATT_READONLY;
}
return parent::GetInitialStateAttributeFlags($sAttCode, $aReasons);
}
]]></code>
</method>
<method id="GetDefaultMailServer">
<static>false</static>
<access>public</access>
<code><![CDATA[
public function GetDefaultMailServer()
{
return 'imap.gmail.com';
}
]]></code>
</method>
<method id="GetScope">
<static>false</static>
<access>public</access>
<code><![CDATA[
public function GetScope()
{
if (!empty($this->Get('advanced_scope'))) {
return $this->Get('advanced_scope');
}
$aScopes = $this->Get('scope')->GetValues();
$aRawScopes = [];
foreach ($aScopes as $sScope) {
switch ($sScope) {
case 'SMTP':
$aRawScopes['https://mail.google.com/'] = 'https://mail.google.com/';
break;
case 'IMAP':
$aRawScopes['https://mail.google.com/'] = 'https://mail.google.com/';
break;
}
}
return implode(' ', $aRawScopes);
}
]]></code>
</method>
</methods>
</class>
</classes>
<menus>
<menu id="OAuthClient" xsi:type="OQLMenuNode" _delta="define">
<rank>100</rank>
<parent>ConfigurationTools</parent>
<oql><![CDATA[SELECT OAuthClient]]></oql>
<do_search>1</do_search>
<enable_admin_only>0</enable_admin_only>
<enable_class>OAuthClient</enable_class>
<enable_action>UR_ACTION_MODIFY</enable_action>
</menu>
</menus>
<user_rights>
<groups>
<group id="OauthConnection" _delta="define">
<classes>
<class id="OAuthClient"/>
</classes>
</group>
</groups>
<profiles>
</profiles>
</user_rights>
</itop_design>

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('DE DE', 'German', 'Deutsch', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access token...~~',
'Menu:RegenerateTokens' => 'Regenerate access token...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('DE DE', 'German', 'Deutsch', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('DE DE', 'German', 'Deutsch', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('DE DE', 'German', 'Deutsch', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,117 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('EN US', 'English', 'English', [
'Menu:CreateMailbox' => 'Create a mailbox...',
'Menu:OAuthClient' => 'OAuth Client',
'Menu:OAuthClient+' => '',
'Menu:GenerateTokens' => 'Generate access token...',
'Menu:RegenerateTokens' => 'Regenerate access token...',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP',
'itop-oauth-client:TestSMTP' => 'Email send test',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client',
'itop-oauth-client:Message:TokenCreated' => 'Access token created',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'The combination Login (%1$s) and Use for SMTP (%2$s) has already be used for OAuth Client',
'OAuthClient:baseinfo' => 'Base Information',
'OAuthClient:scope' => 'Scope',
]);
//
// Class: OAuthClient
//
Dict::Add('EN US', 'English', 'English', [
'Class:OAuthClient' => 'Oauth Client',
'Class:OAuthClient/Attribute:provider' => 'Provider',
'Class:OAuthClient/Attribute:provider+' => '',
'Class:OAuthClient/Attribute:name' => 'Login',
'Class:OAuthClient/Attribute:name+' => 'In general, this is your email address',
'Class:OAuthClient/Attribute:status' => 'Status',
'Class:OAuthClient/Attribute:status+' => 'After creation, use the action “Generate access token” to be able to use this OAuth client',
'Class:OAuthClient/Attribute:status/Value:active' => 'Access token generated',
'Class:OAuthClient/Attribute:status/Value:inactive' => 'No Access token',
'Class:OAuthClient/Attribute:description' => 'Description',
'Class:OAuthClient/Attribute:description+' => '',
'Class:OAuthClient/Attribute:client_id' => 'Client id',
'Class:OAuthClient/Attribute:client_id+' => 'A long string of characters provided by your OAuth2 provider',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret',
'Class:OAuthClient/Attribute:client_secret+' => 'Another long string of characters provided by your OAuth2 provider',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token',
'Class:OAuthClient/Attribute:refresh_token+' => '',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '',
'Class:OAuthClient/Attribute:token' => 'Access token',
'Class:OAuthClient/Attribute:token+' => '',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration',
'Class:OAuthClient/Attribute:token_expiration+' => '',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url',
'Class:OAuthClient/Attribute:redirect_url+' => 'This url must be copied in the OAuth2 configuration of the provider',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list',
'Class:OAuthClient/Attribute:mailbox_list+' => '',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('EN US', 'English', 'English', array(
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope',
'Class:OAuthClientAzure/Attribute:scope+' => 'Usually default selection is appropriate',
'Class:OAuthClientAzure/Attribute:scope/Value:SMTP' => 'SMTP',
'Class:OAuthClientAzure/Attribute:scope/Value:SMTP+' => '',
'Class:OAuthClientAzure/Attribute:scope/Value:IMAP' => 'IMAP',
'Class:OAuthClientAzure/Attribute:scope/Value:IMAP+' => '',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Advanced scope',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence on the “Scope” selection which is then ignored',
'Class:OAuthClientAzure/Attribute:used_scope' => 'Used scope',
'Class:OAuthClientAzure/Attribute:used_scope+' => '',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Simple',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '',
'Class:OAuthClientAzure/Attribute:used_scope/Value:advanced' => 'Advanced',
'Class:OAuthClientAzure/Attribute:used_scope/Value:advanced+' => '',
'Class:OAuthClientAzure/Attribute:used_for_smtp' => 'Used for SMTP',
'Class:OAuthClientAzure/Attribute:used_for_smtp+' => 'At least one OAuth client must have this flag to “Yes”, if you want iTop to use it for sending mails',
'Class:OAuthClientAzure/Attribute:used_for_smtp/Value:yes' => 'Yes',
'Class:OAuthClientAzure/Attribute:used_for_smtp/Value:no' => 'No',
));
//
// Class: OAuthClientGoogle
//
Dict::Add('EN US', 'English', 'English', array(
'Class:OAuthClientGoogle' => 'OAuth client for Google',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope',
'Class:OAuthClientGoogle/Attribute:scope+' => 'Usually default selection is appropriate',
'Class:OAuthClientGoogle/Attribute:scope/Value:SMTP' => 'SMTP',
'Class:OAuthClientGoogle/Attribute:scope/Value:SMTP+' => '',
'Class:OAuthClientGoogle/Attribute:scope/Value:IMAP' => 'IMAP',
'Class:OAuthClientGoogle/Attribute:scope/Value:IMAP+' => '',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Advanced scope',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence on the “Scope” selection which is then ignored',
'Class:OAuthClientGoogle/Attribute:used_scope' => 'Used scope',
'Class:OAuthClientGoogle/Attribute:used_scope+' => '',
'Class:OAuthClientGoogle/Attribute:used_scope/Value:simple' => 'Simple',
'Class:OAuthClientGoogle/Attribute:used_scope/Value:simple+' => '',
'Class:OAuthClientGoogle/Attribute:used_scope/Value:advanced' => 'Advanced',
'Class:OAuthClientGoogle/Attribute:used_scope/Value:advanced+' => '',
'Class:OAuthClientGoogle/Attribute:used_for_smtp' => 'Used for SMTP',
'Class:OAuthClientGoogle/Attribute:used_for_smtp+' => 'At least one OAuth client must have this flag to “Yes”, if you want iTop to use it for sending mails',
'Class:OAuthClientGoogle/Attribute:used_for_smtp/Value:yes' => 'Yes',
'Class:OAuthClientGoogle/Attribute:used_for_smtp/Value:no' => 'No',
));

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,116 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('FR FR', 'French', 'Français', [
'Menu:CreateMailbox' => 'Créer une boite mail...',
'Menu:OAuthClient' => 'Client OAuth',
'Menu:OAuthClient+' => '',
'Menu:GenerateTokens' => 'Créer un jeton d\'accès...',
'Menu:RegenerateTokens' => 'Recréer un jeton d\'accès..',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Création de boite mail',
'itop-oauth-client:UsedForSMTP' => 'Ce client Oauth est utilisé pour SMTP',
'itop-oauth-client:TestSMTP' => 'Tester l\'envoi de mail',
'itop-oauth-client:MissingOAuthClient' => 'Il n\'y a pas de client OAuth pour l\'utilisateur %1$s',
'itop-oauth-client:Message:MissingToken' => 'Générez le jeton d\'accès avant d\'utiliser ce client OAuth',
'itop-oauth-client:Message:TokenCreated' => 'Le jeton d\'accès à été créé',
'itop-oauth-client:Message:TokenRecreated' => 'Le jeton d\'accès à été renouvelé',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'La combinaison Login (%1$s) and Utilisé pour SMTP (%2$s) a déjà été utilisée pour OAuth Client',
'OAuthClient:baseinfo' => 'Information',
'OAuthClient:scope' => 'Scope',
]);
//
// Class: OAuthClient
//
Dict::Add('FR FR', 'French', 'Français', [
'Class:OAuthClient' => 'Client OAuth',
'Class:OAuthClient/Attribute:provider' => 'Fournisseur',
'Class:OAuthClient/Attribute:provider+' => '',
'Class:OAuthClient/Attribute:name' => 'Login',
'Class:OAuthClient/Attribute:name+' => 'L\'adresse email à utiliser chez ce fournisseur',
'Class:OAuthClient/Attribute:status' => 'Statut',
'Class:OAuthClient/Attribute:status+' => 'Après la création, effectuer l\'action \'Créer un jeton d\'accès...\' pour activer ce client OAuth',
'Class:OAuthClient/Attribute:status/Value:active' => 'Jeton d\'accès créé',
'Class:OAuthClient/Attribute:status/Value:inactive' => 'Pas de jeton d\'accès',
'Class:OAuthClient/Attribute:description' => 'Description',
'Class:OAuthClient/Attribute:description+' => '',
'Class:OAuthClient/Attribute:client_id' => 'ID Client',
'Class:OAuthClient/Attribute:client_id+' => 'Recopier la chaine fournie par votre fournisseur OAuth2',
'Class:OAuthClient/Attribute:client_secret' => 'Code secret du client',
'Class:OAuthClient/Attribute:client_secret+' => 'Recopier l\'information fournie par votre fournisseur OAuth2',
'Class:OAuthClient/Attribute:refresh_token' => 'Jeton de renouvellement',
'Class:OAuthClient/Attribute:refresh_token+' => '',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Date d\'expiration du jeton de renouvellement',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '',
'Class:OAuthClient/Attribute:token' => 'Jeton d\'accès',
'Class:OAuthClient/Attribute:token+' => '',
'Class:OAuthClient/Attribute:token_expiration' => 'Date d\'expiration du jeton d\'accès',
'Class:OAuthClient/Attribute:token_expiration+' => '',
'Class:OAuthClient/Attribute:redirect_url' => 'URL de redirection',
'Class:OAuthClient/Attribute:redirect_url+' => 'Cet URL doit être recopié dans la configuration OAuth2 de votre fournisseur',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list',
'Class:OAuthClient/Attribute:mailbox_list+' => '',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('FR FR', 'French', 'Français', array(
'Class:OAuthClientAzure' => 'Client OAuth pour Microsoft Azure',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)',
'Class:OAuthClientAzure/Attribute:scope' => 'Niveaux d\'accès',
'Class:OAuthClientAzure/Attribute:scope+' => 'Les niveaux par défaut sont les plus souvent suffisants',
'Class:OAuthClientAzure/Attribute:scope/Value:SMTP' => 'SMTP',
'Class:OAuthClientAzure/Attribute:scope/Value:SMTP+' => '',
'Class:OAuthClientAzure/Attribute:scope/Value:IMAP' => 'IMAP',
'Class:OAuthClientAzure/Attribute:scope/Value:IMAP+' => '',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Niveaux d\'accès avancé',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'A saisir, lorsque les niveaux prédéfinis ne suffisent pas',
'Class:OAuthClientAzure/Attribute:used_scope' => 'Niveaux d\'accès utilisés',
'Class:OAuthClientAzure/Attribute:used_scope+' => '',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Simple',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '',
'Class:OAuthClientAzure/Attribute:used_scope/Value:advanced' => 'Avancé',
'Class:OAuthClientAzure/Attribute:used_scope/Value:advanced+' => '',
'Class:OAuthClientAzure/Attribute:used_for_smtp' => 'Utilisé pour SMTP',
'Class:OAuthClientAzure/Attribute:used_for_smtp+' => 'Le Client OAuth utilisé pour l\'envoi d\'emails doit être à \'Oui\'',
'Class:OAuthClientAzure/Attribute:used_for_smtp/Value:yes' => 'Oui',
'Class:OAuthClientAzure/Attribute:used_for_smtp/Value:no' => 'Non',
));
//
// Class: OAuthClientGoogle
//
Dict::Add('FR FR', 'French', 'Français', array(
'Class:OAuthClientGoogle' => 'Client OAuth pour Google',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Class:OAuthClientGoogle/Attribute:scope' => 'Niveaux d\'accès',
'Class:OAuthClientGoogle/Attribute:scope+' => 'Les niveaux par défaut sont les plus souvent suffisants',
'Class:OAuthClientGoogle/Attribute:scope/Value:SMTP' => 'SMTP',
'Class:OAuthClientGoogle/Attribute:scope/Value:SMTP+' => '',
'Class:OAuthClientGoogle/Attribute:scope/Value:IMAP' => 'IMAP',
'Class:OAuthClientGoogle/Attribute:scope/Value:IMAP+' => '',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Niveaux d\'accès avancé',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'A saisir, lorsque les niveaux prédéfinis ne suffisent pas',
'Class:OAuthClientGoogle/Attribute:used_scope' => 'Niveaux d\'accès utilisés',
'Class:OAuthClientGoogle/Attribute:used_scope+' => '',
'Class:OAuthClientGoogle/Attribute:used_scope/Value:simple' => 'Simple',
'Class:OAuthClientGoogle/Attribute:used_scope/Value:advanced' => 'Avancé',
'Class:OAuthClientGoogle/Attribute:used_scope/Value:advanced+' => '',
'Class:OAuthClientGoogle/Attribute:used_for_smtp' => 'Utilisé pour SMTP',
'Class:OAuthClientGoogle/Attribute:used_for_smtp+' => 'Le Client OAuth utilisé pour l\'envoi d\'emails doit être à \'Oui\'',
'Class:OAuthClientGoogle/Attribute:used_for_smtp/Value:yes' => 'Oui',
'Class:OAuthClientGoogle/Attribute:used_for_smtp/Value:no' => 'Non',
));

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('HU HU', 'Hungarian', 'Magyar', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('HU HU', 'Hungarian', 'Magyar', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('HU HU', 'Hungarian', 'Magyar', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('HU HU', 'Hungarian', 'Magyar', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,24 @@
<?php
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\OAuthClient;
use Combodo\iTop\OAuthClient\Controller\OAuthClientController;
require_once(APPROOT.'application/startup.inc.php');
if (version_compare(ITOP_DESIGN_LATEST_VERSION , '3.0') >= 0) {
$sTemplates = MODULESROOT.'itop-oauth-client/templates';
} else {
$sTemplates = MODULESROOT.'itop-oauth-client/templates/legacy';
}
$oUpdateController = new OAuthClientController($sTemplates, 'itop-oauth-client');
$oUpdateController->AllowOnlyAdmin();
$oUpdateController->SetDefaultOperation('CreateMailbox');
$oUpdateController->HandleOperation();

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('IT IT', 'Italian', 'Italiano', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('IT IT', 'Italian', 'Italiano', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('IT IT', 'Italian', 'Italiano', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('IT IT', 'Italian', 'Italiano', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('JA JP', 'Japanese', '日本語', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('JA JP', 'Japanese', '日本語', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('JA JP', 'Japanese', '日本語', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('JA JP', 'Japanese', '日本語', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,53 @@
<?php
//
// iTop module definition file
//
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-oauth-client/2.7.7',
array(
// Identification
//
'label' => 'OAuth 2.0 client',
'category' => 'business',
// Setup
//
'dependencies' => array(
'itop-welcome-itil/2.7.7,'
),
'mandatory' => false,
'visible' => true,
// Components
//
'datamodel' => array(
'vendor/autoload.php',
'model.itop-oauth-client.php', // Contains the PHP code generated by the "compilation" of datamodel.remote-authent-oauth.xml
'src/Service/PopupMenuExtension.php',
'src/Service/ApplicationUIExtension.php',
),
'webservice' => array(
),
'data.struct' => array(
// add your 'structure' definition XML files here,
),
'data.sample' => array(
// add your sample data XML files here,
),
// Documentation
//
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any
'doc.more_information' => '', // hyperlink to more information, if any
// Default settings
//
'settings' => array(
// Module specific settings go here, if any
),
)
);

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('NL NL', 'Dutch', 'Nederlands', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('NL NL', 'Dutch', 'Nederlands', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('NL NL', 'Dutch', 'Nederlands', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('NL NL', 'Dutch', 'Nederlands', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('RU RU', 'Russian', 'Русский', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('RU RU', 'Russian', 'Русский', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('RU RU', 'Russian', 'Русский', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('RU RU', 'Russian', 'Русский', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,77 @@
<?php
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\OAuthClient\Controller;
use cmdbAbstractObject;
use Combodo\iTop\Application\TwigBase\Controller\Controller;
use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory;
use Dict;
use IssueLog;
use MetaModel;
use utils;
class AjaxOauthClientController extends Controller
{
const LOG_CHANNEL = 'OAuth';
public function OperationGetOAuthAuthorizationUrl()
{
$sClass = utils::ReadParam('class');
$sId = utils::ReadParam('id');
IssueLog::Debug("GetAuthorizationUrl for $sClass::$sId", self::LOG_CHANNEL);
/** @var \OAuthClient $oOAuthClient */
$oOAuthClient = MetaModel::GetObject($sClass, $sId);
$aResult = ['status' => 'success', 'data' => []];
$sAuthorizationUrl = OAuthClientProviderFactory::GetAuthorizationUrl($oOAuthClient);
$aResult['data']['authorization_url'] = $sAuthorizationUrl;
$this->DisplayJSONPage($aResult);
}
public function OperationGetDisplayAuthenticationResults()
{
$sClass = utils::ReadParam('class');
$sId = utils::ReadParam('id');
IssueLog::Debug("GetDisplayAuthenticationResults for $sClass::$sId", self::LOG_CHANNEL);
/** @var \OAuthClient $oOAuthClient */
$oOAuthClient = MetaModel::GetObject($sClass, $sId);
$bIsCreation = empty($oOAuthClient->Get('token'));
$sRedirectUrl = utils::ReadParam('redirect_url', '', false, 'raw');
$sRedirectUrlQuery = parse_url($sRedirectUrl)['query'];
$aQuery = [];
parse_str($sRedirectUrlQuery, $aQuery);
$sCode = $aQuery['code'];
$oAccessToken = OAuthClientProviderFactory::GetAccessTokenFromCode($oOAuthClient, $sCode);
$oOAuthClient->SetAccessToken($oAccessToken);
cmdbAbstractObject::SetSessionMessage(
$sClass,
$sId,
"$sClass:$sId:TokenCreated",
$bIsCreation ? Dict::S('itop-oauth-client:Message:TokenCreated') : Dict::S('itop-oauth-client:Message:TokenRecreated'),
'ok',
1,
true
);
$aResult = ['status' => 'success'];
$aResult['data'] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=details&class=$sClass&id=$sId";
$this->DisplayJSONPage($aResult);
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\OAuthClient\Controller;
use Combodo\iTop\Application\TwigBase\Controller\Controller;
use IssueLog;
use MetaModel;
use utils;
class OAuthClientController extends Controller
{
const LOG_CHANNEL = 'OAuth';
public function OperationCreateMailbox()
{
$aParams = [];
$sClass = utils::ReadParam('class');
$sId = utils::ReadParam('id');
IssueLog::Debug("CreateMailbox for $sClass::$sId", self::LOG_CHANNEL);
$oOAuthClient = MetaModel::GetObject($sClass, $sId);
$sLogin = $oOAuthClient->Get('name');
$sDefaultServer = $oOAuthClient->GetDefaultMailServer();
$sDefaultPort = $oOAuthClient->GetDefaultMailServerPort();
$aParams['sURL'] = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class=MailInboxOAuth'.
'&default[mailbox]=INBOX'.
'&default[server]='.$sDefaultServer.
'&default[port]='.$sDefaultPort.
'&default[oauth_client_id]='.$sId.
'&default[login]='.$sLogin;
$this->DisplayPage($aParams);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\OAuthClient\Service;
use AbstractApplicationUIExtension;
use OAuthClient;
use utils;
class ApplicationUIExtension extends AbstractApplicationUIExtension
{
public function GetHilightClass($oObject)
{
if ($oObject instanceof OAuthClient) {
// Possible return values are:
// HILIGHT_CLASS_CRITICAL, HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
$oConfig = utils::GetConfig();
if ($oObject->Get('status') == 'inactive') {
return HILIGHT_CLASS_WARNING;
} elseif ($oObject->Get('used_for_smtp') == 'yes' && $oConfig->Get('email_transport_smtp.username') == $oObject->Get('name')) {
return HILIGHT_CLASS_OK;
}
}
return HILIGHT_CLASS_NONE;
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\OAuthClient\Service;
use ApplicationContext;
use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory;
use Dict;
use iPopupMenuExtension;
use JSPopupMenuItem;
use OAuthClient;
use SeparatorPopupMenuItem;
use URLPopupMenuItem;
use utils;
class PopupMenuExtension implements \iPopupMenuExtension
{
const MODULE_CODE = 'itop-oauth-client';
/**
* @inheritDoc
*/
public static function EnumItems($iMenuId, $param)
{
$aResult = [];
switch ($iMenuId) {
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
$oObj = $param;
if ($oObj instanceof OAuthClient) {
$bHasToken = !empty($oObj->Get('token'));
$aResult[] = new SeparatorPopupMenuItem();
$oAppContext = new ApplicationContext();
$sMenu = $bHasToken ? 'Menu:RegenerateTokens' : 'Menu:GenerateTokens';
$sObjClass = get_class($oObj);
$sClass = $sObjClass;
$sId = $oObj->GetKey();
$sAjaxUri = utils::GetAbsoluteUrlModulePage(static::MODULE_CODE, 'ajax.php');
// Add a new menu item that triggers a custom JS function defined in our own javascript file: js/sample.js
$sJSFileUrl = utils::GetAbsoluteUrlModulesRoot().static::MODULE_CODE.'/assets/js/oauth_connect.js';
$sRedirectUri = OAuthClientProviderFactory::GetRedirectUri();
$aResult[] = new JSPopupMenuItem(
$sMenu.' from '.$sObjClass,
Dict::S($sMenu),
"OAuthConnect('$sClass', $sId, '$sAjaxUri', '$sRedirectUri')",
[$sJSFileUrl]
);
if ($bHasToken) {
$aScopes = $oObj->Get('scope')->GetValues();
if (in_array('IMAP', $aScopes)) {
$aParams = $oAppContext->GetAsHash();
$sMenu = 'Menu:CreateMailbox';
$sObjClass = get_class($oObj);
$aParams['class'] = $sObjClass;
$aParams['id'] = $oObj->GetKey();
$aParams['operation'] = 'CreateMailbox';
$aResult[] = new URLPopupMenuItem(
$sMenu.' from '.$sObjClass,
Dict::S($sMenu),
utils::GetAbsoluteUrlModulePage(static::MODULE_CODE, 'index.php', $aParams)
);
}
}
}
break;
default:
// Unknown type of menu, do nothing
break;
}
return $aResult;
}
}

View File

@@ -0,0 +1,3 @@
{# @copyright Copyright (C) 2010-2022 Combodo SARL #}
{# @license http://opensource.org/licenses/AGPL-3.0 #}

View File

@@ -0,0 +1,4 @@
{# @copyright Copyright (C) 2010-2022 Combodo SARL #}
{# @license http://opensource.org/licenses/AGPL-3.0 #}
window.location.href = '{{ sURL|raw }}'

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitd52424b43ff18219f2ec935428aff074::getLoader();

View File

@@ -0,0 +1,572 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-var array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
}

View File

@@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
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.

View File

@@ -0,0 +1,13 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Combodo\\iTop\\OAuthClient\\Controller\\AjaxOauthClientController' => $baseDir . '/src/Controller/AjaxOauthClientController.php',
'Combodo\\iTop\\OAuthClient\\Controller\\OAuthClientController' => $baseDir . '/src/Controller/OAuthClientController.php',
'Combodo\\iTop\\OAuthClient\\Service\\PopupMenuExtension' => $baseDir . '/src/Service/PopupMenuExtension.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Combodo\\iTop\\OAuthClient\\' => array($baseDir . '/src'),
);

View File

@@ -0,0 +1,46 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitd52424b43ff18219f2ec935428aff074
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitd52424b43ff18219f2ec935428aff074', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInitd52424b43ff18219f2ec935428aff074', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitd52424b43ff18219f2ec935428aff074::getInitializer($loader));
} else {
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->setClassMapAuthoritative(true);
$loader->register(true);
return $loader;
}
}

View File

@@ -0,0 +1,39 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitd52424b43ff18219f2ec935428aff074
{
public static $prefixLengthsPsr4 = array (
'C' =>
array (
'Combodo\\iTop\\OAuthClient\\' => 25,
),
);
public static $prefixDirsPsr4 = array (
'Combodo\\iTop\\OAuthClient\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
);
public static $classMap = array (
'Combodo\\iTop\\OAuthClient\\Controller\\AjaxOauthClientController' => __DIR__ . '/../..' . '/src/Controller/AjaxOauthClientController.php',
'Combodo\\iTop\\OAuthClient\\Controller\\OAuthClientController' => __DIR__ . '/../..' . '/src/Controller/OAuthClientController.php',
'Combodo\\iTop\\OAuthClient\\Service\\PopupMenuExtension' => __DIR__ . '/../..' . '/src/Service/PopupMenuExtension.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitd52424b43ff18219f2ec935428aff074::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitd52424b43ff18219f2ec935428aff074::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitd52424b43ff18219f2ec935428aff074::$classMap;
}, null, ClassLoader::class);
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('ZH CN', 'Chinese', '简体中文', [
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:OAuthClient' => 'OAuth Client~~',
'Menu:OAuthClient+' => '~~',
'Menu:GenerateTokens' => 'Generate access tokens...~~',
'Menu:RegenerateTokens' => 'Regenerate access tokens...~~',
'itop-oauth-client/Operation:CreateMailBox/Title' => 'Mailbox creation~~',
'itop-oauth-client:UsedForSMTP' => 'This OAuth client is used for SMTP~~',
'itop-oauth-client:TestSMTP' => 'Email send test~~',
'itop-oauth-client:MissingOAuthClient' => 'Missing Oauth client for user name %1$s~~',
'itop-oauth-client:Message:MissingToken' => 'Generate access token before using this OAuth client~~',
'itop-oauth-client:Message:TokenCreated' => 'Access token created~~',
'itop-oauth-client:Message:TokenRecreated' => 'Access token regenerated~~',
]);
//
// Class: OAuthClient
//
Dict::Add('ZH CN', 'Chinese', '简体中文', [
'Class:OAuthClient' => 'Oauth Client~~',
'Class:OAuthClient/Attribute:provider' => 'Provider~~',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => 'Login~~',
'Class:OAuthClient/Attribute:name+' => '~~',
'Class:OAuthClient/Attribute:scope' => 'Scope~~',
'Class:OAuthClient/Attribute:scope+' => '~~',
'Class:OAuthClient/Attribute:description' => 'Description~~',
'Class:OAuthClient/Attribute:description+' => '~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => '~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
'Class:OAuthClient/Attribute:client_secret+' => '~~',
'Class:OAuthClient/Attribute:refresh_token' => 'Refresh token~~',
'Class:OAuthClient/Attribute:refresh_token+' => '~~',
'Class:OAuthClient/Attribute:refresh_token_expiration' => 'Refresh token expiration~~',
'Class:OAuthClient/Attribute:refresh_token_expiration+' => '~~',
'Class:OAuthClient/Attribute:token' => 'Access token~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClient/Attribute:redirect_url' => 'Redirect url~~',
'Class:OAuthClient/Attribute:redirect_url+' => '~~',
'Class:OAuthClient/Attribute:mailbox_list' => 'Mailbox list~~',
'Class:OAuthClient/Attribute:mailbox_list+' => '~~',
]);
//
// Class: OAuthClientAzure
//
Dict::Add('ZH CN', 'Chinese', '简体中文', [
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
]);
//
// Class: OAuthClientGoogle
//
Dict::Add('ZH CN', 'Chinese', '简体中文', [
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
]);

View File

@@ -162,12 +162,20 @@ class BrickCollection
// - Home
$this->aHomeOrdering = $this->aAllowedBricks;
usort($this->aHomeOrdering, function (PortalBrick $a, PortalBrick $b) {
return $a->GetRankHome() > $b->GetRankHome();
if ($a->GetRankHome() === $b->GetRankHome()) {
return 0;
}
return $a->GetRankHome() > $b->GetRankHome() ? 1 : -1;
});
// - Navigation menu
$this->aNavigationMenuOrdering = $this->aAllowedBricks;
usort($this->aNavigationMenuOrdering, function (PortalBrick $a, PortalBrick $b) {
return $a->GetRankNavigationMenu() > $b->GetRankNavigationMenu();
if ($a->GetRankNavigationMenu() === $b->GetRankNavigationMenu()) {
return 0;
}
return $a->GetRankNavigationMenu() > $b->GetRankNavigationMenu() ? 1 : -1;
});
}

View File

@@ -481,7 +481,11 @@ class ManageBrick extends PortalBrick
if (!$this->IsGroupingByDistinctValues($sName))
{
usort($this->aGrouping[$sName]['groups'], function ($a, $b) {
return $a['rank'] > $b['rank'];
if ($a['rank'] === $b['rank']) {
return 0;
}
return $a['rank'] > $b['rank'] ? 1 : -1;
});
}

View File

@@ -91,7 +91,10 @@ class Lists extends AbstractConfiguration
}
// - Sorting list items by rank
usort($aListItems, function ($a, $b) {
return $a['rank'] > $b['rank'];
if ($a['rank'] == $b['rank']) {
return 0;
}
return $a['rank'] > $b['rank'] ? 1 : -1;
});
$aClassLists[$sListId] = $aListItems;
}

View File

@@ -91,8 +91,9 @@ class ObjectFormManager extends FormManager
* @return array formmanager_data as a PHP array
*
* @since 2.7.6 3.0.0 N°4384 method creation : factorize as this is used twice now
* @since 2.7.7 3.0.1 N°4867 now only used once, but we decided to keep this method anyway
*/
protected static function DecodeFormManagerData($formManagerData)
public static function DecodeFormManagerData($formManagerData)
{
if (is_array($formManagerData)) {
return $formManagerData;
@@ -106,31 +107,18 @@ class ObjectFormManager extends FormManager
* - formobject_class : The class of the object that is being edited/viewed
* - formmode : view|edit|create
* - values for parent
* @param bool $bTrustContent if false then won't allow modified TWIG content
*
* @return \Combodo\iTop\Portal\Form\ObjectFormManager new instance init from JSON data
*
* @inheritDoc
* @throws \Exception
* @throws \SecurityException if twig content is present and $bTrustContent is false
*
* @since 2.7.6 3.0.0 N°4384 new $bTrustContent parameter
* @since 2.7.7 3.0.1 N°4867 remove param $bTrustContent
*/
public static function FromJSON($sJson, $bTrustContent = false)
public static function FromJSON($sJson)
{
$aJson = static::DecodeFormManagerData($sJson);
$oConfig = utils::GetConfig();
$bIsContentCheckEnabled = $oConfig->GetModuleSetting(PORTAL_ID, 'enable_formmanager_content_check', true);
if ($bIsContentCheckEnabled && (false === $bTrustContent)) {
/** @noinspection NestedPositiveIfStatementsInspection */
if (isset($aJson['formproperties']['layout']['type']) && ($aJson['formproperties']['layout']['type'] === 'twig')) {
// There will be an IssueLog above in the hierarchy due to the exception, but we are logging here so that we can output the JSON data !
IssueLog::Error('Portal received a query with forbidden twig content!', \LogChannels::PORTAL, ['formmanager_data' => $aJson]);
throw new \SecurityException('Twig content not allowed in this context!');
}
}
/** @var \Combodo\iTop\Portal\Form\ObjectFormManager $oFormManager */
$oFormManager = parent::FromJSON($sJson);
@@ -183,37 +171,6 @@ class ObjectFormManager extends FormManager
return $oFormManager;
}
/**
* @param string $sPostedFormManagerData received data from the browser
* @param array $aOriginalFormProperties data generated server side
*
* @return bool true if the data are identical
*
* @since 2.7.6 3.0.0 N°4384 Check formmanager_data
*/
public static function CanTrustFormLayoutContent($sPostedFormManagerData, $aOriginalFormProperties)
{
$aPostedFormManagerData = static::DecodeFormManagerData($sPostedFormManagerData);
$sPostedFormLayoutType = (isset($aPostedFormManagerData['formproperties']['layout']['type'])) ? $aPostedFormManagerData['formproperties']['layout']['type'] : '';
if ($sPostedFormLayoutType === 'xhtml') {
return true;
}
// We need to parse the content so that autoclose tags are returned correctly (`<div />` => `<div></div>`)
$oHtmlDocument = new \DOMDocument();
$sPostedFormLayoutContent = (isset($aPostedFormManagerData['formproperties']['layout']['content'])) ? $aPostedFormManagerData['formproperties']['layout']['content'] : '';
$oHtmlDocument->loadXML('<root>'.$sPostedFormLayoutContent.'</root>');
$sPostedFormLayoutRendered = $oHtmlDocument->saveHTML();
$sOriginalFormLayoutContent = (isset($aOriginalFormProperties['layout']['content'])) ? $aOriginalFormProperties['layout']['content'] : '';
$oHtmlDocument->loadXML('<root>'.$sOriginalFormLayoutContent.'</root>');
$sOriginalFormLayoutContentRendered = $oHtmlDocument->saveHTML();
return ($sPostedFormLayoutRendered === $sOriginalFormLayoutContentRendered);
}
/**
*
* @return \Symfony\Component\DependencyInjection\ContainerInterface
@@ -703,7 +660,7 @@ class ObjectFormManager extends FormManager
/** @var Field $oField */
$oField = null;
if (is_callable(get_class($oAttDef).'::MakeFormField'))
if (is_callable([$oAttDef, 'MakeFormField']))
{
$oField = $oAttDef->MakeFormField($this->oObject);
}
@@ -1172,16 +1129,18 @@ class ObjectFormManager extends FormManager
$sObjectClass = get_class($this->oObject);
try {
// modification flags
$bIsNew = $this->oObject->IsNew();
$bWasModified = $this->oObject->IsModified();
$bActivateTriggers = (!$bIsNew && $bWasModified);
// Forcing allowed writing on the object if necessary. This is used in some particular cases.
$bAllowWrite = ($sObjectClass === 'Person' && $this->oObject->GetKey() == UserRights::GetContactId());
$bAllowWrite = $this->oContainer->get('security_helper')->IsActionAllowed($bIsNew ? UR_ACTION_CREATE : UR_ACTION_MODIFY, $sObjectClass, $this->oObject->GetKey());
if ($bAllowWrite) {
$this->oObject->AllowWrite(true);
}
// Writing object to DB
$bIsNew = $this->oObject->IsNew();
$bWasModified = $this->oObject->IsModified();
$bActivateTriggers = (!$bIsNew && $bWasModified);
try
{
$this->oObject->DBWrite();

View File

@@ -132,12 +132,10 @@ class ObjectFormHandlerHelper
$bModal = ($oRequest->isXmlHttpRequest() && empty($sOperation));
// - Retrieve form properties
$aOriginalFormProperties = ApplicationHelper::GetLoadedFormFromClass($this->aCombodoPortalInstanceConf['forms'], $sObjectClass, $sMode);
if ($aFormProperties === null)
{
$aFormProperties = $aOriginalFormProperties;
$aFormProperties = ApplicationHelper::GetLoadedFormFromClass($this->aCombodoPortalInstanceConf['forms'], $sObjectClass, $sMode);
}
// - Create and
if (empty($sOperation))
{
@@ -299,8 +297,8 @@ class ObjectFormHandlerHelper
throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Parameters formmanager_class and formmanager_data must be defined.');
}
$bTrustContent = $sFormManagerClass::CanTrustFormLayoutContent($sFormManagerData, $aOriginalFormProperties);
$oFormManager = $sFormManagerClass::FromJSON($sFormManagerData, $bTrustContent);
$this->CheckReadFormDataAllowed($sFormManagerData);
$oFormManager = $sFormManagerClass::FromJSON($sFormManagerData);
$oFormManager->SetContainer($this->oContainer);
// Applying action rules if present
@@ -438,6 +436,29 @@ class ObjectFormHandlerHelper
return $oTwig->render($sId, $aData);
}
/**
* Check if read object include in form data is allowed, throw an exception otherwise.
*
* @since 2.7.7
*
* @param $sFormManagerData form data to check
*
* @return void
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function CheckReadFormDataAllowed($sFormManagerData){
$aJsonFromData = ObjectFormManager::DecodeFormManagerData($sFormManagerData);
if(isset($aJsonFromData['formobject_class'])
&& isset($aJsonFromData['formobject_id'])
&& !$this->oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $aJsonFromData['formobject_class'], $aJsonFromData['formobject_id'])){
throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Form data access denied.');
}
}
/**
* Return an array of the available modes for a form.
*

View File

@@ -103,6 +103,12 @@ class SecurityHelper
return false;
}
// Forcing allowed writing on the object if necessary. This is used in some particular cases.
$bObjectIsCurrentUser = ($sObjectClass === 'Person' && $sObjectId == UserRights::GetContactId());
if(in_array($sAction , array(UR_ACTION_MODIFY, UR_ACTION_READ)) && $bObjectIsCurrentUser){
return true;
}
// Checking the scopes layer
// - Transforming scope action as there is only 2 values
$sScopeAction = ($sAction === UR_ACTION_READ) ? UR_ACTION_READ : UR_ACTION_MODIFY;

View File

@@ -99,7 +99,14 @@ class AppExtension extends AbstractExtension
return $sUrl;
});
//since 2.7.7 3.0.2 3.1.0 N°4867 "Twig content not allowed" error when use the extkey widget search icon in the user portal
//overwrite native twig filter : disable use of 'system' filter
$filters[] = new Twig_SimpleFilter('filter', function ($array, $arrow) {
if ($arrow == 'system'){
return json_encode($array);
}
return twig_array_filter($array, $arrow);
});
return $filters;
}

View File

@@ -1047,4 +1047,14 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Class:AsyncTask/Attribute:event_id+' => '~~',
'Class:AsyncTask/Attribute:finalclass' => 'Final class~~',
'Class:AsyncTask/Attribute:finalclass+' => '~~',
'Class:AsyncTask/Attribute:status' => 'Status~~',
'Class:AsyncTask/Attribute:status+' => '~~',
'Class:AsyncTask/Attribute:remaining_retries' => 'Remaining retries~~',
'Class:AsyncTask/Attribute:remaining_retries+' => '~~',
'Class:AsyncTask/Attribute:last_error_code' => 'Last error code~~',
'Class:AsyncTask/Attribute:last_error_code+' => '~~',
'Class:AsyncTask/Attribute:last_error' => 'Last error~~',
'Class:AsyncTask/Attribute:last_error+' => '~~',
'Class:AsyncTask/Attribute:last_attempt' => 'Last attempt~~',
'Class:AsyncTask/Attribute:last_attempt+' => '~~',
));

View File

@@ -455,6 +455,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'UI:Error:ObjectsAlreadyDeleted' => 'Chyba: objekt byl již odstraněn!',
'UI:Error:BulkDeleteNotAllowedOn_Class' => 'Nemáte oprávnění k hromadnému odstranění objektů třídy %1$s',
'UI:Error:DeleteNotAllowedOn_Class' => 'Nemáte oprávnění k odstranění objektů třídy %1$s',
'UI:Error:ReadNotAllowedOn_Class' => 'You are not allowed to view objects of class %1$s~~',
'UI:Error:BulkModifyNotAllowedOn_Class' => 'Nemáte oprávnění k hromadné aktualizaci objektů třídy %1$s',
'UI:Error:ObjectAlreadyCloned' => 'Chyba: objekt byl již naklonován!',
'UI:Error:ObjectAlreadyCreated' => 'Chyba: objekt byl již vytvořen!',
@@ -463,6 +464,9 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'UI:Error:InvalidDashboard' => 'Error: invalid dashboard~~',
'UI:Error:MaintenanceMode' => 'Application is currently in maintenance~~',
'UI:Error:MaintenanceTitle' => 'Maintenance~~',
'UI:Error:InvalidToken' => 'Error: the requested operation has already been performed (CSRF token not found)~~',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)~~',
'UI:GroupBy:Count' => 'Množství',
'UI:GroupBy:Count+' => 'Množství prvků',
@@ -1553,6 +1557,8 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'UI:Search:Criteria:Raw:Filtered' => 'Filtered~~',
'UI:Search:Criteria:Raw:FilteredOn' => 'Filtered on %1$s~~',
'UI:StateChanged' => 'State changed~~',
));
//

View File

@@ -1045,4 +1045,14 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'Class:AsyncTask/Attribute:event_id+' => '~~',
'Class:AsyncTask/Attribute:finalclass' => 'Final class~~',
'Class:AsyncTask/Attribute:finalclass+' => '~~',
'Class:AsyncTask/Attribute:status' => 'Status~~',
'Class:AsyncTask/Attribute:status+' => '~~',
'Class:AsyncTask/Attribute:remaining_retries' => 'Remaining retries~~',
'Class:AsyncTask/Attribute:remaining_retries+' => '~~',
'Class:AsyncTask/Attribute:last_error_code' => 'Last error code~~',
'Class:AsyncTask/Attribute:last_error_code+' => '~~',
'Class:AsyncTask/Attribute:last_error' => 'Last error~~',
'Class:AsyncTask/Attribute:last_error+' => '~~',
'Class:AsyncTask/Attribute:last_attempt' => 'Last attempt~~',
'Class:AsyncTask/Attribute:last_attempt+' => '~~',
));

View File

@@ -442,6 +442,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'UI:Error:ObjectsAlreadyDeleted' => 'Fejl: objekterne er allerede slettet!',
'UI:Error:BulkDeleteNotAllowedOn_Class' => 'Du har ikke tilladelse til at foretage en masse sletning af objekter i klassen %1$s',
'UI:Error:DeleteNotAllowedOn_Class' => 'Du har ikke tilladelse til at slette objekter af klassen %1$s',
'UI:Error:ReadNotAllowedOn_Class' => 'You are not allowed to view objects of class %1$s~~',
'UI:Error:BulkModifyNotAllowedOn_Class' => 'Du har ikke tilladelse til at foretage en masse opdatering af objekter i klassen %1$s',
'UI:Error:ObjectAlreadyCloned' => 'Fejl: objektet er allerede klonet!',
'UI:Error:ObjectAlreadyCreated' => 'Fejl: objektet er allerede oprettet!',
@@ -450,6 +451,9 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'UI:Error:InvalidDashboard' => 'Error: invalid dashboard~~',
'UI:Error:MaintenanceMode' => 'Application is currently in maintenance~~',
'UI:Error:MaintenanceTitle' => 'Maintenance~~',
'UI:Error:InvalidToken' => 'Error: the requested operation has already been performed (CSRF token not found)~~',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)~~',
'UI:GroupBy:Count' => 'Antal',
'UI:GroupBy:Count+' => 'Antal af elementer',
@@ -1542,6 +1546,8 @@ Ved tilknytningen til en trigger, bliver hver handling tildelt et "rækkefølge"
'UI:Search:Criteria:Raw:Filtered' => 'Filtered~~',
'UI:Search:Criteria:Raw:FilteredOn' => 'Filtered on %1$s~~',
'UI:StateChanged' => 'State changed~~',
));
//

View File

@@ -1044,4 +1044,14 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:AsyncTask/Attribute:event_id+' => '',
'Class:AsyncTask/Attribute:finalclass' => 'Final Class',
'Class:AsyncTask/Attribute:finalclass+' => '',
'Class:AsyncTask/Attribute:status' => 'Status~~',
'Class:AsyncTask/Attribute:status+' => '~~',
'Class:AsyncTask/Attribute:remaining_retries' => 'Remaining retries~~',
'Class:AsyncTask/Attribute:remaining_retries+' => '~~',
'Class:AsyncTask/Attribute:last_error_code' => 'Last error code~~',
'Class:AsyncTask/Attribute:last_error_code+' => '~~',
'Class:AsyncTask/Attribute:last_error' => 'Last error~~',
'Class:AsyncTask/Attribute:last_error+' => '~~',
'Class:AsyncTask/Attribute:last_attempt' => 'Last attempt~~',
'Class:AsyncTask/Attribute:last_attempt+' => '~~',
));

View File

@@ -441,6 +441,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'UI:Error:ObjectsAlreadyDeleted' => 'Fehler: die Objekte wurden bereits gelöscht!',
'UI:Error:BulkDeleteNotAllowedOn_Class' => 'Sie sind nicht berechtigt, mehrere Objekte der Klasse %1$s zu löschen',
'UI:Error:DeleteNotAllowedOn_Class' => 'Sie sind nicht berechtigt, Objekte der Klasse zu löschen %1$s',
'UI:Error:ReadNotAllowedOn_Class' => 'You are not allowed to view objects of class %1$s~~',
'UI:Error:BulkModifyNotAllowedOn_Class' => 'Sie sind nicht berechtigt, diese Massenaktualisierung der Objekte der Klasse "%1$s" durchzuführen.',
'UI:Error:ObjectAlreadyCloned' => 'Fehler: das Objekt wurde bereits dupliziert!',
'UI:Error:ObjectAlreadyCreated' => 'Fehler: das Objekt wurde bereits erstellt!',
@@ -449,6 +450,9 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'UI:Error:InvalidDashboard' => 'Fehler: Ungültiges Dashboard',
'UI:Error:MaintenanceMode' => 'Die Anwendung befindet sich derzeit im Wartungsmodus.',
'UI:Error:MaintenanceTitle' => 'Wartung',
'UI:Error:InvalidToken' => 'Error: the requested operation has already been performed (CSRF token not found)~~',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)~~',
'UI:GroupBy:Count' => 'Anzahl',
'UI:GroupBy:Count+' => 'Anzahl der Elemente',
@@ -1540,6 +1544,8 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm
'UI:Search:Criteria:Raw:Filtered' => 'Gefiltert',
'UI:Search:Criteria:Raw:FilteredOn' => 'Gefiltert über %1$s',
'UI:StateChanged' => 'State changed~~',
));
//

View File

@@ -468,6 +468,8 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Error:MaintenanceTitle' => 'Maintenance',
'UI:Error:InvalidToken' => 'Error: the requested operation has already been performed (CSRF token not found)',
'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP provider %1$s does not exist (email_transport_smtp.oauth.provider)',
'UI:GroupBy:Count' => 'Count',
'UI:GroupBy:Count+' => 'Number of elements',
'UI:CountOfObjects' => '%1$d objects matching the criteria.',

View File

@@ -22,62 +22,62 @@
* @license http://opensource.org/licenses/AGPL-3.0
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
*/
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Core:DeletedObjectLabel' => '%1s (eliminado)',
'Core:DeletedObjectTip' => 'Elemento ha sido Eliminado en %1$s (%2$s)',
'Core:DeletedObjectTip' => 'Elemento ha sido Eliminado en %1$s (%2$s)',
'Core:UnknownObjectLabel' => 'Elemento No Encontrado (Clase: %1$s, Identificador: %2$d)',
'Core:UnknownObjectTip' => 'El Elemento no pudo ser encontrado. Pudo haber sido eliminado hace tiempo y purgado de la Bitácora.',
'Core:UniquenessDefaultError' => 'Regla de unicidad \'%1$s\' en error',
'Core:AttributeLinkedSet' => 'Arreglo de objetos',
'Core:AttributeLinkedSet' => 'Arreglo de objetos',
'Core:AttributeLinkedSet+' => 'Cualquier tipo de objetos [subclass] de la misma clase',
'Core:AttributeDashboard' => 'Panel de Control',
'Core:AttributeDashboard' => 'Panel de Control',
'Core:AttributeDashboard+' => 'Panel de control y supervisión',
'Core:AttributePhoneNumber' => 'Número telefónico',
'Core:AttributePhoneNumber' => 'Número telefónico',
'Core:AttributePhoneNumber+' => '',
'Core:AttributeObsolescenceDate' => 'Fecha de Obsolescencia',
'Core:AttributeObsolescenceDate' => 'Fecha de Obsolescencia',
'Core:AttributeObsolescenceDate+' => '',
'Core:AttributeTagSet' => 'Lista de etiquetas',
'Core:AttributeTagSet+' => '',
'Core:AttributeSet:placeholder' => 'Click to agregar',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s de %3$s)',
'Core:AttributeTagSet' => 'Lista de etiquetas',
'Core:AttributeTagSet+' => '',
'Core:AttributeSet:placeholder' => 'Click to agregar',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s de %3$s)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses' => '%1$s (%2$s de clases hijas)',
'Core:AttributeCaseLog' => 'Bitácora',
'Core:AttributeCaseLog' => 'Bitácora',
'Core:AttributeCaseLog+' => '',
'Core:AttributeMetaEnum' => 'Enumeración Calculada',
'Core:AttributeMetaEnum' => 'Enumeración Calculada',
'Core:AttributeMetaEnum+' => '',
'Core:AttributeLinkedSetIndirect' => 'Arreglo de objetos (N-N)',
'Core:AttributeLinkedSetIndirect' => 'Arreglo de objetos (N-N)',
'Core:AttributeLinkedSetIndirect+' => 'Cualquier tipo de objetos [subclass] de la misma clase',
'Core:AttributeInteger' => 'Entero',
'Core:AttributeInteger' => 'Entero',
'Core:AttributeInteger+' => 'Valor numérico (puede ser negativo)',
'Core:AttributeDecimal' => 'Decimal',
'Core:AttributeDecimal' => 'Decimal',
'Core:AttributeDecimal+' => 'Valor decimal (puede ser negativo)',
'Core:AttributeBoolean' => 'Booleano',
'Core:AttributeBoolean+' => 'Booleano',
'Core:AttributeBoolean' => 'Booleano',
'Core:AttributeBoolean+' => 'Booleano',
'Core:AttributeBoolean/Value:null' => 'Nulo',
'Core:AttributeBoolean/Value:yes' => 'Si',
'Core:AttributeBoolean/Value:no' => 'No',
'Core:AttributeBoolean/Value:yes' => 'Si',
'Core:AttributeBoolean/Value:no' => 'No',
'Core:AttributeArchiveFlag' => 'Bandera de Archivado',
'Core:AttributeArchiveFlag/Value:yes' => 'Si',
'Core:AttributeArchiveFlag' => 'Bandera de Archivado',
'Core:AttributeArchiveFlag/Value:yes' => 'Si',
'Core:AttributeArchiveFlag/Value:yes+' => 'Este objeto es solo visible en modo Archivado',
'Core:AttributeArchiveFlag/Value:no' => 'No',
'Core:AttributeArchiveFlag/Label' => 'Archivado',
'Core:AttributeArchiveFlag/Label+' => '',
'Core:AttributeArchiveDate/Label' => 'Fecha de Archivado',
'Core:AttributeArchiveFlag/Value:no' => 'No',
'Core:AttributeArchiveFlag/Label' => 'Archivado',
'Core:AttributeArchiveFlag/Label+' => '',
'Core:AttributeArchiveDate/Label' => 'Fecha de Archivado',
'Core:AttributeArchiveDate/Label+' => '',
'Core:AttributeObsolescenceFlag' => 'Bandera de Obsolescencia',
@@ -193,24 +193,24 @@ Operadores:<br/>
'Core:AttributeTable' => 'Tabla',
'Core:AttributeTable+' => 'Arreglo indexado con dos dimensiones',
'Core:AttributePropertySet' => 'Propiedades',
'Core:AttributePropertySet' => 'Propiedades',
'Core:AttributePropertySet+' => 'Lista de propiedades sin tipo (nombre y valor)',
'Core:AttributeFriendlyName' => 'Nombre común',
'Core:AttributeFriendlyName' => 'Nombre común',
'Core:AttributeFriendlyName+' => 'Atributo creado automáticamente; el nombre común es obtenido de varios atributos',
'Core:FriendlyName-Label' => 'Nombre común',
'Core:FriendlyName-Label' => 'Nombre común',
'Core:FriendlyName-Description' => 'Nombre común',
'Core:AttributeTag' => 'Etiquetas',
'Core:AttributeTag' => 'Etiquetas',
'Core:AttributeTag+' => '',
'Core:Context=REST/JSON' => 'REST',
'Core:Context=Synchro' => 'Synchro',
'Core:Context=Setup' => 'Configuración',
'Core:Context=REST/JSON' => 'REST',
'Core:Context=Synchro' => 'Synchro',
'Core:Context=Setup' => 'Configuración',
'Core:Context=GUI:Console' => 'Consola',
'Core:Context=CRON' => 'cron',
'Core:Context=GUI:Portal' => 'Portal',
'Core:Context=CRON' => 'cron',
'Core:Context=GUI:Portal' => 'Portal',
));
@@ -223,7 +223,7 @@ Operadores:<br/>
// Class: CMDBChange
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:CMDBChange' => 'Cambio',
'Class:CMDBChange+' => 'Cambios',
'Class:CMDBChange/Attribute:date' => 'Fecha',
@@ -236,7 +236,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: CMDBChangeOp
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:CMDBChangeOp' => 'Operación de Cambios',
'Class:CMDBChangeOp+' => 'Operación de Cambios',
'Class:CMDBChangeOp/Attribute:change' => 'Cambio',
@@ -257,7 +257,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: CMDBChangeOpCreate
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:CMDBChangeOpCreate' => 'Creación de Objeto',
'Class:CMDBChangeOpCreate+' => 'Creación de Objeto',
));
@@ -266,7 +266,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: CMDBChangeOpDelete
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:CMDBChangeOpDelete' => 'Borrado de Objeto',
'Class:CMDBChangeOpDelete+' => 'Borrado de Objeto',
));
@@ -275,7 +275,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: CMDBChangeOpSetAttribute
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:CMDBChangeOpSetAttribute' => 'Cambio en Objeto',
'Class:CMDBChangeOpSetAttribute+' => 'Cambio en Objeto',
'Class:CMDBChangeOpSetAttribute/Attribute:attcode' => 'Atributo',
@@ -286,7 +286,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: CMDBChangeOpSetAttributeScalar
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:CMDBChangeOpSetAttributeScalar' => 'Cambio de Propiedad',
'Class:CMDBChangeOpSetAttributeScalar+' => 'Cambio de Propiedades escalares del Objeto',
'Class:CMDBChangeOpSetAttributeScalar/Attribute:oldvalue' => 'Valor Anterior',
@@ -295,7 +295,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:CMDBChangeOpSetAttributeScalar/Attribute:newvalue+' => 'Nuevo Valor del Atributo',
));
// Used by CMDBChangeOp... & derived classes
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Change:ObjectCreated' => 'Objeto Creado',
'Change:ObjectDeleted' => 'Objeto Eliminado',
'Change:ObjectModified' => 'Objeto Modificado',
@@ -314,7 +314,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: CMDBChangeOpSetAttributeBlob
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:CMDBChangeOpSetAttributeBlob' => 'Cambio de Datos',
'Class:CMDBChangeOpSetAttributeBlob+' => 'Cambio de Datos',
'Class:CMDBChangeOpSetAttributeBlob/Attribute:prevdata' => 'Valor Anterior',
@@ -325,7 +325,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: CMDBChangeOpSetAttributeText
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:CMDBChangeOpSetAttributeText' => 'Cambio de Texto',
'Class:CMDBChangeOpSetAttributeText+' => 'Cambio de Texto',
'Class:CMDBChangeOpSetAttributeText/Attribute:prevdata' => 'Valor Anterior',
@@ -336,7 +336,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: Event
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:Event' => 'Bitácora de Eventos',
'Class:Event+' => 'Evento interno de aplicación',
'Class:Event/Attribute:message' => 'Mensaje',
@@ -353,7 +353,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: EventNotification
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:EventNotification' => 'Notificación de Evento',
'Class:EventNotification+' => 'Notificación de Evento',
'Class:EventNotification/Attribute:trigger_id' => 'Disparador',
@@ -368,7 +368,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: EventNotificationEmail
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:EventNotificationEmail' => 'Correo Electrónico de Notificación de Evento',
'Class:EventNotificationEmail+' => 'Correo Electrónico de Notificación de Evento',
'Class:EventNotificationEmail/Attribute:to' => 'Para',
@@ -391,7 +391,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: EventIssue
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:EventIssue' => 'Registro de Evento',
'Class:EventIssue+' => 'Evidencia de un evento (warning, error, etc.)',
'Class:EventIssue/Attribute:issue' => 'Evento',
@@ -414,7 +414,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: EventWebService
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:EventWebService' => 'Evento de WebService',
'Class:EventWebService+' => 'Evidencia de una llamada de servicio Web',
'Class:EventWebService/Attribute:verb' => 'Verbo',
@@ -431,7 +431,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:EventWebService/Attribute:data+' => 'Datos de Resultado',
));
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:EventRestService' => 'Llamada REST/JSON',
'Class:EventRestService+' => 'Traza de llamada a servicio REST/JSON',
'Class:EventRestService/Attribute:operation' => 'Operación',
@@ -452,7 +452,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: EventLoginUsage
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:EventLoginUsage' => 'Uso de la Cuenta',
'Class:EventLoginUsage+' => 'Uso de la Cuenta',
'Class:EventLoginUsage/Attribute:user_id' => 'Usuario',
@@ -467,7 +467,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: Action
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:Action' => 'Acción Personalizada',
'Class:Action+' => 'Acción definida por el usuario',
'Class:Action/Attribute:name' => 'Nombre',
@@ -492,7 +492,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: ActionNotification
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:ActionNotification' => 'Notificación',
'Class:ActionNotification+' => 'Notificación (resúmen)',
));
@@ -501,7 +501,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: ActionEmail
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:ActionEmail' => 'Notificación por Correo Electrónico',
'Class:ActionEmail+' => 'Notificación por Correo Electrónico',
'Class:ActionEmail/Attribute:test_recipient' => 'Destinatario de Prueba',
@@ -534,24 +534,24 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: Trigger
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:Trigger' => 'Disparador',
'Class:Trigger+' => 'Disparador',
'Class:Trigger/Attribute:description' => 'Descripción',
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:Trigger' => 'Disparador',
'Class:Trigger+' => 'Disparador',
'Class:Trigger/Attribute:description' => 'Descripción',
'Class:Trigger/Attribute:description+' => 'Descripción',
'Class:Trigger/Attribute:action_list' => 'Acciones',
'Class:Trigger/Attribute:action_list' => 'Acciones',
'Class:Trigger/Attribute:action_list+' => 'Acciones realizadas cuando se activó el disparador',
'Class:Trigger/Attribute:finalclass' => 'Clase',
'Class:Trigger/Attribute:finalclass+' => 'Clase',
'Class:Trigger/Attribute:context' => 'Contexto',
'Class:Trigger/Attribute:context+' => 'Contexto para permitir el inicio del disparador',
'Class:Trigger/Attribute:finalclass' => 'Clase',
'Class:Trigger/Attribute:finalclass+' => 'Clase',
'Class:Trigger/Attribute:context' => 'Contexto',
'Class:Trigger/Attribute:context+' => 'Contexto para permitir el inicio del disparador',
));
//
// Class: TriggerOnObject
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:TriggerOnObject' => 'Disparador (Depende de la clase)',
'Class:TriggerOnObject+' => 'Disparador en una clase de objeto dada',
'Class:TriggerOnObject/Attribute:target_class' => 'Clase destino',
@@ -566,7 +566,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: TriggerOnPortalUpdate
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:TriggerOnPortalUpdate' => 'Disparador (cuando se actualiza desde el portal)',
'Class:TriggerOnPortalUpdate+' => 'Disparador cuando un usuario actualiza desde el portal',
));
@@ -575,7 +575,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: TriggerOnStateChange
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:TriggerOnStateChange' => 'Disparador (en cambio de estado)',
'Class:TriggerOnStateChange+' => 'Disparador en cambio de estado de objeto',
'Class:TriggerOnStateChange/Attribute:state' => 'Estado',
@@ -586,7 +586,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: TriggerOnStateEnter
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:TriggerOnStateEnter' => 'Disparador (entrando a un estado)',
'Class:TriggerOnStateEnter+' => 'Disparador en cambio de estado de objeto - entrando',
));
@@ -595,7 +595,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: TriggerOnStateLeave
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:TriggerOnStateLeave' => 'Disparador (saliendo de un estado)',
'Class:TriggerOnStateLeave+' => 'Disparador en cambio de estado de objeto - saliendo',
));
@@ -604,7 +604,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: TriggerOnObjectCreate
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:TriggerOnObjectCreate' => 'Disparador (creación de objeto)',
'Class:TriggerOnObjectCreate+' => 'Disparador en la creación de objeto (hija de clase) de una clase dada',
));
@@ -613,7 +613,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: TriggerOnObjectDelete
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:TriggerOnObjectDelete' => 'Disparador (eliminando un objecto)',
'Class:TriggerOnObjectDelete+' => 'Disparador al eliminar un objecto de la clase dada [o una clase hija] ',
));
@@ -622,7 +622,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: TriggerOnObjectUpdate
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:TriggerOnObjectUpdate' => 'Disparador (actualizando un objecto)',
'Class:TriggerOnObjectUpdate+' => 'Disparador al actualizar un objeto de la clase dada [o una clase hija]',
'Class:TriggerOnObjectUpdate/Attribute:target_attcodes' => 'Campos objetivo',
@@ -633,7 +633,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: TriggerOnThresholdReached
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:TriggerOnThresholdReached' => 'Disparador (en umbral)',
'Class:TriggerOnThresholdReached+' => 'Disparador en umbral Stop-Watch alcanzado',
'Class:TriggerOnThresholdReached/Attribute:stop_watch_code' => 'Detener watch',
@@ -646,7 +646,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Class: lnkTriggerAction
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:lnkTriggerAction' => 'Relación Acción y Disparador',
'Class:lnkTriggerAction+' => 'Relación Acción y Disparador',
'Class:lnkTriggerAction/Attribute:action_id' => 'Acción',
@@ -664,7 +664,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
//
// Synchro Data Source
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:SynchroDataSource/Attribute:name' => 'Nombre',
'Class:SynchroDataSource/Attribute:name+' => 'Nombre de la Fuente de Datos',
'Class:SynchroDataSource/Attribute:description' => 'Descripción',
@@ -912,80 +912,80 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Core:BulkExport:PDFPageOrientation' => 'Orientación de la Página:',
'Core:BulkExport:PageOrientation-L' => 'Horizontal',
'Core:BulkExport:PageOrientation-P' => 'Vertical',
'Core:BulkExport:XMLFormat' => 'Archivo XML (*.xml)',
'Core:BulkExport:XMLOptions' => 'Opciones XML',
'Core:BulkExport:SpreadsheetFormat' => 'Formato Tabla HTML (*.html)',
'Core:BulkExport:SpreadsheetOptions' => 'Opciones de Tabla',
'Core:BulkExport:OptionNoLocalize' => 'Código de exportación en lugar de etiqueta',
'Core:BulkExport:OptionLinkSets' => 'Incluir objetos ligados',
'Core:BulkExport:OptionFormattedText' => 'Conservar formato de texto',
'Core:BulkExport:ScopeDefinition' => 'Definición de los objetos a exportar',
'Core:BulkExportLabelOQLExpression' => 'Consulta OQL:',
'Core:BulkExportLabelPhrasebookEntry' => 'Entrada Consulta de Libreta de Consultas:',
'Core:BulkExportMessageEmptyOQL' => 'Por favor ingrese una consulta OQL válida.',
'Core:BulkExportMessageEmptyPhrasebookEntry' => 'Por favor seleccione un entrada válida de la libreta de consultas.',
'Core:BulkExportQueryPlaceholder' => 'Escriba una consulta OQL aquí...',
'Core:BulkExportCanRunNonInteractive' => 'Click aquí para ejecutar la exportación en modo no interactivo.',
'Core:BulkExportLegacyExport' => 'Click aquí para acceder a la exportación tradicional.',
'Core:BulkExport:XLSXOptions' => 'Opciones Excel',
'Core:BulkExport:TextFormat' => 'Compos texto conteniendo algunas marcas HTML',
'Core:BulkExport:DateTimeFormat' => 'Formato de fecha y hora',
'Core:BulkExport:XMLFormat' => 'Archivo XML (*.xml)',
'Core:BulkExport:XMLOptions' => 'Opciones XML',
'Core:BulkExport:SpreadsheetFormat' => 'Formato Tabla HTML (*.html)',
'Core:BulkExport:SpreadsheetOptions' => 'Opciones de Tabla',
'Core:BulkExport:OptionNoLocalize' => 'Código de exportación en lugar de etiqueta',
'Core:BulkExport:OptionLinkSets' => 'Incluir objetos ligados',
'Core:BulkExport:OptionFormattedText' => 'Conservar formato de texto',
'Core:BulkExport:ScopeDefinition' => 'Definición de los objetos a exportar',
'Core:BulkExportLabelOQLExpression' => 'Consulta OQL:',
'Core:BulkExportLabelPhrasebookEntry' => 'Entrada Consulta de Libreta de Consultas:',
'Core:BulkExportMessageEmptyOQL' => 'Por favor ingrese una consulta OQL válida.',
'Core:BulkExportMessageEmptyPhrasebookEntry' => 'Por favor seleccione un entrada válida de la libreta de consultas.',
'Core:BulkExportQueryPlaceholder' => 'Escriba una consulta OQL aquí...',
'Core:BulkExportCanRunNonInteractive' => 'Click aquí para ejecutar la exportación en modo no interactivo.',
'Core:BulkExportLegacyExport' => 'Click aquí para acceder a la exportación tradicional.',
'Core:BulkExport:XLSXOptions' => 'Opciones Excel',
'Core:BulkExport:TextFormat' => 'Compos texto conteniendo algunas marcas HTML',
'Core:BulkExport:DateTimeFormat' => 'Formato de fecha y hora',
'Core:BulkExport:DateTimeFormatDefault_Example' => 'Formato por omisión (%1$s), ej. %2$s',
'Core:BulkExport:DateTimeFormatCustom_Format' => 'Formato personalizado: %1$s',
'Core:BulkExport:PDF:PageNumber' => 'Página %1$s',
'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
'Core:DateTime:Placeholder_j' => 'D', // Day of the month: 1 or 2 digits (without leading zero)
'Core:DateTime:Placeholder_m' => 'MM', // Month on 2 digits i.e. 01-12
'Core:DateTime:Placeholder_n' => 'M', // Month on 1 or 2 digits 1-12
'Core:DateTime:Placeholder_Y' => 'AAAA', // Year on 4 digits
'Core:DateTime:Placeholder_y' => 'AA', // Year on 2 digits
'Core:DateTime:Placeholder_H' => 'hh', // Hour 00..23
'Core:DateTime:Placeholder_h' => 'h', // Hour 01..12
'Core:DateTime:Placeholder_G' => 'hh', // Hour 0..23
'Core:DateTime:Placeholder_g' => 'h', // Hour 1..12
'Core:DateTime:Placeholder_a' => 'am/pm', // am/pm (lowercase)
'Core:DateTime:Placeholder_A' => 'AM/PM', // AM/PM (uppercase)
'Core:DateTime:Placeholder_i' => 'mm', // minutes, 2 digits: 00..59
'Core:DateTime:Placeholder_s' => 'ss', // seconds, 2 digits 00..59
'Core:Validator:Default' => 'Formato incorrecto',
'Core:Validator:Mandatory' => 'Por favor, ingrese este campo',
'Core:Validator:MustBeInteger' => 'Debe ser un entero',
'Core:Validator:MustSelectOne' => 'Por favor, seleccione uno',
'Core:BulkExport:DateTimeFormatCustom_Format' => 'Formato personalizado: %1$s',
'Core:BulkExport:PDF:PageNumber' => 'Página %1$s',
'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
'Core:DateTime:Placeholder_j' => 'D', // Day of the month: 1 or 2 digits (without leading zero)
'Core:DateTime:Placeholder_m' => 'MM', // Month on 2 digits i.e. 01-12
'Core:DateTime:Placeholder_n' => 'M', // Month on 1 or 2 digits 1-12
'Core:DateTime:Placeholder_Y' => 'AAAA', // Year on 4 digits
'Core:DateTime:Placeholder_y' => 'AA', // Year on 2 digits
'Core:DateTime:Placeholder_H' => 'hh', // Hour 00..23
'Core:DateTime:Placeholder_h' => 'h', // Hour 01..12
'Core:DateTime:Placeholder_G' => 'hh', // Hour 0..23
'Core:DateTime:Placeholder_g' => 'h', // Hour 1..12
'Core:DateTime:Placeholder_a' => 'am/pm', // am/pm (lowercase)
'Core:DateTime:Placeholder_A' => 'AM/PM', // AM/PM (uppercase)
'Core:DateTime:Placeholder_i' => 'mm', // minutes, 2 digits: 00..59
'Core:DateTime:Placeholder_s' => 'ss', // seconds, 2 digits 00..59
'Core:Validator:Default' => 'Formato incorrecto',
'Core:Validator:Mandatory' => 'Por favor, ingrese este campo',
'Core:Validator:MustBeInteger' => 'Debe ser un entero',
'Core:Validator:MustSelectOne' => 'Por favor, seleccione uno',
));
//
// Class: TagSetFieldData
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:TagSetFieldData' => '%2$s para la clase %1$s',
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:TagSetFieldData' => '%2$s para la clase %1$s',
'Class:TagSetFieldData+' => 'Datos de campo',
'Class:TagSetFieldData/Attribute:code' => 'Código',
'Class:TagSetFieldData/Attribute:code+' => 'Código interno. Debe contener al menos tres caracteres alfanuméricos',
'Class:TagSetFieldData/Attribute:label' => 'Etiqueta',
'Class:TagSetFieldData/Attribute:label+' => 'Etiqueta mostrada',
'Class:TagSetFieldData/Attribute:description' => 'Descripción',
'Class:TagSetFieldData/Attribute:code' => 'Código',
'Class:TagSetFieldData/Attribute:code+' => 'Código interno. Debe contener al menos tres caracteres alfanuméricos',
'Class:TagSetFieldData/Attribute:label' => 'Etiqueta',
'Class:TagSetFieldData/Attribute:label+' => 'Etiqueta mostrada',
'Class:TagSetFieldData/Attribute:description' => 'Descripción',
'Class:TagSetFieldData/Attribute:description+' => 'Descripción de la etiqueta',
'Class:TagSetFieldData/Attribute:finalclass' => 'Clase',
'Class:TagSetFieldData/Attribute:obj_class' => 'Clase de objeto',
'Class:TagSetFieldData/Attribute:obj_attcode' => 'Código de campo',
'Class:TagSetFieldData/Attribute:finalclass' => 'Clase',
'Class:TagSetFieldData/Attribute:obj_class' => 'Clase de objeto',
'Class:TagSetFieldData/Attribute:obj_attcode' => 'Código de campo',
'Core:TagSetFieldData:ErrorDeleteUsedTag' => 'Etiquetas es uso no pueden ser borradas',
'Core:TagSetFieldData:ErrorDeleteUsedTag' => 'Etiquetas es uso no pueden ser borradas',
'Core:TagSetFieldData:ErrorDuplicateTagCodeOrLabel' => 'Los códigos o las etiquetas deben ser únicos',
'Core:TagSetFieldData:ErrorTagCodeSyntax' => 'El código de la etiqueta debe contener entre 3 y %1$d caracteres alfanuméricos',
'Core:TagSetFieldData:ErrorTagCodeReservedWord' => 'El código elegido es una palabra reservada',
'Core:TagSetFieldData:ErrorTagLabelSyntax' => 'La etiqueta no odebe contener \'%1$s\' y no puede estar vacía',
'Core:TagSetFieldData:ErrorCodeUpdateNotAllowed' => 'Códigos de etiqueta en uso no pueden ser borrados',
'Core:TagSetFieldData:ErrorClassUpdateNotAllowed' => 'Etiquetas "Object Class" no pueden ser cambiadas',
'Core:TagSetFieldData:ErrorTagCodeSyntax' => 'El código de la etiqueta debe contener entre 3 y %1$d caracteres alfanuméricos',
'Core:TagSetFieldData:ErrorTagCodeReservedWord' => 'El código elegido es una palabra reservada',
'Core:TagSetFieldData:ErrorTagLabelSyntax' => 'La etiqueta no odebe contener \'%1$s\' y no puede estar vacía',
'Core:TagSetFieldData:ErrorCodeUpdateNotAllowed' => 'Códigos de etiqueta en uso no pueden ser borrados',
'Core:TagSetFieldData:ErrorClassUpdateNotAllowed' => 'Etiquetas "Object Class" no pueden ser cambiadas',
'Core:TagSetFieldData:ErrorAttCodeUpdateNotAllowed' => 'Etiquetas "Attribute Code" no pueden ser cambiadas',
'Core:TagSetFieldData:WhereIsThisTagTab' => 'Uso de la etiqueta (%1$d)',
'Core:TagSetFieldData:NoEntryFound' => 'No hay entradas para esta etiqueta',
'Core:TagSetFieldData:WhereIsThisTagTab' => 'Uso de la etiqueta (%1$d)',
'Core:TagSetFieldData:NoEntryFound' => 'No hay entradas para esta etiqueta',
));
//
// Class: DBProperty
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:DBProperty' => 'Propiedad BD',
'Class:DBProperty+' => 'Propiedad de Base de Datos',
'Class:DBProperty/Attribute:name' => 'Nombre',
@@ -1003,7 +1003,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
//
// Class: BackgroundTask
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:BackgroundTask' => 'Tarea en Segundo Plano',
'Class:BackgroundTask+' => 'Tarea en Segundo Plano',
'Class:BackgroundTask/Attribute:class_name' => 'Nombre de Clase',
@@ -1033,7 +1033,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
//
// Class: AsyncTask
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:AsyncTask' => 'Tarea Asíncrona',
'Class:AsyncTask+' => 'Tarea Asíncrona',
'Class:AsyncTask/Attribute:created' => 'Creado',
@@ -1046,4 +1046,14 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:AsyncTask/Attribute:event_id+' => 'Evento',
'Class:AsyncTask/Attribute:finalclass' => 'Clase',
'Class:AsyncTask/Attribute:finalclass+' => 'Clase',
'Class:AsyncTask/Attribute:status' => 'Status~~',
'Class:AsyncTask/Attribute:status+' => '~~',
'Class:AsyncTask/Attribute:remaining_retries' => 'Remaining retries~~',
'Class:AsyncTask/Attribute:remaining_retries+' => '~~',
'Class:AsyncTask/Attribute:last_error_code' => 'Last error code~~',
'Class:AsyncTask/Attribute:last_error_code+' => '~~',
'Class:AsyncTask/Attribute:last_error' => 'Last error~~',
'Class:AsyncTask/Attribute:last_error+' => '~~',
'Class:AsyncTask/Attribute:last_attempt' => 'Last attempt~~',
'Class:AsyncTask/Attribute:last_attempt+' => '~~',
));

File diff suppressed because it is too large Load Diff

View File

@@ -1053,8 +1053,6 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:AsyncTask/Attribute:last_error+' => '',
'Class:AsyncTask/Attribute:last_attempt' => 'Dernière tentative',
'Class:AsyncTask/Attribute:last_attempt+' => '',
));
// Additional language entries not present in English dict

View File

@@ -451,6 +451,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI:Error:MaintenanceTitle' => 'Maintenance',
'UI:Error:InvalidToken' => 'Erreur: l\'opération a déjà été effectuée (CSRF token not found)',
'UI:Error:SMTP:UnknownVendor' => 'Le provider SMTP Oauth 2.0 %1$s n\'existe pas',
'UI:GroupBy:Count' => 'Nombre',
'UI:GroupBy:Count+' => 'Nombre d\'éléments',
'UI:CountOfObjects' => '%1$d objets correspondants aux critères.',

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