瀏覽代碼

Added option to customize the colors and icon

Edgard 5 年之前
父節點
當前提交
f31838e507
共有 5 個文件被更改,包括 274 次插入34 次删除
  1. 1 0
      .vscode/settings.json
  2. 25 0
      front/picture.send.php
  3. 15 9
      hook.php
  4. 232 24
      inc/provider.class.php
  5. 1 1
      setup.php

+ 1 - 0
.vscode/settings.json

@@ -3,6 +3,7 @@
   "phpsab.standard": ".phpcs.xml",
   "phpsab.composerJsonPath": "composer.json",
   "intelephense.environment.includePaths": ["../../"],
+  "intelephense.format.braces": "k&r",
   "[php]": {
     "editor.defaultFormatter": "bmewburn.vscode-intelephense-client"
   }

+ 25 - 0
front/picture.send.php

@@ -0,0 +1,25 @@
+<?php
+include('../../../inc/includes.php');
+
+$provider = new PluginSinglesignonProvider();
+$path = false;
+
+if (isset($_GET['id'])) { // docid for document
+   if (!$provider->getFromDB($_GET['id'])) {
+      Html::displayErrorAndDie(__('Unknown file'), true);
+   }
+
+   $path = $provider->fields['picture'];
+} else if (isset($_GET['path'])) {
+   $path = $_GET['path'];
+} else {
+   Html::displayErrorAndDie(__('Invalid filename'), true);
+}
+
+$path = GLPI_PLUGIN_DOC_DIR . "/singlesignon/" . $path;
+
+if (!file_exists($path)) {
+   Html::displayErrorAndDie(__('File not found'), true); // Not found
+}
+
+Toolbox::sendFile($path, $logo);

+ 15 - 9
hook.php

@@ -21,13 +21,13 @@ function plugin_singlesignon_display_login() {
       }
 
       $url = PluginSinglesignonProvider::getCallbackUrl($row['id'], $query);
