├── export.png ├── locales ├── fr_FR.mo ├── glpi.pot └── fr_FR.po ├── screenshots ├── view.png ├── params.png └── menuAuto.png ├── tools ├── extract_template.sh ├── update_mo.pl └── update_po.pl ├── .github └── workflows │ ├── updatepot.yml │ ├── generatemo.yml │ └── release.yml ├── README.md ├── index.php ├── ajax ├── index.php ├── dropdownsavedsearches.php ├── customsearchcriterias.php └── periodicityfields.php ├── front ├── index.php ├── exportconfig.php ├── menu.php ├── document.send.php ├── config.form.php ├── exportconfig.form.php └── files.php ├── setup.php ├── src ├── Config.php ├── Customsearchcriteria.php ├── Menu.php ├── Profile.php ├── Files.php └── Exportconfig.php ├── hook.php ├── autoexportsearches.xml └── LICENSE /export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfotelGLPI/autoexportsearches/master/export.png -------------------------------------------------------------------------------- /locales/fr_FR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfotelGLPI/autoexportsearches/master/locales/fr_FR.mo -------------------------------------------------------------------------------- /screenshots/view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfotelGLPI/autoexportsearches/master/screenshots/view.png -------------------------------------------------------------------------------- /screenshots/params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfotelGLPI/autoexportsearches/master/screenshots/params.png -------------------------------------------------------------------------------- /screenshots/menuAuto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfotelGLPI/autoexportsearches/master/screenshots/menuAuto.png -------------------------------------------------------------------------------- /tools/extract_template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Only strings with domain specified are extracted (use Xt args of keyword param to set number of args needed) 4 | 5 | xgettext *.php */*.php */*.php --copyright-holder='AutoExportSearches Development Team' --package-name='GLPI - AutoExportSearches plugin' -o locales/glpi.pot -L PHP --add-comments=TRANS --from-code=UTF-8 --force-po \ 6 | --keyword=_n:1,2,4t --keyword=__s:1,2t --keyword=__:1,2t --keyword=_e:1,2t --keyword=_x:1c,2,3t \ 7 | --keyword=_ex:1c,2,3t --keyword=_nx:1c,2,3,5t --keyword=_sx:1c,2,3t 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tools/update_mo.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | #!/usr/bin/perl -w 3 | 4 | if (@ARGV!=0){ 5 | print "USAGE update_mo.pl\n\n"; 6 | 7 | exit(); 8 | } 9 | 10 | 11 | opendir(DIRHANDLE,'locales')||die "ERROR: can not read current directory\n"; 12 | foreach (readdir(DIRHANDLE)){ 13 | if ($_ ne '..' && $_ ne '.'){ 14 | 15 | if(!(-l "$dir/$_")){ 16 | if (index($_,".po",0)==length($_)-3) { 17 | $lang=$_; 18 | $lang=~s/\.po//; 19 | 20 | `msgfmt locales/$_ -o locales/$lang.mo`; 21 | } 22 | } 23 | 24 | } 25 | } 26 | closedir DIRHANDLE; 27 | 28 | # 29 | # 30 | -------------------------------------------------------------------------------- /.github/workflows/updatepot.yml: -------------------------------------------------------------------------------- 1 | name: Update POT 2 | on: 3 | push: 4 | branches: [ master ] 5 | paths-ignore: 6 | - 'locales/**' 7 | 8 | env: 9 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 10 | jobs: 11 | run: 12 | 13 | name: Update POT 14 | 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout repo 18 | uses: actions/checkout@v4 19 | 20 | - name: install xgettext 21 | 22 | run: sudo apt-get install gettext; 23 | - name: Update POT 24 | run: sh tools/extract_template.sh; 25 | 26 | - name: Commit changes 27 | uses: EndBug/add-and-commit@v9 28 | with: 29 | message: "Update POT" 30 | 31 | - name: Push changes 32 | uses: actions-go/push@master 33 | with: 34 | commit-message: '' 35 | 36 | -------------------------------------------------------------------------------- /tools/update_po.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | #!/usr/bin/perl -w 3 | 4 | if (@ARGV!=2){ 5 | print "USAGE update_po.pl transifex_login transifex_password\n\n"; 6 | 7 | exit(); 8 | } 9 | $user = $ARGV[0]; 10 | $password = $ARGV[1]; 11 | 12 | opendir(DIRHANDLE,'locales')||die "ERROR: can not read current directory\n"; 13 | foreach (readdir(DIRHANDLE)){ 14 | if ($_ ne '..' && $_ ne '.'){ 15 | 16 | if(!(-l "$dir/$_")){ 17 | if (index($_,".po",0)==length($_)-3) { 18 | $lang=$_; 19 | $lang=~s/\.po//; 20 | 21 | `wget --user=$user --password=$password --output-document=locales/$_ http://www.transifex.com/api/2/project/GLPI_autoexportsearches/resource/glpipot/translation/$lang/?file=$_`; 22 | } 23 | } 24 | 25 | } 26 | } 27 | closedir DIRHANDLE; 28 | 29 | # 30 | # 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Plugin AutoExportSearches for GLPI 2 | 3 | ## FR 4 | 5 | Ce plugin permet d'exporter le résultat de recherches sauvegardées vers des fichiers CSV avec une action automatique.
6 | Pour chaque export configuré, il est possible de définir une adresse courriel à laquelle envoyer le fichier, ainsi qu'une périodicité pour le déclenchement de l'export. 7 | 8 | L'interface de configuration des exports est accessible dans le menu **Outils**. 9 | Dans le cas d'envoi par courriel, l'adresse utilisée pour l'expéditeur est celle définie dans le champ "Courriel de l'expéditeur" de la configuration des notifications par courriel. 10 | 11 | ## EN 12 | 13 | This plugin allows for the export of saved searches's results in CSV files with a cron task. 14 | For each configured export, you can define an email address to which the file will be sent, and an interval between each export. 15 | 16 | You can access the export configuration screen from the **Tools** menu. 17 | The mails will be sent using the address defined in the mail notification setting, field "Sender email". 18 | 19 | -------------------------------------------------------------------------------- /.github/workflows/generatemo.yml: -------------------------------------------------------------------------------- 1 | name: Generate MO 2 | on: 3 | push: 4 | branches: [ master ] 5 | paths: 6 | - '**.po' 7 | env: 8 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 9 | jobs: 10 | run: 11 | 12 | name: Generate mo 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout repo 16 | uses: actions/checkout@v4 17 | 18 | - name: Setup Perl environment 19 | # You may pin to the exact commit or the version. 20 | # uses: shogo82148/actions-setup-perl@8d2e3d59a9516b785ed32169d48a4888eaa9b514 21 | uses: shogo82148/actions-setup-perl@v1 22 | - name: msgfmt 23 | # You may pin to the exact commit or the version. 24 | # uses: whtsky/msgfmt-action@6b2181f051b002182d01a1e1f1aff216230c5a4d 25 | uses: whtsky/msgfmt-action@20190305 26 | - name: Generate mo 27 | run: perl tools/update_mo.pl; 28 | 29 | - name: Commit changes 30 | uses: EndBug/add-and-commit@v9 31 | with: 32 | 33 | message: "Generate mo" 34 | - name: Push changes 35 | 36 | uses: actions-go/push@master 37 | 38 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | -------------------------------------------------------------------------------- /ajax/index.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | -------------------------------------------------------------------------------- /front/index.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | -------------------------------------------------------------------------------- /front/exportconfig.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | use Glpi\Exception\Http\AccessDeniedHttpException; 30 | use GlpiPlugin\Autoexportsearches\Exportconfig; 31 | use GlpiPlugin\Autoexportsearches\Menu; 32 | 33 | Session::checkLoginUser(); 34 | 35 | Html::header(Menu::getTypeName(2), '', 'tools',Menu::class,Exportconfig::getType()); 36 | $export = new Exportconfig(); 37 | 38 | if ($export->canView()) { 39 | Search::show(Exportconfig::getType()); 40 | 41 | } else { 42 | throw new AccessDeniedHttpException(); 43 | } 44 | 45 | Html::footer(); 46 | -------------------------------------------------------------------------------- /front/menu.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | use Glpi\Exception\Http\AccessDeniedHttpException; 30 | 31 | use GlpiPlugin\Autoexportsearches\Menu; 32 | 33 | Session::checkLoginUser(); 34 | //central or helpdesk access 35 | if (Session::getCurrentInterface() == 'central') { 36 | Html::header(Menu::getTypeName(2), '', 'tools',Menu::class); 37 | } else { 38 | Html::helpHeader(Menu::getTypeName(2)); 39 | } 40 | 41 | if (Session::haveRight("plugin_autoexportsearches_exportconfigs", READ)) { 42 | Menu::showMenu(); 43 | } else { 44 | throw new AccessDeniedHttpException(); 45 | } 46 | 47 | if (Session::getCurrentInterface() == 'central') { 48 | Html::footer(); 49 | } else { 50 | Html::helpFooter(); 51 | } 52 | -------------------------------------------------------------------------------- /front/document.send.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | use Glpi\Exception\Http\BadRequestHttpException; 30 | use GlpiPlugin\Autoexportsearches\Config; 31 | use GlpiPlugin\Autoexportsearches\Files; 32 | use Symfony\Component\HttpFoundation\BinaryFileResponse; 33 | use Symfony\Component\HttpFoundation\ResponseHeaderBag; 34 | 35 | $files = new Files(); 36 | 37 | $check_download = $files::canDownload(); 38 | 39 | if (isset($_GET["file"]) && $check_download) { // for other file 40 | 41 | $config = new Config(); 42 | $config->getFromDB(1); 43 | $dir = GLPI_PLUGIN_DOC_DIR . $config->getField('folder') . '/'; 44 | $filename = basename($_GET["file"]); 45 | if(is_file("$dir$filename")){ 46 | $response = new BinaryFileResponse($dir . $filename); 47 | $response->setContentDisposition( 48 | ResponseHeaderBag::DISPOSITION_ATTACHMENT, 49 | $filename 50 | ); 51 | $response->send(); 52 | exit; 53 | }else{ 54 | throw new BadRequestHttpException('Invalid filename'); 55 | } 56 | }else{ 57 | throw new BadRequestHttpException('Unauthorized access to this file'); 58 | } 59 | -------------------------------------------------------------------------------- /front/config.form.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | global $CFG_GLPI; 30 | 31 | use GlpiPlugin\Autoexportsearches\Config; 32 | use GlpiPlugin\Autoexportsearches\Files; 33 | use GlpiPlugin\Autoexportsearches\Menu; 34 | 35 | $plugin = new Plugin(); 36 | 37 | if ($plugin->isActivated("autoexportsearches")) { 38 | 39 | Session::checkRight("config", UPDATE); 40 | $config = new Config(); 41 | if (isset($_POST["update"])) { 42 | if ($config->getFromDB(1)) { 43 | if(isset($_POST['monthBeforePurge']) && is_numeric($_POST['monthBeforePurge'])){ 44 | $config->update(['id' => 1, 'monthBeforePurge' => $_POST['monthBeforePurge']]); 45 | } 46 | } else { 47 | $config->add($_POST); 48 | } 49 | Html::back(); 50 | } else { 51 | Html::header(Files::getTypeName(2), '', "tools",Menu::class); 52 | $config->showConfigForm(); 53 | Html::footer(); 54 | } 55 | 56 | } else { 57 | Html::header(__('Setup'), '', "config", "plugin"); 58 | echo "


"; 59 | echo "\"warning\"

