1625 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			1625 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * ---------------------------------------------------------------------
 | |
|  * GLPI - Gestionnaire Libre de Parc Informatique
 | |
|  * Copyright (C) 2015-2020 Teclib' and contributors.
 | |
|  *
 | |
|  * http://glpi-project.org
 | |
|  *
 | |
|  * based on GLPI - Gestionnaire Libre de Parc Informatique
 | |
|  * Copyright (C) 2003-2014 by the INDEPNET Development Team.
 | |
|  *
 | |
|  * ---------------------------------------------------------------------
 | |
|  *
 | |
|  * LICENSE
 | |
|  *
 | |
|  * This file is part of GLPI.
 | |
|  *
 | |
|  * GLPI is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * GLPI is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with GLPI. If not, see <http://www.gnu.org/licenses/>.
 | |
|  * ---------------------------------------------------------------------
 | |
|  */
 | |
| 
 | |
| use Glpi\Event;
 | |
| 
 | |
| if (!defined('GLPI_ROOT')) {
 | |
|    die("Sorry. You can't access this file directly");
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Document class
 | |
| **/
 | |
| class Document extends CommonDBTM {
 | |
| 
 | |
|    // From CommonDBTM
 | |
|    public $dohistory                   = true;
 | |
| 
 | |
|    static protected $forward_entity_to = ['Document_Item'];
 | |
| 
 | |
|    static $rightname                   = 'document';
 | |
|    static $tag_prefix                  = '#';
 | |
|    protected $usenotepad               = true;
 | |
| 
 | |
| 
 | |
|    static function getTypeName($nb = 0) {
 | |
|       return _n('Document', 'Documents', $nb);
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Check if given object can have Document
 | |
|     *
 | |
|     * @since 0.85
 | |
|     *
 | |
|     * @param string|object $item An object or a string
 | |
|     *
 | |
|     * @return boolean
 | |
|    **/
 | |
|    static function canApplyOn($item) {
 | |
|       global $CFG_GLPI;
 | |
| 
 | |
|       // All devices can have documents!
 | |
|       if (is_a($item, 'Item_Devices', true)
 | |
|           || is_a($item, 'CommonDevice', true)) {
 | |
|          return true;
 | |
|       }
 | |
| 
 | |
|       // We also allow direct items to check
 | |
|       if ($item instanceof CommonGLPI) {
 | |
|          $item = $item->getType();
 | |
|       }
 | |
| 
 | |
|       if (in_array($item, $CFG_GLPI['document_types'])) {
 | |
|          return true;
 | |
|       }
 | |
| 
 | |
|       return false;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Get all the types that can have a document
 | |
|     *
 | |
|     * @since 0.85
 | |
|     *
 | |
|     * @return array of the itemtypes
 | |
|    **/
 | |
|    static function getItemtypesThatCanHave() {
 | |
|       global $CFG_GLPI;
 | |
| 
 | |
|       return array_merge($CFG_GLPI['document_types'],
 | |
|                          CommonDevice::getDeviceTypes(),
 | |
|                          Item_Devices::getDeviceTypes());
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * @see CommonGLPI::getMenuShorcut()
 | |
|     *
 | |
|     * @since 0.85
 | |
|    **/
 | |
|    static function getMenuShorcut() {
 | |
|       return 'd';
 | |
|    }
 | |
| 
 | |
| 
 | |
|    static function canCreate() {
 | |
| 
 | |
|       // Have right to add document OR ticket followup
 | |
|       return (Session::haveRight('document', CREATE)
 | |
|               || Session::haveRight('followup', ITILFollowup::ADDMYTICKET));
 | |
|    }
 | |
| 
 | |
| 
 | |
|    function canCreateItem() {
 | |
| 
 | |
|       if (isset($this->input['itemtype']) && isset($this->input['items_id'])) {
 | |
|          if ($item = getItemForItemtype($this->input['itemtype'])) {
 | |
|             if ($item->canAddItem('Document')) {
 | |
|                return true;
 | |
|             }
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       // From Ticket Document Tab => check right to add followup.
 | |
|       if (isset($this->fields['tickets_id'])
 | |
|           && ($this->fields['tickets_id'] > 0)) {
 | |
| 
 | |
|          $ticket = new Ticket();
 | |
|          if ($ticket->getFromDB($this->fields['tickets_id'])) {
 | |
|             return $ticket->canAddFollowups();
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       if (Document::canCreate()) {
 | |
|          return parent::canCreateItem();
 | |
|       }
 | |
|       return false;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    function cleanDBonPurge() {
 | |
| 
 | |
|       $this->deleteChildrenAndRelationsFromDb(
 | |
|          [
 | |
|             Document_Item::class,
 | |
|          ]
 | |
|       );
 | |
| 
 | |
|       // UNLINK DU FICHIER
 | |
|       if (!empty($this->fields["filepath"])) {
 | |
|          if (is_file(GLPI_DOC_DIR."/".$this->fields["filepath"])
 | |
|              && !is_dir(GLPI_DOC_DIR."/".$this->fields["filepath"])
 | |
|              && (countElementsInTable($this->getTable(),
 | |
|                                      ['sha1sum' => $this->fields["sha1sum"] ]) <= 1)) {
 | |
| 
 | |
|             if (unlink(GLPI_DOC_DIR."/".$this->fields["filepath"])) {
 | |
|                Session::addMessageAfterRedirect(sprintf(__('Succesful deletion of the file %s'),
 | |
|                                                          GLPI_DOC_DIR."/".$this->fields["filepath"]));
 | |
|             } else {
 | |
|                Session::addMessageAfterRedirect(sprintf(__('Failed to delete the file %s'),
 | |
|                                                         GLPI_DOC_DIR."/".$this->fields["filepath"]),
 | |
|                                                 false, ERROR);
 | |
|             }
 | |
|          }
 | |
|       }
 | |
|    }
 | |
| 
 | |
| 
 | |
|    function defineTabs($options = []) {
 | |
| 
 | |
|       $ong = [];
 | |
|       $this->addDefaultFormTab($ong);
 | |
|       $this->addStandardTab('Document_Item', $ong, $options);
 | |
|       $this->addStandardTab('Notepad', $ong, $options);
 | |
|       $this->addStandardTab('Log', $ong, $options);
 | |
| 
 | |
|       return $ong;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    function prepareInputForAdd($input) {
 | |
|       global $CFG_GLPI;
 | |
| 
 | |
|       // security (don't accept filename from $_REQUEST)
 | |
|       if (array_key_exists('filename', $_REQUEST)) {
 | |
|          unset($input['filename']);
 | |
|       }
 | |
| 
 | |
|       if ($uid = Session::getLoginUserID()) {
 | |
|          $input["users_id"] = Session::getLoginUserID();
 | |
|       }
 | |
| 
 | |
|       // Create a doc only selecting a file from a item form
 | |
|       $create_from_item = false;
 | |
|       if (isset($input["items_id"])
 | |
|           && isset($input["itemtype"])
 | |
|           && ($item = getItemForItemtype($input["itemtype"]))
 | |
|           && ($input["items_id"] > 0)) {
 | |
| 
 | |
|          $typename = $item->getTypeName(1);
 | |
|          $name     = NOT_AVAILABLE;
 | |
| 
 | |
|          if ($item->getFromDB($input["items_id"])) {
 | |
|             $name = $item->getNameID();
 | |
|          }
 | |
|          //TRANS: %1$s is Document, %2$s is item type, %3$s is item name
 | |
|          $input["name"] = addslashes(Html::resume_text(sprintf(__('%1$s: %2$s'),
 | |
|                                                                Document::getTypeName(1),
 | |
|                                                        sprintf(__('%1$s - %2$s'), $typename, $name)),
 | |
|                                                        200));
 | |
|          $create_from_item = true;
 | |
|       }
 | |
| 
 | |
|       $upload_ok = false;
 | |
|       if (isset($input["_filename"]) && !(empty($input["_filename"]) == 1)) {
 | |
|          $upload_ok = $this->moveDocument($input, stripslashes(array_shift($input["_filename"])));
 | |
|       } else if (isset($input["upload_file"]) && !empty($input["upload_file"])) {
 | |
|          // Move doc from upload dir
 | |
|          $upload_ok = $this->moveUploadedDocument($input, $input["upload_file"]);
 | |
|       } else if (isset($input['filepath']) && file_exists(GLPI_DOC_DIR.'/'.$input['filepath'])) {
 | |
|          // Document is created using an existing document file
 | |
|          $upload_ok = true;
 | |
|       }
 | |
| 
 | |
|       // Tag
 | |
|       if (isset($input["_tag_filename"]) && !empty($input["_tag_filename"]) == 1) {
 | |
|          $input['tag'] = array_shift($input["_tag_filename"]);
 | |
|       }
 | |
| 
 | |
|       if (!isset($input["tag"]) || empty($input["tag"])) {
 | |
|          $input['tag'] = Rule::getUuid();
 | |
|       }
 | |
| 
 | |
|       // Upload failed : do not create document
 | |
|       if ($create_from_item && !$upload_ok) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       // Default document name
 | |
|       if ((!isset($input['name']) || empty($input['name']))
 | |
|           && isset($input['filename'])) {
 | |
|          $input['name'] = $input['filename'];
 | |
|       }
 | |
| 
 | |
|       unset($input["upload_file"]);
 | |
| 
 | |
|       // Don't add if no file
 | |
|       if (isset($input["_only_if_upload_succeed"])
 | |
|           && $input["_only_if_upload_succeed"]
 | |
|           && (!isset($input['filename']) || empty($input['filename']))) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       // Set default category for document linked to tickets
 | |
|       if (isset($input['itemtype']) && ($input['itemtype'] == 'Ticket')
 | |
|           && (!isset($input['documentcategories_id']) || ($input['documentcategories_id'] == 0))) {
 | |
|          $input['documentcategories_id'] = $CFG_GLPI["documentcategories_id_forticket"];
 | |
|       }
 | |
| 
 | |
|       /* Unicity check
 | |
|       if (isset($input['sha1sum'])) {
 | |
|          // Check if already upload in the current entity
 | |
|          $crit = array('sha1sum'=>$input['sha1sum'],
 | |
|                        'entities_id'=>$input['entities_id']);
 | |
|          foreach ($DB->request($this->getTable(), $crit) as $data) {
 | |
|             $link=$this->getFormURL();
 | |
|             Session::addMessageAfterRedirect(__('"A document with that filename has already been attached to another record.').
 | |
|                " : <a href=\"".$link."?id=".
 | |
|                      $data['id']."\">".$data['name']."</a>",
 | |
|                false, ERROR, true);
 | |
|             return false;
 | |
|          }
 | |
|       } */
 | |
|       return $input;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    function post_addItem() {
 | |
| 
 | |
|       if (isset($this->input["items_id"])
 | |
|           && isset($this->input["itemtype"])
 | |
|           && (($this->input["items_id"] > 0)
 | |
|               || (($this->input["items_id"] == 0)
 | |
|                   && ($this->input["itemtype"] == 'Entity')))
 | |
|           && !empty($this->input["itemtype"])) {
 | |
| 
 | |
|          $docitem = new Document_Item();
 | |
|          $docitem->add(['documents_id' => $this->fields['id'],
 | |
|                              'itemtype'     => $this->input["itemtype"],
 | |
|                              'items_id'     => $this->input["items_id"]]);
 | |
| 
 | |
|          Event::log($this->fields['id'], "documents", 4, "document",
 | |
|                   //TRANS: %s is the user login
 | |
|                     sprintf(__('%s adds a link with an item'), $_SESSION["glpiname"]));
 | |
|       }
 | |
|    }
 | |
| 
 | |
| 
 | |
|    public function post_getFromDB() {
 | |
|       if (isAPI()
 | |
|           && (isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT'] == 'application/octet-stream'
 | |
|               || isset($_GET['alt']) && $_GET['alt'] == 'media')) {
 | |
|          // This is a API request to download the document
 | |
|          $this->send();
 | |
|          exit();
 | |
|       }
 | |
|    }
 | |
| 
 | |
| 
 | |
|    function prepareInputForUpdate($input) {
 | |
| 
 | |
|       // security (don't accept filename from $_REQUEST)
 | |
|       if (array_key_exists('filename', $_REQUEST)) {
 | |
|          unset($input['filename']);
 | |
|       }
 | |
| 
 | |
|       if (isset($input['current_filepath'])) {
 | |
|          if (isset($input["_filename"]) && !empty($input["_filename"]) == 1) {
 | |
|             $this->moveDocument($input, stripslashes(array_shift($input["_filename"])));
 | |
|          } else if (isset($input["upload_file"]) && !empty($input["upload_file"])) {
 | |
|             // Move doc from upload dir
 | |
|             $this->moveUploadedDocument($input, $input["upload_file"]);
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       unset($input['current_filepath']);
 | |
|       unset($input['current_filename']);
 | |
| 
 | |
|       return $input;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Print the document form
 | |
|     *
 | |
|     * @param $ID        integer ID of the item
 | |
|     * @param $options   array
 | |
|     *     - target filename : where to go when done.
 | |
|     *     - withtemplate boolean : template or basic item
 | |
|     *
 | |
|     * @return void
 | |
|    **/
 | |
|    function showForm($ID, $options = []) {
 | |
|       $this->initForm($ID, $options);
 | |
|       // $options['formoptions'] = " enctype='multipart/form-data'";
 | |
|       $this->showFormHeader($options);
 | |
| 
 | |
|       $showuserlink = 0;
 | |
|       if (Session::haveRight('user', READ)) {
 | |
|          $showuserlink = 1;
 | |
|       }
 | |
|       if ($ID > 0) {
 | |
|          echo "<tr><th colspan='2'>";
 | |
|          if ($this->fields["users_id"]>0) {
 | |
|             printf(__('Added by %s'), getUserName($this->fields["users_id"], $showuserlink));
 | |
|          } else {
 | |
|             echo " ";
 | |
|          }
 | |
|          echo "</th>";
 | |
|          echo "<th colspan='2'>";
 | |
| 
 | |
|          //TRANS: %s is the datetime of update
 | |
|          printf(__('Last update on %s'), Html::convDateTime($this->fields["date_mod"]));
 | |
| 
 | |
|          echo "</th></tr>\n";
 | |
|       }
 | |
| 
 | |
|       echo "<tr class='tab_bg_1'>";
 | |
|       echo "<td>".__('Name')."</td>";
 | |
|       echo "<td>";
 | |
|       Html::autocompletionTextField($this, "name");
 | |
|       echo "</td>";
 | |
|       if ($ID > 0) {
 | |
|          echo "<td>".__('Current file')."</td>";
 | |
|          echo "<td>".$this->getDownloadLink('', 45);
 | |
|          echo "<input type='hidden' name='current_filepath' value='".$this->fields["filepath"]."'>";
 | |
|          echo "<input type='hidden' name='current_filename' value='".$this->fields["filename"]."'>";
 | |
|          echo "</td>";
 | |
|       } else {
 | |
|          echo "<td colspan=2> </td>";
 | |
|       }
 | |
|       echo "</tr>";
 | |
| 
 | |
|       echo "<tr class='tab_bg_1'>";
 | |
|       echo "<td>".__('Heading')."</td>";
 | |
|       echo "<td>";
 | |
|       DocumentCategory::dropdown(['value' => $this->fields["documentcategories_id"]]);
 | |
|       echo "</td>";
 | |
|       if ($ID > 0) {
 | |
|          echo "<td>".sprintf(__('%1$s (%2$s)'), __('Checksum'), __('SHA1'))."</td>";
 | |
|          echo "<td>".$this->fields["sha1sum"];
 | |
|          echo "</td>";
 | |
|       } else {
 | |
|          echo "<td colspan=2> </td>";
 | |
|       }
 | |
|       echo "</tr>";
 | |
| 
 | |
|       echo "<tr class='tab_bg_1'>";
 | |
|       echo "<td>".__('Web link')."</td>";
 | |
|       echo "<td>";
 | |
|       Html::autocompletionTextField($this, "link");
 | |
|       echo "</td>";
 | |
|       echo "<td rowspan='3' class='middle'>".__('Comments')."</td>";
 | |
|       echo "<td class='middle' rowspan='3'>";
 | |
|       echo "<textarea cols='45' rows='6' name='comment' >".$this->fields["comment"]."</textarea>";
 | |
|       echo "</td></tr>";
 | |
| 
 | |
|       echo "<tr class='tab_bg_1'>";
 | |
|       echo "<td>".__('MIME type')."</td>";
 | |
|       echo "<td>";
 | |
|       Html::autocompletionTextField($this, "mime");
 | |
|       echo "</td></tr>";
 | |
| 
 | |
|       echo "<tr class='tab_bg_1'>";
 | |
|       echo "<td>".__('Blacklisted for import')."</td>";
 | |
|       echo "<td>";
 | |
|       Dropdown::showYesNo("is_blacklisted", $this->fields["is_blacklisted"]);
 | |
|       echo "</td></tr>";
 | |
| 
 | |
|       echo "<tr class='tab_bg_1'>";
 | |
|       echo "<td>".__('Use a FTP installed file')."</td>";
 | |
|       echo "<td>";
 | |
|       $this->showUploadedFilesDropdown("upload_file");
 | |
|       echo "</td>";
 | |
| 
 | |
|       echo "<td>".sprintf(__('%1$s (%2$s)'), __('File'), self::getMaxUploadSize())."</td>";
 | |
|       echo "<td>";
 | |
|       Html::file();
 | |
|       echo "</td></tr>";
 | |
| 
 | |
|       $this->showFormButtons($options);
 | |
| 
 | |
|       return true;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Get max upload size from php config
 | |
|    **/
 | |
|    static function getMaxUploadSize() {
 | |
| 
 | |
|       $max_size  = Toolbox::return_bytes_from_ini_vars(ini_get("upload_max_filesize"));
 | |
|       $max_size /= 1024*1024;
 | |
|       //TRANS: %s is a size
 | |
|       return sprintf(__('%s Mio max'), round($max_size, 1));
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Send a document to navigator
 | |
|     *
 | |
|     * @param string $context Context to resize image, if any
 | |
|    **/
 | |
|    function send($context = null) {
 | |
|       $file = GLPI_DOC_DIR."/".$this->fields['filepath'];
 | |
|       if ($context !== null) {
 | |
|          $file = self::getImage($file, $context);
 | |
|       }
 | |
|       Toolbox::sendFile($file, $this->fields['filename'], $this->fields['mime']);
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Get download link for a document
 | |
|     *
 | |
|     * @param string  $params    additonal parameters to be added to the link (default '')
 | |
|     * @param integer $len       maximum length of displayed string (default 20)
 | |
|     *
 | |
|    **/
 | |
|    function getDownloadLink($params = '', $len = 20) {
 | |
|       global $DB,$CFG_GLPI;
 | |
| 
 | |
|       $splitter = explode("/", $this->fields['filename']);
 | |
| 
 | |
|       if (count($splitter) == 2) {
 | |
|          // Old documents in EXT/filename
 | |
|          $fileout = $splitter[1];
 | |
|       } else {
 | |
|          // New document
 | |
|          $fileout = $this->fields['filename'];
 | |
|       }
 | |
| 
 | |
|       $initfileout = $fileout;
 | |
| 
 | |
|       if (Toolbox::strlen($fileout) > $len) {
 | |
|          $fileout = Toolbox::substr($fileout, 0, $len)."…";
 | |
|       }
 | |
| 
 | |
|       $out   = '';
 | |
|       $open  = '';
 | |
|       $close = '';
 | |
|       if (self::canView()
 | |
|           || $this->canViewFile(['tickets_id' => $this->fields['tickets_id']])) {
 | |
|          $open  = "<a href='".$CFG_GLPI["root_doc"]."/front/document.send.php?docid=".
 | |
|                     $this->fields['id'].$params."' alt=\"".$initfileout."\"
 | |
|                     title=\"".$initfileout."\"target='_blank'>";
 | |
|          $close = "</a>";
 | |
|       }
 | |
|       $splitter = explode("/", $this->fields['filepath']);
 | |
| 
 | |
|       if (count($splitter)) {
 | |
|          $iterator = $DB->request([
 | |
|             'SELECT' => 'icon',
 | |
|             'FROM'   => 'glpi_documenttypes',
 | |
|             'WHERE'  => [
 | |
|                'ext'    => ['LIKE', $splitter[0]],
 | |
|                'icon'   => ['<>', '']
 | |
|             ]
 | |
|          ]);
 | |
| 
 | |
|          if (count($iterator) > 0) {
 | |
|             $result = $iterator->next();
 | |
|             $icon = $result['icon'];
 | |
|             if (!file_exists(GLPI_ROOT."/pics/icones/$icon")) {
 | |
|                $icon = "defaut-dist.png";
 | |
|             }
 | |
|             $out .= " <img class='middle' style='margin-left:3px; margin-right:6px;' alt=\"".
 | |
|                               $initfileout."\" title=\"".$initfileout."\" src='".
 | |
|                               $CFG_GLPI["typedoc_icon_dir"]."/$icon'>";
 | |
|          }
 | |
|       }
 | |
|       $out .= "$open<span class='b'>$fileout</span>$close";
 | |
| 
 | |
|       return $out;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * find a document with a file attached
 | |
|     *
 | |
|     * @param integer $entity    entity of the document
 | |
|     * @param string  $path      path of the searched file
 | |
|     *
 | |
|     * @return boolean
 | |
|    **/
 | |
|    function getFromDBbyContent($entity, $path) {
 | |
| 
 | |
|       global $DB;
 | |
| 
 | |
|       if (empty($path)) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       $sum = sha1_file($path);
 | |
|       if (!$sum) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       $doc_iterator = $DB->request(
 | |
|          [
 | |
|             'SELECT' => 'id',
 | |
|             'FROM'   => $this->getTable(),
 | |
|             'WHERE'  => [
 | |
|                $this->getTable() . '.sha1sum'      => $sum,
 | |
|                $this->getTable() . '.entities_id'  => $entity
 | |
|             ],
 | |
|             'LIMIT'  => 1,
 | |
|          ]
 | |
|       );
 | |
| 
 | |
|       if ($doc_iterator->count() === 0) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       $doc_data = $doc_iterator->next();
 | |
|       return $this->getFromDB($doc_data['id']);
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Check is the curent user is allowed to see the file
 | |
|     *
 | |
|     * @param array $options Options (only 'tickets_id' used)
 | |
|     *
 | |
|     * @return boolean
 | |
|    **/
 | |
|    function canViewFile(array $options = []) {
 | |
| 
 | |
|       // Check if it is my doc
 | |
|       if (Session::getLoginUserID()
 | |
|           && ($this->can($this->fields["id"], READ)
 | |
|               || ($this->fields["users_id"] === Session::getLoginUserID()))) {
 | |
|          return true;
 | |
|       }
 | |
| 
 | |
|       if ($this->canViewFileFromReminder()) {
 | |
|          return true;
 | |
|       }
 | |
| 
 | |
|       if ($this->canViewFileFromKnowbaseItem()) {
 | |
|          return true;
 | |
|       }
 | |
| 
 | |
|       if (isset($options["changes_id"])
 | |
|           && $this->canViewFileFromItilObject('Change', $options["changes_id"])) {
 | |
|          return true;
 | |
|       }
 | |
| 
 | |
|       if (isset($options["problems_id"])
 | |
|           && $this->canViewFileFromItilObject('Problem', $options["problems_id"])) {
 | |
|          return true;
 | |
|       }
 | |
| 
 | |
|       // The following case should be reachable from the API
 | |
|       self::loadAPISessionIfExist();
 | |
| 
 | |
|       if (isset($options["tickets_id"])
 | |
|           && $this->canViewFileFromItilObject('Ticket', $options["tickets_id"])) {
 | |
|          return true;
 | |
|       }
 | |
| 
 | |
|       return false;
 | |
|    }
 | |
| 
 | |
|    /**
 | |
|     * Try to load the session from the API Tolen
 | |
|     *
 | |
|     * @since 9.5
 | |
|     */
 | |
|    private static function loadAPISessionIfExist() {
 | |
|       $session_token = \Toolbox::getHeader('Session-Token');
 | |
| 
 | |
|       // No api token found
 | |
|       if ($session_token === null) {
 | |
|          return;
 | |
|       }
 | |
| 
 | |
|       $current_session = session_id();
 | |
| 
 | |
|       // Clean current session
 | |
|       if (!empty($current_session) && $current_session !== $session_token) {
 | |
|          session_destroy();
 | |
|       }
 | |
| 
 | |
|       // Load API session
 | |
|       session_id($session_token);
 | |
|       Session::start();
 | |
|    }
 | |
| 
 | |
|    /**
 | |
|     * Check if file of current instance can be viewed from a Reminder.
 | |
|     *
 | |
|     * @global DBmysql $DB
 | |
|     * @return boolean
 | |
|     *
 | |
|     * @TODO Use DBmysqlIterator instead of raw SQL
 | |
|     */
 | |
|    private function canViewFileFromReminder() {
 | |
| 
 | |
|       global $DB;
 | |
| 
 | |
|       if (!Session::getLoginUserID()) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       $criteria = array_merge_recursive(
 | |
|          [
 | |
|             'COUNT'     => 'cpt',
 | |
|             'FROM'      => 'glpi_documents_items',
 | |
|             'LEFT JOIN' => [
 | |
|                'glpi_reminders'  => [
 | |
|                   'ON' => [
 | |
|                      'glpi_documents_items'  => 'items_id',
 | |
|                      'glpi_reminders'        => 'id', [
 | |
|                         'AND' => [
 | |
|                            'glpi_documents_items.itemtype'  => 'Reminder'
 | |
|                         ]
 | |
|                      ]
 | |
|                   ]
 | |
|                ]
 | |
|             ],
 | |
|             'WHERE'     => [
 | |
|                'glpi_documents_items.documents_id' => $this->fields['id']
 | |
|             ]
 | |
|          ],
 | |
|          Reminder::getVisibilityCriteria()
 | |
|       );
 | |
| 
 | |
|       $result = $DB->request($criteria)->next();
 | |
|       return $result['cpt'] > 0;
 | |
|    }
 | |
| 
 | |
|    /**
 | |
|     * Check if file of current instance can be viewed from a KnowbaseItem.
 | |
|     *
 | |
|     * @global array $CFG_GLPI
 | |
|     * @global DBmysql $DB
 | |
|     * @return boolean
 | |
|     */
 | |
|    private function canViewFileFromKnowbaseItem() {
 | |
| 
 | |
|       global $CFG_GLPI, $DB;
 | |
| 
 | |
|       // Knowbase items can be viewed by non connected user in case of public FAQ
 | |
|       if (!Session::getLoginUserID() && !$CFG_GLPI['use_public_faq']) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       if (!Session::haveRight(KnowbaseItem::$rightname, READ)
 | |
|           && !Session::haveRight(KnowbaseItem::$rightname, KnowbaseItem::READFAQ)
 | |
|           && !$CFG_GLPI['use_public_faq']) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       $visibilityCriteria = KnowbaseItem::getVisibilityCriteria();
 | |
| 
 | |
|       $request = [
 | |
|          'FROM'      => 'glpi_documents_items',
 | |
|          'COUNT'     => 'cpt',
 | |
|          'LEFT JOIN' => [
 | |
|             'glpi_knowbaseitems' => [
 | |
|                'FKEY' => [
 | |
|                   'glpi_knowbaseitems'   => 'id',
 | |
|                   'glpi_documents_items' => 'items_id',
 | |
|                   ['AND' => ['glpi_documents_items.itemtype' => 'KnowbaseItem']]
 | |
|                ]
 | |
|             ]
 | |
|          ],
 | |
|          'WHERE'     => [
 | |
|             'glpi_documents_items.documents_id' => $this->fields['id'],
 | |
|          ]
 | |
|       ];
 | |
| 
 | |
|       if (array_key_exists('LEFT JOIN', $visibilityCriteria) && count($visibilityCriteria['LEFT JOIN']) > 0) {
 | |
|          $request['LEFT JOIN'] += $visibilityCriteria['LEFT JOIN'];
 | |
|       }
 | |
|       if (array_key_exists('WHERE', $visibilityCriteria) && count($visibilityCriteria['WHERE']) > 0) {
 | |
|          $request['WHERE'] += $visibilityCriteria['WHERE'];
 | |
|       }
 | |
| 
 | |
|       $result = $DB->request($request)->next();
 | |
| 
 | |
|       return $result['cpt'] > 0;
 | |
|    }
 | |
| 
 | |
|    /**
 | |
|     * Check if file of current instance can be viewed from a CommonITILObject.
 | |
|     *
 | |
|     * @global DBmysql $DB
 | |
|     * @param string  $itemtype
 | |
|     * @param integer $items_id
 | |
|     * @return boolean
 | |
|     */
 | |
|    private function canViewFileFromItilObject($itemtype, $items_id) {
 | |
| 
 | |
|       global $DB;
 | |
| 
 | |
|       if (!Session::getLoginUserID()) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       /* @var CommonITILObject $itil */
 | |
|       $itil = new $itemtype();
 | |
| 
 | |
|       if (!$itil->can($items_id, READ)) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       $itil->getFromDB($items_id);
 | |
| 
 | |
|       $result = $DB->request([
 | |
|          'FROM'  => Document_Item::getTable(),
 | |
|          'COUNT' => 'cpt',
 | |
|          'WHERE' => [
 | |
|             $itil->getAssociatedDocumentsCriteria(),
 | |
|             'documents_id' => $this->fields['id']
 | |
|          ]
 | |
|       ])->next();
 | |
| 
 | |
|       return $result['cpt'] > 0;
 | |
|    }
 | |
| 
 | |
|    static function rawSearchOptionsToAdd($itemtype = null) {
 | |
|       $tab = [];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => 'document',
 | |
|          'name'               => self::getTypeName(Session::getPluralNumber())
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '119',
 | |
|          'table'              => 'glpi_documents_items',
 | |
|          'field'              => 'id',
 | |
|          'name'               => _x('quantity', 'Number of documents'),
 | |
|          'forcegroupby'       => true,
 | |
|          'usehaving'          => true,
 | |
|          'datatype'           => 'count',
 | |
|          'massiveaction'      => false,
 | |
|          'joinparams'         => [
 | |
|             'jointype'           => 'itemtype_item'
 | |
|          ]
 | |
|       ];
 | |
| 
 | |
|       return $tab;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    function rawSearchOptions() {
 | |
|       $tab = [];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => 'common',
 | |
|          'name'               => __('Characteristics')
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '1',
 | |
|          'table'              => $this->getTable(),
 | |
|          'field'              => 'name',
 | |
|          'name'               => __('Name'),
 | |
|          'datatype'           => 'itemlink',
 | |
|          'massiveaction'      => false,
 | |
|          'autocomplete'       => true,
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '2',
 | |
|          'table'              => $this->getTable(),
 | |
|          'field'              => 'id',
 | |
|          'name'               => __('ID'),
 | |
|          'massiveaction'      => false,
 | |
|          'datatype'           => 'number'
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '3',
 | |
|          'table'              => $this->getTable(),
 | |
|          'field'              => 'filename',
 | |
|          'name'               => __('File'),
 | |
|          'massiveaction'      => false,
 | |
|          'datatype'           => 'string'
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '4',
 | |
|          'table'              => $this->getTable(),
 | |
|          'field'              => 'link',
 | |
|          'name'               => __('Web link'),
 | |
|          'datatype'           => 'weblink',
 | |
|          'autocomplete'       => true,
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '5',
 | |
|          'table'              => $this->getTable(),
 | |
|          'field'              => 'mime',
 | |
|          'name'               => __('MIME type'),
 | |
|          'datatype'           => 'string',
 | |
|          'autocomplete'       => true,
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '6',
 | |
|          'table'              => $this->getTable(),
 | |
|          'field'              => 'tag',
 | |
|          'name'               => __('Tag'),
 | |
|          'datatype'           => 'text',
 | |
|          'massiveaction'      => false
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '7',
 | |
|          'table'              => 'glpi_documentcategories',
 | |
|          'field'              => 'completename',
 | |
|          'name'               => __('Heading'),
 | |
|          'datatype'           => 'dropdown'
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '80',
 | |
|          'table'              => 'glpi_entities',
 | |
|          'field'              => 'completename',
 | |
|          'name'               => Entity::getTypeName(1),
 | |
|          'massiveaction'      => false,
 | |
|          'datatype'           => 'dropdown'
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '86',
 | |
|          'table'              => $this->getTable(),
 | |
|          'field'              => 'is_recursive',
 | |
|          'name'               => __('Child entities'),
 | |
|          'datatype'           => 'bool'
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '19',
 | |
|          'table'              => $this->getTable(),
 | |
|          'field'              => 'date_mod',
 | |
|          'name'               => __('Last update'),
 | |
|          'datatype'           => 'datetime',
 | |
|          'massiveaction'      => false
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '121',
 | |
|          'table'              => $this->getTable(),
 | |
|          'field'              => 'date_creation',
 | |
|          'name'               => __('Creation date'),
 | |
|          'datatype'           => 'datetime',
 | |
|          'massiveaction'      => false
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '20',
 | |
|          'table'              => $this->getTable(),
 | |
|          'field'              => 'sha1sum',
 | |
|          'name'               => sprintf(__('%1$s (%2$s)'), __('Checksum'), __('SHA1')),
 | |
|          'massiveaction'      => false,
 | |
|          'datatype'           => 'string'
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '16',
 | |
|          'table'              => $this->getTable(),
 | |
|          'field'              => 'comment',
 | |
|          'name'               => __('Comments'),
 | |
|          'datatype'           => 'text'
 | |
|       ];
 | |
| 
 | |
|       $tab[] = [
 | |
|          'id'                 => '72',
 | |
|          'table'              => 'glpi_documents_items',
 | |
|          'field'              => 'id',
 | |
|          'name'               => _x('quantity', 'Number of associated items'),
 | |
|          'forcegroupby'       => true,
 | |
|          'usehaving'          => true,
 | |
|          'datatype'           => 'count',
 | |
|          'massiveaction'      => false,
 | |
|          'joinparams'         => [
 | |
|             'jointype'           => 'child'
 | |
|          ]
 | |
|       ];
 | |
| 
 | |
|       // add objectlock search options
 | |
|       $tab = array_merge($tab, ObjectLock::rawSearchOptionsToAdd(get_class($this)));
 | |
| 
 | |
|       $tab = array_merge($tab, Notepad::rawSearchOptionsToAdd());
 | |
| 
 | |
|       return $tab;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Move a file to a new location
 | |
|     * Work even if dest file already exists
 | |
|     *
 | |
|     * @param string $srce   source file path
 | |
|     * @param string $dest   destination file path
 | |
|     *
 | |
|     * @return boolean : success
 | |
|    **/
 | |
|    static function renameForce($srce, $dest) {
 | |
| 
 | |
|       // File already present
 | |
|       if (is_file($dest)) {
 | |
|          // As content is the same (sha1sum), no need to copy
 | |
|          @unlink($srce);
 | |
|          return true;
 | |
|       }
 | |
|       // Move
 | |
|       return rename($srce, $dest);
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Move an uploadd document (files in GLPI_DOC_DIR."/_uploads" dir)
 | |
|     *
 | |
|     * @param array  $input     array of datas used in adding process (need current_filepath)
 | |
|     * @param string $filename  filename to move
 | |
|     *
 | |
|     * @return boolean for success / $input array is updated
 | |
|    **/
 | |
|    public function moveUploadedDocument(array &$input, $filename) {
 | |
|       $prefix = '';
 | |
|       if (isset($input['_prefix_filename'])) {
 | |
|          $prefix = array_shift($input['_prefix_filename']);
 | |
|       }
 | |
| 
 | |
|       $fullpath = GLPI_UPLOAD_DIR."/".$filename;
 | |
|       $filename = str_replace($prefix, '', $filename);
 | |
| 
 | |
|       if (!is_dir(GLPI_UPLOAD_DIR)) {
 | |
|          Session::addMessageAfterRedirect(__("Upload directory doesn't exist"), false, ERROR);
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       if (!is_file($fullpath)) {
 | |
|          Session::addMessageAfterRedirect(sprintf(__('File %s not found.'), $fullpath),
 | |
|                                           false, ERROR);
 | |
|          return false;
 | |
|       }
 | |
|       $sha1sum  = sha1_file($fullpath);
 | |
|       $dir      = self::isValidDoc($filename);
 | |
|       $new_path = self::getUploadFileValidLocationName($dir, $sha1sum);
 | |
| 
 | |
|       if (!$sha1sum || !$dir || !$new_path) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       // Delete old file (if not used by another doc)
 | |
|       if (isset($input['current_filepath'])
 | |
|           && !empty($input['current_filepath'])
 | |
|           && is_file(GLPI_DOC_DIR."/".$input['current_filepath'])
 | |
|           && (countElementsInTable('glpi_documents',
 | |
|                                   ['sha1sum' => sha1_file(GLPI_DOC_DIR."/".
 | |
|                                              $input['current_filepath']) ]) <= 1)) {
 | |
| 
 | |
|          if (unlink(GLPI_DOC_DIR."/".$input['current_filepath'])) {
 | |
|             Session::addMessageAfterRedirect(sprintf(__('Succesful deletion of the file %s'),
 | |
|                                                     $input['current_filename']));
 | |
|          } else {
 | |
|             // TRANS: %1$s is the curent filename, %2$s is its directory
 | |
|             Session::addMessageAfterRedirect(sprintf(__('Failed to delete the file %1$s (%2$s)'),
 | |
|                                                      $input['current_filename'],
 | |
|                                                      GLPI_DOC_DIR."/".$input['current_filepath']),
 | |
|                                              false, ERROR);
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       // Local file : try to detect mime type
 | |
|       $input['mime'] = Toolbox::getMime($fullpath);
 | |
| 
 | |
|       if (is_writable(GLPI_UPLOAD_DIR)
 | |
|           && is_writable ($fullpath)) { // Move if allowed
 | |
| 
 | |
|          if (self::renameForce($fullpath, GLPI_DOC_DIR."/".$new_path)) {
 | |
|             Session::addMessageAfterRedirect(__('Document move succeeded.'));
 | |
|          } else {
 | |
|             Session::addMessageAfterRedirect(__('File move failed.'), false, ERROR);
 | |
|             return false;
 | |
|          }
 | |
| 
 | |
|       } else { // Copy (will overwrite dest file is present)
 | |
|          if (copy($fullpath, GLPI_DOC_DIR."/".$new_path)) {
 | |
|             Session::addMessageAfterRedirect(__('Document copy succeeded.'));
 | |
|          } else {
 | |
|             Session::addMessageAfterRedirect(__('File move failed'), false, ERROR);
 | |
|             return false;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       // For display
 | |
|       $input['filename'] = addslashes($filename);
 | |
|       // Storage path
 | |
|       $input['filepath'] = $new_path;
 | |
|       // Checksum
 | |
|       $input['sha1sum']  = $sha1sum;
 | |
|       return true;
 | |
|    }
 | |
| 
 | |
|    /**
 | |
|     * Move a document (files in GLPI_DOC_DIR."/_tmp" dir)
 | |
|     *
 | |
|     * @param array  $input     array of datas used in adding process (need current_filepath)
 | |
|     * @param string $filename  filename to move
 | |
|     *
 | |
|     * @return boolean for success / $input array is updated
 | |
|    **/
 | |
|    static function moveDocument(array &$input, $filename) {
 | |
|       $prefix = '';
 | |
|       if (isset($input['_prefix_filename'])) {
 | |
|          $prefix = array_shift($input['_prefix_filename']);
 | |
|       }
 | |
| 
 | |
|       $fullpath = GLPI_TMP_DIR."/".$filename;
 | |
|       $filename = str_replace($prefix, '', $filename);
 | |
|       if (!is_dir(GLPI_TMP_DIR)) {
 | |
|          Session::addMessageAfterRedirect(__("Temporary directory doesn't exist"), false, ERROR);
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       if (!is_file($fullpath)) {
 | |
|          Session::addMessageAfterRedirect(sprintf(__('File %s not found.'), $fullpath),
 | |
|                                           false, ERROR);
 | |
|          return false;
 | |
|       }
 | |
|       $sha1sum  = sha1_file($fullpath);
 | |
|       $dir      = self::isValidDoc($filename);
 | |
|       $new_path = self::getUploadFileValidLocationName($dir, $sha1sum);
 | |
| 
 | |
|       if (!$sha1sum || !$dir || !$new_path) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       // Delete old file (if not used by another doc)
 | |
|       if (isset($input['current_filepath'])
 | |
|           && !empty($input['current_filepath'])
 | |
|           && is_file(GLPI_DOC_DIR."/".$input['current_filepath'])
 | |
|           && (countElementsInTable('glpi_documents',
 | |
|                                   ['sha1sum' => sha1_file(GLPI_DOC_DIR."/".
 | |
|                                              $input['current_filepath']) ]) <= 1)) {
 | |
| 
 | |
|          if (unlink(GLPI_DOC_DIR."/".$input['current_filepath'])) {
 | |
|             Session::addMessageAfterRedirect(sprintf(__('Succesful deletion of the file %s'),
 | |
|                                                     $input['current_filename']));
 | |
|          } else {
 | |
|             // TRANS: %1$s is the curent filename, %2$s is its directory
 | |
|             Session::addMessageAfterRedirect(sprintf(__('Failed to delete the file %1$s (%2$s)'),
 | |
|                                                      $input['current_filename'],
 | |
|                                                      GLPI_DOC_DIR."/".$input['current_filepath']),
 | |
|                                              false, ERROR);
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       // Local file : try to detect mime type
 | |
|       $input['mime'] = Toolbox::getMime($fullpath);
 | |
| 
 | |
|       if (is_writable(GLPI_TMP_DIR)
 | |
|           && is_writable ($fullpath)) { // Move if allowed
 | |
| 
 | |
|          if (self::renameForce($fullpath, GLPI_DOC_DIR."/".$new_path)) {
 | |
|             Session::addMessageAfterRedirect(__('Document move succeeded.'));
 | |
|          } else {
 | |
|             Session::addMessageAfterRedirect(__('File move failed.'), false, ERROR);
 | |
|             return false;
 | |
|          }
 | |
| 
 | |
|       } else { // Copy (will overwrite dest file is present)
 | |
|          if (copy($fullpath, GLPI_DOC_DIR."/".$new_path)) {
 | |
|             Session::addMessageAfterRedirect(__('Document copy succeeded.'));
 | |
|          } else {
 | |
|             Session::addMessageAfterRedirect(__('File move failed'), false, ERROR);
 | |
|             return false;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       // For display
 | |
|       $input['filename'] = addslashes($filename);
 | |
|       // Storage path
 | |
|       $input['filepath'] = $new_path;
 | |
|       // Checksum
 | |
|       $input['sha1sum']  = $sha1sum;
 | |
|       return true;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Upload a new file
 | |
|     *
 | |
|     * @param &$input    array of datas need for add/update (will be completed)
 | |
|     * @param $FILEDESC        FILE descriptor
 | |
|     *
 | |
|     * @return true on success
 | |
|    **/
 | |
|    static function uploadDocument(array &$input, $FILEDESC) {
 | |
| 
 | |
|       if (!count($FILEDESC)
 | |
|           || empty($FILEDESC['name'])
 | |
|           || !is_file($FILEDESC['tmp_name'])) {
 | |
| 
 | |
|          switch ($FILEDESC['error']) {
 | |
|             case 1 :
 | |
|             case 2 :
 | |
|                Session::addMessageAfterRedirect(__('File too large to be added.'), false, ERROR);
 | |
|                break;
 | |
| 
 | |
|             case 4 :
 | |
|                // Session::addMessageAfterRedirect(__('No file specified.'),false,ERROR);
 | |
|                break;
 | |
|          }
 | |
| 
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       $sha1sum = sha1_file($FILEDESC['tmp_name']);
 | |
|       $dir     = self::isValidDoc($FILEDESC['name']);
 | |
|       $path    = self::getUploadFileValidLocationName($dir, $sha1sum);
 | |
| 
 | |
|       if (!$sha1sum || !$dir || !$path) {
 | |
|          return false;
 | |
|       }
 | |
| 
 | |
|       // Delete old file (if not used by another doc)
 | |
|       if (isset($input['current_filepath'])
 | |
|           && !empty($input['current_filepath'])
 | |
|           && (countElementsInTable('glpi_documents',
 | |
|                                   ['sha1sum'=> sha1_file(GLPI_DOC_DIR."/".
 | |
|                                              $input['current_filepath']) ]) <= 1)) {
 | |
| 
 | |
|          if (unlink(GLPI_DOC_DIR."/".$input['current_filepath'])) {
 | |
|             Session::addMessageAfterRedirect(sprintf(__('Succesful deletion of the file %s'),
 | |
|                                                      $input['current_filename']));
 | |
|          } else {
 | |
|             // TRANS: %1$s is the curent filename, %2$s is its directory
 | |
|             Session::addMessageAfterRedirect(sprintf(__('Failed to delete the file %1$s (%2$s)'),
 | |
|                                                      $input['current_filename'],
 | |
|                                                      GLPI_DOC_DIR."/".$input['current_filepath']),
 | |
|                                              false, ERROR);
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       // Mime type from client
 | |
|       if (isset($FILEDESC['type']) && !empty($FILEDESC['type'])) {
 | |
|          $input['mime'] = $FILEDESC['type'];
 | |
|       }
 | |
| 
 | |
|       // Move uploaded file
 | |
|       if (self::renameForce($FILEDESC['tmp_name'], GLPI_DOC_DIR."/".$path)) {
 | |
|          Session::addMessageAfterRedirect(__('The file is valid. Upload is successful.'));
 | |
|          // For display
 | |
|          $input['filename'] = addslashes($FILEDESC['name']);
 | |
|          // Storage path
 | |
|          $input['filepath'] = $path;
 | |
|          // Checksum
 | |
|          $input['sha1sum']  = $sha1sum;
 | |
|          return true;
 | |
|       }
 | |
|       Session::addMessageAfterRedirect(__('Potential upload attack or file too large. Moving temporary file failed.'),
 | |
|                                        false, ERROR);
 | |
|       return false;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Find a valid path for the new file
 | |
|     *
 | |
|     * @param string $dir      dir to search a free path for the file
 | |
|     * @param string $sha1sum  SHA1 of the file
 | |
|     *
 | |
|     * @return string
 | |
|    **/
 | |
|    static function getUploadFileValidLocationName($dir, $sha1sum) {
 | |
|       if (empty($dir)) {
 | |
|          $message = __('Unauthorized file type');
 | |
| 
 | |
|          if (Session::haveRight('dropdown', READ)) {
 | |
|             $dt       = new DocumentType();
 | |
|             $message .= " <a target='_blank' href='".$dt->getSearchURL()."' class='pointer'>
 | |
|                          <i class='fa fa-info'</i><span class='sr-only'>" . __('Manage document types')  . "</span></a>";
 | |
|          }
 | |
|          Session::addMessageAfterRedirect($message, false, ERROR);
 | |
|          return '';
 | |
|       }
 | |
| 
 | |
|       if (!is_dir(GLPI_DOC_DIR)) {
 | |
|          Session::addMessageAfterRedirect(sprintf(__("The directory %s doesn't exist."),
 | |
|                                                   GLPI_DOC_DIR),
 | |
|                                           false, ERROR);
 | |
|          return '';
 | |
|       }
 | |
|       $subdir = $dir.'/'.substr($sha1sum, 0, 2);
 | |
| 
 | |
|       if (!is_dir(GLPI_DOC_DIR."/".$subdir)
 | |
|           && @mkdir(GLPI_DOC_DIR."/".$subdir, 0777, true)) {
 | |
|          Session::addMessageAfterRedirect(sprintf(__('Create the directory %s'),
 | |
|                                                   GLPI_DOC_DIR."/".$subdir));
 | |
|       }
 | |
| 
 | |
|       if (!is_dir(GLPI_DOC_DIR."/".$subdir)) {
 | |
|          Session::addMessageAfterRedirect(sprintf(__('Failed to create the directory %s. Verify that you have the correct permission'),
 | |
|                                                   GLPI_DOC_DIR."/".$subdir),
 | |
|                                           false, ERROR);
 | |
|          return '';
 | |
|       }
 | |
|       return $subdir.'/'.substr($sha1sum, 2).'.'.$dir;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Show dropdown of uploaded files
 | |
|     *
 | |
|     * @param $myname dropdown name
 | |
|    **/
 | |
|    static function showUploadedFilesDropdown($myname) {
 | |
|       if (is_dir(GLPI_UPLOAD_DIR)) {
 | |
| 
 | |
|          $uploaded_files = [];
 | |
|          if ($handle = opendir(GLPI_UPLOAD_DIR)) {
 | |
|             while (false !== ($file = readdir($handle))) {
 | |
|                if (($file != '.') && ($file != '..') && ($file != 'remove.txt')) {
 | |
|                   $dir = self::isValidDoc($file);
 | |
|                   if (!empty($dir)) {
 | |
|                      $uploaded_files[$file] = $file;
 | |
|                   }
 | |
|                }
 | |
|             }
 | |
|             closedir($handle);
 | |
|          }
 | |
| 
 | |
|          if (count($uploaded_files)) {
 | |
|             Dropdown::showFromArray($myname, $uploaded_files, ['display_emptychoice' => true]);
 | |
|          } else {
 | |
|             echo __('No file available');
 | |
|          }
 | |
| 
 | |
|       } else {
 | |
|          echo __("Upload directory doesn't exist");
 | |
|       }
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * Is this file a valid file ? check based on file extension
 | |
|     *
 | |
|     * @param string $filename filename to clean
 | |
|    **/
 | |
|    static function isValidDoc($filename) {
 | |
|       global $DB;
 | |
| 
 | |
|       $splitter = explode(".", $filename);
 | |
|       $ext      = end($splitter);
 | |
| 
 | |
|       $iterator = $DB->request([
 | |
|          'FROM'   => 'glpi_documenttypes',
 | |
|          'WHERE'  => [
 | |
|             'ext'             => ['LIKE', $ext],
 | |
|             'is_uploadable'   => 1
 | |
|          ]
 | |
|       ]);
 | |
| 
 | |
|       if (count($iterator)) {
 | |
|          return Toolbox::strtoupper($ext);
 | |
|       }
 | |
| 
 | |
|       // Not found try with regex one
 | |
|       $iterator = $DB->request([
 | |
|          'FROM'   => 'glpi_documenttypes',
 | |
|          'WHERE'  => [
 | |
|             'ext'             => ['LIKE', '/%/'],
 | |
|             'is_uploadable'   => 1
 | |
|          ]
 | |
|       ]);
 | |
| 
 | |
|       while ($data = $iterator->next()) {
 | |
|          if (preg_match(Toolbox::unclean_cross_side_scripting_deep($data['ext'])."i",
 | |
|                         $ext, $results) > 0) {
 | |
|             return Toolbox::strtoupper($ext);
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       return "";
 | |
|    }
 | |
| 
 | |
|    /**
 | |
|     * Make a select box for link document
 | |
|     *
 | |
|     * Parameters which could be used in options array :
 | |
|     *    - name : string / name of the select (default is documents_id)
 | |
|     *    - entity : integer or array / restrict to a defined entity or array of entities
 | |
|     *                   (default -1 : no restriction)
 | |
|     *    - used : array / Already used items ID: not to display in dropdown (default empty)
 | |
|     *
 | |
|     * @param $options array of possible options
 | |
|     *
 | |
|     * @return integer|string
 | |
|     *    integer if option display=true (random part of elements id)
 | |
|     *    string if option display=false (HTML code)
 | |
|    **/
 | |
|    static function dropdown($options = []) {
 | |
|       global $DB, $CFG_GLPI;
 | |
| 
 | |
|       $p['name']    = 'documents_id';
 | |
|       $p['entity']  = '';
 | |
|       $p['used']    = [];
 | |
|       $p['display'] = true;
 | |
| 
 | |
|       if (is_array($options) && count($options)) {
 | |
|          foreach ($options as $key => $val) {
 | |
|             $p[$key] = $val;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       $subwhere = [
 | |
|          'glpi_documents.is_deleted'   => 0,
 | |
|       ] + getEntitiesRestrictCriteria('glpi_documents', '', $p['entity'], true);
 | |
| 
 | |
|       if (count($p['used'])) {
 | |
|          $subwhere['NOT'] = ['id' => array_merge([0], $p['used'])];
 | |
|       }
 | |
| 
 | |
|       $criteria = [
 | |
|          'FROM'   => 'glpi_documentcategories',
 | |
|          'WHERE'  => [
 | |
|             'id' => new QuerySubQuery([
 | |
|                'SELECT'          => 'documentcategories_id',
 | |
|                'DISTINCT'        => true,
 | |
|                'FROM'            => 'glpi_documents',
 | |
|                'WHERE'           => $subwhere
 | |
|             ])
 | |
|          ],
 | |
|          'ORDER'  => 'name'
 | |
|       ];
 | |
|       $iterator = $DB->request($criteria);
 | |
| 
 | |
|       $values = [];
 | |
|       while ($data = $iterator->next()) {
 | |
|          $values[$data['id']] = $data['name'];
 | |
|       }
 | |
|       $rand = mt_rand();
 | |
|       $out  = Dropdown::showFromArray('_rubdoc', $values, ['width'               => '30%',
 | |
|                                                                 'rand'                => $rand,
 | |
|                                                                 'display'             => false,
 | |
|                                                                 'display_emptychoice' => true]);
 | |
|       $field_id = Html::cleanId("dropdown__rubdoc$rand");
 | |
| 
 | |
|       $params   = ['rubdoc' => '__VALUE__',
 | |
|                         'entity' => $p['entity'],
 | |
|                         'rand'   => $rand,
 | |
|                         'myname' => $p['name'],
 | |
|                         'used'   => $p['used']];
 | |
| 
 | |
|       $out .= Ajax::updateItemOnSelectEvent($field_id, "show_".$p['name'].$rand,
 | |
|                                             $CFG_GLPI["root_doc"]."/ajax/dropdownRubDocument.php",
 | |
|                                             $params, false);
 | |
|       $out .= "<span id='show_".$p['name']."$rand'>";
 | |
|       $out .= "</span>\n";
 | |
| 
 | |
|       $params['rubdoc'] = 0;
 | |
|       $out .= Ajax::updateItem("show_".$p['name'].$rand,
 | |
|                                $CFG_GLPI["root_doc"]. "/ajax/dropdownRubDocument.php",
 | |
|                                $params, false);
 | |
|       if ($p['display']) {
 | |
|          echo $out;
 | |
|          return $rand;
 | |
|       }
 | |
|       return $out;
 | |
|    }
 | |
| 
 | |
| 
 | |
|    static function getMassiveActionsForItemtype(array &$actions, $itemtype, $is_deleted = 0,
 | |
|                                                 CommonDBTM $checkitem = null) {
 | |
|       $action_prefix = 'Document_Item'.MassiveAction::CLASS_ACTION_SEPARATOR;
 | |
| 
 | |
|       if (self::canApplyOn($itemtype)) {
 | |
|          if (Document::canView()) {
 | |
|             $actions[$action_prefix.'add']    = "<i class='ma-icon far fa-file'></i>".
 | |
|                                                 _x('button', 'Add a document');
 | |
|             $actions[$action_prefix.'remove'] = _x('button', 'Remove a document');
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       if ((is_a($itemtype, __CLASS__, true)) && (static::canUpdate())) {
 | |
|          $actions[$action_prefix.'add_item']    = _x('button', 'Add an item');
 | |
|          $actions[$action_prefix.'remove_item'] = _x('button', 'Remove an item');
 | |
|       }
 | |
|    }
 | |
| 
 | |
| 
 | |
|    /**
 | |
|     * @since 0.85
 | |
|     *
 | |
|     * @param $string
 | |
|     *
 | |
|     * @return string
 | |
|    **/
 | |
|    static function getImageTag($string) {
 | |
|       return self::$tag_prefix.$string.self::$tag_prefix;
 | |
|    }
 | |
| 
 | |
|    /**
 | |
|     * Is file an image
 | |
|     *
 | |
|     * @since 9.2.1
 | |
|     *
 | |
|     * @param string $file File name
 | |
|     *
 | |
|     * @return boolean
 | |
|     */
 | |
|    public static function isImage($file) {
 | |
|       if (!file_exists($file)) {
 | |
|          return false;
 | |
|       }
 | |
|       if (extension_loaded('exif')) {
 | |
|          if (filesize($file) < 12) {
 | |
|             return false;
 | |
|          }
 | |
|          $etype = exif_imagetype($file);
 | |
|          return in_array($etype, [IMAGETYPE_JPEG, IMAGETYPE_GIF, IMAGETYPE_PNG, IMAGETYPE_BMP]);
 | |
|       } else {
 | |
|          Toolbox::logWarning('For security reasons, you should consider using exif PHP extension to properly check images.');
 | |
|          $fileinfo = finfo_open(FILEINFO_MIME_TYPE);
 | |
|          return in_array(
 | |
|             finfo_file($fileinfo, $file),
 | |
|             ['image/jpeg', 'image/png','image/gif', 'image/bmp']
 | |
|          );
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    /**
 | |
|     * Get image path for a specified context.
 | |
|     * Will call image resize if needed.
 | |
|     *
 | |
|     * @since 9.2.1
 | |
|     *
 | |
|     * @param string  $path    Original path
 | |
|     * @param string  $context Context
 | |
|     * @param integer $mwidth  Maximal width
 | |
|     * @param integer $mheight Maximal height
 | |
|     *
 | |
|     * @return string Image path on disk
 | |
|     */
 | |
|    public static function getImage($path, $context, $mwidth = null, $mheight = null) {
 | |
|       if ($mwidth === null && $mheight === null) {
 | |
|          switch ($context) {
 | |
|             case 'mail':
 | |
|                $mwidth = 400;
 | |
|                $mheight = 300;
 | |
|                break;
 | |
|             case 'timeline':
 | |
|                $mwidth = 100;
 | |
|                $mheight = 100;
 | |
|                break;
 | |
|             default:
 | |
|                throw new \RuntimeException("Unknown context $context!");
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       //let's see if original image needs resize
 | |
|       $img_infos  = getimagesize($path);
 | |
|       if (!($img_infos[0] > $mwidth) && !($img_infos[1] > $mheight)) {
 | |
|          //no resize needed
 | |
|          return $path;
 | |
|       }
 | |
| 
 | |
|       $infos = pathinfo($path);
 | |
|       // output images with possible transparency to png, other to jpg
 | |
|       $extension = in_array(strtolower($infos['extension']), ['png', 'gif']) ? 'png' : 'jpg';
 | |
|       $context_path = sprintf(
 | |
|          '%1$s_%2$s-%3$s.%4$s',
 | |
|          $infos['dirname'] . '/' . $infos['filename'],
 | |
|          $mwidth,
 | |
|          $mheight,
 | |
|          $extension
 | |
|       );
 | |
| 
 | |
|       //let's check if file already exists
 | |
|       if (file_exists($context_path)) {
 | |
|          return $context_path;
 | |
|       }
 | |
| 
 | |
|       //do resize
 | |
|       $result = Toolbox::resizePicture(
 | |
|          $path,
 | |
|          $context_path,
 | |
|          $mwidth,
 | |
|          $mheight,
 | |
|          0,
 | |
|          0,
 | |
|          0,
 | |
|          0,
 | |
|          ($mwidth > $mheight ? $mwidth : $mheight)
 | |
|       );
 | |
|       return ($result ? $context_path : $path);
 | |
|    }
 | |
| 
 | |
|    /**
 | |
|     * Give cron information
 | |
|     *
 | |
|     * @param string $name task's name
 | |
|     *
 | |
|     * @return array of information
 | |
|    **/
 | |
|    static function cronInfo($name) {
 | |
| 
 | |
|       switch ($name) {
 | |
|          case 'cleanorphans' :
 | |
|             return ['description' => __('Clean orphaned documents')];
 | |
|       }
 | |
|       return [];
 | |
|    }
 | |
| 
 | |
|    /**
 | |
|     * Cron for clean orphan documents (without Document_Item)
 | |
|     *
 | |
|     * @param CronTask $task CronTask object
 | |
|     *
 | |
|     * @return integer (0 : nothing done - 1 : done)
 | |
|    **/
 | |
|    static function cronCleanOrphans(CronTask $task) {
 | |
|       global $DB;
 | |
| 
 | |
|       $dtable = static::getTable();
 | |
|       $ditable = Document_Item::getTable();
 | |
|       //documents tht are nt present in Document_Item are oprhan
 | |
|       $iterator = $DB->request([
 | |
|          'SELECT'    => ["$dtable.id"],
 | |
|          'FROM'      => $dtable,
 | |
|          'LEFT JOIN' => [
 | |
|             $ditable => [
 | |
|                'ON'  => [
 | |
|                   $dtable  => 'id',
 | |
|                   $ditable => 'documents_id'
 | |
|                ]
 | |
|             ]
 | |
|          ],
 | |
|          'WHERE'     => [
 | |
|                "$ditable.documents_id" => null
 | |
|          ]
 | |
|       ]);
 | |
| 
 | |
|       $nb = 0;
 | |
|       if (count($iterator)) {
 | |
|          while ($row = $iterator->next()) {
 | |
|             $doc = new Document();
 | |
|             $doc->delete(['id' => $row['id']], true);
 | |
|             ++$nb;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       if ($nb) {
 | |
|          $task->addVolume($nb);
 | |
|          $task->log("Documents : $nb");
 | |
|       }
 | |
| 
 | |
|       return ($nb > 0 ? 1 : 0);
 | |
|    }
 | |
| 
 | |
| 
 | |
|    static function getIcon() {
 | |
|       return "far fa-file";
 | |
|    }
 | |
| }
 |