diff --git a/.composer-require-checker.config.json b/.composer-require-checker.config.json
index 5f722f81e1..17f8e78ad1 100644
--- a/.composer-require-checker.config.json
+++ b/.composer-require-checker.config.json
@@ -19,8 +19,7 @@
"DB", "DBSlave",
"// GLPI base constants (they are not detected as they are dynamically declared)",
- "GLPI_AJAX_DASHBOARD", "GLPI_ALLOW_IFRAME_IN_RICH_TEXT", "GLPI_CALDAV_IMPORT_STATE", "GLPI_CACHE_DIR", "GLPI_CRON_DIR", "GLPI_CSRF_EXPIRES", "GLPI_CSRF_MAX_TOKENS", "GLPI_USE_IDOR_CHECK", "GLPI_IDOR_EXPIRES", "GLPI_DEMO_MODE", "GLPI_DOC_DIR", "GLPI_DUMP_DIR", "GLPI_FORCE_EMPTY_SQL_MODE", "GLPI_GRAPH_DIR", "GLPI_INSTALL_MODE", "GLPI_LOCAL_I18N_DIR", "GLPI_LOCK_DIR", "GLPI_LOG_DIR", "GLPI_MARKETPLACE_DIR", "GLPI_MARKETPLACE_PLUGINS_API_URI", "GLPI_MARKETPLACE_PRERELEASES", "GLPI_NETWORK_REGISTRATION_API_URL", "GLPI_PICTURE_DIR", "GLPI_PLUGIN_DOC_DIR", "GLPI_RSS_DIR", "GLPI_SESSION_DIR", "GLPI_TELEMETRY_URI", "GLPI_TMP_DIR", "GLPI_UPLOAD_DIR", "GLPI_USE_CSRF_CHECK", "GLPI_USER_AGENT_EXTRA_COMMENTS", "GLPI_VAR_DIR",
- "GLPI_MARKETPLACE_ALLOW_OVERRIDE", "GLPI_MARKETPLACE_MANUAL_DOWNLOADS",
+ "GLPI_AJAX_DASHBOARD", "GLPI_ALLOW_IFRAME_IN_RICH_TEXT", "GLPI_CALDAV_IMPORT_STATE", "GLPI_CACHE_DIR", "GLPI_CRON_DIR", "GLPI_CSRF_EXPIRES", "GLPI_CSRF_MAX_TOKENS", "GLPI_USE_IDOR_CHECK", "GLPI_IDOR_EXPIRES", "GLPI_DEMO_MODE", "GLPI_DOC_DIR", "GLPI_DUMP_DIR", "GLPI_FORCE_EMPTY_SQL_MODE", "GLPI_GRAPH_DIR", "GLPI_INSTALL_MODE", "GLPI_LOCAL_I18N_DIR", "GLPI_LOCK_DIR", "GLPI_LOG_DIR", "GLPI_NETWORK_REGISTRATION_API_URL", "GLPI_PICTURE_DIR", "GLPI_PLUGIN_DOC_DIR", "GLPI_RSS_DIR", "GLPI_SESSION_DIR", "GLPI_TELEMETRY_URI", "GLPI_TMP_DIR", "GLPI_UPLOAD_DIR", "GLPI_USE_CSRF_CHECK", "GLPI_USER_AGENT_EXTRA_COMMENTS", "GLPI_VAR_DIR",
"// GLPI optionnal constants",
"GLPI_FORCE_MAIL", "GLPI_LOG_LVL",
diff --git a/.eslintrc.json b/.eslintrc.json
index 106c1e053f..3ab6f89327 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -4,7 +4,6 @@
"/config/*",
"/files/*",
"/lib/*",
- "/marketplace/*",
"/node_modules/*",
"/plugins/*",
"/public/lib/*",
diff --git a/.github/actions/lint_php-lint.sh b/.github/actions/lint_php-lint.sh
index c1d7be7a7d..27652c5a05 100755
--- a/.github/actions/lint_php-lint.sh
+++ b/.github/actions/lint_php-lint.sh
@@ -6,7 +6,6 @@ echo $ROOT_DIR
echo "Check for syntax errors"
vendor/bin/parallel-lint \
--exclude ./files/ \
- --exclude ./marketplace/ \
--exclude ./plugins/ \
--exclude ./tools/vendor/ \
--exclude ./vendor/ \
@@ -28,5 +27,5 @@ vendor/bin/phpcs \
-p \
--extensions=php \
--standard=vendor/glpi-project/coding-standard/GlpiStandard/ \
- --ignore="/.git/,^$ROOT_DIR/(config|files|lib|marketplace|node_modules|plugins|tests/config|vendor)/" \
+ --ignore="/.git/,^$ROOT_DIR/(config|files|lib|node_modules|plugins|tests/config|vendor)/" \
.
diff --git a/.gitignore b/.gitignore
index ff281ff523..21b7d49dbe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,6 @@
/config/glpicrypt.key
/config/local_define.php
/tests/config_db*
-/marketplace/
/plugins/
/files/
/.buildpath
diff --git a/README.md b/README.md
index 612a5d8f3f..217a19b437 100644
--- a/README.md
+++ b/README.md
@@ -66,7 +66,6 @@ It is distributed under the GNU GENERAL PUBLIC LICENSE Version 2 - please consul
- ldap (users authentication)
- openssl (encrypted communication)
- sodium (performances enhancement on sensitive data encryption/decryption)
- - zip and bz2 (installation of zip and bz2 packages from marketplace)
* Supported browsers:
- Edge
diff --git a/ajax/marketplace.php b/ajax/marketplace.php
deleted file mode 100644
index 358c8f9d3e..0000000000
--- a/ajax/marketplace.php
+++ /dev/null
@@ -1,117 +0,0 @@
-.
- * ---------------------------------------------------------------------
- */
-
-global $PLUGINS_EXCLUDED;
-
-// follow download progress of a plugin with a minimal loading of files
-// So we get a ajax answer in 5ms instead 100ms
-if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "get_dl_progress") {
- if (!defined('GLPI_ROOT')) {
- define('GLPI_ROOT', dirname(__DIR__));
- }
-
- include_once GLPI_ROOT . '/inc/based_config.php';
- Session::setPath();
- Session::start();
-
- echo $_SESSION['marketplace_dl_progress'][$_REQUEST['key']] ?? 0;
- exit;
-}
-
-if ($_REQUEST["action"] == "download_plugin" || $_REQUEST["action"] == "update_plugin") {
- // Do not load plugin that will be updated, to be able to load its new informations
- // by redefining its plugin_version_ function after files replacement.
- $PLUGINS_EXCLUDED = [$_REQUEST['key']];
-}
-
-
-// get common marketplace action, load GLPI framework
-include ("../inc/includes.php");
-
-Session::checkRight("config", UPDATE);
-
-use Glpi\Marketplace\Controller as MarketplaceController;
-use Glpi\Marketplace\View as MarketplaceView;
-
-if (isset($_REQUEST['key'])) {
- $marketplace_ctrl = new MarketplaceController($_REQUEST['key']);
- if ($_REQUEST["action"] == "download_plugin"
- || $_REQUEST["action"] == "update_plugin") {
- $marketplace_ctrl->downloadPlugin();
- }
- if ($_REQUEST["action"] == "clean_plugin") {
- if ($marketplace_ctrl->cleanPlugin()) {
- echo "cleaned";
- }
- }
- if ($_REQUEST["action"] == "install_plugin") {
- $marketplace_ctrl->installPlugin();
- }
- if ($_REQUEST["action"] == "uninstall_plugin") {
- $marketplace_ctrl->uninstallPlugin();
- }
- if ($_REQUEST["action"] == "enable_plugin") {
- $marketplace_ctrl->enablePlugin();
- }
- if ($_REQUEST["action"] == "disable_plugin") {
- $marketplace_ctrl->disablePlugin();
- }
-
- echo MarketplaceView::getButtons($_REQUEST['key']);
-}
-
-if ($_REQUEST["action"] == "refresh_plugin_list") {
- switch ($_REQUEST['tab']) {
- default:
- case 'discover':
- echo MarketplaceView::discover(
- $_REQUEST['force'] ?? false,
- true,
- $_REQUEST['tag'] ?? "",
- $_REQUEST['filter'] ?? "",
- $_REQUEST['page'] ?? 1,
- $_REQUEST['sort'] ?? "sort-alpha-asc"
- );
- break;
- case 'installed':
- echo MarketplaceView::installed(true, true, $_REQUEST['filter'] ?? "");
- break;
- }
-}
-
-if ($_REQUEST["action"] == "getPagination") {
- echo MarketplaceView::getPaginationHtml(
- $_REQUEST['page'] ?? 1,
- $_REQUEST['total'] ?? 1,
- true
- );
-}
diff --git a/composer.json b/composer.json
index 56fd0c3718..1cfd34eb90 100644
--- a/composer.json
+++ b/composer.json
@@ -89,9 +89,9 @@
"testweb": "php vendor/bin/atoum -p 'php -d memory_limit=512M' --debug --force-terminal --use-dot-report --bootstrap-file tests/bootstrap.php --no-code-coverage --max-children-number 1 -d tests/web",
"testldap": "php vendor/bin/atoum -p 'php -d memory_limit=512M' --debug --force-terminal --use-dot-report --bootstrap-file tests/bootstrap.php --no-code-coverage --max-children-number 1 -d tests/LDAP",
"testimap": "php vendor/bin/atoum -p 'php -d memory_limit=512M' --debug --force-terminal --use-dot-report --bootstrap-file tests/bootstrap.php --no-code-coverage --max-children-number 1 -d tests/imap",
- "csp": "vendor/bin/phpcs --parallel=500 --cache -p --extensions=php --standard=vendor/glpi-project/coding-standard/GlpiStandard/ --ignore=\"/.git/,^$(pwd)/(config|files|lib|marketplace|node_modules|plugins|tests/config|vendor)/\" ./",
- "cs": "vendor/bin/phpcs -d memory_limit=512M --cache -p --extensions=php --standard=vendor/glpi-project/coding-standard/GlpiStandard/ --ignore=\"/.git/,^$(pwd)/(config|files|lib|marketplace|node_modules|plugins|tests/config|vendor)/\" ./",
- "lint": "vendor/bin/parallel-lint --exclude files --exclude marketplace --exclude plugins --exclude vendor --exclude tools/vendor .",
+ "csp": "vendor/bin/phpcs --parallel=500 --cache -p --extensions=php --standard=vendor/glpi-project/coding-standard/GlpiStandard/ --ignore=\"/.git/,^$(pwd)/(config|files|lib|node_modules|plugins|tests/config|vendor)/\" ./",
+ "cs": "vendor/bin/phpcs -d memory_limit=512M --cache -p --extensions=php --standard=vendor/glpi-project/coding-standard/GlpiStandard/ --ignore=\"/.git/,^$(pwd)/(config|files|lib|node_modules|plugins|tests/config|vendor)/\" ./",
+ "lint": "vendor/bin/parallel-lint --exclude files --exclude plugins --exclude vendor --exclude tools/vendor .",
"post-install-cmd": [
"@php -r \"file_put_contents('.composer.hash', sha1_file('composer.lock'));\"",
"patch -f -p1 -d vendor/laminas/laminas-mail/ < tools/laminas-mail-128.patch || echo 'Error applying patch, retrieving some mail attachement could fail'"
diff --git a/css/marketplace.scss b/css/marketplace.scss
deleted file mode 100644
index 3bd7716a4d..0000000000
--- a/css/marketplace.scss
+++ /dev/null
@@ -1,478 +0,0 @@
-
-$break_phones: 900px;
-$break_ss_screen: 1080px;
-$break_s_screen: 1400px;
-
-.marketplace {
- $left_width: 150px;
- text-align: left;
- display: flex;
- background-color: #EEE;
- padding: 10px;
- margin: 0 -10px;
-
- @media screen and (max-width: $break_phones) {
- display: block;
- }
-
- .left-panel {
- width: $left_width;
-
- @media screen and (max-width: $break_phones) {
- width: initial;
- }
-
- .plugins-tags {
- vertical-align: top;
- display: inline-block;
- width: 100%;
- font-size: 1.1em;
-
- @media screen and (max-width: $break_phones) {
- display: flex;
- flex-wrap: wrap;
- margin: 0 0 10px 10px;
- }
-
- .tag {
- background: #FFF;
- border-radius: 3px;
- border: 1px solid #dddddd;
- padding: 5px;
- margin: 0 3px 3px 0;
- text-align: center;
- cursor: pointer;
-
- &:hover {
- background: #d1d1d1;
- }
-
- &.active {
- background: #747474;
- color: #FFF;
- border-color: transparent;
- }
- }
- }
- }
-
- .right-panel {
- padding-left: 5px;
- width: 100%;
-
- .left-panel+& {
- width: calc(100% - #{$left_width});
- }
-
- .top-panel {
- display: flex;
-
- .filter-list {
- flex: 1;
- margin: 0 8px 5px 5px;
- height: 25px;
- }
-
- .controls {
- width: 170px;
-
- .select2 {
- .select2-selection.select2-selection--single {
- height: 25px;
- text-overflow: clip;
- text-overflow: unset;
- padding-left: 0;
- }
- .select2-selection.select2-selection--single:before {
- content: '';
- }
- }
-
- i {
- vertical-align: middle;
- cursor: pointer;
- }
- }
- }
-
- .plugins {
- display: flex;
- flex-wrap: wrap;
- position: relative;
-
- @media screen and (max-width: $break_phones) {
- flex-direction: column;
- }
-
- .loading-plugins {
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- -webkit-backdrop-filter: blur(2px);
- backdrop-filter: blur(2px);
- background-color: rgba(0, 0, 0, 0.5);
-
- i.fas {
- position: absolute;
- top: 50%;
- left: calc(50% - 1.5rem);
- color: white;
- font-size: 3rem;
- }
- }
-
- .plugin {
- $margin_width: 10px;
- width: calc(25% - #{$margin_width}); //minus margin
- margin: calc(#{$margin_width} / 2);
- margin-bottom: 10px;
- border-radius: 3px;
- background-color: #FFF;
- border: 1px solid #c2c2c2;
- display: flex;
- flex-direction: column;
- align-items: stretch;
- justify-content: space-between;
- box-sizing: border-box;
-
- @media screen and (max-width: $break_s_screen) {
- width: calc(33% - #{$margin_width});
- }
-
- @media screen and (max-width: $break_ss_screen) {
- width: calc(50% - #{$margin_width});
- }
-
- @media screen and (max-width: $break_phones) {
- width: calc(100% - #{$margin_width});
- }
-
- .main {
- display: flex;
- padding-bottom: 5px;
-
- .icon {
- flex-shrink: 0;
- padding: 5px;
- width: 70px;
-
- img {
- object-fit: contain;
- max-width: 50px;
- max-height: 50px;
- margin: 2px 10px;
- }
-
- .icon-text {
- display: block;
- margin-top: 2px;
- text-align: center;
- width: 50px;
- height: 50px;
- line-height: 50px;
- font-size: 1.4em;
- font-weight: bold;
- text-transform: uppercase;
- margin: 2px 10px;
- }
- }
-
- .details {
- padding: 5px 10px;
-
- .title {
- margin: 2px 0 5px;
- font-size: 1.25em;
- }
-
- .offers {
- .badge {
- margin: 1px;
- padding: 2px 5px;
- border-radius: 3px;
- background-color: #d1d1d1;
- border: 1px solid #5a5a5a;
- color: #202020;
- font-weight: bold;
-
- &.glpi-network {
- background-color: #ffd45f;
- color: #352804;
- border-color: #c69100;
- }
-
- i {
- font-size: 10px;
- margin: 1px 3px 0 0;
- vertical-align: top;
- color: rgba(0, 0, 0, .5);
- }
- }
- }
-
- .description {
- margin: 5px 0;
- }
- }
-
- .buttons {
- width: 25px;
- padding: 5px;
- margin-left: auto;
-
- button {
- background-color: #FAFAFA;
- border: 1px solid #dddddd;
- cursor: pointer;
- width: 25px;
- padding: 3px 5px;
- margin: 1px;
-
- &:hover {
- background-color: #dddddd;
- }
-
- i {
- color: #666;
- }
-
- &[data-action="disable_plugin"] {
- i {
- color: #6ebb43;
- }
- }
-
- &[data-action="download_plugin"]:hover,
- &[data-action="install_plugin"]:hover,
- &[data-action="enable_plugin"]:hover {
- background-color: rgb(226, 247, 226);
- border-color: rgb(91, 150, 91);
- i {
- color: rgb(74, 121, 74);
- }
- }
-
- &[data-action="uninstall_plugin"]:hover,
- &[data-action="disable_plugin"]:hover {
- background-color: rgb(245, 199, 199);
- border-color: rgb(190, 83, 83);
- i {
- color: rgb(105, 60, 60);
- }
- }
-
- &.download_manually,
- &.need_offers {
- background-color: rgb(253, 240, 215);
- border-color: rgb(221, 188, 125);
- i {
- color: #8f5a0a;
- }
-
- &:hover {
- background-color: rgb(253, 227, 179);
- border-color: rgb(97, 82, 54);
-
- i {
- color: rgb(97, 82, 54);
- }
- }
- }
- }
-
- progress {
- -webkit-appearance: none;
- appearance: none;
- border: none;
- width: 25px;
- height: 5px;
-
- &::-webkit-progress-bar {
- background: transparent;
- border: 1px solid #c2c2c2;
- }
- }
-
- .plugin-error {
- color: red;
- margin: 1px;
- padding: 3px 5px;
- }
- }
- }
-
- .footer {
- background-color: #FAFAFA;
- border-top: 1px solid rgb(221, 221, 221);
- display: flex;
- align-items: stretch;
- justify-content: space-between;
- padding: 5px;
- color: #666;
-
- .misc-left {
- .note {
- width: 85px;
-
- i.fas, i.far {
- color: #ffbb00;
- }
- }
-
- .links {
- a {
- i {
- color: #999;
-
- &:hover {
- color: #000;
- }
- }
- }
- }
- }
-
- .misc-right {
- width: calc(100% - 85px);
- text-align: left;
-
- i {
- margin-right: 5px;
- }
-
- .authors {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- }
- }
-
- .misc-right {
- i {
- margin-right: 5px;
- }
-
- .authors {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- .license, .authors {
- a {
- font-size: 12px;
- }
- }
- }
- }
- }
-
- .pagination {
- width: 100%;
- display: flex;
- justify-content: center;
-
- li {
- padding: 8px 16px;
- transition: background-color .1s;
- border: 1px solid #c2c2c2;
- background-color: white;
-
- &.current {
- background-color: #979797;
- border-color: #979797;
- color: white;
- font-weight: bold;
-
- & + li {
- border-left-width: 0;
- }
- }
-
- &.nav-disabled {
- color: #c2c2c2;
- }
-
- &.nb_plugin {
- border: none;
- background-color: transparent;
- }
-
- &.dots {
- padding: 8px 10px;
- }
-
- &:not(.current):not(.nb_plugin):not(.nav-disabled):hover {
- background-color: #ddd;
- cursor: pointer;
- }
-
- &:first-child {
- border-top-left-radius: 3px;
- border-bottom-left-radius: 3px;
- }
-
- &:last-child {
- border-top-right-radius: 3px;
- border-bottom-right-radius: 3px;
- }
-
- &:not(:first-child) {
- border-left-width: 0;
- }
- }
- }
-
- .network-mail {
- display: inline-block;
- margin: 20px 10px;
- }
- }
-
- &.installed {
- .plugins {
- .plugin {
- min-height: 84px;
- margin-bottom: 5px;
-
- .main {
- height: 100%;
-
- .details {
- flex: 1;
- border-right: 1px solid #DDD;
- padding: 5px 0 0 10px;
- height: 100%;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- text-overflow: ellipsis;
-
- .misc-right {
- color: #8f8f8f;
- }
- }
-
- .buttons {
- width: 26px;
- padding: 4px 5px;
- }
- }
-
- .footer {
- background: none;
- border: none;
- margin: -25px 36px 0 0;
-
- .misc-left {
- margin-left: 15px;
- }
- }
- }
- }
- }
-}
diff --git a/css/palettes/_darker.scss b/css/palettes/_darker.scss
index 53aa94d76f..3167f2cbdf 100644
--- a/css/palettes/_darker.scss
+++ b/css/palettes/_darker.scss
@@ -848,61 +848,6 @@ div.progress {
border: 1px solid #333;
}
-/** marketplace **/
-.marketplace {
- background-color: inherit;
-
- .left-panel .plugins-tags .tag {
- background: #292929;
- border-color: #000;
- }
-
- .right-panel {
- .plugins {
- .plugin {
- background-color: transparent;
- border-color: #000;
-
- .main .buttons button {
- background-color: #000;
- border-color: #000;
- }
-
- .footer .misc-left .links a i {
- color: rgb(102, 102, 102);
- }
-
- .footer {
- background-color: #151515;
- border-top-color: #000;
- }
- }
- }
-
- .pagination {
- li {
- background-color: #292929;
- border-color: #000;
- color: rgb(170, 170, 170);
-
- &.current {
- color: rgb(128, 128, 128);
- background: #000;
- border-color: #000;
- }
-
- &.nav-disabled {
- color: #666;
- }
-
- &:not(.current):not(.nb_plugin):not(.nav-disabled):hover {
- background-color: #474747;
- }
- }
- }
- }
-}
-
.ui-tabs-nav li.ui-tabs-tab:focus, .ui-tabs-nav li.ui-tabs-tab a:focus {
outline: none;
}
diff --git a/front/marketplace.download.php b/front/marketplace.download.php
deleted file mode 100644
index bc7e0c7777..0000000000
--- a/front/marketplace.download.php
+++ /dev/null
@@ -1,42 +0,0 @@
-.
- * ---------------------------------------------------------------------
- */
-
-include ("../inc/includes.php");
-
-Session::checkRight("config", UPDATE);
-
-use Glpi\Marketplace\Controller as MarketplaceController;
-
-if (isset($_REQUEST['key'])) {
- $marketplace_ctrl = new MarketplaceController($_REQUEST['key']);
- $marketplace_ctrl->proxifyPluginArchive();
-}
diff --git a/front/marketplace.php b/front/marketplace.php
deleted file mode 100644
index b443e05544..0000000000
--- a/front/marketplace.php
+++ /dev/null
@@ -1,47 +0,0 @@
-.
- * ---------------------------------------------------------------------
- */
-
-include ('../inc/includes.php');
-
-Session::checkRight("config", UPDATE);
-
-// This has to be called before search process is called, in order to add
-// "new" plugins in DB to be able to display them.
-$plugin = new Plugin();
-$plugin->checkStates(true);
-
-Html::header(__('Marketplace'), $_SERVER['PHP_SELF'], "config", "plugin", "marketplace");
-
-$market_view = new \Glpi\Marketplace\View();
-$market_view->display();
-
-Html::footer();
diff --git a/front/plugin.php b/front/plugin.php
index 2ec51f9d72..d900458761 100644
--- a/front/plugin.php
+++ b/front/plugin.php
@@ -41,8 +41,6 @@
Html::header(__('Setup'), $_SERVER['PHP_SELF'], "config", "plugin");
-\Glpi\Marketplace\View::showFeatureSwitchDialog();
-
$catalog_btn = '
'
. '
'
. __('See the catalog of plugins')
diff --git a/inc/based_config.php b/inc/based_config.php
index 8a7dcdc492..5be91ade59 100644
--- a/inc/based_config.php
+++ b/inc/based_config.php
@@ -55,7 +55,6 @@
// Constants related to system paths
'GLPI_CONFIG_DIR' => GLPI_ROOT . '/config', // Path for configuration files (db, security key, ...)
'GLPI_VAR_DIR' => GLPI_ROOT . '/files', // Path for all files
- 'GLPI_MARKETPLACE_DIR' => GLPI_ROOT . '/marketplace', // Path for marketplace plugins
'GLPI_DOC_DIR' => '{GLPI_VAR_DIR}', // Path for documents storage
'GLPI_CACHE_DIR' => '{GLPI_VAR_DIR}/_cache', // Path for cache
'GLPI_CRON_DIR' => '{GLPI_VAR_DIR}/_cron', // Path for cron storage
@@ -82,11 +81,6 @@
// Constants related to GLPI Project external services
'GLPI_TELEMETRY_URI' => 'https://telemetry.glpi-project.org', // Telemetry project URL
'GLPI_INSTALL_MODE' => is_dir(GLPI_ROOT . '/.git') ? 'GIT' : 'TARBALL', // Install mode for telemetry
- 'GLPI_MARKETPLACE_PLUGINS_API_URI' => '{GLPI_NETWORK_SERVICES}/api/glpi-plugins/',
- // TODO set false before final release of 9.5.0 and remove this comment
- 'GLPI_MARKETPLACE_PRERELEASES' => false, // display pre-releases of plugins in marketplace
- 'GLPI_MARKETPLACE_ALLOW_OVERRIDE' => true, // allow marketplace to override a plugin found outside GLPI_MARKETPLACE_DIR
- 'GLPI_MARKETPLACE_MANUAL_DOWNLOADS' => true, // propose manual download link of plugins which cannot be installed/updated by marketplace
'GLPI_USER_AGENT_EXTRA_COMMENTS' => '', // Extra comment to add to GLPI User-Agent
// Other constants
@@ -143,7 +137,6 @@ function ($matches) {
// Order in this array is important (priority to first found).
if (!defined('PLUGINS_DIRECTORIES')) {
define('PLUGINS_DIRECTORIES', [
- GLPI_MARKETPLACE_DIR,
GLPI_ROOT . '/plugins',
]);
} else if (!is_array(PLUGINS_DIRECTORIES)) {
diff --git a/inc/define.php b/inc/define.php
index b229144cab..5c85c2f10d 100644
--- a/inc/define.php
+++ b/inc/define.php
@@ -337,7 +337,7 @@
'ObjectLock', 'PlanningRecall', 'Problem',
'Project', 'ProjectTask', 'Reservation',
'SoftwareLicense', 'Ticket', 'User',
- 'SavedSearch_Alert', 'Certificate', 'Glpi\\Marketplace\\Controller',
+ 'SavedSearch_Alert', 'Certificate',
'Domain'];
$CFG_GLPI["contract_types"] = array_merge(['Computer', 'Monitor', 'NetworkEquipment',
@@ -515,7 +515,6 @@
'notificationtemplate' => ['tinymce']
],
'plugin'=> [
- 'marketplace' => ['marketplace']
]
],
'admin' => ['clipboard'],
diff --git a/inc/html.class.php b/inc/html.class.php
index 7453f73d68..39000f32ef 100644
--- a/inc/html.class.php
+++ b/inc/html.class.php
@@ -1294,11 +1294,6 @@ static function includeHeader($title = '', $sector = 'none', $item = 'none', $op
Html::requireJs('dashboard');
}
- if (in_array('marketplace', $jslibs)) {
- echo Html::scss('css/marketplace');
- Html::requireJs('marketplace');
- }
-
if (in_array('rack', $jslibs)) {
Html::requireJs('rack');
}
@@ -6549,9 +6544,6 @@ static public function requireJs($name) {
case 'dashboard':
$_SESSION['glpi_js_toload'][$name][] = 'js/dashboard.js';
break;
- case 'marketplace':
- $_SESSION['glpi_js_toload'][$name][] = 'js/marketplace.js';
- break;
case 'gridstack':
$_SESSION['glpi_js_toload'][$name][] = 'public/lib/gridstack.js';
break;
diff --git a/inc/includes.php b/inc/includes.php
index ea98092141..da4782b5b2 100644
--- a/inc/includes.php
+++ b/inc/includes.php
@@ -148,7 +148,7 @@
&& !isAPI()
&& isset($_POST) && is_array($_POST) && count($_POST)) {
// No ajax pages
- if (!preg_match(':'.$CFG_GLPI['root_doc'].'(/(plugins|marketplace)/[^/]*|)/ajax/:', $_SERVER['REQUEST_URI'])) {
+ if (!preg_match(':'.$CFG_GLPI['root_doc'].'(/plugins/[^/]*|)/ajax/:', $_SERVER['REQUEST_URI'])) {
Session::checkCSRF($_POST);
}
}
diff --git a/inc/marketplace/api/plugins.class.php b/inc/marketplace/api/plugins.class.php
deleted file mode 100644
index 3eb64b6426..0000000000
--- a/inc/marketplace/api/plugins.class.php
+++ /dev/null
@@ -1,526 +0,0 @@
-.
- * ---------------------------------------------------------------------
- */
-
-namespace Glpi\Marketplace\Api;
-
-if (!defined('GLPI_ROOT')) {
- die("Sorry. You can't access directly to this file");
-}
-
-use GuzzleHttp\Exception\RequestException;
-use GuzzleHttp\Psr7;
-use GuzzleHttp\Psr7\Response;
-use \GuzzleHttp\Client as Guzzle_Client;
-use \GLPINetwork;
-use \Toolbox;
-use \Session;
-
-class Plugins {
- protected $httpClient = null;
- protected $last_error = "";
-
- public const COL_PAGE = 200;
- protected const TIMEOUT = 5;
-
- static $plugins = [];
-
- function __construct(bool $connect = false) {
- global $CFG_GLPI;
-
- $options = [
- 'base_uri' => GLPI_MARKETPLACE_PLUGINS_API_URI,
- 'connect_timeout' => self::TIMEOUT,
- ];
-
- // add proxy string if configured in glpi
- if (!empty($CFG_GLPI["proxy_name"])) {
- $proxy_creds = !empty($CFG_GLPI["proxy_user"])
- ? $CFG_GLPI["proxy_user"].":".Toolbox::sodiumDecrypt($CFG_GLPI["proxy_passwd"])."@"
- : "";
- $proxy_string = "http://{$proxy_creds}".$CFG_GLPI['proxy_name'].":".$CFG_GLPI['proxy_port'];
- $options['proxy'] = $proxy_string;
- }
-
- // init guzzle client with base options
- $this->httpClient = new Guzzle_Client($options);
- }
-
-
- /**
- * Send a http request to services api
- * using the base url set in constructor and the current endpoint
- *
- * @param string $endpoint which resource whe need to query
- * @param array $options array of options for guzzle lib
- * @param string $method GET/POST, etc
- *
- * @return Psr\Http\Message\ResponseInterface|false
- */
- private function request(
- string $endpoint = '',
- array $options = [],
- string $method = 'GET'
- ) {
- if (!GLPINetwork::isRegistered()) {
- // Simulate empty response if registration key is not valid
- return new Response(200, [], '[]');
- }
-
- $options['headers'] = array_merge_recursive(
- [
- 'Accept' => 'application/json',
- 'User-Agent' => GLPINetwork::getGlpiUserAgent(),
- 'X-Registration-Key' => GLPINetwork::getRegistrationKey(),
- 'X-Glpi-Network-Uid' => GLPINetwork::getGlpiNetworkUid(),
- ],
- $options['headers'] ?? []
- );
-
- try {
- $response = $this->httpClient->request($method, $endpoint, $options);
-
- } catch (RequestException $e) {
- $this->last_error = [
- 'title' => "Plugins API error",
- 'exception' => $e->getMessage(),
- 'request' => Psr7\str($e->getRequest()),
- ];
- if ($e->hasResponse()) {
- $this->last_error['response'] = Psr7\str($e->getResponse());
- }
-
- if ($_SESSION['glpi_use_mode'] == Session::DEBUG_MODE) {
- Toolbox::logDebug($this->last_error);
- }
- return false;
- }
-
- return $response;
- }
-
-
- /**
- * Send an http request on an endpoint accepting paginated queries
- *
- * @param string $endpoint which resource whe need to query
- * @param array $options array of options for guzzle lib
- * @param string $method GET/POST, etc
- *
- * @return array full collection
- */
- private function getPaginatedCollection(
- string $endpoint = '',
- array $options = [],
- string $method = 'GET'
- ): array {
- $collection = [];
- $i = 0;
- do {
- $request_options = array_merge_recursive([
- 'headers' => [
- 'X-Range' => ($i * self::COL_PAGE)."-".(($i + 1) * self::COL_PAGE - 1),
- ],
- ], $options);
- $response = $this->request($endpoint, $request_options, $method);
-
- if ($current = ($response !== false ? json_decode($response->getBody(), true) : false)) {
- $collection = array_merge($collection, $current);
- }
-
- $i++;
- } while ($current !== false && count($current));
-
- return $collection;
- }
-
-
- /**
- * Return the full list of avaibles plugins on services API
- *
- * @param bool $force_refresh if false, we will return results stored in local cache
- * @param string $tag_filter filter the plugin list by given tag
- * @param string $string_filter filter the plugin list by given string
- * @param string $sort sort-alpha-asc|sort-alpha-desc|sort-dl|sort-update|sort-added|sort-note
- *
- * @return array collection of plugins
- */
- function getAllPlugins(
- bool $force_refresh = false,
- string $tag_filter = "",
- string $string_filter = "",
- string $sort = 'sort-alpha-asc'
- ) {
- global $GLPI_CACHE;
-
- $plugins_colct = [];
- if (!$force_refresh && $GLPI_CACHE->has('marketplace_all_plugins')) {
- $plugins_colct = $GLPI_CACHE->get('marketplace_all_plugins');
- }
-
- if (!count($plugins_colct)) {
- $plugins = $this->getPaginatedCollection('plugins');
-
- // replace keys indexes by system names
- $plugins_keys = array_column($plugins, 'key');
- $plugins_colct = array_combine($plugins_keys, $plugins);
-
- foreach ($plugins_colct as &$plugin) {
- usort(
- $plugin['versions'],
- function ($a, $b) {
- return version_compare($a['num'], $b['num']);
- }
- );
- }
-
- $GLPI_CACHE->set('marketplace_all_plugins', $plugins_colct, HOUR_TIMESTAMP);
- }
-
- // Filter versions.
- // Done after caching process to be able to handle change of "GLPI_MARKETPLACE_PRERELEASES"
- // without having to purge the cache manually.
- foreach ($plugins_colct as &$plugin) {
- if (!GLPI_MARKETPLACE_PRERELEASES) {
- $plugin['versions'] = array_filter($plugin['versions'], function($version) {
- return !isset($version['stability']) || $version['stability'] === "stable";
- });
- }
-
- if (count($plugin['versions']) === 0) {
- continue;
- }
-
- $higher_version = end($plugin['versions']);
- if (is_array($higher_version)) {
- $plugin['installation_url'] = $higher_version['download_url'];
- $plugin['version'] = $higher_version['num'];
- }
- }
- self::$plugins = $plugins_colct;
-
- // Remove plugins with no versions for current config (i.e. only unstable versions that are not proposed).
- $plugins_colct = array_filter(
- $plugins_colct,
- function($plugin) {
- return count($plugin['versions']) > 0;
- }
- );
-
- if (strlen($tag_filter) > 0) {
- $tagged_plugins = array_column($this->getPluginsForTag($tag_filter), 'key');
- $plugins_colct = array_intersect_key($plugins_colct, array_flip($tagged_plugins));
- }
-
- if (strlen($string_filter) > 0) {
- $plugins_colct = array_filter($plugins_colct, function($plugin) use ($string_filter) {
- return strpos(strtolower(json_encode($plugin)), strtolower($string_filter)) !== false;
- });
- }
-
- // manage sorting of collection
- uasort($plugins_colct, function($plugin1, $plugin2) use ($sort) {
- switch ($sort) {
- case "sort-alpha-asc":
- return strnatcasecmp($plugin1['name'], $plugin2['name']);
- case "sort-alpha-desc":
- return strnatcasecmp($plugin2['name'], $plugin1['name']);
- case "sort-dl":
- return strnatcmp($plugin2['download_count'], $plugin1['download_count']);
- case "sort-update":
- return strnatcmp($plugin2['date_updated'], $plugin1['date_updated']);
- case "sort-added":
- return strnatcmp($plugin2['date_added'], $plugin1['date_added']);
- case "sort-note":
- return strnatcmp($plugin2['note'], $plugin1['note']);
- }
- });
-
- return $plugins_colct;
- }
-
-
- /**
- * Return plugins list for the given page
- *
- * @param bool $force_refresh if false, we will return results stored in local cache
- * @param string $tag_filter filter the plugin list by given tag
- * @param string $string_filter filter the plugin list by given string
- * @param int $page which page to query
- * @param int $nb_per_page how manyu per page we want
- * @param string $sort sort-alpha-asc|sort-alpha-desc|sort-dl|sort-update|sort-added|sort-note
- *
- * @return array full collection
- */
- function getPaginatedPlugins(
- bool $force_refresh = false,
- string $tag_filter = "",
- string $string_filter = "",
- int $page = 1,
- int $nb_per_page = 15,
- string $sort = 'sort-alpha-asc'
- ) {
- $plugins = $this->getAllPlugins($force_refresh, $tag_filter, $string_filter, $sort);
-
- $plugins_page = array_splice($plugins, max($page - 1, 0) * $nb_per_page, $nb_per_page);
- return $plugins_page;
- }
-
-
- /**
- * return the number of available plugins in distant API
- *
- * @param string $tag_filter filter the plugin list by given tag
- *
- * @return int number of plugins
- */
- function getNbPlugins(string $tag_filter = "") {
- $plugins = $this->getAllPlugins(false, $tag_filter);
-
- return count($plugins);
- }
-
-
- /**
- * get top 10 plugins sorted by trending (most downloaded in the last month) criterion
- *
- * @return array collection of plugins
- */
- function getTrendingPlugins() {
- return $this->getTopPlugins("trending");
- }
-
-
- /**
- * get top 10 plugins sorted by popular (most downloaded all time) criterion
- *
- * @return array collection of plugins
- */
- function getPopularPlugins() {
- return $this->getTopPlugins("popular");
- }
-
- /**
- * get top 10 plugins sorted by their submition date (DESC sort) criterion
- *
- * @return array collection of plugins
- */
- function getNewPlugins() {
- return $this->getTopPlugins("new");
- }
-
- /**
- * get top 10 plugins sorted by their update date (DESC sort) criterion
- *
- * @return array collection of plugins
- */
- function getUpdatedPlugins() {
- return $this->getTopPlugins("updated");
- }
-
- /**
- * get top 10 plugins sorted by given criterion (see other getTopXXX methods)
- *
- * @param string $endpoint criterion to filter plugsin
- *
- * @return array collection of plugins
- */
- function getTopPlugins(string $endpoint = "") {
- $response = $this->request("plugin/{$endpoint}");
-
- if ($response === false) {
- return [];
- }
-
- $top = json_decode($response->getBody(), true);
- $key_list = array_column($top, 'key', 'key');
- $plugins = $this->getAllPlugins();
-
- $top_plugins = array_filter($plugins, function($plugin) use($key_list) {
- return in_array($plugin['key'], $key_list);
- });
-
- return $top_plugins;
- }
-
-
- /**
- * Get a single plugin array
- *
- * @param string $key plugin system name
- * @param bool $force_refresh if false, we will return results stored in local cache
- *
- * @return array plugin data
- */
- public function getPlugin(string $key = "", bool $force_refresh = false): array {
- $plugins_list = [];
- if ($force_refresh || !count(self::$plugins)) {
- $plugins_list = $this->getAllPlugins($force_refresh);
- } else {
- $plugins_list = self::$plugins;
- }
-
- return $plugins_list[$key] ?? [];
- }
-
-
- /**
- * Inform plugins API that a plugin (by its key) has been downloaded
- * and the download counter must be incremented
- *
- * @param string $key plugin system key
- *
- * @return void we don't wait for a response, this a fire and forget request
- */
- public function incrementPluginDownload(string $key = "") {
- $this->request(
- "plugin/{$key}/download",
- [
- 'allow_redirects' => false, // Prevent follow redirects to download page sent by Plugins API
- ]
- );
- }
-
-
- /**
- * Get top list of tags for current session language
- *
- * @return array top tags
- */
- public function getTopTags(): array {
- global $CFG_GLPI;
-
- $response = $this->request('tags/top', [
- 'headers' => [
- 'X-Lang' => $CFG_GLPI['languages'][$_SESSION['glpilanguage']][2]
- ]
- ]);
-
- if ($response === false) {
- return [];
- }
-
- $toptags = json_decode($response->getBody(), true);
-
- return $toptags;
- }
-
-
- /**
- * get a plugins collection for the givent tag
- *
- * @param string $tag to filter plugins
- * @param bool $force_refresh if false, we will return results stored in local cache
- *
- * @return array filtered plugin collection
- */
- public function getPluginsForTag(string $tag = "", bool $force_refresh = false): array {
- global $GLPI_CACHE;
-
- $plugins_colct = [];
- if (!$force_refresh && $GLPI_CACHE->has("marketplace_tag_$tag")) {
- $plugins_colct = $GLPI_CACHE->get("marketplace_tag_$tag");
- }
-
- if (!count($plugins_colct)) {
- $plugins_colct = $this->getPaginatedCollection("tags/{$tag}/plugin");
- $GLPI_CACHE->set("marketplace_tag_$tag", $plugins_colct, HOUR_TIMESTAMP);
- }
-
- return $plugins_colct;
- }
-
-
- /**
- * Download plugin archive and follow progress with a session var `marketplace_dl_progress`
- *
- * @param string $url where is the plugin
- * @param string $dest where we store it it
- * @param string $plugin_key plugin system name
- *
- * @return bool
- */
- public function downloadArchive(string $url, string $dest, string $plugin_key, bool $track_progress = true): bool {
- if ($track_progress) {
- if (!isset($_SESSION['marketplace_dl_progress'])) {
- $_SESSION['marketplace_dl_progress'] = [];
- }
- $_SESSION['marketplace_dl_progress'][$plugin_key] = 0;
- }
-
- // close session to permits polling of progress by frontend
- session_write_close();
-
- $options = [
- 'headers' => [
- 'Accept' => '*/*',
- ],
- 'sink' => $dest,
- ];
- if ($track_progress) {
- // track download progress
- $options['progress'] = function($downloadTotal, $downloadedBytes) use ($plugin_key) {
- // Prevent "net::ERR_RESPONSE_HEADERS_TOO_BIG" error
- // Each time Session::start() is called, PHP add a 'Set-Cookie' header,
- // so if a plugin takes more than a few seconds to be downloaded, PHP will set too many
- // 'Set-Cookie' headers and response will not be accepted by browser.
- // We can remove the 'Set-Cookie' here as it will be put back on next instruction (Session::start()).
- header_remove('Set-Cookie');
-
- // restart session to store percentage of download for this plugin
- Session::start();
-
- // calculate percent based on the size and store it in session
- $percent = 0;
- if ($downloadTotal > 0) {
- $percent = round($downloadedBytes * 100 / $downloadTotal);
- }
- $_SESSION['marketplace_dl_progress'][$plugin_key] = $percent;
-
- // reclose session to avoid blocking ajax requests
- session_write_close();
- };
- }
-
- $response = $this->request($url, $options);
-
- // restart session to permits write of vars
- // (later, we also may have some addMessageAfterRedirect to provider errors to user)
- Session::start();
-
- if ($track_progress) {
- // force finish of download (to avoid keeping js loop in case of errors)
- $_SESSION['marketplace_dl_progress'][$plugin_key] = 100;
- }
-
- return $response !== false && $response->getStatusCode() === 200;
- }
-}
diff --git a/inc/marketplace/controller.class.php b/inc/marketplace/controller.class.php
deleted file mode 100644
index cf110ed306..0000000000
--- a/inc/marketplace/controller.class.php
+++ /dev/null
@@ -1,497 +0,0 @@
-.
- * ---------------------------------------------------------------------
- */
-
-namespace Glpi\Marketplace;
-
-if (!defined('GLPI_ROOT')) {
- die("Sorry. You can't access directly to this file");
-}
-
-
-use Glpi\Marketplace\Api\Plugins as PluginsApi;
-use \wapmorgan\UnifiedArchive\UnifiedArchive;
-use \wapmorgan\UnifiedArchive\Exceptions\ArchiveExtractionException;
-use \Plugin;
-use \Toolbox;
-use \Session;
-use \GLPINetwork;
-use \CommonGLPI;
-use \Config;
-use \NotificationEvent;
-use \CronTask;
-
-class Controller extends CommonGLPI {
- protected $plugin_key = "";
-
- static $rightname = 'config';
- static $api = null;
-
- const MP_REPLACE_ASK = 1;
- const MP_REPLACE_YES = 2;
- const MP_REPLACE_NEVER = 3;
-
- function __construct(string $plugin_key = "") {
- $this->plugin_key = $plugin_key;
- }
-
-
- static function getTypeName($nb = 0) {
- return __('Marketplace');
- }
-
- /**
- * singleton return the current api instance
- *
- * @return PluginsApi
- */
- static function getAPI(): PluginsApi {
- return self::$api ?? (self::$api = new PluginsApi());
- }
-
-
- /**
- * Download and uncompress plugin archive
- *
- * @return int plugin status, @see properties of \Plugin class
- */
- function downloadPlugin():int {
- if (!self::hasWriteAccess()) {
- return Plugin::UNKNOWN;
- }
-
- $api = self::getAPI();
- $plugin = $api->getPlugin($this->plugin_key, true);
-
- $url = $plugin['installation_url'] ?? "";
- $filename = basename(parse_url($url, PHP_URL_PATH));
- $dest = GLPI_TMP_DIR . '/' . $filename;
-
- if (!$api->downloadArchive($url, $dest, $this->plugin_key)) {
- Session::addMessageAfterRedirect(
- __('Unable to download plugin archive.'),
- false,
- ERROR
- );
- return Plugin::UNKNOWN;
- }
-
- // extract the archive
- if (!UnifiedArchive::canOpenArchive($dest)) {
- $type = UnifiedArchive::detectArchiveType($dest);
- Session::addMessageAfterRedirect(
- sprintf(__('Plugin archive format is not supported by your system : %s.'), $type),
- false,
- ERROR
- );
- return Plugin::UNKNOWN;
- }
- $archive = UnifiedArchive::open($dest);
- $error = $archive === null;
- if (!$error) {
- // clean dir in case of update
- Toolbox::deleteDir(GLPI_MARKETPLACE_DIR."/{$this->plugin_key}");
-
- try {
- // copy files
- $archive->extractFiles(GLPI_MARKETPLACE_DIR) !== false;
- } catch (ArchiveExtractionException $e) {
- $error = true;
- }
- }
-
- if ($error) {
- Session::addMessageAfterRedirect(
- __('Unable to extract plugin archive.'),
- false,
- ERROR
- );
- return Plugin::UNKNOWN;
- }
-
- $plugin_inst = new Plugin();
-
- if ($plugin_inst->getFromDBbyDir($this->plugin_key)
- && !in_array($plugin_inst->fields['state'], [Plugin::ANEW, Plugin::NOTINSTALLED, Plugin::NOTUPDATED])) {
- // Plugin was already existing, make it "not updated" before checking its state
- // to prevent message like 'Plugin "xxx" version changed. It has been deactivated as its update process has to be launched.'.
- $plugin_inst->update([
- 'id' => $plugin_inst->fields['id'],
- 'state' => Plugin::NOTUPDATED
- ]);
- }
-
- $plugin_inst->checkPluginState($this->plugin_key);
- $plugin_inst->getFromDBbyDir($this->plugin_key);
-
- // inform api the plugin has been downloaded
- $api->incrementPluginDownload($this->plugin_key);
-
- // try to install (or update) directly the plugin
- return $this->installPlugin();
- }
-
-
- /**
- * Get plugin archive from its download URL and serve it to the browser.
- *
- * @return void
- */
- function proxifyPluginArchive(): void {
- // close session to prevent blocking other requests
- session_write_close();
-
- $api = self::getAPI();
- $plugin = $api->getPlugin($this->plugin_key, true);
-
- if (!array_key_exists('installation_url', $plugin) || empty($plugin['installation_url'])) {
- return;
- }
-
- $url = $plugin['installation_url'];
- $filename = basename(parse_url($url, PHP_URL_PATH));
- $dest = GLPI_TMP_DIR . '/' . mt_rand() . '.' . $filename;
-
- if (!$api->downloadArchive($url, $dest, $this->plugin_key, false)) {
- http_response_code(500);
- echo(__('Unable to download plugin archive.'));
- return;
- }
-
- Toolbox::sendFile($dest, $filename);
- }
-
- /**
- * Check if plugin can be overwritten.
- *
- * @return bool
- */
- public function canBeOverwritten(): bool {
- $found_in_marketplace_dir = file_exists(GLPI_MARKETPLACE_DIR . '/' . $this->plugin_key . '/setup.php');
-
- // Compute marketplace dir priority
- $marketplace_priority = null;
- foreach (PLUGINS_DIRECTORIES as $position => $base_dir) {
- if (realpath($base_dir) !== false && realpath($base_dir) === realpath(GLPI_MARKETPLACE_DIR)) {
- $marketplace_priority = -$position;
- break;
- }
- }
-
- $found_outside_marketplace = false;
- $found_dir_priority = null;
- foreach (PLUGINS_DIRECTORIES as $position => $base_dir) {
- if (file_exists($base_dir . '/' . $this->plugin_key . '/setup.php')) {
- $found_outside_marketplace = true;
- $found_dir_priority = -$position;
- break; // Do not search in other directories with lower priorities
- }
- }
-
- if ($found_outside_marketplace) {
- if ($found_dir_priority > $marketplace_priority) {
- // Plugin has been found outside marketplace and marketplace priority is lower than its parent directory
- // -> disallow plugin update from marketplace as it cannot be loaded from there.
- return false;
- } else if ($found_in_marketplace_dir) {
- // Plugin has been found on marketplace and marketplace priority is higher than other location
- // -> allow plugin update from marketplace as it is already loaded from there.
- return is_writable(GLPI_MARKETPLACE_DIR . '/' . $this->plugin_key);
- } else {
- // Plugin has been found outside marketplace and does not exist in marketplace
- // -> allow plugin update unless GLPI_MARKETPLACE_ALLOW_OVERRIDE is false.
- return GLPI_MARKETPLACE_ALLOW_OVERRIDE && self::hasWriteAccess();
- }
- }
-
- return self::hasWriteAccess();
- }
-
-
- /**
- * Check if a given plugin has on update online
- *
- * @param Plugin $plugin_inst
- *
- * @return string|false new version number
- */
- function checkUpdate(Plugin $plugin_inst = null) {
- $api = self::getAPI();
- $api_plugin = $api->getPlugin($this->plugin_key);
- $local_plugin = $plugin_inst->fields;
-
- $api_version = $api_plugin['version'] ?? "";
- $local_version = $local_plugin['version'] ?? "";
-
- if (strlen($api_version) && $api_version !== $local_version) {
- return $api_version;
- }
-
- return false;
- }
-
-
- /**
- * Check for plugins updates
- * Parse all installed plugin and check against API if a news version is available
- *
- * @return array of [plugin_key => new_version_num]
- */
- static function getAllUpdates() {
- $plugin_inst = new Plugin;
- $plugin_inst->init(true);
- $installed = $plugin_inst->getList();
-
- $updates = [];
-
- foreach ($installed as $plugin) {
- $plugin_key = $plugin['directory'];
- $plugin_inst->getFromDBbyDir($plugin_key);
-
- $mk_controller = new self($plugin_key);
- if (false !== ($api_version = $mk_controller->checkUpdate($plugin_inst))) {
- $updates[$plugin_key] = $api_version;
- }
- }
-
- return $updates;
- }
-
-
- static function cronInfo($name) {
- return ['description' => __('Check all plugin updates')];
- }
-
-
- /**
- * Crontask : Check for plugins updates
- *
- * @param CronTask|null $task to log, if NULL display (default NULL)
- *
- * @return integer 0 : nothing to do 1 : done with success
- */
- static function cronCheckAllUpdates(CronTask $task = null):int {
- global $CFG_GLPI;
-
- $cron_status = 0;
-
- if (!GLPINetwork::isRegistered()) {
- return $cron_status;
- }
-
- $updates = self::getAllUpdates();
- if (count($updates)) {
- $cron_status = 1;
- $task->addVolume(count($updates));
- foreach ($updates as $plugin_key => $version) {
- $task->log(sprintf(__("New version for plugin %s: %s"), $plugin_key, $version));
- }
-
- if (!$CFG_GLPI["use_notifications"]) {
- return $cron_status;
- }
-
- NotificationEvent::raiseEvent('checkpluginsupdate', new self(), [
- 'plugins' => $updates
- ]);
- }
-
- return $cron_status;
- }
-
-
- /**
- * Do the current plugin requires some Glpi Network offers
- *
- * @return array [offer ref => offer title]
- */
- function getRequiredOffers(): array {
- $api = self::getAPI();
- $api_plugin = $api->getPlugin($this->plugin_key);
- $offers = array_column(GLPINetwork::getOffers(), 'title', 'offer_reference');
-
- $trans_offers = array_intersect_key($offers, array_flip($api_plugin['required_offers'] ?? []));
-
- return $trans_offers;
- }
-
-
- /**
- * Check a plugin can be download
- *
- * @return bool
- */
- function canBeDownloaded() {
- $api = self::getAPI();
- $api_plugin = $api->getPlugin($this->plugin_key);
-
- return strlen($api_plugin['installation_url'] ?? "") > 0;
- }
-
- /**
- * Check if plugin is eligible inside an higher offer.
- *
- * @return bool
- */
- public function requiresHigherOffer(): bool {
- $api_plugin = self::getAPI()->getPlugin($this->plugin_key);
-
- if (!isset($api_plugin['required_offers'])) {
- return false;
- }
-
- $registration_informations = GLPINetwork::getRegistrationInformations();
- if ($registration_informations['subscription'] !== null
- && $registration_informations['subscription']['is_running']) {
- if (in_array($registration_informations['subscription']['offer_reference'], $api_plugin['required_offers'])) {
- return false;
- }
- }
-
- return true;
- }
-
-
- /**
- * Install current plugin
- *
- * @param bool $disable_messages drop any messages after plugin installation
- *
- * @return bool
- */
- function installPlugin(bool $disable_messages = false):bool {
- $state = $this->setPluginState("install");
-
- if ($disable_messages) {
- $_SESSION['MESSAGE_AFTER_REDIRECT'] = [];
- }
-
- return $state == Plugin::NOTACTIVATED;
- }
-
-
- /**
- * Ununstall current plugin
- *
- * @return bool
- */
- function uninstallPlugin():bool {
- return $this->setPluginState("uninstall") == Plugin::NOTINSTALLED;
- }
-
-
- /**
- * Enable current plugin
- *
- * @return bool
- */
- function enablePlugin():bool {
- return $this->setPluginState("activate") == Plugin::ACTIVATED;
- }
-
-
- /**
- * Disable current plugin
- *
- * @return bool
- */
- function disablePlugin():bool {
- return $this->setPluginState("unactivate") == Plugin::NOTACTIVATED;
- }
-
-
- /**
- * Clean (remove database data) current plugin
- *
- * @return bool
- */
- function cleanPlugin():bool {
- $plugin = new Plugin;
- if ($plugin->getFromDBbyDir($this->plugin_key)) {
- $plugin->clean($plugin->fields['id']);
- }
-
- if (!$plugin->getFromDBbyDir($this->plugin_key)) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Check if marketplace controller has write access to install/update plugins source code.
- *
- * @return bool
- */
- public static function hasWriteAccess(): bool {
- return is_dir(GLPI_MARKETPLACE_DIR) && is_writable(GLPI_MARKETPLACE_DIR);
- }
-
-
- /**
- * Call an action method (install/enable/...) for the current plugin
- * method called internally by installPlugin, uninstallPlugin, enablePlugin, disablePlugin
- *
- * @param string $method
- *
- * @return int plugin status, @see properties of \Plugin class
- */
- private function setPluginState(string $method = ""): int {
- ob_start();
- $plugin = new Plugin;
- $plugin->checkPluginState($this->plugin_key);
- if ($plugin->getFromDBbyDir($this->plugin_key)) {
- call_user_func([$plugin, $method], $plugin->fields['id']);
- }
-
- $plugin->checkPluginState($this->plugin_key);
- $plugin->getFromDBbyDir($this->plugin_key);
-
- // reload plugins
- $plugin->init(true);
-
- ob_end_clean();
-
- return $plugin->fields['state'] ?? -1;
- }
-
-
- /**
- * Return current config of for the replacement of former plugins list
- *
- * @return int config status (self::MP_REPLACE_ASK, self::MP_REPLACE_YES, self::MP_REPLACE_NEVER)
- */
- static function getPluginPageConfig() {
- $config = Config::getConfigurationValues('core', ['marketplace_replace_plugins']);
-
- return (int) ($config['marketplace_replace_plugins'] ?? self::MP_REPLACE_ASK);
- }
-}
diff --git a/inc/marketplace/notificationtargetcontroller.class.php b/inc/marketplace/notificationtargetcontroller.class.php
deleted file mode 100644
index 03277caa58..0000000000
--- a/inc/marketplace/notificationtargetcontroller.class.php
+++ /dev/null
@@ -1,133 +0,0 @@
-.
- * ---------------------------------------------------------------------
- */
-
-namespace Glpi\Marketplace;
-
-if (!defined('GLPI_ROOT')) {
- die("Sorry. You can't access directly to this file");
-}
-
-use \Notification;
-use \NotificationTarget;
-use \Plugin;
-use \Session;
-
-// Class NotificationTarget
-class NotificationTargetController extends NotificationTarget {
-
- /**
- * Overwrite the function in NotificationTarget because there's only one target to be notified
- *
- * @see NotificationTarget::addNotificationTargets()
- **/
- function addNotificationTargets($entity) {
-
- $this->addProfilesToTargets();
- $this->addGroupsToTargets($entity);
- $this->addTarget(Notification::GLOBAL_ADMINISTRATOR, __('Administrator'));
- }
-
-
- function getEvents() {
- return ['checkpluginsupdate' => __('Check all plugin updates')];
- }
-
-
- function addDataForTemplate($event, $options = []) {
- $updated_plugins = $options['plugins'];
- $plugin = new Plugin;
- foreach ($updated_plugins as $plugin_key => $version) {
- $plugin_info = $plugin->getInformationsFromDirectory($plugin_key);
-
- $this->data['plugins'][] = [
- '##plugin.name##' => $plugin_info['name'],
- '##plugin.key##' => $plugin_key,
- '##plugin.version##' => $version,
- '##plugin.old_version##' => $plugin_info['version'],
- ];
- }
-
- $this->getTags();
- foreach ($this->tag_descriptions[NotificationTarget::TAG_LANGUAGE] as $tag => $values) {
- if (!isset($this->data[$tag])) {
- $this->data[$tag] = $values['label'];
- }
- }
- }
-
-
- function getTags() {
- //Tags with just lang
- $tags = [
- 'plugins_updates_available' => __('Some updates are available for your installed plugins!')
- ];
-
- foreach ($tags as $tag => $label) {
- $this->addTagToList([
- 'tag' => $tag,
- 'label' => $label,
- 'value' => false,
- 'lang' => true
- ]);
- }
-
- //Foreach global tags
- $tags = [
- 'plugins' => _n('Plugin', 'Plugins', Session::getPluralNumber()),
- ];
-
- foreach ($tags as $tag => $label) {
- $this->addTagToList([
- 'tag' => $tag,
- 'label' => $label,
- 'value' => false,
- 'foreach' => true,
- ]);
- }
-
- // sub tags
- $tags = [
- 'plugin.name' => __('Plugin name'),
- 'plugin.key' => __('Plugin directory'),
- 'plugin.version' => __('Plugin new version number'),
- 'plugin.old_version' => __('Plugin old version number')
- ];
-
- foreach ($tags as $tag => $label) {
- $this->addTagToList([
- 'tag' => $tag,
- 'label' => $label,
- 'value' => true,
- ]);
- }
- }
-}
diff --git a/inc/marketplace/view.class.php b/inc/marketplace/view.class.php
deleted file mode 100644
index 81d7c92341..0000000000
--- a/inc/marketplace/view.class.php
+++ /dev/null
@@ -1,993 +0,0 @@
-.
- * ---------------------------------------------------------------------
- */
-
-namespace Glpi\Marketplace;
-
-if (!defined('GLPI_ROOT')) {
- die("Sorry. You can't access directly to this file");
-}
-
-use Glpi\Marketplace\Api\Plugins as PluginsApi;
-use Glpi\Marketplace\Controller as Controller;
-use \Html;
-use \Plugin;
-use \Config;
-use \CommonGLPI;
-use \GLPINetwork;
-use \Toolbox;
-
-class View extends CommonGLPI {
- static $rightname = 'config';
- static $api = null;
-
- public $get_item_to_display_tab = true;
-
-
- public const COL_PAGE = 12;
-
- /**
- * singleton return the current api instance
- *
- * @return PluginsApi
- */
- static function getAPI(): PluginsApi {
- return self::$api ?? (self::$api = new PluginsApi());
- }
-
-
- static function getTypeName($nb = 0) {
- return __('Marketplace');
- }
-
-
- static function canCreate() {
- return self::canUpdate();
- }
-
-
- static function getIcon() {
- return "fas fa-store";
- }
-
-
- static function getSearchURL($full = true) {
- global $CFG_GLPI;
-
- $dir = ($full ? $CFG_GLPI['root_doc'] : '');
- return "$dir/front/marketplace.php";
- }
-
-
- function defineTabs($options = []) {
- $tabs = [
- 'no_all_tab' => true
- ];
- $this->addStandardTab(__CLASS__, $tabs, $options);
-
- return $tabs;
- }
-
-
- function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) {
- if ($item->getType() == __CLASS__) {
- return [
- self::createTabEntry(__("Installed")),
- self::createTabEntry(__("Discover")),
- ];
- }
- return '';
- }
-
-
- static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) {
- if ($item->getType() == __CLASS__) {
- switch ($tabnum) {
- case 0:
- self::installed();
- break;
- case 1:
- default:
- self::discover();
- break;
- }
- }
-
- return true;
- }
-
-
- /**
- * Check current reigstration status and display warning messages
- *
- * @return bool
- */
- static function checkRegister() {
- global $CFG_GLPI;
-
- $messages = [];
- $registered = false;
-
- if (!GLPINetwork::isServicesAvailable()) {
- array_push(
- $messages,
- sprintf(__('%1$s services website seems not available from your network or offline'), 'GLPI Network'),
- " ".
- __("Maybe you could setup a proxy").
- " ".
- __("or please check later")
- );
- } else {
- $registered = GLPINetwork::isRegistered();
- if (!$registered) {
- $config_url = $CFG_GLPI['root_doc']."/front/config.form.php?forcetab=".
- urlencode('GLPINetwork$1');
-
- array_push(
- $messages,
- sprintf(__('Your %1$s registration is not valid.'), 'GLPI Network'),
- __('A registration, at least a free one, is required to use marketplace!'),
- "
".sprintf(__('Register on %1$s'), 'GLPI Network')." ".
- __('and'). " ".
- "
".__("fill your registration key in setup.")." "
- );
- }
- }
-
- if (count($messages)) {
- echo "
";
- echo "
";
- echo "
" . implode(' ', $messages) . " ";
- echo "
";
- echo "
";
- }
-
- return $registered;
- }
-
-
- /**
- * Display installed tab (only currently installed plugins)
- *
- * @param bool $force_refresh do not rely on cache to get plugins list
- * @param bool $only_lis display only the li tags in return html (used by ajax queries)
- * @param string $tag_filter filter the plugin list by given tag
- * @param string $string_filter filter the plugin by given string
- *
- * @return void display things
- */
- static function installed(
- bool $force_refresh = false,
- bool $only_lis = false,
- string $string_filter = ""
- ) {
-
- $plugin_inst = new Plugin;
- $plugin_inst->init(true); // reload plugins
- $installed = $plugin_inst->getList();
-
- $apiplugins = [];
- if (self::checkRegister()) {
- $api = self::getAPI();
- $apiplugins = $api->getAllPlugins($force_refresh);
- }
-
- $plugins = [];
- foreach ($installed as $plugin) {
- $key = $plugin['directory'];
- $apidata = $apiplugins[$key] ?? [];
-
- if (strlen($string_filter)
- && strpos(strtolower(json_encode($plugin)), strtolower($string_filter)) === false) {
- continue;
- }
-
- $clean_plugin = [
- 'key' => $key,
- 'name' => $plugin['name'],
- 'logo_url' => $apidata['logo_url'] ?? "",
- 'description' => $apidata['descriptions'][0]['short_description'] ?? "",
- 'authors' => $apidata['authors'] ?? [['id' => 'all', 'name' => $plugin['author'] ?? ""]],
- 'license' => $apidata['license'] ?? $plugin['license'] ?? "",
- 'note' => $apidata['note'] ?? -1,
- 'homepage_url' => $apidata['homepage_url'] ?? "",
- 'issues_url' => $apidata['issues_url'] ?? "",
- 'readme_url' => $apidata['readme_url'] ?? "",
- 'version' => $plugin['version'] ?? "",
- ];
-
- $plugins[] = $clean_plugin;
- }
-
- self::displayList($plugins, "installed", $only_lis);
- }
-
- /**
- * Display discover tab (all availble plugins)
- *
- * @param bool $force_refresh do not rely on cache to get plugins list
- * @param bool $only_lis display only the li tags in return html (used by ajax queries)
- * @param string $tag_filter filter the plugin list by given tag
- * @param string $string_filter filter the plugin by given string
- * @param int $page What's sub page of plugin we want to display
- * @param string $sort sort-alpha-asc|sort-alpha-desc|sort-dl|sort-update|sort-added|sort-note
- *
- * @return void display things
- */
- static function discover(
- bool $force = false,
- bool $only_lis = false,
- string $tag_filter = "",
- string $string_filter = "",
- int $page = 1,
- string $sort = 'sort-alpha-asc'
- ) {
- if (!self::checkRegister()) {
- return;
- }
-
- $api = self::getAPI();
- $plugins = $api->getPaginatedPlugins(
- $force,
- $tag_filter,
- $string_filter,
- $page,
- self::COL_PAGE,
- $sort
- );
-
- if (strlen($string_filter) > 0) {
- $nb_plugins = count($plugins);
- } else {
- $nb_plugins = $api->getNbPlugins($tag_filter);
- }
-
- header("X-GLPI-Marketplace-Total: $nb_plugins");
- self::displayList($plugins, "discover", $only_lis, $nb_plugins, $sort);
- }
-
-
- /**
- * Return HTML part for tags list
- *
- * @return string tags list
- */
- static function getTagsHtml() {
- $api = self::getAPI();
- $tags = $api->getTopTags();
-
- $tags_li = "
".__("All")." ";
- foreach ($tags as $tag) {
- $tags_li.= "
".ucfirst($tag['tag'])." ";
- }
-
- return "
";
- }
-
-
- /**
- * Display a list of plugins
- *
- * @param array $plugins list of plugins returned by
- * - \Plugin::getList
- * - \Glpi\Marketplace\Api\Plugins::getPaginatedPlugins
- * @param string $tab current display tab (discover or installed)
- * @param bool $only_lis display only the li tags in return html (used by ajax queries)
- * @param int $nb_plugins total of plugins ($plugins contains only the current page)
- * @param string $sort sort-alpha-asc|sort-alpha-desc|sort-dl|sort-update|sort-added|sort-note
- *
- * @return false|void displays things
- */
- static function displayList(
- array $plugins = [],
- string $tab = "",
- bool $only_lis = false,
- int $nb_plugins = 0,
- string $sort = 'sort-alpha-asc'
- ) {
- if (!self::canView()) {
- return false;
- }
-
- $plugins_li = "";
- foreach ($plugins as $plugin) {
- $plugin['description'] = self::getLocalizedDescription($plugin);
- $plugins_li.= self::getPluginCard($plugin, $tab);
- }
-
- if (!$only_lis) {
- // check writable state
- if (!Controller::hasWriteAccess()) {
- echo "
- ".
- sprintf(__("We can't write on the markeplace directory (%s)."), GLPI_MARKETPLACE_DIR)." ".
- __("If you want to ease the plugins download, please check permissions and ownership of this directory.")." ".
- __("Otherwise, you will need to download and unzip the plugins archives manually.")." ".
- "
";
- }
-
- $tags_list = $tab != "installed"
- ? "
".self::getTagsHtml()."
"
- : "";
- $pagination = $tab != "installed"
- ? self::getPaginationHtml(1, $nb_plugins)
- : "";
- $sort_controls = "";
- if ($tab === "discover") {
- $sort_controls = "
-
-
- ".__("Alpha ASC")."
-
-
- ".__("Alpha DESC")."
-
-
- ".__("Most popular")."
-
-
- ".__("Last updated")."
-
-
- ".__("Most recent")."
-
-
- ".__("Best notes")."
-
- ";
- }
-
- $yourplugin = __("Your plugin here ? Contact us.");
- $networkmail = GLPI_NETWORK_MAIL;
- $refresh_lbl = __("Refresh plugin list");
- $search_label = __("Filter plugin list");
-
- $marketplace = <<
- {$tags_list}
-
-
-
-HTML;
- echo $marketplace;
- } else {
- echo $plugins_li;
- }
-
- $js = << "+option.text+"");
- };
-
- $('.sort-control').select2({
- templateResult: displaySortIcon,
- templateSelection: displaySortIcon,
- width: 135,
- });
- });
-JS;
- echo Html::scriptBlock($js);
- }
-
- /**
- * Return HTML part for plugin card
- *
- * @param array $plugin informations (title, description, etc) of the plugins
- * @param string $tab current displayed tab (installed or discover)
- *
- * @return string the plugin card
- */
- static function getPluginCard(array $plugin = [], string $tab = "discover"):string {
- $plugin_key = $plugin['key'];
- $plugin_inst = new Plugin;
- $plugin_inst->getFromDBbyDir($plugin_key);
- $plugin_state = Plugin::getStateKey($plugin_inst->fields['state'] ?? -1);
- $buttons = self::getButtons($plugin_key);
-
- $name = Html::clean($plugin['name']);
- $description = Html::clean($plugin['description']);
-
- $authors = Html::clean(implode(', ', array_column($plugin['authors'] ?? [], 'name', 'id')), false);
- $authors_title = Html::clean($authors);
- $authors = strlen($authors)
- ? " {$authors}"
- : "";
-
- $licence = Html::clean($plugin['license'] ?? '');
- $licence = strlen($licence)
- ? " {$licence}"
- : "";
-
- $version = Html::clean($plugin['version'] ?? '');
- $version = strlen($version)
- ? " {$version}"
- : "";
-
- $stars = ($plugin['note'] ?? -1) > 0
- ? self::getStarsHtml($plugin['note'])
- : "";
-
- $home_url = Html::entities_deep($plugin['homepage_url'] ?? "");
- $home_url = strlen($home_url)
- ? "
-
- "
- : "";
-
- $issues_url = Html::entities_deep($plugin['issues_url'] ?? "");
- $issues_url = strlen($issues_url)
- ? "
-
- "
- : "";
-
- $readme_url = Html::entities_deep($plugin['readme_url'] ?? "");
- $readme_url = strlen($readme_url)
- ? "
-
- "
- : "";
- $icon = self::getPluginIcon($plugin);
- $network = self::getNetworkInformations($plugin);
-
- if ($tab === "discover") {
- $card = <<
-
-
{$icon}
-
- {$name}
- $network
- {$description}
-
-
- {$buttons}
-
-
-
-
-HTML;
- } else {
- $card = <<
-
-
{$icon}
-
- {$name}
-
- {$licence}
- {$authors}
- {$version}
-
-
-
- {$buttons}
-
-
-
-
-HTML;
- }
-
- return $card;
- }
-
- /**
- * Return HTML part for plugin stars
- *
- * @param float|int $value current stars note on 5
- *
- * @return string plugins stars html
- */
- static function getStarsHtml(float $value = 0):string {
- $value = min(floor($value * 2) / 2, 5);
-
- $stars = "";
- for ($i = 1; $i < 6; $i++) {
- if ($value >= $i) {
- $stars.= " ";
- } else if ($value + 0.5 == $i) {
- $stars.= " ";
- } else {
- $stars.= " ";
- }
- }
-
- return $stars;
- }
-
-
- /**
- * Return HTML part for plugin buttons
- *
- * @param string $plugin_key system name for the plugin
- *
- * @return string the buttons html
- */
- static function getButtons(string $plugin_key = ""): string {
- global $CFG_GLPI, $PLUGIN_HOOKS;
-
- $rand = mt_rand();
- $plugin_inst = new Plugin;
- $exists = $plugin_inst->getFromDBbyDir($plugin_key);
- $is_installed = $plugin_inst->isInstalled($plugin_key);
- $is_actived = $plugin_inst->isActivated($plugin_key);
- $mk_controller = new Controller($plugin_key);
- $web_update_version = $mk_controller->checkUpdate($plugin_inst);
- $has_web_update = $web_update_version !== false;
- $has_loc_update = $plugin_inst->isUpdatable($plugin_key);
- $can_be_overwritten = $mk_controller->canBeOverwritten();
- $can_be_downloaded = $mk_controller->canBeDownloaded();
- $required_offers = $mk_controller->getRequiredOffers();
- $can_be_updated = $has_web_update && $can_be_overwritten;
- $can_be_cleaned = $exists && !$plugin_inst->isLoadable($plugin_key);
- $config_page = $PLUGIN_HOOKS['config_page'][$plugin_key] ?? "";
-
- $error = "";
- if ($exists) {
- ob_start();
- $do_activate = $plugin_inst->checkVersions($plugin_key);
- if (!$do_activate) {
- $error.= "" . ob_get_contents() . " ";
- }
- ob_end_clean();
-
- $function = 'plugin_' . $plugin_key . '_check_prerequisites';
- if ($do_activate && function_exists($function)) {
- ob_start();
- if (!$function()) {
- $error .= '' . ob_get_contents() . ' ';
- }
- ob_end_clean();
- }
- }
-
- $buttons = "";
-
- if (strlen($error)) {
- $buttons .=" ";
- Html::showToolTip($error, [
- 'applyto' => "plugin-error-$rand",
- ]);
- }
-
- if ($can_be_cleaned) {
- $buttons .="
-
- ";
- } else if ((!$exists && !$mk_controller->hasWriteAccess())
- || ($has_web_update && !$can_be_overwritten && GLPI_MARKETPLACE_MANUAL_DOWNLOADS)) {
- $plugin_data = $mk_controller->getAPI()->getPlugin($plugin_key);
- if (array_key_exists('installation_url', $plugin_data) && $can_be_downloaded) {
- $warning = "";
- if ($has_web_update) {
- $warning = __s("The plugin has an available update but its directory is not writable.")." ";
- }
-
- $warning.= sprintf(
- __s("Download archive manually, you must uncompress it in plugins directory (%s)"),
- GLPI_ROOT . '/plugins'
- );
-
- // Use "marketplace.download.php" proxy if archive is downloadable from GLPI marketplace plugins API
- // as this API will refuse to serve the archive if registration key is not set in headers.
- $download_url = Toolbox::startsWith($plugin_data['installation_url'], GLPI_MARKETPLACE_PLUGINS_API_URI)
- ? $CFG_GLPI['root_doc'] . '/front/marketplace.download.php?key=' . $plugin_key
- : $plugin_data['installation_url'];
-
- $buttons .="
-
- ";
- }
- } else if ($can_be_downloaded) {
- if (!$exists) {
- $buttons .="
-
- ";
- } else if ($can_be_updated) {
- $update_title = sprintf(
- __s("A new version (%s) is available, update ?", 'marketplace'),
- $web_update_version
- );
- $buttons .="
-
- ";
- }
- }
-
- if ($mk_controller->requiresHigherOffer()) {
- $warning = sprintf(
- __s("You need a superior GLPI-Network offer to access to this plugin (%s)"),
- implode(', ', $required_offers)
- );
-
- $buttons .="
-
-
-
- ";
- }
-
- if ($exists && !$can_be_cleaned && !$is_installed && !strlen($error)) {
- $title = __s("Install");
- $icon = "fas fa-folder-plus";
- if ($has_loc_update) {
- $title = __s("Update");
- $icon = "far fa-caret-square-up";
- }
- $buttons .="
-
- ";
- }
-
- if ($is_installed) {
- if (!strlen($error)) {
- if ($is_actived) {
- $buttons .="
-
- ";
- } else {
- $buttons .="
-
- ";
- }
- }
-
- $buttons .="
-
- ";
-
- if (!strlen($error) && $is_actived && $config_page) {
- $plugin_dir = Plugin::getWebDir($plugin_key, true);
- $config_url = "$plugin_dir/$config_page";
- $buttons .="
-
-
-
- ";
- }
- }
-
- return $buttons;
- }
-
- /**
- * Return HTML part for plugin logo/icon
- *
- * @param array $plugin data of the plugin.
- * If it contains a key logo_url, the current will be inserted in a img tag
- * else, it will use initials from plugin friendly name to construct
- * a short and colored logo
- *
- * @return string the jtml for plugin logo
- */
- static function getPluginIcon(array $plugin = []) {
- $icon = "";
-
- $logo_url = Html::entities_deep($plugin['logo_url'] ?? "");
- if (strlen($logo_url)) {
- $icon = " ";
- } else {
- $words = explode(" ", Html::clean($plugin['name']));
- $initials = "";
- for ($i = 0; $i < 2; $i++) {
- if (isset($words[$i])) {
- $initials.= mb_substr($words[$i], 0, 1);
- }
- }
- $bg_color = Toolbox::getColorForString($initials);
- $fg_color = Toolbox::getFgColor($bg_color);
- $icon = "$initials ";
- }
-
- return $icon;
- }
-
-
- /**
- * Return HTML part for Glpi Network informations for a given plugin
- * @param array $plugin data of the plugin.
- * if check agains plugin key if we need some subscription to use it
- * @return string the subscription information html
- */
- static function getNetworkInformations(array $plugin = []): string {
- $mk_controller = new Controller($plugin['key']);
- $require_offers = $mk_controller->getRequiredOffers();
-
- $html = "";
- if (count($require_offers)) {
- $fst_offer = array_splice($require_offers, 0, 1);
- $offerkey = key($fst_offer);
- $offerlabel = current($fst_offer);
-
- $html = "";
- }
-
- return $html;
- }
-
-
- /**
- * Retrieve localized description for a given plugin and matching the session lang
- *
- * @param array $plugin data of the plugin.
- * in the `description` key, we must found an array of localized descirption
- * indexed by lang key, return the good one
- * @param string $version short_description or long_description
- *
- * @return string the localized description
- */
- static function getLocalizedDescription(array $plugin = [], string $version = 'short_description'): string {
- global $CFG_GLPI;
-
- $userlang = $CFG_GLPI['languages'][$_SESSION['glpilanguage']][3] ?? "en";
-
- if (!isset($plugin['descriptions'])) {
- return "";
- }
-
- $description = "";
- $fallback = "";
- foreach ($plugin['descriptions'] as $current) {
- if ($current['lang'] == $userlang) {
- $description = $current[$version];
- break;
- }
-
- if ($current['lang'] == "en") {
- $fallback = $current[$version];
- }
- }
-
- if (strlen($description) === 0) {
- $description = $fallback;
- }
-
- return $description;
- }
-
-
- /**
- * Return HTML part for plugins pagination
- *
- * @param int $current_page
- * @param int $total
- * @param bool $only_li display only the li tags in return html (used by ajax queries)
- *
- * @return string the pagination html
- */
- static function getPaginationHtml(int $current_page = 1, int $total = 1, bool $only_li = false): string {
- if ($total <= self::COL_PAGE) {
- return "";
- }
-
- $nb_pages = ceil($total / self::COL_PAGE);
-
- $prev = max($current_page - 1, 1);
- $next = min($current_page + 1, $nb_pages);
-
- $p_cls = $current_page === 1
- ? "class='nav-disabled'"
- : "";
- $n_cls = $current_page == $nb_pages
- ? "class='nav-disabled'"
- : "";
-
- $html = "";
- if (!$only_li) {
- $html.= "";
- }
-
- return $html;
- }
-
-
- /**
- * Display a dialog inviting the user to switch from former plugin list to marketplace new view.
- *
- * @return void display things
- */
- static function showFeatureSwitchDialog() {
- global $CFG_GLPI;
-
- if (isset($_POST['marketplace_replace'])) {
- $mp_value = isset($_POST['marketplace_replace_plugins_yes'])
- ? Controller::MP_REPLACE_YES
- : (isset($_POST['marketplace_replace_plugins_never'])
- ? Controller::MP_REPLACE_NEVER
- : Controller::MP_REPLACE_ASK);
- Config::setConfigurationValues('core', [
- 'marketplace_replace_plugins' => $mp_value
- ]);
-
- // is user agree, redirect him to marketplace
- if ($mp_value === Controller::MP_REPLACE_YES) {
- Html::redirect($CFG_GLPI["root_doc"]."/front/marketplace.php");
- }
-
- // avoid annoying user for the current session
- $_SESSION['skip_marketplace_invitation'] = true;
- }
-
- // show modal for asking user preference
- if (Controller::getPluginPageConfig() == Controller::MP_REPLACE_ASK
- && !isset($_SESSION['skip_marketplace_invitation'])
- && GLPI_INSTALL_MODE !== 'CLOUD') {
- echo "