"; 60 | echo "" . __('Please activate the plugin', 'autoexportsearches') . "
"; 61 | } 62 | -------------------------------------------------------------------------------- /front/exportconfig.form.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | Session::checkLoginUser(); 30 | 31 | use GlpiPlugin\Autoexportsearches\Exportconfig; 32 | use GlpiPlugin\Autoexportsearches\Menu; 33 | 34 | if (!isset($_GET["id"])) { 35 | $_GET["id"] = ""; 36 | } 37 | if (!isset($_GET["withtemplate"])) { 38 | $_GET["withtemplate"] = ""; 39 | } 40 | 41 | $export = new Exportconfig(); 42 | 43 | if (!isset($_POST['periodicity_open_days'])) { 44 | $_POST['periodicity_open_days'] = 0; 45 | } 46 | 47 | if (isset($_POST["add"])) { 48 | $export->check(-1, CREATE, $_POST); 49 | 50 | $newID = $export->add($_POST); 51 | if ($_SESSION['glpibackcreated']) { 52 | Html::redirect($export->getFormURL() . "?id=" . $newID); 53 | } 54 | Html::back(); 55 | } elseif (isset($_POST["delete"])) { 56 | $export->check($_POST['id'], DELETE); 57 | $export->delete($_POST); 58 | $export->redirectToList(); 59 | } elseif (isset($_POST["restore"])) { 60 | $export->check($_POST['id'], PURGE); 61 | $export->restore($_POST); 62 | $export->redirectToList(); 63 | } elseif (isset($_POST["purge"])) { 64 | $export->check($_POST['id'], PURGE); 65 | $export->delete($_POST, 1); 66 | $export->redirectToList(); 67 | } elseif (isset($_POST["update"])) { 68 | $export->check($_POST['id'], UPDATE); 69 | $export->update($_POST); 70 | Html::back(); 71 | } else { 72 | $export->checkGlobal(READ); 73 | 74 | Html::header( 75 | Menu::getTypeName(2), 76 | '', 77 | 'tools', 78 | Menu::class, 79 | Exportconfig::getType() 80 | ); 81 | 82 | $export->display($_GET); 83 | 84 | Html::footer(); 85 | } 86 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | 2 | on: 3 | push: 4 | # Sequence of patterns matched against refs/tags 5 | tags: 6 | - '*.*.*' # Push events to matching ex:20.15.10 7 | 8 | name: Create release with tag 9 | env: 10 | TAG_VALUE: ${GITHUB_REF/refs\/tags\//} 11 | jobs: 12 | build: 13 | name: Upload Release Asset 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v4 18 | - name: Setup PHP 19 | uses: shivammathur/setup-php@v2 20 | with: 21 | php-version: 7.4 22 | - name: Build project # This would actually build your project, using zip for an example artifact 23 | id: build_ 24 | env: 25 | GITHUB_NAME: ${{ github.event.repository.name }} 26 | 27 | 28 | run: php -v ;sudo apt-get install libxml-xpath-perl; sudo apt-get install composer;echo $(xpath -e '/root/versions/version[num="'${GITHUB_REF/refs\/tags\//}'"]/compatibility/text()' $GITHUB_NAME.xml);echo ::set-output name=version_glpi::$(xpath -e '/root/versions/version[num="'${GITHUB_REF/refs\/tags\//}'"]/compatibility/text()' $GITHUB_NAME.xml); [[ -f composer.json ]] && composer install --no-dev; rm -rf $GITHUB_NAME.xml tools wiki screenshots test .git .github ISSUE_TEMPLATE.md TODO.txt $GITHUB_NAME.png;cd ..; tar -jcvf glpi-$GITHUB_NAME-${GITHUB_REF/refs\/tags\//}.tar.bz2 $GITHUB_NAME;ls -al;echo ::set-output name=tag::${GITHUB_REF/refs\/tags\//};echo ${{ steps.getxml.outputs.info }}; 29 | # run: rm -rf $GITHUB_NAME.xml tools wiki screenshots test ISSUE_TEMPLATE.md TODO.txt $GITHUB_NAME.png; tar -zcvf glpi-$GITHUB_NAME-$GITHUB_TAG.tar.bz2 $GITHUB_NAME 30 | - name: Create Release 31 | id: create_release 32 | uses: actions/create-release@v1 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | with: 36 | tag_name: ${{ github.ref }} 37 | release_name: | 38 | GLPI ${{ steps.build_.outputs.version_glpi }} : Version ${{ github.ref }} disponible / available 39 | body : Version ${{ steps.build_.outputs.tag }} released for GLPI ${{ steps.build_.outputs.version_glpi }} 40 | draft: false 41 | prerelease: true 42 | - name: Upload Release Asset 43 | id: upload-release-asset 44 | uses: actions/upload-release-asset@v1 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | GITHUB_NAME: ${{ github.event.repository.name }} 48 | with: 49 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 50 | asset_path: /home/runner/work/${{ github.event.repository.name }}/glpi-${{ github.event.repository.name }}-${{ steps.build_.outputs.tag }}.tar.bz2 51 | asset_name: glpi-${{ github.event.repository.name }}-${{ steps.build_.outputs.tag }}.tar.bz2 52 | asset_content_type: application/zip 53 | 54 | -------------------------------------------------------------------------------- /ajax/dropdownsavedsearches.php: -------------------------------------------------------------------------------- 1 | . 27 | -------------------------------------------------------------------------- 28 | */ 29 | 30 | global $CFG_GLPI; 31 | 32 | use Glpi\Exception\Http\AccessDeniedHttpException; 33 | 34 | header("Content-Type: text/html; charset=UTF-8"); 35 | Html::header_nocache(); 36 | Session::checkRight('plugin_autoexportsearches_exportconfigs', UPDATE); 37 | 38 | Session::checkLoginUser(); 39 | 40 | if (Session::haveRight("plugin_autoexportsearches_exportconfigs", READ)) { 41 | switch ($_POST['action']) { 42 | case 'loadSearches': 43 | if (isset($_POST["users_id"])) { 44 | $val = $_POST['savedsearches_id']; 45 | if ($_POST['users_id'] != $_POST["current_user"]) { 46 | $val = 0; 47 | } 48 | $rand = mt_rand(); 49 | SavedSearch::dropdown([ 50 | 'name' => 'savedsearches_id', 51 | 'value' => $val, 52 | 'condition' => ['users_id' => $_POST['users_id']], 53 | 'rand' => $_POST["rand"], 54 | ]); 55 | $url = PLUGINAUTOEXPORTSEARCH_WEBDIR . "/ajax/customsearchcriterias.php"; 56 | $exportConfigId = $_POST['exportconfigs_id'] ?? 0; 57 | echo " 58 | 69 | "; 70 | } 71 | break; 72 | } 73 | } else { 74 | throw new AccessDeniedHttpException(); 75 | } 76 | -------------------------------------------------------------------------------- /front/files.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | 30 | use Glpi\Exception\Http\AccessDeniedHttpException; 31 | use GlpiPlugin\Autoexportsearches\Config; 32 | use GlpiPlugin\Autoexportsearches\Files; 33 | use GlpiPlugin\Autoexportsearches\Menu; 34 | 35 | Session::checkLoginUser(); 36 | 37 | if (Session::haveRight("plugin_autoexportsearches_exportconfigs", READ)) { 38 | if (isset($_SESSION['glpiactiveprofile']['interface']) && 39 | $_SESSION['glpiactiveprofile']['interface'] == 'central' && 40 | !isset($_POST['export'])) { 41 | Html::header( 42 | Files::getTypeName(2), 43 | '', 44 | "tools", 45 | Menu::class, 46 | Files::getType() 47 | ); 48 | } elseif (!isset($_POST['export'])) { 49 | Html::helpHeader(Files::getTypeName(2)); 50 | } 51 | 52 | 53 | $files = new Files(); 54 | $config = new Config(); 55 | $config->getFromDB(1); 56 | $dir = GLPI_PLUGIN_DOC_DIR . $config->getField('folder'); 57 | if (isset($_POST["filedelete"])) { 58 | $noFile = true; 59 | foreach ($_POST["filedelete"] as $fileName => $file) { 60 | if ($file == 1) { 61 | $noFile = false; 62 | $files->processFiles("delete", $fileName); 63 | } 64 | } 65 | if (!$noFile) { 66 | Session::addMessageAfterRedirect(__('File successfully deleted', 'autoexportsearches'), true, INFO); 67 | } else { 68 | Session::addMessageAfterRedirect(__('No file selected', 'autoexportsearches'), true, ERROR); 69 | } 70 | Html::back(); 71 | } 72 | 73 | if (!isset($_GET['type'])) { 74 | $files->showMenu(); 75 | } else { 76 | $type = $_GET['type']; 77 | $files->showListFiles($dir, $type); 78 | } 79 | 80 | if (isset($_SESSION['glpiactiveprofile']['interface']) && 81 | $_SESSION['glpiactiveprofile']['interface'] == 'central') { 82 | Html::footer(); 83 | } else { 84 | Html::helpFooter(); 85 | } 86 | }else { 87 | throw new AccessDeniedHttpException(); 88 | } 89 | -------------------------------------------------------------------------------- /setup.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | use GlpiPlugin\Autoexportsearches\Customsearchcriteria; 30 | use GlpiPlugin\Autoexportsearches\Exportconfig; 31 | use GlpiPlugin\Autoexportsearches\Menu; 32 | use GlpiPlugin\Autoexportsearches\Profile; 33 | 34 | define('PLUGIN_AUTOEXPORTSEARCH_VERSION', '2.2.1'); 35 | 36 | global $CFG_GLPI; 37 | 38 | if (!defined("PLUGIN_AUTOEXPORTSEARCH_DIR")) { 39 | define("PLUGIN_AUTOEXPORTSEARCH_DIR", Plugin::getPhpDir("autoexportsearches")); 40 | $root = $CFG_GLPI['root_doc'] . '/plugins/autoexportsearches'; 41 | define("PLUGINAUTOEXPORTSEARCH_WEBDIR", $root); 42 | } 43 | 44 | // Init the hooks of the plugins -Needed 45 | function plugin_init_autoexportsearches() 46 | { 47 | global $PLUGIN_HOOKS; 48 | 49 | $PLUGIN_HOOKS['csrf_compliant']['autoexportsearches'] = true; 50 | $PLUGIN_HOOKS['change_profile']['autoexportsearches'] = [Profile::class, 'initProfile']; 51 | 52 | if (Session::getLoginUserID()) { 53 | if (Session::haveRightsOr('plugin_autoexportsearches_exportconfigs', [READ, CREATE, UPDATE] 54 | ) || Session::haveRightsOr('plugin_autoexportsearches_accessfiles', [READ, CREATE, UPDATE])) { 55 | $PLUGIN_HOOKS['menu_toadd']['autoexportsearches'] = ['tools' => Menu::class]; 56 | } 57 | Plugin::registerClass(Profile::class, ['addtabon' => 'Profile']); 58 | $PLUGIN_HOOKS['use_massive_action']['autoexportsearches'] = 1; 59 | 60 | $PLUGIN_HOOKS['pre_item_update']['autoexportsearches'] = [ 61 | Exportconfig::class => 62 | [Customsearchcriteria::class, 'createCriterias'] 63 | ]; 64 | $PLUGIN_HOOKS['item_add']['autoexportsearches'] = [ 65 | Exportconfig::class => 66 | [Customsearchcriteria::class, 'createCriterias'] 67 | ]; 68 | $PLUGIN_HOOKS['item_purge']['autoexportsearches'] = [ 69 | 'SavedSearch' => 'plugin_autoexportsearches_item_purge', 70 | Exportconfig::class => 'plugin_autoexportsearches_item_purge' 71 | ]; 72 | 73 | if (Session::haveRight("config", READ)) { 74 | $PLUGIN_HOOKS['config_page']['autoexportsearches'] = 'front/config.form.php'; 75 | } 76 | } 77 | } 78 | 79 | /** 80 | * Get the name and the version of the plugin - Needed 81 | *e 82 | * @return array 83 | */ 84 | function plugin_version_autoexportsearches() 85 | { 86 | return [ 87 | 'name' => _n('Auto export searches', 'Auto exports searches', 2, 'autoexportsearches'), 88 | 'version' => PLUGIN_AUTOEXPORTSEARCH_VERSION, 89 | 'author' => "Infotel, Alban LESELLIER", 90 | 'license' => 'GPLv2+', 91 | 'homepage' => '', 92 | 'requirements' => [ 93 | 'glpi' => [ 94 | 'min' => '11.0', 95 | 'max' => '12.0', 96 | 'dev' => false 97 | ] 98 | ] 99 | ]; 100 | } 101 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | . 27 | -------------------------------------------------------------------------- 28 | */ 29 | 30 | namespace GlpiPlugin\Autoexportsearches; 31 | 32 | use CommonDBTM; 33 | use DBConnection; 34 | use Html; 35 | use Migration; 36 | use Toolbox; 37 | 38 | if (!defined('GLPI_ROOT')) { 39 | die("Sorry. You can't access directly to this file"); 40 | } 41 | 42 | class Config extends CommonDBTM 43 | { 44 | public static $rightname = 'plugin_autoexportsearches_configs'; 45 | 46 | public static function install(Migration $migration) 47 | { 48 | global $DB; 49 | 50 | $default_charset = DBConnection::getDefaultCharset(); 51 | $default_collation = DBConnection::getDefaultCollation(); 52 | $default_key_sign = DBConnection::getDefaultPrimaryKeySignOption(); 53 | $table = self::getTable(); 54 | 55 | if (!$DB->tableExists($table)) { 56 | $query = "CREATE TABLE `$table` ( 57 | `id` int {$default_key_sign} NOT NULL auto_increment, 58 | `folder` varchar(255) collate utf8mb4_unicode_ci NOT NULL default '', 59 | `monthBeforePurge` int {$default_key_sign} NOT NULL DEFAULT '0', 60 | PRIMARY KEY (`id`) 61 | ) ENGINE=InnoDB DEFAULT CHARSET={$default_charset} COLLATE={$default_collation} ROW_FORMAT=DYNAMIC;"; 62 | 63 | $DB->doQuery($query); 64 | 65 | $DB->insert( 66 | $table, 67 | ['id' => 1, 68 | 'folder' => 'autoexportsearches', 69 | 'monthBeforePurge' => 3] 70 | ); 71 | } 72 | } 73 | 74 | public static function uninstall() 75 | { 76 | global $DB; 77 | 78 | $DB->dropTable(self::getTable(), true); 79 | } 80 | 81 | 82 | /** 83 | * Show form 84 | * 85 | * @return boolean 86 | */ 87 | public function showConfigForm() 88 | { 89 | 90 | if (!$this->canView() && !$this->canUpdate()) { 91 | return false; 92 | } 93 | 94 | if (! $this->getFromDB(1)) { 95 | $this->getEmpty(); 96 | } 97 | 98 | echo "
"; 99 | echo "
"; 100 | echo ""; 101 | 102 | echo ""; 103 | echo ""; 104 | echo ""; 107 | echo ""; 108 | 109 | echo ""; 112 | 113 | echo "
" . __('Setup') . "
" . __('Number of months before purge files', 'autoexportsearches') . ""; 105 | echo Html::input('monthBeforePurge', ['value' => $this->fields['monthBeforePurge'], 'size' => 6]); 106 | echo "
"; 110 | echo Html::submit(_sx('button', 'Save'), ['name' => 'update', 'class' => 'btn btn-primary']); 111 | echo "
"; 114 | Html::closeForm(); 115 | 116 | return true; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Customsearchcriteria.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | namespace GlpiPlugin\Autoexportsearches; 30 | 31 | use CommonDBTM; 32 | use DBConnection; 33 | use Migration; 34 | 35 | if (!defined('GLPI_ROOT')) { 36 | die("Sorry. You can't access directly to this file"); 37 | } 38 | 39 | class Customsearchcriteria extends CommonDBTM 40 | { 41 | const CRITERIA_FIRST_DAY_OF_MONTH = 'first day of '; 42 | const CRITERIA_FIRST_DAY_OF_WEEK = 'last monday'; 43 | 44 | public static function install(Migration $migration) 45 | { 46 | global $DB; 47 | 48 | $default_charset = DBConnection::getDefaultCharset(); 49 | $default_collation = DBConnection::getDefaultCollation(); 50 | $default_key_sign = DBConnection::getDefaultPrimaryKeySignOption(); 51 | $table = self::getTable(); 52 | 53 | if (!$DB->tableExists($table)) { 54 | $query = "CREATE TABLE `$table` ( 55 | `id` int {$default_key_sign} NOT NULL auto_increment, 56 | `exportconfigs_id` int {$default_key_sign} NOT NULL COMMENT 'RELATION to glpi_plugin_autoexportsearches_exportconfigs (id)', 57 | `savedsearches_id` int {$default_key_sign} NOT NULL COMMENT 'RELATION to glpi_savedsearches (id)', 58 | `criteria_field` int {$default_key_sign} NOT NULL, 59 | `criteria_value` VARCHAR(255) NOT NULL, 60 | `criteria_searchtype` VARCHAR(255) NOT NULL, 61 | PRIMARY KEY (`id`), 62 | KEY `exportconfigs_id` (`exportconfigs_id`), 63 | KEY `savedsearches_id` (`savedsearches_id`) 64 | ) ENGINE=InnoDB DEFAULT CHARSET={$default_charset} COLLATE={$default_collation} ROW_FORMAT=DYNAMIC;"; 65 | 66 | $DB->doQuery($query); 67 | } 68 | } 69 | 70 | public static function uninstall() 71 | { 72 | global $DB; 73 | 74 | $DB->dropTable(self::getTable(), true); 75 | } 76 | 77 | public static function createCriterias(ExportConfig $exportConfig) 78 | { 79 | global $DB; 80 | // clear old relations (in case of update with the saved search criterias changed) 81 | $DB->delete( 82 | 'glpi_plugin_autoexportsearches_customsearchcriterias', 83 | [ 84 | 'exportconfigs_id' => $exportConfig->fields['id'], 85 | 'savedsearches_id' => $exportConfig->fields['savedsearches_id'] 86 | ] 87 | ); 88 | 89 | if (isset($exportConfig->input['custom_criterias'])) { 90 | $customCriterias = $exportConfig->input['custom_criterias']; 91 | if (is_array($customCriterias)) { 92 | foreach ($customCriterias as $criteria) { 93 | $criteria['exportconfigs_id'] = $exportConfig->fields['id']; 94 | $self = new self(); 95 | $self->add($criteria); 96 | } 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /hook.php: -------------------------------------------------------------------------------- 1 | . 27 | -------------------------------------------------------------------------- 28 | */ 29 | 30 | use GlpiPlugin\Autoexportsearches\Config; 31 | use GlpiPlugin\Autoexportsearches\Customsearchcriteria; 32 | use GlpiPlugin\Autoexportsearches\Exportconfig; 33 | use GlpiPlugin\Autoexportsearches\Files; 34 | use GlpiPlugin\Autoexportsearches\Profile; 35 | use function Safe\mkdir; 36 | 37 | function plugin_autoexportsearches_install() 38 | { 39 | 40 | $migration = new Migration(PLUGIN_AUTOEXPORTSEARCH_VERSION); 41 | 42 | // Adds the right(s) to all pre-existing profiles with no access by default 43 | Profile::initProfile(); 44 | 45 | // Grants full access to profiles that can update the Config (super-admins) 46 | $migration->addRight(Exportconfig::$rightname, ALLSTANDARDRIGHT, [Config::$rightname => UPDATE]); 47 | 48 | Exportconfig::install($migration); 49 | 50 | Config::install($migration); 51 | 52 | Customsearchcriteria::install($migration); 53 | 54 | Files::install($migration); 55 | 56 | $migration->executeMigration(); 57 | 58 | $rep_files_autoexportsearches = GLPI_PLUGIN_DOC_DIR . "/autoexportsearches"; 59 | if (!is_dir($rep_files_autoexportsearches)) { 60 | mkdir($rep_files_autoexportsearches); 61 | } 62 | 63 | Profile::createFirstAccess($_SESSION['glpiactiveprofile']['id']); 64 | 65 | return true; 66 | } 67 | 68 | // Uninstall process for plugin : need to return true if succeeded 69 | /** 70 | * @return bool 71 | * @throws GlpitestSQLError 72 | */ 73 | function plugin_autoexportsearches_uninstall() 74 | { 75 | 76 | Exportconfig::uninstall(); 77 | 78 | Config::uninstall(); 79 | 80 | Customsearchcriteria::uninstall(); 81 | 82 | CronTask::unregister("autoexportsearches"); 83 | 84 | $rep_files_autoexportsearches = GLPI_PLUGIN_DOC_DIR . "/autoexportsearches"; 85 | 86 | if (is_dir($rep_files_autoexportsearches)) { 87 | array_map('unlink', glob($rep_files_autoexportsearches . '/*')); 88 | rmdir($rep_files_autoexportsearches); 89 | } 90 | 91 | return true; 92 | } 93 | 94 | // Define dropdown relations 95 | /** 96 | * @return array|string[][] 97 | */ 98 | function plugin_autoexportsearches_getDatabaseRelations() 99 | { 100 | $plugin = new Plugin(); 101 | if ($plugin->isActivated("autoexportsearches")) { 102 | return [ 103 | "glpi_savedsearches" => ["glpi_plugin_autoexportsearches_exportconfigs" => "savedsearches_id"], 104 | "glpi_users" => ["glpi_plugin_autoexportsearches_exportconfigs" => "users_id"], 105 | ]; 106 | } else { 107 | return []; 108 | } 109 | } 110 | 111 | function plugin_autoexportsearches_item_purge(CommonDBTM $item) 112 | { 113 | global $DB; 114 | if ($item::getType() === SavedSearch::getType()) { 115 | // relation field set to 0 by the core when deleted (because of getDatabaseRelations?) 116 | $DB->delete('glpi_plugin_autoexportsearches_exportconfigs', [ 117 | 'savedsearches_id' => 0, 118 | ]); 119 | $DB->delete('glpi_plugin_autoexportsearches_customsearchcriterias', [ 120 | 'savedsearches_id' => 0, 121 | ]); 122 | } elseif ($item::getType() === Exportconfig::getType()) { 123 | $DB->delete('glpi_plugin_autoexportsearches_customsearchcriterias', [ 124 | 'exportconfigs_id' => $item->fields['id'], 125 | ]); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Menu.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | namespace GlpiPlugin\Autoexportsearches; 30 | 31 | use CommonDBTM; 32 | use Session; 33 | 34 | /** 35 | * Class Menu 36 | */ 37 | class Menu extends CommonDBTM 38 | { 39 | static $rightname = ''; 40 | 41 | /** 42 | * @param int $nb 43 | * 44 | * @return string 45 | */ 46 | static function getMenuName($nb = 1) 47 | { 48 | return _n('Auto export', 'Auto exports', $nb, 'autoexportsearches'); 49 | } 50 | 51 | static function getIcon() 52 | { 53 | return "ti ti-file-export"; //todo find a other 54 | } 55 | 56 | static function getMenuContent() 57 | { 58 | 59 | $menu = []; 60 | $menu['icon'] = self::getIcon(); 61 | $menu['title'] = self::getMenuName(2); 62 | 63 | $menu['page'] = self::getSearchURL(false); 64 | $menu['options'][Exportconfig::getType()] = [ 65 | 'title' => Exportconfig::getTypeName(2), 66 | 'page' => Exportconfig::getSearchURL(false), 67 | 'links' => [ 68 | 'search' => Exportconfig::getSearchURL(false), 69 | 'add' => Exportconfig::getFormURL(false) 70 | ] 71 | ]; 72 | 73 | $menu['options'][Files::getType()] = [ 74 | 'title' => Files::getTypeName(2), 75 | 'page' => Files::getSearchURL(false), 76 | 77 | ]; 78 | 79 | $menu['links']['config'] = Config::getFormURL(false); 80 | //Link to config page in admin plugins list 81 | $menu['config_page'] = Config::getFormURL(false); 82 | 83 | $menu['options']['config']['title'] = __('Setup'); 84 | $menu['options']['config']['page'] = Config::getFormURL(false); 85 | $menu['options']['config']['links']['search'] = Config::getFormURL(false); 86 | $menu['options']['config']['links']['add'] = Config::getFormURL(false); 87 | 88 | 89 | return $menu; 90 | } 91 | 92 | static function removeRightsFromSession() 93 | { 94 | if (isset($_SESSION['glpimenu'][Menu::class])) { 95 | unset($_SESSION['glpimenu'][Menu::class]); 96 | } 97 | } 98 | 99 | static function showMenu() 100 | { 101 | 102 | echo "
103 | "; 104 | echo ""; 105 | 106 | echo ""; 107 | if (Session::haveRight("plugin_autoexportsearches_exportconfigs", READ)) { 108 | echo ""; 113 | } 114 | if (Session::haveRight("plugin_autoexportsearches_accessfiles", READ)) { 115 | echo ""; 120 | } 121 | echo ""; 122 | 123 | echo "
" . __('Menu', 'autoexportsearches') . "
"; 109 | echo ""; 110 | echo ""; 111 | echo "
" . __('Export config list to export', 'autoexportsearches') . "
"; 112 | echo "
"; 116 | echo ""; 117 | echo ""; 118 | echo "
" . __('List of export files', 'autoexportsearches') . "
"; 119 | echo "
"; 124 | echo "
"; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /autoexportsearches.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | AutoExportSearches 4 | autoexportsearches 5 | stable 6 | https://raw.githubusercontent.com/InfotelGLPI/autoexportsearches/master/export.png 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | https://github.com/InfotelGLPI/autoexportsearches 18 | https://github.com/InfotelGLPI/autoexportsearches/releases 19 | https://github.com/InfotelGLPI/autoexportsearches/issues 20 | https://github.com/InfotelGLPI/autoexportsearches/blob/master/README.md 21 | 22 | Alban Lesellier 23 | Infotel 24 | 25 | 26 | 27 | 2.2.1 28 | ~11.0 29 | https://github.com/InfotelGLPI/autoexportsearches/releases/download/2.2.1/glpi-autoexportsearches-2.2.1.tar.bz2 30 | 31 | 32 | 2.2.0 33 | ~11.0 34 | https://github.com/InfotelGLPI/autoexportsearches/releases/download/2.2.0/glpi-autoexportsearches-2.2.0.tar.bz2 35 | 36 | 37 | 2.1.6 38 | ~10.0 39 | https://github.com/InfotelGLPI/autoexportsearches/releases/download/2.1.6/glpi-autoexportsearches-2.1.6.tar.bz2 40 | 41 | 42 | 2.1.5 43 | ~10.0 44 | https://github.com/InfotelGLPI/autoexportsearches/releases/download/2.1.5/glpi-autoexportsearches-2.1.5.tar.bz2 45 | 46 | 47 | 2.1.4 48 | ~10.0 49 | https://github.com/InfotelGLPI/autoexportsearches/releases/download/2.1.4/glpi-autoexportsearches-2.1.4.tar.bz2 50 | 51 | 52 | 2.1.3 53 | ~10.0 54 | https://github.com/InfotelGLPI/autoexportsearches/releases/download/2.1.3/glpi-autoexportsearches-2.1.3.tar.bz2 55 | 56 | 57 | 2.1.2 58 | ~10.0 59 | https://github.com/InfotelGLPI/autoexportsearches/releases/download/2.1.2/glpi-autoexportsearches-2.1.2.tar.bz2 60 | 61 | 62 | 2.1.1 63 | ~10.0 64 | https://github.com/InfotelGLPI/autoexportsearches/releases/download/2.1.1/glpi-autoexportsearches-2.1.1.tar.bz2 65 | 66 | 67 | 2.1.0 68 | ~10.0 69 | https://github.com/InfotelGLPI/autoexportsearches/releases/download/2.1.0/glpi-autoexportsearches-2.1.0.tar.bz2 70 | 71 | 72 | 1.0.0 73 | ~9.5 74 | 75 | 76 | 77 | en_GB 78 | fr_FR 79 | 80 | 81 | 82 | 83 | Export 84 | Recherche sauvegardée 85 | 86 | 87 | 88 | Export 89 | Saved searches 90 | 91 | 92 | 93 | https://raw.githubusercontent.com/InfotelGLPI/autoexportsearches/master/screenshots/menuAuto.png 94 | https://raw.githubusercontent.com/InfotelGLPI/autoexportsearches/master/screenshots/view.png 95 | https://raw.githubusercontent.com/InfotelGLPI/autoexportsearches/master/screenshots/params.png 96 | 97 | 98 | -------------------------------------------------------------------------------- /locales/glpi.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR AutoExportSearches Development Team 3 | # This file is distributed under the same license as the GLPI - AutoExportSearches plugin package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: GLPI - AutoExportSearches plugin\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2025-12-05 12:09+0000\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" 20 | 21 | #: setup.php:87 22 | msgid "Auto export searches" 23 | msgid_plural "Auto exports searches" 24 | msgstr[0] "" 25 | msgstr[1] "" 26 | 27 | #: ajax/customsearchcriterias.php:81 28 | msgid "Customise the export of" 29 | msgstr "" 30 | 31 | #: ajax/customsearchcriterias.php:89 32 | msgid "Value" 33 | msgstr "" 34 | 35 | #: ajax/customsearchcriterias.php:90 36 | msgid "Customise" 37 | msgstr "" 38 | 39 | #: ajax/periodicityfields.php:51 40 | msgid "Periodicity (in minutes)" 41 | msgstr "" 42 | 43 | #: ajax/periodicityfields.php:67 44 | msgid "Periodicity (in hours)" 45 | msgstr "" 46 | 47 | #: ajax/periodicityfields.php:81 48 | msgid "Periodicity (in days)" 49 | msgstr "" 50 | 51 | #: ajax/periodicityfields.php:92 ajax/periodicityfields.php:174 52 | msgid "Work day only" 53 | msgstr "" 54 | 55 | #: ajax/periodicityfields.php:95 56 | msgid "If this option is checked, the export will be done only on worked day" 57 | msgstr "" 58 | 59 | #: ajax/periodicityfields.php:143 60 | msgid "Weekday" 61 | msgstr "" 62 | 63 | #: ajax/periodicityfields.php:158 64 | msgid "Day of the month" 65 | msgstr "" 66 | 67 | #: ajax/periodicityfields.php:170 68 | msgid "" 69 | "For months having less days than the selected day, the export will be done " 70 | "on the last day of the month." 71 | msgstr "" 72 | 73 | #: ajax/periodicityfields.php:177 74 | msgid "" 75 | "If this option is checked, the export will be done the first work day from " 76 | "the selected day" 77 | msgstr "" 78 | 79 | #: front/config.form.php:60 80 | msgid "Please activate the plugin" 81 | msgstr "" 82 | 83 | #: front/files.php:66 84 | msgid "File successfully deleted" 85 | msgstr "" 86 | 87 | #: front/files.php:68 88 | msgid "No file selected" 89 | msgstr "" 90 | 91 | #: src/Config.php:103 92 | msgid "Number of months before purge files" 93 | msgstr "" 94 | 95 | #: src/Exportconfig.php:77 src/Profile.php:216 96 | msgid "Auto export config" 97 | msgstr "" 98 | 99 | #: src/Exportconfig.php:229 src/Exportconfig.php:289 100 | msgid "User who owns the saved search" 101 | msgstr "" 102 | 103 | #: src/Exportconfig.php:237 src/Exportconfig.php:305 104 | msgid "Saved search to export" 105 | msgstr "" 106 | 107 | #: src/Exportconfig.php:254 108 | msgid "Last export" 109 | msgstr "" 110 | 111 | #: src/Exportconfig.php:285 112 | msgid "Search to export" 113 | msgstr "" 114 | 115 | #: src/Exportconfig.php:336 116 | msgid "Periodicity type" 117 | msgstr "" 118 | 119 | #: src/Exportconfig.php:344 120 | msgid "Every x days" 121 | msgstr "" 122 | 123 | #: src/Exportconfig.php:347 124 | msgid "Every x minutes" 125 | msgstr "" 126 | 127 | #: src/Exportconfig.php:348 128 | msgid "Every x hours" 129 | msgstr "" 130 | 131 | #: src/Exportconfig.php:377 132 | msgid "Options" 133 | msgstr "" 134 | 135 | #: src/Exportconfig.php:381 136 | msgid "Send mail to" 137 | msgstr "" 138 | 139 | #: src/Exportconfig.php:584 140 | msgid "Export saved searches" 141 | msgstr "" 142 | 143 | #: src/Files.php:51 144 | msgid "Download files" 145 | msgstr "" 146 | 147 | #: src/Files.php:123 148 | msgid "No export files available" 149 | msgstr "" 150 | 151 | #: src/Files.php:157 152 | msgid "Files" 153 | msgstr "" 154 | 155 | #: src/Files.php:223 156 | msgid "File name" 157 | msgstr "" 158 | 159 | #: src/Files.php:227 160 | msgid "Generation date" 161 | msgstr "" 162 | 163 | #: src/Files.php:303 164 | msgid "No file to download in the directory" 165 | msgstr "" 166 | 167 | #: src/Files.php:308 168 | msgid "The folder doesn't exist" 169 | msgstr "" 170 | 171 | #: src/Files.php:422 172 | msgid "Delete export files" 173 | msgstr "" 174 | 175 | #: src/Menu.php:48 176 | msgid "Auto export" 177 | msgid_plural "Auto exports" 178 | msgstr[0] "" 179 | msgstr[1] "" 180 | 181 | #: src/Menu.php:104 182 | msgid "Menu" 183 | msgstr "" 184 | 185 | #: src/Menu.php:111 186 | msgid "Export config list to export" 187 | msgstr "" 188 | 189 | #: src/Menu.php:118 190 | msgid "List of export files" 191 | msgstr "" 192 | 193 | #: src/Profile.php:179 194 | msgid "Access to download files" 195 | msgstr "" 196 | 197 | #: src/Profile.php:223 198 | msgid "Access export files" 199 | msgstr "" 200 | 201 | #: src/Profile.php:228 202 | msgid "Configuration" 203 | msgstr "" 204 | -------------------------------------------------------------------------------- /locales/fr_FR.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR AutoExportSearches Development Team 3 | # This file is distributed under the same license as the GLPI - AutoExportSearches plugin package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Xavier CAILLAUD , 2025 8 | # Edison Jaçe, 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: GLPI - AutoExportSearches plugin\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-03-17 15:03+0000\n" 16 | "PO-Revision-Date: 2025-02-21 16:08+0000\n" 17 | "Last-Translator: Edison Jaçe, 2025\n" 18 | "Language-Team: French (France) (https://app.transifex.com/infotelGLPI/teams/205440/fr_FR/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: fr_FR\n" 23 | "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" 24 | 25 | #: setup.php:80 26 | msgid "Auto export searches" 27 | msgid_plural "Auto exports searches" 28 | msgstr[0] "Export automatique de recherches" 29 | msgstr[1] "Exports automatiques de recherches" 30 | msgstr[2] "Exports automatiques de recherches" 31 | 32 | #: ajax/customsearchcriterias.php:78 33 | msgid "Customise the export of" 34 | msgstr "Personnaliser l'export de" 35 | 36 | #: ajax/customsearchcriterias.php:86 37 | msgid "Value" 38 | msgstr "Valeur" 39 | 40 | #: ajax/customsearchcriterias.php:87 41 | msgid "Customise" 42 | msgstr "Personnaliser" 43 | 44 | #: ajax/periodicityfields.php:48 45 | msgid "Periodicity (in minutes)" 46 | msgstr "Périodicité (en minutes)" 47 | 48 | #: ajax/periodicityfields.php:64 49 | msgid "Periodicity (in hours)" 50 | msgstr "Périodicité (en heures)" 51 | 52 | #: ajax/periodicityfields.php:78 53 | msgid "Periodicity (in days)" 54 | msgstr "Périodicité ( en jours)" 55 | 56 | #: ajax/periodicityfields.php:89 ajax/periodicityfields.php:171 57 | msgid "Work day only" 58 | msgstr "Seulement les jours ouvrés" 59 | 60 | #: ajax/periodicityfields.php:92 61 | msgid "If this option is checked, the export will be done only on worked day" 62 | msgstr "Si cette option est coché, l'export ne sera fait que les jours ouvrés" 63 | 64 | #: ajax/periodicityfields.php:140 65 | msgid "Weekday" 66 | msgstr "Jour de la semaine" 67 | 68 | #: ajax/periodicityfields.php:155 69 | msgid "Day of the month" 70 | msgstr "Jour du mois" 71 | 72 | #: ajax/periodicityfields.php:167 73 | msgid "" 74 | "For months having less days than the selected day, the export will be done " 75 | "on the last day of the month." 76 | msgstr "" 77 | "Pour les mois plus court que le jour sélectionné, l'export sera fait le " 78 | "dernier jour du mois." 79 | 80 | #: ajax/periodicityfields.php:174 81 | msgid "" 82 | "If this option is checked, the export will be done the first work day from " 83 | "the selected day" 84 | msgstr "" 85 | "Si cette option est cochée, l'export sera fait le jour premier ouvré à " 86 | "partir du jour sélectionné" 87 | 88 | #: front/config.form.php:56 89 | msgid "Please activate the plugin" 90 | msgstr "Veuillez activer le plugin" 91 | 92 | #: front/files.php:66 93 | msgid "File successfully deleted" 94 | msgstr "Fichier supprimé avec succès" 95 | 96 | #: front/files.php:68 97 | msgid "No file selected" 98 | msgstr "Pas de fichier sélectionné" 99 | 100 | #: inc/config.class.php:58 101 | msgid "Number of months before purge files" 102 | msgstr "Nombre de mois avant la purge des fichiers" 103 | 104 | #: inc/exportconfig.class.php:59 inc/profile.class.php:187 105 | msgid "Auto export config" 106 | msgstr "Configuration de l'export automatique" 107 | 108 | #: inc/exportconfig.class.php:86 inc/exportconfig.class.php:140 109 | msgid "User who owns the saved search" 110 | msgstr "Utilisateur ayant la vue à exporter" 111 | 112 | #: inc/exportconfig.class.php:94 inc/exportconfig.class.php:156 113 | msgid "Saved search to export" 114 | msgstr "Recherche sauvegardée à exporter" 115 | 116 | #: inc/exportconfig.class.php:111 117 | msgid "Last export" 118 | msgstr "Dernier export" 119 | 120 | #: inc/exportconfig.class.php:135 121 | msgid "Search to export" 122 | msgstr "Recherche sauvegardée à exporter" 123 | 124 | #: inc/exportconfig.class.php:187 125 | msgid "Periodicity type" 126 | msgstr "Type de périodicité" 127 | 128 | #: inc/exportconfig.class.php:195 129 | msgid "Every x days" 130 | msgstr "Tout les x jours" 131 | 132 | #: inc/exportconfig.class.php:198 133 | msgid "Every x minutes" 134 | msgstr "Toutes les x minutes" 135 | 136 | #: inc/exportconfig.class.php:199 137 | msgid "Every x hours" 138 | msgstr "Toutes les x heures" 139 | 140 | #: inc/exportconfig.class.php:228 141 | msgid "Options" 142 | msgstr "Options" 143 | 144 | #: inc/exportconfig.class.php:232 145 | msgid "Send mail to" 146 | msgstr "Envoyé par mail à l'adresse" 147 | 148 | #: inc/exportconfig.class.php:559 149 | msgid "Export saved searches" 150 | msgstr "Recherches sauvegardées exportées" 151 | 152 | #: inc/files.class.php:43 153 | msgid "Download files" 154 | msgstr "Téléchargements des fichiers" 155 | 156 | #: inc/files.class.php:104 157 | msgid "No export files available" 158 | msgstr "Pas de fichiers exportés disponibles" 159 | 160 | #: inc/files.class.php:138 161 | msgid "Files" 162 | msgstr "Fichiers" 163 | 164 | #: inc/files.class.php:204 165 | msgid "File name" 166 | msgstr "Nom du fichier" 167 | 168 | #: inc/files.class.php:208 169 | msgid "Generation date" 170 | msgstr "Date de génération" 171 | 172 | #: inc/files.class.php:286 173 | msgid "No file to download in the directory" 174 | msgstr "Pas de fichier à télécharger dans le dossier" 175 | 176 | #: inc/files.class.php:291 177 | msgid "The folder doesn't exist" 178 | msgstr "Le dossier n'existe pas" 179 | 180 | #: inc/files.class.php:405 181 | msgid "Delete export files" 182 | msgstr "Supprimer les fichiers exportés" 183 | 184 | #: inc/menu.class.php:41 185 | msgid "Auto export" 186 | msgid_plural "Auto exports" 187 | msgstr[0] "Export automatique" 188 | msgstr[1] "Exports automatiques" 189 | msgstr[2] "Exports automatiques" 190 | 191 | #: inc/menu.class.php:94 192 | msgid "Menu" 193 | msgstr "Menu" 194 | 195 | #: inc/menu.class.php:101 196 | msgid "Export config list to export" 197 | msgstr "Liste des configurations d'export à exporter" 198 | 199 | #: inc/menu.class.php:108 200 | msgid "List of export files" 201 | msgstr "Liste des fichiers exportés" 202 | 203 | #: inc/profile.class.php:152 204 | msgid "Access to download files" 205 | msgstr "Téléchargements des fichiers" 206 | 207 | #: inc/profile.class.php:194 208 | msgid "Access export files" 209 | msgstr "Accès aux fichiers exportés" 210 | 211 | #: inc/profile.class.php:199 212 | msgid "Configuration" 213 | msgstr "Configuration" 214 | -------------------------------------------------------------------------------- /ajax/customsearchcriterias.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | use Glpi\Exception\Http\AccessDeniedHttpException; 30 | use GlpiPlugin\Autoexportsearches\Customsearchcriteria; 31 | 32 | header("Content-Type: text/html; charset=UTF-8"); 33 | Html::header_nocache(); 34 | Session::checkRight('plugin_autoexportsearches_exportconfigs', UPDATE); 35 | 36 | Session::checkLoginUser(); 37 | 38 | $savedSearchId = null; 39 | if (isset($_POST['savedsearches_id']) && $_POST['savedsearches_id']) { 40 | $savedSearchId = $_POST['savedsearches_id']; 41 | } 42 | if (Session::haveRight("plugin_autoexportsearches_exportconfigs", READ)) { 43 | if ($savedSearchId) { 44 | $translations = [ 45 | 'equals' => __('is'), 46 | 'notequals' => __('is not'), 47 | 'lessthan' => __('before'), 48 | 'morethan' => __('after'), 49 | 'contains' => __('contains'), 50 | 'notcontains' => __('not contains') 51 | ]; 52 | 53 | echo ""; 54 | $search = new SavedSearch(); 55 | if ($search->getFromDB($savedSearchId)) { 56 | $url = "?" . $search->fields["query"]; 57 | $url_components = parse_url($url); 58 | parse_str($url_components['query'], $p); 59 | if (isset($p['itemtype'])) { 60 | $item = getItemForItemtype($p['itemtype']); 61 | if ($item instanceof CommonITILObject) { 62 | $fields = $item->getSearchOptionsMain(); 63 | $dateFields = array_filter($fields, function ($f) { 64 | if (array_key_exists('datatype', $f)) { 65 | return $f['datatype'] === 'datetime'; 66 | } 67 | return false; 68 | }); 69 | $dateFieldsIds = array_map(function ($f) { 70 | return $f['id']; 71 | }, $dateFields); 72 | $headerAdded = false; 73 | foreach ($p['criteria'] as $index => $criteria) { 74 | if (in_array($criteria['field'], $dateFieldsIds)) { 75 | $value = $criteria['value']; 76 | if (str_starts_with($value, '-') 77 | && (str_contains($value, 'MONTH') || str_contains($value, 'WEEK'))) { 78 | if (!$headerAdded) { 79 | echo ""; 80 | echo ""; 86 | echo ""; 87 | echo ""; 88 | echo ""; 89 | echo ""; 90 | echo ""; 91 | echo ""; 92 | $headerAdded = true; 93 | } 94 | 95 | $customValue = null; 96 | $customCriteria = new Customsearchcriteria(); 97 | if ($customCriteria->getFromDBByCrit([ 98 | 'savedsearches_id' => $savedSearchId, 99 | 'exportconfigs_id' => $_POST['exportconfigs_id'], 100 | 'criteria_field' => $criteria['field'], 101 | 'criteria_searchtype' => $criteria['searchtype'] 102 | ])) { 103 | $customValue = $customCriteria->fields['criteria_value']; 104 | } 105 | 106 | $field = array_filter($dateFields, function ($f) use ($criteria) { 107 | return $f['id'] == $criteria['field']; 108 | }); 109 | $field = reset($field); 110 | 111 | $timeValue = str_contains($value, 'MONTH') ? 'month' : 'week'; 112 | $searchValue = $translations[$criteria['searchtype']] . ' : -' . sprintf( 113 | _n("%d $timeValue", "%d $timeValue" . 's', $value[1]), 114 | $value[1] 115 | ); 116 | $label = $timeValue === 'month' ? __('Beginning of the month') : __('Monday'); 117 | $inputValue = $timeValue === 'month' ? Customsearchcriteria::CRITERIA_FIRST_DAY_OF_MONTH : Customsearchcriteria::CRITERIA_FIRST_DAY_OF_WEEK; 118 | $checked = $customValue === $inputValue ? 'checked' : ''; 119 | echo " 120 | 121 | 124 | 127 | 134 | 135 | "; 136 | } 137 | } 138 | } 139 | } 140 | } 141 | } 142 | echo "

" . __( 81 | 'Customise the export of', 82 | 'autoexportsearches' 83 | ) . __( 84 | $p['itemtype'] 85 | ) . "

" . __('Criterion') . "
" . __('Value', 'autoexportsearches') . "
" . __('Customise', 'autoexportsearches') . "
122 | 123 | 125 | $searchValue 126 | 128 | 129 | 130 | 131 | 132 | 133 |
"; 143 | } 144 | } else { 145 | throw new AccessDeniedHttpException(); 146 | } 147 | 148 | -------------------------------------------------------------------------------- /ajax/periodicityfields.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | use Glpi\Exception\Http\AccessDeniedHttpException; 30 | use GlpiPlugin\Autoexportsearches\Exportconfig; 31 | 32 | header("Content-Type: text/html; charset=UTF-8"); 33 | Html::header_nocache(); 34 | 35 | Session::checkLoginUser(); 36 | Session::checkRight('plugin_autoexportsearches_exportconfigs', UPDATE); 37 | 38 | $id = 0; 39 | if (isset($_POST['id']) && $_POST['id']) { 40 | $id = $_POST['id']; 41 | } 42 | if (Session::haveRight("plugin_autoexportsearches_exportconfigs", READ) 43 | && Session::haveRight("plugin_autoexportsearches_exportconfigs", UPDATE)) { 44 | $exportConfig = null; 45 | if ($id > 0) { 46 | $exportConfig = new Exportconfig(); 47 | $exportConfig->getFromDB($id); 48 | } 49 | switch ($_POST['periodicity_type']) { 50 | case Exportconfig::PERIODICITY_MINUTES: 51 | echo "" . __('Periodicity (in minutes)', 'autoexportsearches') . "
"; 52 | 53 | $rand = mt_rand(); 54 | 55 | Dropdown::showNumber( 56 | 'periodicity', 57 | [ 58 | 'value' => $exportConfig ? $exportConfig->fields['periodicity'] : 1, 59 | 'rand' => $rand, 60 | 'min' => 30, 61 | 'max' => 59 62 | ] 63 | ); 64 | echo "
"; 65 | break; 66 | case Exportconfig::PERIODICITY_HOURS: 67 | echo "" . __('Periodicity (in hours)', 'autoexportsearches') . "
"; 68 | $rand = mt_rand(); 69 | Dropdown::showNumber( 70 | 'periodicity', 71 | [ 72 | 'value' => $exportConfig ? $exportConfig->fields['periodicity'] : 1, 73 | 'rand' => $rand, 74 | 'min' => 1, 75 | 'max' => 23 76 | ] 77 | ); 78 | echo "
"; 79 | break; 80 | case Exportconfig::PERIODICITY_DAYS: 81 | echo "" . __('Periodicity (in days)', 'autoexportsearches') . ""; 82 | echo "
"; 83 | $rand = mt_rand(); 84 | Dropdown::showNumber( 85 | 'periodicity', 86 | [ 87 | 'value' => $exportConfig ? $exportConfig->fields['periodicity'] : 1, 88 | 'rand' => $rand, 89 | 'min' => 1 90 | ] 91 | ); 92 | $openDaysLabel = __('Work day only', 'autoexportsearches'); 93 | $checked = $exportConfig ? $exportConfig->fields['periodicity_open_days'] == 1 ? 'checked' : '' : ''; 94 | $openDaysExplanation = __( 95 | 'If this option is checked, the export will be done only on worked day', 96 | 'autoexportsearches' 97 | ); 98 | echo " 99 |
100 |
101 |
102 | 106 | 107 |
108 |
109 | 123 | "; 124 | echo " 125 | 138 | "; 139 | echo ""; 140 | break; 141 | 142 | case Exportconfig::PERIODICITY_WEEKLY: 143 | echo "" . __('Weekday', 'autoexportsearches') . ""; 144 | 145 | $rand = mt_rand(); 146 | Dropdown::showFromArray( 147 | 'periodicity', 148 | Toolbox::getDaysOfWeekArray(), 149 | [ 150 | 'value' => $exportConfig ? $exportConfig->fields['periodicity'] : 1, 151 | 'rand' => $rand 152 | ] 153 | ); 154 | echo ""; 155 | break; 156 | 157 | case Exportconfig::PERIODICITY_MONTHLY: 158 | echo "" . __('Day of the month', 'autoexportsearches') . "
"; 159 | $rand = mt_rand(); 160 | Dropdown::showNumber( 161 | 'periodicity', 162 | [ 163 | 'value' => $exportConfig ? $exportConfig->fields['periodicity'] : 1, 164 | 'rand' => $rand, 165 | 'min' => 1, 166 | 'max' => 31 167 | ] 168 | ); 169 | echo '' . __( 170 | 'For months having less days than the selected day, the export will be done on the last day of the month.', 171 | 'autoexportsearches' 172 | ) . ''; 173 | echo '
'; 174 | $openDaysLabel = __('Work day only', 'autoexportsearches'); 175 | $checked = $exportConfig ? $exportConfig->fields['periodicity_open_days'] == 1 ? 'checked' : '' : ''; 176 | $openDaysExplanation = __( 177 | 'If this option is checked, the export will be done the first work day from the selected day', 178 | 'autoexportsearches' 179 | ); 180 | echo " 181 | 182 |
183 |
184 | 188 | 189 |
190 |
191 | 205 | "; 206 | echo ""; 207 | break; 208 | } 209 | } else { 210 | throw new AccessDeniedHttpException(); 211 | } 212 | 213 | 214 | -------------------------------------------------------------------------------- /src/Profile.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | namespace GlpiPlugin\Autoexportsearches; 30 | 31 | use CommonGLPI; 32 | use DbUtils; 33 | use Html; 34 | use ProfileRight; 35 | use Session; 36 | 37 | if (!defined('GLPI_ROOT')) { 38 | die("Sorry. You can't access directly to this file"); 39 | } 40 | 41 | /** 42 | * Class Profile 43 | */ 44 | class Profile extends \Profile 45 | { 46 | 47 | static $rightname = "profile"; 48 | 49 | /** 50 | * @param CommonGLPI $item 51 | * @param int $withtemplate 52 | * 53 | * @return string 54 | */ 55 | public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) 56 | { 57 | 58 | if ($item->getType() == 'Profile') { 59 | if ($item->getField('interface') == 'central') { 60 | return self::createTabEntry(Menu::getMenuName(2)); 61 | } 62 | } 63 | return ''; 64 | } 65 | 66 | static function getIcon() 67 | { 68 | return Menu::getIcon(); 69 | } 70 | /** 71 | * @param CommonGLPI $item 72 | * @param int $tabnum 73 | * @param int $withtemplate 74 | * 75 | * @return bool 76 | */ 77 | public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) 78 | { 79 | 80 | if ($item->getType() == 'Profile') { 81 | $ID = $item->getID(); 82 | $prof = new self(); 83 | 84 | self::addDefaultProfileInfos( 85 | $ID, 86 | ['plugin_autoexportsearches_exportconfigs' => 0, 87 | 'plugin_autoexportsearches_accessfiles' => 0, 88 | 'plugin_autoexportsearches_configs' => 0] 89 | ); 90 | $prof->showForm($ID); 91 | } 92 | return true; 93 | } 94 | 95 | /** 96 | * @param $ID 97 | */ 98 | static function createFirstAccess($ID) 99 | { 100 | //85 101 | self::addDefaultProfileInfos( 102 | $ID, 103 | ['plugin_autoexportsearches_exportconfigs' => ALLSTANDARDRIGHT, 104 | 'plugin_autoexportsearches_accessfiles' => 1, 105 | 'plugin_autoexportsearches_configs' => 1], 106 | true 107 | ); 108 | } 109 | 110 | /** 111 | * @param $profiles_id 112 | * @param $rights 113 | * @param bool $drop_existing 114 | * 115 | * @internal param $profile 116 | */ 117 | static function addDefaultProfileInfos($profiles_id, $rights, $drop_existing = false) 118 | { 119 | 120 | $profileRight = new ProfileRight(); 121 | $dbu = new DbUtils(); 122 | foreach ($rights as $right => $value) { 123 | if ($dbu->countElementsInTable( 124 | 'glpi_profilerights', 125 | ["profiles_id" => $profiles_id, "name" => $right] 126 | ) && $drop_existing) { 127 | $profileRight->deleteByCriteria(['profiles_id' => $profiles_id, 'name' => $right]); 128 | } 129 | if (!$dbu->countElementsInTable( 130 | 'glpi_profilerights', 131 | ["profiles_id" => $profiles_id, "name" => $right] 132 | )) { 133 | $myright['profiles_id'] = $profiles_id; 134 | $myright['name'] = $right; 135 | $myright['rights'] = $value; 136 | $profileRight->add($myright); 137 | 138 | //Add right to the current session 139 | $_SESSION['glpiactiveprofile'][$right] = $value; 140 | } 141 | } 142 | } 143 | 144 | /** 145 | * Show profile form 146 | * 147 | * @param int $profiles_id 148 | * @param bool $openform 149 | * @param bool $closeform 150 | * 151 | * @return true 152 | * @internal param int $items_id id of the profile 153 | * @internal param value $target url of target 154 | * 155 | */ 156 | function showForm($profiles_id = 0, $openform = true, $closeform = true) 157 | { 158 | 159 | echo "
"; 160 | if (($canedit = Session::haveRightsOr(self::$rightname, [CREATE, UPDATE, PURGE])) 161 | && $openform) { 162 | $profile = new \Profile(); 163 | echo ""; 164 | } 165 | 166 | $profile = new \Profile(); 167 | $profile->getFromDB($profiles_id); 168 | // if ($profile->getField('interface') == 'central') { 169 | $rights = $this->getAllRights(); 170 | $profile->displayRightsChoiceMatrix($rights, ['canedit' => $canedit, 171 | 'default_class' => 'tab_bg_2', 172 | 'title' => __('General')]); 173 | 174 | echo ""; 175 | // echo "\n"; 176 | 177 | $effective_rights = ProfileRight::getProfileRights($profiles_id, ['plugin_autoexportsearches_accessfiles', 'plugin_autoexportsearches_configs']); 178 | echo ""; 179 | echo ""; 180 | echo "\n"; 184 | 185 | echo ""; 186 | echo ""; 187 | echo "\n"; 191 | echo "
" . __('Helpdesk') . "
" . __('Access to download files', 'autoexportsearches') . ""; 181 | Html::showCheckbox(['name' => '_plugin_autoexportsearches_accessfiles', 182 | 'checked' => $effective_rights['plugin_autoexportsearches_accessfiles']]); 183 | echo "
" . __('Configuration') . ""; 188 | Html::showCheckbox(['name' => '_plugin_autoexportsearches_configs', 189 | 'checked' => $effective_rights['plugin_autoexportsearches_configs']]); 190 | echo "
"; 192 | 193 | if ($canedit 194 | && $closeform 195 | ) { 196 | echo "
"; 197 | echo Html::hidden('id', ['value' => $profiles_id]); 198 | echo Html::submit(_sx('button', 'Save'), ['name' => 'update', 'class' => 'btn btn-primary']); 199 | echo "
\n"; 200 | Html::closeForm(); 201 | } 202 | echo "
"; 203 | 204 | return true; 205 | } 206 | 207 | /** 208 | * @param bool $all 209 | * 210 | * @return array 211 | */ 212 | static function getAllRights($all = false) 213 | { 214 | $rights = [ 215 | ['itemtype' => Exportconfig::class, 216 | 'label' => __('Auto export config', 'autoexportsearches'), 217 | 'field' => 'plugin_autoexportsearches_exportconfigs' 218 | ], 219 | ]; 220 | 221 | if ($all) { 222 | $rights[] = ['itemtype' => Exportconfig::class, 223 | 'label' => __('Access export files', 'autoexportsearches'), 224 | 'field' => 'plugin_autoexportsearches_accessfiles' 225 | ]; 226 | 227 | $rights[] = ['itemtype' => Exportconfig::class, 228 | 'label' => __('Configuration', 'autoexportsearches'), 229 | 'field' => 'plugin_autoexportsearches_configs' 230 | ]; 231 | } 232 | 233 | return $rights; 234 | } 235 | 236 | /** 237 | * Init profiles 238 | * 239 | * @param $old_right 240 | * 241 | * @return int 242 | */ 243 | 244 | static function translateARight($old_right) 245 | { 246 | switch ($old_right) { 247 | case '': 248 | return 0; 249 | case 'r': 250 | return READ; 251 | case 'w': 252 | return ALLSTANDARDRIGHT + READNOTE + UPDATENOTE; 253 | case '0': 254 | case '1': 255 | return $old_right; 256 | 257 | default: 258 | return 0; 259 | } 260 | } 261 | 262 | 263 | /** 264 | * Initialize profiles, and migrate it necessary 265 | */ 266 | static function initProfile() 267 | { 268 | global $DB; 269 | $profile = new self(); 270 | $dbu = new DbUtils(); 271 | //Add new rights in glpi_profilerights table 272 | foreach ($profile->getAllRights(true) as $data) { 273 | if ($dbu->countElementsInTable( 274 | "glpi_profilerights", 275 | ["name" => $data['field']] 276 | ) == 0) { 277 | ProfileRight::addProfileRights([$data['field']]); 278 | } 279 | } 280 | $profileId = $_SESSION['glpiactiveprofile']['id']; 281 | 282 | foreach ($DB->request([ 283 | 'FROM' => 'glpi_profilerights', 284 | 'WHERE' => [ 285 | 'profiles_id' => $profileId, 286 | 'name' => ['LIKE', '%plugin_autoexportsearches%'] 287 | ] 288 | ]) as $prof) { 289 | $_SESSION['glpiactiveprofile'][$prof['name']] = $prof['rights']; 290 | } 291 | } 292 | 293 | 294 | static function removeRightsFromSession() 295 | { 296 | foreach (self::getAllRights(true) as $right) { 297 | if (isset($_SESSION['glpiactiveprofile'][$right['field']])) { 298 | unset($_SESSION['glpiactiveprofile'][$right['field']]); 299 | } 300 | } 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /src/Files.php: -------------------------------------------------------------------------------- 1 | . 26 | -------------------------------------------------------------------------- 27 | */ 28 | 29 | namespace GlpiPlugin\Autoexportsearches; 30 | 31 | use CommonDBTM; 32 | use CronTask; 33 | use Html; 34 | use Migration; 35 | use ProfileRight; 36 | 37 | if (!defined('GLPI_ROOT')) { 38 | die("Sorry. You can't access directly to this file"); 39 | } 40 | 41 | /** 42 | * Class Files 43 | */ 44 | class Files extends CommonDBTM 45 | { 46 | 47 | static $rightname = 'plugin_autoexportsearches_accessfiles'; 48 | 49 | static function getTypeName($nb = 0) 50 | { 51 | return __('Download files', 'autoexportsearches'); 52 | } 53 | 54 | public static function install(Migration $migration) 55 | { 56 | CronTask::Register( 57 | Files::class, 58 | 'DeleteFile', 59 | MONTH_TIMESTAMP, 60 | ['state' => CronTask::STATE_DISABLE] 61 | ); 62 | } 63 | 64 | static function canDownload() 65 | { 66 | return ProfileRight::getProfileRights( 67 | $_SESSION['glpiactiveprofile']['id'], 68 | ['plugin_autoexportsearches_accessfiles'] 69 | ); 70 | } 71 | 72 | function showMenu() 73 | { 74 | 75 | echo "
"; 76 | echo ""; 77 | echo ""; 78 | echo ""; 79 | echo ""; 80 | $types = self::getTypes(); 81 | $max = count($types); 82 | for ($i = 0; $i < $max; $i += 3) { 83 | echo ""; 84 | if (($max - $i) >= 3) { 85 | $size = 2; 86 | } elseif (($max - $i) == 2) { 87 | $size = 3; 88 | } else { 89 | $size = 6; 90 | } 91 | if (isset($types[$i])) { 92 | $type = $types[$i]; 93 | echo ""; 98 | } 99 | 100 | if (isset($types[$i + 1])) { 101 | $type = $types[$i + 1]; 102 | echo ""; 107 | } 108 | 109 | if (isset($types[$i + 2])) { 110 | $type = $types[$i + 2]; 111 | echo ""; 116 | } 117 | 118 | 119 | echo ""; 120 | } 121 | if ($max == 0) { 122 | echo ""; 125 | } 126 | echo "
" . self::getTypeName() . "
"; 94 | echo ""; 95 | echo ""; 96 | echo "
" . $type . "
"; 97 | echo "
"; 103 | echo ""; 104 | echo ""; 105 | echo "
" . $type . "
"; 106 | echo "
"; 112 | echo ""; 113 | echo ""; 114 | echo "
" . $type . "
"; 115 | echo "
"; 123 | echo __("No export files available", 'autoexportsearches'); 124 | echo "
"; 127 | } 128 | 129 | function getTypes() 130 | { 131 | $types = []; 132 | $config = new Config(); 133 | $config->getFromDB(1); 134 | $dir = GLPI_PLUGIN_DOC_DIR . $config->getField('folder'); 135 | //If the dir folder exist 136 | if (is_dir($dir)) { 137 | // Get all files in an array 138 | $files = scandir($dir); 139 | foreach ($files as $file) { 140 | $type = substr($file, 0, strpos($file, '_')); 141 | if (!in_array($type, $types) && !is_dir($dir . "/" . $file)) { 142 | array_push($types, $type); 143 | } 144 | } 145 | } 146 | return $types; 147 | } 148 | 149 | /** Show All files in a HTML table 150 | * @param $dir 151 | */ 152 | function showListFiles($dir, $type) 153 | { 154 | global $CFG_GLPI; 155 | 156 | echo "
"; 157 | echo "

" . __('Files', 'autoexportsearches') . "

"; 158 | //If the dir folder exist 159 | if (is_dir($dir)) { 160 | // Get all files in an array 161 | $files = $this->processFiles("get", "", $type); 162 | 163 | // If there is files in the folder 164 | if ($files == true) { 165 | //Pagination 166 | $limitBegin = 0; 167 | $nbRows = count($files); 168 | if (isset($_GET['start'])) { 169 | $limitBegin = $_GET['start']; 170 | } 171 | if (isset($_SESSION['glpilist_limit'])) { 172 | $limitNb = $_SESSION['glpilist_limit']; 173 | } else { 174 | $limitNb = 0; 175 | } 176 | $target = PLUGINAUTOEXPORTSEARCH_WEBDIR . '/front/files.php?type=' . $type; 177 | if (isset($_GET['orderType'])) { 178 | $parameters = "orderCol=" . $_GET['orderCol'] . "&orderType=" . $_GET['orderType']; 179 | } else { 180 | $parameters = ""; 181 | } 182 | Html::printPager($limitBegin, $nbRows, $target, $parameters); 183 | echo ""; 184 | 185 | echo ""; 186 | echo ""; 187 | // Checkbox colomn for select delete datas 188 | echo ""; 208 | $ordertype = "ASC"; 209 | if (isset($_GET['orderType'])) { 210 | if ($_GET['orderType'] == "ASC") { 211 | $ordertype = "DESC"; 212 | } else { 213 | $ordertype = "ASC"; 214 | } 215 | } 216 | 217 | $start = 0; 218 | if (isset($_GET['start'])) { 219 | $start = $_GET['start']; 220 | } 221 | 222 | echo ""; 226 | echo ""; 230 | echo ""; 231 | 232 | //Sort table order with headers 233 | if (isset($_GET['orderCol'])) { 234 | switch ($_GET['orderCol']) { 235 | case 'name': 236 | case 'date': 237 | if (isset($_GET['orderType'])) { 238 | if ($_GET['orderType'] == 'ASC') { 239 | sort($files); 240 | } elseif ($_GET['orderType'] == 'DESC') { 241 | rsort($files); 242 | } 243 | } 244 | break; 245 | case 'month': 246 | if (isset($_GET['orderType'])) { 247 | if ($_GET['orderType'] == 'ASC') { 248 | usort($files, [$this, 'sortArrayAsc']); 249 | } elseif ($_GET['orderType'] == 'DESC') { 250 | usort($files, [$this, 'sortArrayDesc']); 251 | } 252 | } 253 | break; 254 | } 255 | } 256 | 257 | $plugin_dir = PLUGINAUTOEXPORTSEARCH_WEBDIR; 258 | $i = 0; 259 | foreach ($files as $key => $file) { 260 | if ($key >= $limitBegin && 261 | $key < ($limitNb + $limitBegin)) { 262 | //Show datas from file name 263 | echo ""; 264 | $dateFile = $this->getDateFile($file, 'YmdHis'); 265 | echo ""; 268 | if ($this::canDownload()) { 269 | echo ""; 270 | } else { 271 | echo ""; 272 | } 273 | $dateFormated = substr($dateFile, 0, 10); 274 | $afterDate = substr($dateFile, 11); 275 | if ((strpos($afterDate, "csv") === false) && ($_SESSION["glpilanguage"] == "fr_FR")) { 276 | $dateFormated1 = preg_replace("/(\d{4})-(\d{2})-(\d{2})/", "$3-$2-$1", substr($dateFile, 0, 10)); 277 | $dateFormated2 = preg_replace("/(\d{2})-(\d{2})-(\d{2})/", "$1h$2min$3s", substr($dateFile, 11)); 278 | $dateFormated = $dateFormated1 . " " . $dateFormated2; 279 | } elseif ((strpos($afterDate, "csv") === false) && ($_SESSION["glpilanguage"] !== "fr_FR")) { 280 | $dateFormated = str_replace("-", ":", substr($dateFile, 11)); 281 | $dateFormated = substr($dateFile, 0, 10) . " " . $dateFormated; 282 | } elseif ((strpos($afterDate, "csv") === true) && ($_SESSION["glpilanguage"] == "fr_FR")) { 283 | $dateFormated1 = preg_replace("/(\d{4})-(\d{2})-(\d{2})/", "$3-$2-$1", substr($dateFile, 0, 10)); 284 | $dateFormated = $dateFormated1; 285 | } elseif ((strpos($afterDate, "csv") === true) && ($_SESSION["glpilanguage"] !== "fr_FR")) { 286 | $dateFormated .= ""; 287 | } 288 | echo ""; 289 | $i++; 290 | } 291 | } 292 | echo "
189 |
190 | "; 192 | echo ""; 202 | echo " 206 |
207 |
" . __( 223 | 'File name', 224 | 'autoexportsearches' 225 | ) . "" . __( 227 | 'Generation date', 228 | 'autoexportsearches' 229 | ) . "
"; 266 | echo Html::showCheckbox(["name" => "filedelete[$file]"]); 267 | echo " $file $file" . $dateFormated . "
"; 293 | 294 | echo "
"; 295 | echo Html::submit( 296 | __("Delete"), 297 | ['confirm' => __('Confirm the final deletion?'), 'class' => 'btn btn-primary'] 298 | ); 299 | echo Html::closeForm(false); 300 | echo "
"; 301 | } else { 302 | echo "
" . __( 303 | 'No file to download in the directory', 304 | 'autoexportsearches' 305 | ) . "
"; 306 | } 307 | } else { 308 | echo "
" . __('The folder doesn\'t exist', 'autoexportsearches') . "
"; 309 | } 310 | } 311 | 312 | function sortArrayAsc($a, $b) 313 | { 314 | $aMonth = substr($a, strpos($a, "_") + 5, 2); 315 | $bMonth = substr($b, strpos($b, "_") + 5, 2); 316 | return $aMonth > $bMonth; 317 | } 318 | 319 | function sortArrayDesc($a, $b) 320 | { 321 | $aMonth = substr($a, strpos($a, "_") + 5, 2); 322 | $bMonth = substr($b, strpos($b, "_") + 5, 2); 323 | return $aMonth < $bMonth; 324 | } 325 | 326 | /** Get date in file name 327 | * @param $file 328 | * @param string $formatOut 329 | * 330 | * @return bool|string 331 | */ 332 | function getDateFile($file, $formatOut = "Ymd") 333 | { 334 | switch ($formatOut) { 335 | case "Y": 336 | $out = substr($file, strpos($file, "_") + 1, 4); 337 | break; 338 | case "m": 339 | $out = substr($file, strpos($file, "_") + 6, 2); 340 | break; 341 | case "d": 342 | $out = substr($file, strpos($file, "_") + 9, 2); 343 | break; 344 | case "Ymd": 345 | $out = substr($file, strpos($file, "_") + 1, 10); 346 | break; 347 | case "YmdHis": 348 | $out = substr($file, strpos($file, "_") + 1, 19); 349 | break; 350 | } 351 | $out = str_replace("_", "-", $out); 352 | 353 | return $out; 354 | } 355 | 356 | /** Function that makes actions around files 357 | * @param $action 358 | * @param string $file 359 | * 360 | * @return array|bool 361 | */ 362 | function processFiles($action, $file = "", $type = "") 363 | { 364 | $config = new Config(); 365 | $config->getFromDB(1); 366 | $dir = GLPI_PLUGIN_DOC_DIR . $config->getField('folder'); 367 | 368 | switch ($action) { 369 | case "get": 370 | $res = []; 371 | // Get files in defined dir 372 | $files = scandir($dir); 373 | foreach ($files as $file) { 374 | // if the file is not a folder 375 | if ($type != "" && strpos($file, $type) > -1) { 376 | if (!is_dir($dir . "/" . $file)) { 377 | $res[] = $file; 378 | } 379 | } 380 | } 381 | break; 382 | case "delete": 383 | // delete file 384 | $res = unlink($dir . "/" . $file); 385 | break; 386 | } 387 | return $res; 388 | } 389 | 390 | /** Function for delete files after $nbMonths 391 | * @param $nbMonths 392 | */ 393 | function deleteByMonths($nbMonths) 394 | { 395 | $today = date("Ymd"); 396 | $files = $this->processFiles("get"); 397 | if (is_array($files)) { 398 | foreach ($files as $file) { 399 | $dateFile = strtotime($this->getDateFile($file)); 400 | $nbMonthsToAdd = "+" . $nbMonths . " months"; 401 | $dateDiff = strtotime($nbMonthsToAdd, $dateFile); 402 | $dateToDelete = date('Ymd', $dateDiff); 403 | if ($today > $dateToDelete) { 404 | $this->processFiles("delete", $file); 405 | } 406 | } 407 | } 408 | } 409 | 410 | ////// CRON FUNCTIONS /////// 411 | //Cron action 412 | /** 413 | * @param $name 414 | * 415 | * @return array 416 | */ 417 | static function cronInfo($name) 418 | { 419 | switch ($name) { 420 | case 'DeleteFile': 421 | return [ 422 | 'description' => __('Delete export files', 'autoexportsearches') 423 | ]; // Optional 424 | break; 425 | } 426 | return []; 427 | } 428 | 429 | /** 430 | * Cron action 431 | * 432 | * @param $task for log 433 | * @global $CFG_GLPI 434 | * 435 | * @global $DB 436 | */ 437 | static function cronDeleteFile($task = null) 438 | { 439 | $CronTask = new CronTask(); 440 | if ($CronTask->getFromDBbyName(Files::class, "DeleteFile")) { 441 | if ($CronTask->fields["state"] == CronTask::STATE_DISABLE) { 442 | return 0; 443 | } 444 | } else { 445 | return 0; 446 | } 447 | 448 | $config = new Config(); 449 | $config->getFromDB(1); 450 | $nbMonths = $config->getField('monthBeforePurge'); 451 | 452 | $autoexportsearchesFiles = new self(); 453 | $autoexportsearchesFiles->deleteByMonths($nbMonths); 454 | return 1; 455 | } 456 | } 457 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /src/Exportconfig.php: -------------------------------------------------------------------------------- 1 | . 27 | -------------------------------------------------------------------------- 28 | */ 29 | 30 | namespace GlpiPlugin\Autoexportsearches; 31 | 32 | use Ajax; 33 | use Auth; 34 | use CommonDBTM; 35 | use CronTask; 36 | use DBConnection; 37 | use Dropdown; 38 | use GLPIMailer; 39 | use GLPINetwork; 40 | use Html; 41 | use Migration; 42 | use Profile; 43 | use SavedSearch; 44 | use Search; 45 | use Session; 46 | use User; 47 | 48 | if (!defined('GLPI_ROOT')) { 49 | die("Sorry. You can't access directly to this file"); 50 | } 51 | 52 | /** 53 | * Class Exportconfig 54 | */ 55 | class Exportconfig extends CommonDBTM 56 | { 57 | public const PERIODICITY_MINUTES = 3; 58 | public const PERIODICITY_HOURS = 4; 59 | public const PERIODICITY_DAYS = 0; 60 | public const PERIODICITY_WEEKLY = 1; 61 | public const PERIODICITY_MONTHLY = 2; 62 | 63 | public static $rightname = 'plugin_autoexportsearches_exportconfigs'; 64 | // static $rightname = 'ticket'; 65 | 66 | 67 | /** 68 | * functions mandatory 69 | * getTypeName(), canCreate(), canView() 70 | * 71 | * @param int $nb 72 | * 73 | * @return string 74 | */ 75 | public static function getTypeName($nb = 0) 76 | { 77 | return __('Auto export config', 'autoexportsearches'); 78 | } 79 | 80 | public static function install(Migration $migration) 81 | { 82 | global $DB; 83 | 84 | $default_charset = DBConnection::getDefaultCharset(); 85 | $default_collation = DBConnection::getDefaultCollation(); 86 | $default_key_sign = DBConnection::getDefaultPrimaryKeySignOption(); 87 | $table = self::getTable(); 88 | 89 | if (!$DB->tableExists($table)) { 90 | $query = "CREATE TABLE `$table` ( 91 | `id` int {$default_key_sign} NOT NULL auto_increment, 92 | `users_id` int {$default_key_sign} NOT NULL DEFAULT '0' COMMENT 'RELATION to glpi_users (id)', 93 | `savedsearches_id` int {$default_key_sign} NOT NULL DEFAULT '0' COMMENT 'RELATION to glpi_savedsearches (id)', 94 | `last_export` timestamp NULL DEFAULT NULL, 95 | `periodicity_type` int NOT NULL DEFAULT '0', 96 | `periodicity` int {$default_key_sign} NOT NULL DEFAULT '1', 97 | `periodicity_open_days` tinyint NOT NULL DEFAULT '0', 98 | `is_active` tinyint NOT NULL DEFAULT '1', 99 | `is_deleted` tinyint NOT NULL DEFAULT '0', 100 | `sendto` VARCHAR(255) DEFAULT '', 101 | PRIMARY KEY (`id`), 102 | KEY `users_id` (`users_id`), 103 | KEY `savedsearches_id` (`savedsearches_id`) 104 | ) ENGINE=InnoDB DEFAULT CHARSET={$default_charset} COLLATE={$default_collation} ROW_FORMAT=DYNAMIC;"; 105 | 106 | $DB->doQuery($query); 107 | } 108 | 109 | if (!$DB->fieldExists($table, "sendto")) { 110 | $migration->addField($table, "sendto", "VARCHAR(255) DEFAULT ''"); 111 | $migration->migrationOneTable($table); 112 | } 113 | if ($DB->fieldExists($table, "searches_id")) { 114 | $migration->changeField($table, 'searches_id', 'savedsearches_id', "int {$default_key_sign} NOT NULL DEFAULT '0'"); 115 | $migration->migrationOneTable($table); 116 | } 117 | if (!$DB->fieldExists($table, "periodicity_type")) { 118 | $migration->addField($table, "periodicity_type", "int NOT NULL DEFAULT '0'"); 119 | $migration->migrationOneTable($table); 120 | $query = $DB->buildUpdate( 121 | $table, 122 | [ 123 | 'periodicity_type' => 0, 124 | ], 125 | [1] 126 | ); 127 | $DB->doQuery($query); 128 | } 129 | 130 | if (!$DB->fieldExists($table, "periodicity_open_days")) { 131 | $migration->addField($table, "periodicity_open_days", "tinyint NOT NULL DEFAULT '0'"); 132 | $migration->migrationOneTable($table); 133 | $query = $DB->buildUpdate( 134 | $table, 135 | [ 136 | 'periodicity_open_days' => 0, 137 | ], 138 | [1] 139 | ); 140 | $DB->doQuery($query); 141 | } 142 | 143 | CronTask::Register( 144 | Exportconfig::class, 145 | 'AutoexportsearchesExportconfigExport', 146 | DAY_TIMESTAMP, 147 | ['mode' => CronTask::MODE_EXTERNAL] 148 | ); 149 | 150 | //Displayprefs 151 | $prefs = [2 => 1, 152 | 3 => 2, 153 | 5 => 3, 154 | 6 => 4]; 155 | foreach ($prefs as $num => $rank) { 156 | if (!countElementsInTable( 157 | "glpi_displaypreferences", 158 | ['itemtype' => Exportconfig::class, 159 | 'num' => $num, 160 | 'users_id' => 0 161 | ] 162 | ) 163 | ) { 164 | $DB->insert( 165 | 'glpi_displaypreferences', 166 | ['itemtype' => Exportconfig::class, 167 | 'num' => $num, 168 | 'rank' => $rank, 169 | 'users_id' => 0, 170 | 'interface' => 'central'] 171 | ); 172 | } 173 | } 174 | 175 | $query = $DB->buildDelete( 176 | 'glpi_crontasks', 177 | ['itemtype' => ['LIKE', 'PluginAutoexportsearches' . '%']] 178 | ); 179 | $DB->doQuery($query); 180 | } 181 | 182 | public static function uninstall() 183 | { 184 | global $DB; 185 | 186 | $DB->dropTable(self::getTable(), true); 187 | 188 | $itemtypes = ['Alert', 189 | 'DisplayPreference', 190 | 'Document_Item', 191 | 'ImpactItem', 192 | 'Item_Ticket', 193 | 'Link_Itemtype', 194 | 'Notepad', 195 | 'SavedSearch', 196 | 'DropdownTranslation', 197 | 'NotificationTemplate', 198 | 'Notification']; 199 | foreach ($itemtypes as $itemtype) { 200 | $item = new $itemtype; 201 | $item->deleteByCriteria(['itemtype' => Exportconfig::class]); 202 | } 203 | } 204 | 205 | /** 206 | * @return array 207 | */ 208 | public function rawSearchOptions() 209 | { 210 | $tab = []; 211 | 212 | $tab[] = [ 213 | 'id' => 'common', 214 | 'name' => self::getTypeName(2), 215 | ]; 216 | $tab[] = [ 217 | 'id' => '1', 218 | 'table' => self::getTable(), 219 | 'field' => 'id', 220 | 'name' => __('ID'), 221 | 'massiveaction' => false, 222 | 'datatype' => 'itemlink', 223 | ]; 224 | 225 | $tab[] = [ 226 | 'id' => '2', 227 | 'table' => User::getTable(), 228 | 'field' => 'name', 229 | 'name' => __('User who owns the saved search', 'autoexportsearches'), 230 | 'datatype' => 'dropdown', 231 | ]; 232 | 233 | $tab[] = [ 234 | 'id' => '3', 235 | 'table' => SavedSearch::getTable(), 236 | 'field' => 'name', 237 | 'name' => __('Saved search to export', 'autoexportsearches'), 238 | 'datatype' => 'dropdown', 239 | 'linkfield' => 'savedsearches_id', 240 | ]; 241 | 242 | // $tab[] = [ 243 | // 'id' => '4', 244 | // 'table' => self::getTable(), 245 | // 'field' => 'periodicity_type', 246 | // 'name' => __('Periodicity type', 'autoexportsearches'), 247 | // 'datatype' => 'text' 248 | // ]; 249 | 250 | $tab[] = [ 251 | 'id' => '5', 252 | 'table' => self::getTable(), 253 | 'field' => 'last_export', 254 | 'name' => __('Last export', 'autoexportsearches'), 255 | 'datatype' => 'datetime', 256 | ]; 257 | $tab[] = [ 258 | 'id' => '6', 259 | 'table' => self::getTable(), 260 | 'field' => 'is_active', 261 | 'name' => __('Active'), 262 | 'datatype' => 'bool', 263 | ]; 264 | 265 | 266 | return $tab; 267 | } 268 | 269 | public function showForm($ID, $options = []) 270 | { 271 | global $CFG_GLPI; 272 | 273 | 274 | $this->initForm($ID, $options); 275 | $this->showFormHeader($options); 276 | 277 | echo ""; 278 | echo "" . __('Active') . ""; 279 | echo ""; 280 | Dropdown::showYesNo("is_active", $this->fields['is_active']); 281 | echo ""; 282 | echo ""; 283 | 284 | echo ""; 285 | echo "

" . __('Search to export', 'autoexportsearches') . "

"; 286 | echo ""; 287 | 288 | echo ""; 289 | echo "" . __('User who owns the saved search', 'autoexportsearches') . ""; 290 | echo ""; 291 | 292 | $rand = mt_rand(); 293 | User::dropdown([ 294 | 'name' => 'users_id', 295 | 'value' => $this->fields["users_id"], 296 | // 'entity' => $this->fields["entities_id"], 297 | 'right' => 'own_ticket', 298 | 'rand' => $rand, 299 | ]); 300 | echo ""; 301 | echo ""; 302 | 303 | echo ""; 304 | 305 | echo "" . __('Saved search to export', 'autoexportsearches') . ""; 306 | echo ""; 307 | echo ""; 308 | echo ""; 309 | $params = [ 310 | "users_id" => '__VALUE__', 311 | "current_user" => $this->fields['users_id'], 312 | 'savedsearches_id' => $this->fields["savedsearches_id"], 313 | 'exportconfigs_id' => $ID, 314 | "rand" => $rand, 315 | "action" => "loadSearches", 316 | ]; 317 | $url = PLUGINAUTOEXPORTSEARCH_WEBDIR . "/ajax/dropdownsavedsearches.php"; 318 | Ajax::updateItemOnSelectEvent("dropdown_users_id$rand", "savedSearches", $url, $params); 319 | 320 | echo " 321 | 326 | "; 327 | 328 | echo ""; 329 | echo ""; 330 | 331 | echo ""; 332 | echo "

" . __('Periodicity') . "

"; 333 | echo ""; 334 | 335 | echo ""; 336 | echo "" . __('Periodicity type', 'autoexportsearches') . ""; 337 | echo ""; 338 | 339 | $rand = mt_rand(); 340 | Dropdown::showFromArray( 341 | 'periodicity_type', 342 | [ 343 | 344 | self::PERIODICITY_DAYS => __('Every x days', 'autoexportsearches'), 345 | self::PERIODICITY_WEEKLY => _x('periodicity', 'Weekly'), 346 | self::PERIODICITY_MONTHLY => _x('periodicity', 'Monthly'), 347 | self::PERIODICITY_MINUTES => __('Every x minutes', 'autoexportsearches'), 348 | self::PERIODICITY_HOURS => __('Every x hours', 'autoexportsearches'), 349 | ], 350 | [ 351 | 'value' => $this->fields['periodicity_type'], 352 | 'rand' => $rand, 353 | ] 354 | ); 355 | echo ""; 356 | 357 | echo ""; 358 | $url = PLUGINAUTOEXPORTSEARCH_WEBDIR . "/ajax/periodicityfields.php"; 359 | // let ajax determine the fields shown depending on the choosen periodicity_type 360 | echo " 361 | 374 | "; 375 | 376 | echo ""; 377 | echo "

" . __('Options', 'autoexportsearches') . "

"; 378 | echo ""; 379 | 380 | echo ""; 381 | echo "" . __('Send mail to', 'autoexportsearches') . ""; 382 | echo ""; 383 | echo Html::input('sendto', ['type' => 'mail', 'value' => $this->fields['sendto']]); 384 | echo ""; 385 | 386 | echo ""; 387 | 388 | 389 | $this->showFormButtons($options); 390 | 391 | return true; 392 | } 393 | 394 | /** 395 | * @return array 396 | */ 397 | public static function getMenuContent() 398 | { 399 | $menu['title'] = self::getMenuName(2); 400 | $menu['page'] = self::getSearchURL(false); 401 | $menu['links']['search'] = self::getSearchURL(false); 402 | 403 | $menu['icon'] = static::getIcon(); 404 | if (self::canCreate()) { 405 | $menu['links']['add'] = PLUGINAUTOEXPORTSEARCH_WEBDIR . "/front/exportconfig.form.php"; 406 | } 407 | 408 | 409 | return $menu; 410 | } 411 | 412 | 413 | public static function getIcon() 414 | { 415 | return "ti ti-tags"; 416 | } 417 | 418 | 419 | /** 420 | * Display datas extracted from DB 421 | * 422 | * @param array $data Array of search datas prepared to get datas 423 | * 424 | * @return false 425 | **/ 426 | public static function createCSVFile(array $data, $filename) 427 | { 428 | global $CFG_GLPI; 429 | 430 | $file = fopen($filename, "w"); 431 | if (!$file) { 432 | return false; 433 | } 434 | 435 | // UTF-8 BOM pour Excel 436 | fwrite($file, pack("CCC", 0xef, 0xbb, 0xbf)); 437 | 438 | $headers = []; 439 | $metanames = []; 440 | 441 | 442 | foreach ($data['data']['cols'] as $col) { 443 | $name = $col['name']; 444 | 445 | // Gestion du groupname 446 | if (isset($col['groupname'])) { 447 | if (is_array($col['groupname'])) { 448 | $col['groupname'] = $col['groupname']['name']; 449 | } 450 | $name = $col['groupname'] . ' - ' . $name; 451 | } 452 | 453 | 454 | if ($data['itemtype'] != $col['itemtype']) { 455 | if (!isset($metanames[$col['itemtype']])) { 456 | if ($metaitem = getItemForItemtype($col['itemtype'])) { 457 | $metanames[$col['itemtype']] = $metaitem->getTypeName(); 458 | } else { 459 | $metanames[$col['itemtype']] = $col['itemtype']; 460 | } 461 | } 462 | $name = $metanames[$col['itemtype']] . ' - ' . $name; 463 | } 464 | 465 | $headers[] = $name; 466 | } 467 | 468 | 469 | if (isset($CFG_GLPI["union_search_type"][$data['itemtype']])) { 470 | $headers[] = __('Item type'); 471 | } 472 | 473 | 474 | fputcsv($file, $headers, ';'); 475 | 476 | 477 | foreach ($data['data']['rows'] as $row) { 478 | $line = []; 479 | 480 | foreach ($data['data']['cols'] as $col) { 481 | $colkey = "{$col['itemtype']}_{$col['id']}"; 482 | $value = $row[$colkey]['displayname'] ?? ''; 483 | 484 | 485 | $value = html_entity_decode(strip_tags($value), ENT_QUOTES | ENT_HTML5, 'UTF-8'); 486 | $value = preg_replace("/[\r\n]+/", ' ', $value); 487 | 488 | $line[] = trim($value); 489 | } 490 | 491 | 492 | if (isset($CFG_GLPI["union_search_type"][$data['itemtype']])) { 493 | $type = $row['TYPE'] ?? $data['itemtype']; 494 | $typename = ''; 495 | if ($item = getItemForItemtype($type)) { 496 | $typename = $item->getTypeName(); 497 | } 498 | $line[] = $typename; 499 | } 500 | 501 | fputcsv($file, $line, ';'); 502 | } 503 | 504 | fclose($file); 505 | return true; 506 | } 507 | 508 | 509 | 510 | 511 | public static function executeExport($plugin_exportconfigs_id) 512 | { 513 | global $CFG_GLPI; 514 | 515 | 516 | $export = new self(); 517 | $export->getFromDB($plugin_exportconfigs_id); 518 | 519 | $search = new SavedSearch(); 520 | $customSearchCriteria = new Customsearchcriteria(); 521 | if ($search->getFromDB($export->fields['savedsearches_id'])) { 522 | $url = "?" . $search->fields["query"]; 523 | $url_components = parse_url($url); 524 | parse_str($url_components['query'], $p); 525 | $p["display_type"] = Search::CSV_OUTPUT; 526 | $p["export_all"] = 1; 527 | $itemtype = $search->fields["itemtype"]; 528 | $weekday = date('w'); 529 | 530 | $customCriterias = $customSearchCriteria->find(['exportconfigs_id' => $plugin_exportconfigs_id]); 531 | foreach ($customCriterias as $customCriteria) { 532 | $criteria = array_filter($p['criteria'], function ($c) use ($customCriteria) { 533 | return $c['field'] == $customCriteria['criteria_field'] && $c['searchtype'] == $customCriteria['criteria_searchtype']; 534 | }); 535 | $criteria = reset($criteria); 536 | if (preg_match('/^-\d+MONTH$/', $criteria['value']) 537 | && $customCriteria['criteria_value'] == Customsearchcriteria::CRITERIA_FIRST_DAY_OF_MONTH) { 538 | $normalValue = strtotime($criteria['value']); 539 | $monthYearString = date('F Y', $normalValue); 540 | $newValue = strtotime( 541 | Customsearchcriteria::CRITERIA_FIRST_DAY_OF_MONTH, 542 | strtotime($monthYearString) 543 | ); 544 | $criteria['value'] = date('Y-m-d', $newValue) . '00:00:00'; 545 | } 546 | if (preg_match('/^-\d+WEEK$/', $criteria['value']) 547 | && $customCriteria['criteria_value'] == Customsearchcriteria::CRITERIA_FIRST_DAY_OF_WEEK) { 548 | // don't need to adjust if its already monday 549 | if ($weekday != 1) { 550 | $normalValue = strtotime($criteria['value']); 551 | $newValue = strtotime( 552 | Customsearchcriteria::CRITERIA_FIRST_DAY_OF_WEEK, 553 | $normalValue 554 | ); 555 | $criteria['value'] = date('Y-m-d', $newValue) . '00:00:00'; 556 | } 557 | } 558 | } 559 | 560 | $params = Search::manageParams($itemtype, $p, 1, 1); 561 | $name = Dropdown::getDropdownName('glpi_savedsearches', $export->fields['savedsearches_id']); 562 | $name .= "_" . date('Y_m_d_H_i_s') . ".csv"; 563 | $titleMail = $name; 564 | $filename = GLPI_PLUGIN_DOC_DIR . "/autoexportsearches/" . $name; 565 | self::createCSVFile(Search::getDatas($itemtype, $params), $filename); 566 | if (!empty($export->fields['sendto'])) { 567 | self::sendMail($titleMail, $export->fields['sendto'], $name, $filename); 568 | } 569 | } 570 | } 571 | 572 | // Cron action 573 | 574 | /** 575 | * @param $name 576 | * 577 | * @return array 578 | */ 579 | public static function cronInfo($name) 580 | { 581 | switch ($name) { 582 | case 'AutoexportsearchesExportconfigExport': 583 | return [ 584 | 'description' => __('Export saved searches', 'autoexportsearches'), 585 | ]; // Optional 586 | } 587 | return []; 588 | } 589 | 590 | public static function sendMail($title, $recipient, $filename, $filepath) 591 | { 592 | global $CFG_GLPI; 593 | 594 | $mmail = new GLPIMailer(); 595 | 596 | $mmail->AddCustomHeader("Auto-Submitted: auto-generated"); 597 | // For exchange 598 | $mmail->AddCustomHeader("X-Auto-Response-Suppress: OOF, DR, NDR, RN, NRN"); 599 | if (empty($CFG_GLPI["from_email"])) { 600 | $config = new Config(); 601 | $results = $config->find(['name' => 'from_email']); 602 | 603 | foreach ($results as $result) { 604 | $mmail->SetFrom($result['value'], $CFG_GLPI["from_email_name"], false); 605 | } 606 | } else { 607 | $mmail->SetFrom($CFG_GLPI["from_email"], $CFG_GLPI["from_email_name"], false); 608 | } 609 | 610 | $text = __('Mail autoexportsearches'); 611 | 612 | $mmail->AddAddress($recipient, $recipient); 613 | $mmail->Subject = "[GLPI] " . $title; 614 | $mmail->Body = $text; 615 | 616 | // $mmail->AddEmbeddedImage($filepath, 617 | // 0, 618 | // $filename, 619 | // 'base64', 620 | // 'text/csv'); 621 | $mmail->addAttachment( 622 | $filepath, 623 | $filename 624 | ); 625 | 626 | 627 | if (!$mmail->Send()) { 628 | Session::addMessageAfterRedirect( 629 | __('Failed to send email to ' . $recipient), 630 | false, 631 | ERROR 632 | ); 633 | GLPINetwork::addErrorMessageAfterRedirect(); 634 | return false; 635 | } else { 636 | Session::addMessageAfterRedirect(__('Mail send to ' . $recipient)); 637 | return true; 638 | } 639 | } 640 | 641 | /** 642 | * Cron action on badges : ExpiredBadges or BadgesWhichExpire 643 | * 644 | * @param $task 645 | * 646 | * 647 | * @return int 648 | */ 649 | public static function cronAutoexportsearchesExportconfigExport($task = null) 650 | { 651 | global $DB, $CFG_GLPI; 652 | if (!isset($CFG_GLPI['planning_work_days'])) { 653 | $CFG_GLPI['planning_work_days'] = importArrayFromDB($CFG_GLPI['planning_work_days']); 654 | } 655 | 656 | $cron_status = 0; 657 | $old_memory = ini_set("memory_limit", "-1"); 658 | $old_execution = ini_set("max_execution_time", "0"); 659 | $dateActual = strtotime(date("Y-m-d")); 660 | $day = date('j'); // 1 to 31, today 661 | $weekday = date('w'); // 0 to 6, today 662 | $month = date('m'); // 01 to 12, current month 663 | $monthLength = date('t'); // 28 to 31, length of the current month 664 | $exportConfig = new Exportconfig(); 665 | $exportConfigs = $exportConfig->find([ 666 | 'is_deleted' => 0, 667 | 'is_active' => 1, 668 | ]); 669 | $count = 0; 670 | $user_id_back = Session::getLoginUserID(); 671 | $user = new User(); 672 | foreach ($exportConfigs as $export) { 673 | // check if export has to be done 674 | if ($export['periodicity_type'] == self::PERIODICITY_MINUTES) { 675 | // Frequency in minutes 676 | $dateActual = strtotime(date("Y-m-d H:i:s")); 677 | $delay = 60 * intval($export['periodicity']); // Delay calculation in seconds 678 | 679 | if ($export['last_export'] != null) { 680 | $dateEnd = strtotime($export['last_export']) + $delay; 681 | if ($dateEnd > $dateActual) { 682 | continue; 683 | } 684 | } 685 | } elseif ($export['periodicity_type'] == self::PERIODICITY_HOURS) { 686 | // Periodicity in hours 687 | $dateActual = strtotime(date("Y-m-d H:i:s")); 688 | $delay = 3600 * intval($export['periodicity']); // Delay calculation in seconds 689 | if ($export['last_export'] != null) { 690 | $dateEnd = strtotime($export['last_export']) + $delay; 691 | if ($dateEnd > $dateActual) { 692 | continue; 693 | } 694 | } 695 | } elseif ($export['periodicity_type'] == self::PERIODICITY_DAYS) { 696 | // every worked day 697 | if ($export['periodicity'] == 1 && $export['periodicity_open_days'] == 1) { 698 | if (!in_array($weekday, $CFG_GLPI['planning_work_days'])) { 699 | continue; 700 | } 701 | } 702 | // regular every x days 703 | $delay = DAY_TIMESTAMP * intval($export['periodicity']); 704 | if ($export['last_export'] != null) { 705 | $dateEnd = strtotime($export['last_export']) + $delay; 706 | if ($dateEnd > $dateActual) { 707 | continue; 708 | } 709 | } 710 | } elseif ($export['periodicity_type'] == self::PERIODICITY_WEEKLY) { 711 | if ($weekday != $export['periodicity']) { 712 | continue; 713 | } 714 | } elseif ($export['periodicity_type'] == self::PERIODICITY_MONTHLY) { 715 | if ($export['last_export'] != null) { 716 | $exportMonth = date('m', strtotime($export['last_export'])); 717 | // already done for this month 718 | if ($exportMonth == $month) { 719 | continue; 720 | } 721 | // too early in the month, 722 | // second condition for shorter months 723 | if ($day < $export['periodicity'] 724 | && $export['periodicity'] <= $monthLength) { 725 | continue; 726 | } 727 | // for shorter month, do the export on the last day of the month if periodicity > duration of the current month 728 | if ($export['periodicity'] > $monthLength 729 | && $day != $monthLength) { 730 | continue; 731 | } 732 | // second condition ensure that it happens at least once a month even if the last day isn't a workday 733 | if ($export['periodicity_open_days'] == 1 && $day != $monthLength) { 734 | // today's not a work day, 735 | if (!in_array($weekday, $CFG_GLPI['planning_work_days'])) { 736 | continue; 737 | } 738 | } 739 | } else { 740 | // for the first export, only done on the exact date or the last day of the month 741 | if ($day != $export['periodicity'] && $export['periodicity'] <= $monthLength) { 742 | continue; 743 | } 744 | if ($export['periodicity'] > $monthLength && $day != $monthLength) { 745 | continue; 746 | } 747 | if ($export['periodicity_open_days'] == 1 && $day != $monthLength) { 748 | if (!in_array($weekday, $CFG_GLPI['planning_work_days'])) { 749 | continue; 750 | } 751 | } 752 | } 753 | } 754 | 755 | $_SESSION["glpicronuserrunning"] = $export['users_id']; 756 | $_SESSION['glpidefault_entity'] = 0; 757 | Session::initEntityProfiles($export['users_id']); 758 | $user = new User(); 759 | $user->getFromDB($export['users_id']); 760 | $profile = new Profile(); 761 | $savedProfile = $_SESSION['glpiactiveprofile'] ?? 0; 762 | if ($profile->getFromDB($user->fields['profiles_id'])) { 763 | $_SESSION['glpiactiveprofile'] = $profile->fields; 764 | } 765 | $_SESSION['glpiname'] = 'crontab'; 766 | $_SESSION['glpiactiveentities'] = getSonsOf('glpi_entities', 0); 767 | $user->getFromDB($export['users_id']); 768 | $auth = new Auth(); 769 | $auth->auth_succeded = true; 770 | $auth->user = $user; 771 | Session::init($auth); 772 | Session::loadGroups(); 773 | $user = new User(); 774 | $user->getFromDB($export['users_id']); 775 | $_SESSION["glpiID"] = $export['users_id']; 776 | $_SESSION["glpicronuserrunning"] = $export['users_id']; 777 | self::executeExport($export['id']); 778 | $_SESSION['glpiactiveprofile'] = $savedProfile; 779 | 780 | // add custom criterias for call to createCriterias 781 | $customSearchCriteria = new Customsearchcriteria(); 782 | $customCriterias = $customSearchCriteria->find(['exportconfigs_id' => $export['id']]); 783 | $export['custom_criterias'] = $customCriterias; 784 | if ($export['periodicity_type'] == self::PERIODICITY_MINUTES || $export['periodicity_type'] == self::PERIODICITY_HOURS) { 785 | $export['last_export'] = date("Y-m-d H:i:s"); 786 | } else { 787 | $export['last_export'] = date("Y-m-d"); 788 | } 789 | 790 | $exportConfig->update($export); 791 | $count++; 792 | } 793 | $user->getFromDB($user_id_back); 794 | $auth = new Auth(); 795 | $auth->auth_succeded = true; 796 | $auth->user = $user; 797 | Session::init($auth); 798 | ini_set("memory_limit", $old_memory); 799 | ini_set("max_execution_time", $old_execution); 800 | $task->addVolume($count); 801 | 802 | return true; 803 | } 804 | } 805 | --------------------------------------------------------------------------------