-      $html[] = PluginSinglesignonProvider::renderButton($url, $row['name']);
+      $html[] = PluginSinglesignonProvider::renderButton($url, $row);
    }
 
    if (!empty($html)) {
       echo '<div class="singlesignon-box">';
       echo implode(" \n", $html);
-      echo PluginSinglesignonProvider::renderButton('#', __('GLPI'), 'vsubmit old-login');
+      echo PluginSinglesignonProvider::renderButton('#', ['name' => __('GLPI')], 'vsubmit old-login');
       echo '</div>';
       ?>
       <style>
@@ -51,6 +51,11 @@ function plugin_singlesignon_display_login() {
             text-align: center;
             box-sizing: border-box;
          }
+         #boxlogin .singlesignon-box .vsubmit img {
+            max-height: 20px;
+            max-width: 100px;
+            vertical-align: sub;
+         }
       </style>
       <script type="text/javascript">
          $(document).ready(function() {
@@ -154,13 +159,6 @@ function plugin_singlesignon_install() {
                ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
 
       $DB->query($query) or die("error creating glpi_plugin_singlesignon_providers " . $DB->error());
-
-      //      $query = "INSERT INTO `glpi_plugin_singlesignon_providers`
-      //                       (`id`, `name`, `serial`, `is_deleted`)
-      //                VALUES (1, 'example 1', 'serial 1', 0),
-      //                       (2, 'example 2', 'serial 2', 0),
-      //                       (3, 'example 3', 'serial 3', 0)";
-      //      $DB->query($query) or die("error populate glpi_plugin_example " . $DB->error());
    }
 
    // add display preferences
@@ -176,6 +174,14 @@ function plugin_singlesignon_install() {
       $DB->query("INSERT INTO `glpi_displaypreferences` VALUES (NULL,'PluginSinglesignonProvider','10','6','0');");
    }
 
+   if (version_compare($currentVersion, PLUGIN_SINGLESIGNON_VERSION, '<=')) {
+      $query = "ALTER TABLE `glpi_plugin_singlesignon_providers`
+                ADD `picture` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+                ADD `bgcolor` varchar(7) DEFAULT NULL,
+                ADD `color` varchar(7) DEFAULT NULL";
+      $DB->query($query) or die("error adding picture column " . $DB->error());
+   }
+
    Config::setConfigurationValues('singlesignon', [
       'version' => PLUGIN_SINGLESIGNON_VERSION,
    ]);

+ 232 - 24
inc/provider.class.php

@@ -137,7 +137,82 @@ class PluginSinglesignonProvider extends CommonDBTM {
       echo "<td colspan='3'><input type='text' style='width:96%' name='url_resource_owner_details' value='" . $this->fields["url_resource_owner_details"] . "'></td>";
       echo "</tr>\n";
 
+      echo "<tr class='tab_bg_1'>";
+      echo "<th colspan='4'>" . __('Personalization') . "</th>";
+      echo "</tr>\n";
+
+      echo "<tr class='tab_bg_1'>";
+      echo "<td>" . __('Background color') . "</td>";
+      echo "<td>";
+      Html::showColorField(
+         'bgcolor',
+         [
+            'value'  => $this->fields['bgcolor'],
+         ]
+      );
+      echo "&nbsp;";
+      echo Html::getCheckbox([
+         'title' => __('Clear'),
+         'name'  => '_blank_bgcolor',
+         'checked' => empty($this->fields['bgcolor']),
+      ]);
+      echo "&nbsp;" . __('Clear');
+      echo "</td>";
+      echo "<td>" . __('Color') . "</td>";
+      echo "<td>";
+      Html::showColorField(
+         'color',
+         [
+            'value'  => $this->fields['color'],
+         ]
+      );
+      echo "&nbsp;";
+      echo Html::getCheckbox([
+         'title' => __('Clear'),
+         'name'  => '_blank_color',
+         'checked' => empty($this->fields['color']),
+      ]);
+      echo "&nbsp;" . __('Clear');
+      echo "</td>";
+      echo "</tr>\n";
+
+      echo "<tr class='tab_bg_1'>";
+      echo "<td>" . __('Picture') . "</td>";
+      echo "<td colspan='3'>";
+      if (!empty($this->fields['picture'])) {
+         echo Html::image(static::getPictureUrl($this->fields['picture']), [
+            'style' => 'max-width: 100px; max-height: 50px;',
+            'class' => 'picture_square'
+         ]);
+         echo "&nbsp;";
+         echo Html::getCheckbox([
+            'title' => __('Clear'),
+            'name'  => '_blank_picture'
+         ]);
+         echo "&nbsp;" . __('Clear');
+      } else {
+         echo Html::file([
+            'name'       => 'picture',
+            'onlyimages' => true,
+         ]);
+      }
+      echo "</td>";
+      echo "</tr>\n";
+
+      echo '<script type="text/javascript">
+      $("[name=bgcolor]").on("change", function (e) {
+         $("[name=_blank_bgcolor]").prop("checked", false).attr("checked", false);
+      });
+      $("[name=color]").on("change", function (e) {
+         $("[name=_blank_color]").prop("checked", false).attr("checked", false);
+      });
+      </script>';
+
       if ($ID) {
+         echo "<tr class='tab_bg_1'>";
+         echo "<th colspan='4'>" . __('Test') . "</th>";
+         echo "</tr>\n";
+
          $url = self::getCallbackUrl($ID);
          $fullUrl = $this->getBaseURL() . $url;
          echo "<tr class='tab_bg_1'>";
@@ -177,6 +252,10 @@ class PluginSinglesignonProvider extends CommonDBTM {
       return $this->prepareInput($input);
    }
 
+   function cleanDBonPurge() {
+      static::deletePicture($this->fields['picture']);
+   }
+
    /**
     * Prepares input (for update and add)
     *
@@ -234,14 +313,50 @@ class PluginSinglesignonProvider extends CommonDBTM {
       if (count($error_detected)) {
          foreach ($error_detected as $error) {
             Session::addMessageAfterRedirect(
-                  $error,
-                  true,
-                  ERROR
+               $error,
+               true,
+               ERROR
             );
          }
          return false;
       }
 
+      if (isset($input["_blank_bgcolor"])
+         && $input["_blank_bgcolor"]
+      ) {
+         $input['bgcolor'] = '';
+      }
+
+      if (isset($input["_blank_color"])
+         && $input["_blank_color"]
+      ) {
+         $input['color'] = '';
+      }
+
+      if (isset($input["_blank_picture"])
+         && $input["_blank_picture"]
+      ) {
+         $input['picture'] = '';
+
+         if (array_key_exists('picture', $this->fields)) {
+            static::deletePicture($this->fields['picture']);
+         }
+      }
+
+      if (isset($input["_picture"])) {
+         $picture = array_shift($input["_picture"]);
+
+         if ($dest = static::savePicture(GLPI_TMP_DIR . '/' . $picture)) {
+            $input['picture'] = $dest;
+         } else {
+            Session::addMessageAfterRedirect(__('Unable to save picture file.'), true, ERROR);
+         }
+
+         if (array_key_exists('picture', $this->fields)) {
+            static::deletePicture($this->fields['picture']);
+         }
+      }
+
       return $input;
    }
 
@@ -508,7 +623,7 @@ class PluginSinglesignonProvider extends CommonDBTM {
             Html::submit(_x('button', 'Post'), ['name' => 'massiveaction']) .
             " " . __('Write in item history', 'example');
             return true;
-         case 'do_nothing' :
+         case 'do_nothing':
             echo "&nbsp;" . Html::submit(_x('button', 'Post'), ['name' => 'massiveaction']) .
             " " . __('but do nothing :)', 'example');
             return true;
@@ -525,7 +640,7 @@ class PluginSinglesignonProvider extends CommonDBTM {
       global $DB;
 
       switch ($ma->getAction()) {
-         case 'DoIt' :
+         case 'DoIt':
             if ($item->getType() == 'Computer') {
                Session::addMessageAfterRedirect(__("Right it is the type I want...", 'example'));
                Session::addMessageAfterRedirect(__('Write in item history', 'example'));
@@ -546,8 +661,8 @@ class PluginSinglesignonProvider extends CommonDBTM {
             }
             return;
 
-         case 'do_nothing' :
-            If ($item->getType() == 'PluginExampleExample') {
+         case 'do_nothing':
+            if ($item->getType() == 'PluginExampleExample') {
                Session::addMessageAfterRedirect(__("Right it is the type I want...", 'example'));
                Session::addMessageAfterRedirect(__("But... I say I will do nothing for:", 'example'));
                foreach ($ids as $id) {
@@ -562,11 +677,15 @@ class PluginSinglesignonProvider extends CommonDBTM {
             } else {
                $ma->itemDone($item->getType(), $ids, MassiveAction::ACTION_KO);
             }
-            Return;
+            return;
       }
       parent::processMassiveActionsForOneItemtype($ma, $item, $ids);
    }
 
+   static function getIcon() {
+      return "fas fa-user-lock";
+   }
+
    public static function getDefault($type, $key, $default = null) {
       if (static::$default === null) {
          $content = file_get_contents(dirname(__FILE__) . '/../providers.json');
@@ -803,13 +922,13 @@ class PluginSinglesignonProvider extends CommonDBTM {
       $url = $this->getAccessTokenUrl();
 
       $content = Toolbox::callCurl($url, [
-               CURLOPT_HTTPHEADER => [
-                  "Accept: application/json",
-               ],
-               CURLOPT_POST => true,
-               CURLOPT_POSTFIELDS => http_build_query($params),
-               CURLOPT_SSL_VERIFYHOST => false,
-               CURLOPT_SSL_VERIFYPEER => false,
+         CURLOPT_HTTPHEADER => [
+            "Accept: application/json",
+         ],
+         CURLOPT_POST => true,
+         CURLOPT_POSTFIELDS => http_build_query($params),
+         CURLOPT_SSL_VERIFYHOST => false,
+         CURLOPT_SSL_VERIFYPEER => false,
       ]);
 
       try {
@@ -849,9 +968,9 @@ class PluginSinglesignonProvider extends CommonDBTM {
       $headers = Plugin::doHookFunction("sso:resource_owner_header", $headers);
 
       $content = Toolbox::callCurl($url, [
-               CURLOPT_HTTPHEADER => $headers,
-               CURLOPT_SSL_VERIFYHOST => false,
-               CURLOPT_SSL_VERIFYPEER => false,
+         CURLOPT_HTTPHEADER => $headers,
+         CURLOPT_SSL_VERIFYHOST => false,
+         CURLOPT_SSL_VERIFYPEER => false,
       ]);
 
       try {
@@ -864,9 +983,9 @@ class PluginSinglesignonProvider extends CommonDBTM {
       if ($this->getClientType() === "linkedin") {
          $email_url = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))";
          $content = Toolbox::callCurl($email_url, [
-                  CURLOPT_HTTPHEADER => $headers,
-                  CURLOPT_SSL_VERIFYHOST => false,
-                  CURLOPT_SSL_VERIFYPEER => false,
+            CURLOPT_HTTPHEADER => $headers,
+            CURLOPT_SSL_VERIFYHOST => false,
+            CURLOPT_SSL_VERIFYPEER => false,
          ]);
 
          try {
@@ -1014,9 +1133,98 @@ class PluginSinglesignonProvider extends CommonDBTM {
       return $data;
    }
 
-   public static function renderButton($url, $name, $class = 'oauth-login') {
-      return '<span><a href="' . $url . '" class="singlesignon vsubmit ' . $class . '">' .
-      sprintf(__sso('Login with %s'), $name) . '</a></span>';
+   static public function startsWith($haystack, $needle) {
+      $length = strlen($needle);
+      return (substr($haystack, 0, $length) === $needle);
+   }
+
+   static function getPictureUrl($path) {
+      global $CFG_GLPI;
+
+      $path = Html::cleanInputText($path); // prevent xss
+
+      if (empty($path)) {
+         return null;
+      }
+
+      return $CFG_GLPI['root_doc'] . '/plugins/singlesignon/front/picture.send.php?path=' . $path;
+   }
+
+   static public function savePicture($src, $uniq_prefix = null) {
+
+      if (function_exists('Document::isImage') && !Document::isImage($src)) {
+         return false;
+      }
+
+      $filename     = uniqid($uniq_prefix);
+      $ext          = pathinfo($src, PATHINFO_EXTENSION);
+      $subdirectory = substr($filename, -2); // subdirectory based on last 2 hex digit
+
+      $basePath = GLPI_PLUGIN_DOC_DIR . "/singlesignon";
+      $i = 0;
+      do {
+         // Iterate on possible suffix while dest exists.
+         // This case will almost never exists as dest is based on an unique id.
+         $dest = $basePath
+         . '/' . $subdirectory
+         . '/' . $filename . ($i > 0 ? '_' . $i : '') . '.' . $ext;
+         $i++;
+      } while (file_exists($dest));
+
+      if (!is_dir($basePath . '/' . $subdirectory) && !mkdir($basePath . '/' . $subdirectory)) {
+         return false;
+      }
+
+      if (!rename($src, $dest)) {
+         return false;
+      }
+
+      return substr($dest, strlen($basePath . '/')); // Return dest relative to GLPI_PICTURE_DIR
+   }
+
+   public static function deletePicture($path) {
+      $basePath = GLPI_PLUGIN_DOC_DIR . "/singlesignon";
+      $fullpath = $basePath . '/' . $path;
+
+      if (!file_exists($fullpath)) {
+         return false;
+      }
+
+      $fullpath = realpath($fullpath);
+      if (!static::startsWith($fullpath, realpath($basePath))) {
+         return false;
+      }
+
+      return @unlink($fullpath);
    }
 
+   public static function renderButton($url, $data, $class = 'oauth-login') {
+      $btn = '<span><a href="' . $url . '" class="singlesignon vsubmit ' . $class . '"';
+
+      $style = '';
+      if ((isset($data['bgcolor']) && $data['bgcolor'])) {
+         $style .= 'background-color: ' . $data['bgcolor'] . ';';
+      }
+      if ((isset($data['color']) && $data['color'])) {
+         $style .= 'color: ' . $data['color'] . ';';
+      }
+      if ($style) {
+         $btn .= ' style="' . $style . '"';
+      }
+      $btn .= '>';
+
+      if (isset($data['picture']) && $data['picture']) {
+         $btn .= Html::image(
+            static::getPictureUrl($data['picture']),
+            [
+               'style' => 'max-height: 20px;',
+            ]
+         );
+         $btn .= ' ';
+      }
+
+      $btn .= sprintf(__sso('Login with %s'), $data['name']);
+      $btn .= '</a></span>';
+      return $btn;
+   }
 }

+ 1 - 1
setup.php

@@ -1,6 +1,6 @@
 <?php
 
-define('PLUGIN_SINGLESIGNON_VERSION', '1.0.0');
+define('PLUGIN_SINGLESIGNON_VERSION', '1.1.0');
 
 $folder = basename(dirname(__FILE__));