mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-19 16:48:42 +02:00
N°2249 - Supportability - Updater module (application upgrade)
This commit is contained in:
@@ -16,7 +16,10 @@
|
||||
<module>itop-profiles-itil</module>
|
||||
<module>itop-welcome-itil</module>
|
||||
<module>itop-tickets</module>
|
||||
<module>itop-hub-connector</module>
|
||||
<module>itop-files-information</module>
|
||||
<module>itop-twig-base</module>
|
||||
<module>combodo-db-tools</module>
|
||||
<module>itop-core-update</module>
|
||||
</modules>
|
||||
<mandatory>true</mandatory>
|
||||
</choice>
|
||||
|
||||
6
datamodels/2.x/itop-core-update/README.md
Normal file
6
datamodels/2.x/itop-core-update/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# itop-core-update
|
||||
Application upgrade
|
||||
|
||||
This feature allows administrators to upgrade the application from a downloaded package.
|
||||
|
||||
namespace Combodo\iTop\CoreUpdate;
|
||||
21
datamodels/2.x/itop-core-update/ajax.php
Normal file
21
datamodels/2.x/itop-core-update/ajax.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\CoreUpdate;
|
||||
|
||||
use Combodo\iTop\CoreUpdate\Controller\AjaxController;
|
||||
use ContextTag;
|
||||
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
new ContextTag('Setup');
|
||||
|
||||
$oUpdateController = new AjaxController();
|
||||
$oUpdateController->DisableInDemoMode();
|
||||
$oUpdateController->AllowOnlyAdmin();
|
||||
|
||||
// Allow parallel execution of ajax requests
|
||||
session_write_close();
|
||||
$oUpdateController->HandleOperation();
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
|
||||
<menus>
|
||||
<menu id="iTopUpdate" xsi:type="WebPageMenuNode" _delta="define">
|
||||
<rank>60</rank>
|
||||
<parent>System</parent>
|
||||
<url>$pages/exec.php?exec_module=itop-core-update&exec_page=index.php&c[menu]=iTopUpdate&maintenance=true</url>
|
||||
<enable_admin_only>1</enable_admin_only>
|
||||
</menu>
|
||||
</menus>
|
||||
</itop_design>
|
||||
108
datamodels/2.x/itop-core-update/en.dict.itop-core-update.php
Normal file
108
datamodels/2.x/itop-core-update/en.dict.itop-core-update.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
Dict::Add('EN US', 'English', 'English', array(
|
||||
'iTopUpdate:UI:PageTitle' => 'Application Upgrade',
|
||||
'itop-core-update:UI:SelectUpdateFile' => 'Application Upgrade',
|
||||
'itop-core-update:UI:ConfirmUpdate' => 'Application Upgrade',
|
||||
'itop-core-update:UI:UpdateCoreFiles' => 'Application Upgrade',
|
||||
'itop-core-update:UI:UpdateDone' => 'Application Upgrade',
|
||||
|
||||
'iTopUpdate:UI:SelectUpdateFile' => 'Select an upgrade file to upload',
|
||||
'iTopUpdate:UI:CheckUpdate' => 'Verify upgrade file',
|
||||
'iTopUpdate:UI:ConfirmInstallFile' => 'You are about to install %1$s',
|
||||
'iTopUpdate:UI:DoUpdate' => 'Upgrade',
|
||||
'iTopUpdate:UI:CurrentVersion' => 'Current installed version',
|
||||
'iTopUpdate:UI:Back' => 'Back',
|
||||
'iTopUpdate:UI:Cancel' => 'Cancel',
|
||||
'iTopUpdate:UI:Continue' => 'Continue',
|
||||
'iTopUpdate:UI:WithDBBackup' => 'Database backup',
|
||||
'iTopUpdate:UI:WithFilesBackup' => 'Application files backup',
|
||||
'iTopUpdate:UI:WithoutBackup' => 'No backup is planned',
|
||||
'iTopUpdate:UI:Backup' => 'Backup generated before update',
|
||||
'iTopUpdate:UI:DoFilesArchive' => 'Archive application files',
|
||||
'iTopUpdate:UI:UploadArchive' => 'Select a package to upload',
|
||||
'iTopUpdate:UI:ServerFile' => 'Path of a package already on the server',
|
||||
|
||||
'iTopUpdate:UI:Status' => 'Status',
|
||||
'iTopUpdate:UI:Action' => 'Update',
|
||||
'iTopUpdate:UI:History' => 'Versions History',
|
||||
'iTopUpdate:UI:Progress' => 'Progress of the upgrade',
|
||||
|
||||
'iTopUpdate:UI:DoBackup:Label' => 'Backup files and database',
|
||||
'iTopUpdate:UI:DoBackup:Warning' => 'Backup is not recommended due to limited available disk space',
|
||||
|
||||
'iTopUpdate:UI:DiskFreeSpace' => 'Disk free space',
|
||||
'iTopUpdate:UI:ItopDiskSpace' => 'iTop disk space',
|
||||
'iTopUpdate:UI:DBDiskSpace' => 'Database disk space',
|
||||
'iTopUpdate:UI:FileUploadMaxSize' => 'File upload max size',
|
||||
|
||||
'iTopUpdate:UI:PostMaxSize' => 'PHP ini value post_max_size: %1$s',
|
||||
'iTopUpdate:UI:UploadMaxFileSize' => 'PHP ini value upload_max_filesize: %1$s',
|
||||
|
||||
'iTopUpdate:UI:CanCoreUpdate:Loading' => 'Checking filesystem',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Error' => 'Checking filesystem failed (%1$s)',
|
||||
'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Checking filesystem failed (File not exist %1$s)',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Checking filesystem failed',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated',
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start',
|
||||
'iTopUpdate:UI:SetupMessage:EnterMaintenance' => 'Entering maintenance mode',
|
||||
'iTopUpdate:UI:SetupMessage:Backup' => 'Database backup',
|
||||
'iTopUpdate:UI:SetupMessage:FilesArchive' => 'Archive application files',
|
||||
'iTopUpdate:UI:SetupMessage:CopyFiles' => 'Copy new version files',
|
||||
'iTopUpdate:UI:SetupMessage:Compile' => 'Upgrade application and database',
|
||||
'iTopUpdate:UI:SetupMessage:ExitMaintenance' => 'Exiting maintenance mode',
|
||||
'iTopUpdate:UI:SetupMessage:UpdateDone' => 'Upgrade completed',
|
||||
|
||||
// Errors
|
||||
'iTopUpdate:Error:MissingFunction' => 'Impossible to start upgrade, missing function',
|
||||
'iTopUpdate:Error:MissingFile' => 'Missing file: %1$s',
|
||||
'iTopUpdate:Error:CorruptedFile' => 'File %1$s is corrupted',
|
||||
'iTopUpdate:Error:BadFileFormat' => 'Upgrade file is not a zip file',
|
||||
'iTopUpdate:Error:BadFileContent' => 'Upgrade file is not an application archive',
|
||||
'iTopUpdate:Error:BadItopProduct' => 'Upgrade file is not compatible with your application',
|
||||
'iTopUpdate:Error:Copy' => 'Error, cannot copy \'%1$s\' to \'%2$s\'',
|
||||
'iTopUpdate:Error:FileNotFound' => 'File not found',
|
||||
'iTopUpdate:Error:NoFile' => 'No file provided',
|
||||
'iTopUpdate:Error:InvalidToken' => 'Invalid token',
|
||||
'iTopUpdate:Error:UpdateFailed' => 'Upgrade failed ',
|
||||
'iTopUpdate:Error:FileUploadMaxSizeTooSmall' => 'The upload max size seems too small for update. Please change the PHP configuration.',
|
||||
|
||||
'iTopUpdate:UI:RestoreArchive' => 'You can restore your application from the archive \'%1$s\'',
|
||||
'iTopUpdate:UI:RestoreBackup' => 'You can restore the database from \'%1$s\'',
|
||||
'iTopUpdate:UI:MaintenanceModeActive' => 'The application is currently under maintenance, no user can access the application. You have to run a setup or restore the application archive to return in normal mode.',
|
||||
'iTopUpdate:UI:UpdateDone' => 'Upgrade successful',
|
||||
'Menu:iTopUpdate' => 'Application Upgrade',
|
||||
'Menu:iTopUpdate+' => 'Application Upgrade',
|
||||
|
||||
// Missing itop entries
|
||||
'Class:ModuleInstallation/Attribute:installed' => 'Installed on',
|
||||
'Class:ModuleInstallation/Attribute:name' => 'Name',
|
||||
'Class:ModuleInstallation/Attribute:version' => 'Version',
|
||||
'Class:ModuleInstallation/Attribute:comment' => 'Comment',
|
||||
));
|
||||
|
||||
|
||||
108
datamodels/2.x/itop-core-update/fr.dict.itop-core-update.php
Normal file
108
datamodels/2.x/itop-core-update/fr.dict.itop-core-update.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'iTopUpdate:UI:PageTitle' => 'Mise à jour de l\'application',
|
||||
'itop-core-update:UI:SelectUpdateFile' => 'Mise à jour',
|
||||
'itop-core-update:UI:ConfirmUpdate' => 'Mise à jour',
|
||||
'itop-core-update:UI:UpdateCoreFiles' => 'Mise à jour',
|
||||
'iTopUpdate:UI:SelectUpdateFile' => 'Sélectionner un fichier de mise à jour',
|
||||
'iTopUpdate:UI:CheckUpdate' => 'Vérification de la mise à jour',
|
||||
'iTopUpdate:UI:ConfirmInstallFile' => 'La mise à jour %1$s va être installée',
|
||||
'iTopUpdate:UI:DoUpdate' => 'Mettre à jour',
|
||||
'iTopUpdate:UI:DoBackup' => 'Faire une sauvegarde des fichiers et de la base',
|
||||
'iTopUpdate:UI:CurrentVersion' => 'Version installée',
|
||||
'iTopUpdate:UI:Back' => 'Annuler',
|
||||
'iTopUpdate:UI:WithBackup' => 'Avec sauvegarde de l\'application, l\'archive sera dans \'%1$s\'',
|
||||
'iTopUpdate:UI:WithoutBackup' => 'Pas de sauvegarde',
|
||||
'iTopUpdate:UI:Status' => 'Versions installées',
|
||||
'iTopUpdate:UI:InstallationCanBeUpdated' => 'L\'application peut être mise à jour',
|
||||
'iTopUpdate:UI:InstallationCanNotBeUpdated' => 'L\'application ne peut pas être mise à jour',
|
||||
'iTopUpdate:UI:DiskFreeSpace' => 'Taille disque disponible',
|
||||
'iTopUpdate:UI:ItopDiskSpace' => 'Taille disque utilisée par l\'application',
|
||||
'iTopUpdate:UI:DBDiskSpace' => 'Taille disque utilisée par la base de données',
|
||||
'iTopUpdate:UI:FileUploadMaxSize' => 'Taille maximale de chargement de fichier',
|
||||
|
||||
'iTopUpdate:UI:PostMaxSize' => 'Valeur PHP ini post_max_size : %1$s',
|
||||
'iTopUpdate:UI:UploadMaxFileSize' => 'Valeur PHP ini upload_max_filesize : %1$s',
|
||||
|
||||
'iTopUpdate:UI:Cancel' => 'Annuler',
|
||||
'iTopUpdate:UI:Continue' => 'Continuer',
|
||||
'iTopUpdate:UI:WithDBBackup' => 'Sauvegarde de la base de données',
|
||||
'iTopUpdate:UI:WithFilesBackup' => 'Archive des fichiers de l\'application' ,
|
||||
'iTopUpdate:UI:Backup' => 'Sauvegarde effectuée avant la mise à jour',
|
||||
'iTopUpdate:UI:DoFilesArchive' => 'Archive les fichiers de l\'application',
|
||||
|
||||
'iTopUpdate:UI:Action' => 'Mettre à jour',
|
||||
'iTopUpdate:UI:History' => 'Historique des versions',
|
||||
'iTopUpdate:UI:Progress' => 'Progression de la mise à jour',
|
||||
'iTopUpdate:UI:DoBackup:Label' => 'Sauvegarde de la base de données',
|
||||
'iTopUpdate:UI:DoBackup:Warning' => 'La sauvegarde n\'est pas conseillée à cause du manque de place disque disponible',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Loading' => 'Vérification des fichiers',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Error' => 'Échec de la vérification des fichiers (%1$s)',
|
||||
'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => 'Échec de la vérification des fichiers (Fichier manquant %1$s)',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Failed' => 'Échec de la vérification des fichiers',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Yes' => 'L\'application peut être mise à jour',
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'L\'application ne peut pas être mise à jour : %1$s',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Prêt pour l\installation',
|
||||
'iTopUpdate:UI:SetupMessage:EnterMaintenance' => 'Application en maintenance',
|
||||
'iTopUpdate:UI:SetupMessage:Backup' => 'Sauvegarde des fichiers de l\'application',
|
||||
'iTopUpdate:UI:SetupMessage:FilesArchive' => 'Archivage des fichiers de l\'application',
|
||||
'iTopUpdate:UI:SetupMessage:CopyFiles' => 'Copie des fichiers de la nouvelle version',
|
||||
'iTopUpdate:UI:SetupMessage:Compile' => 'Mise à jour de l\'application et de la base de données',
|
||||
'iTopUpdate:UI:SetupMessage:ExitMaintenance' => 'Application en utilisation normale',
|
||||
'iTopUpdate:UI:SetupMessage:UpdateDone' => 'Installation terminée',
|
||||
|
||||
// Errors
|
||||
'iTopUpdate:Error:MissingFunction' => 'Impossible de mettre à jour',
|
||||
'iTopUpdate:Error:MissingFile' => 'Ficher manquant : %1$s',
|
||||
'iTopUpdate:Error:CorruptedFile' => 'Le fichier %1$s est corrompu',
|
||||
'iTopUpdate:Error:BadFileFormat' => 'Le fichier de mise à jour n\'est pas au format "zip"' ,
|
||||
'iTopUpdate:Error:Copy' => 'Erreur : impossible de copier le fichier \'%1$s\' dans \'%2$s\'',
|
||||
'iTopUpdate:Error:FileNotFound' => 'Fichier manquant',
|
||||
'iTopUpdate:Error:InvalidToken' => 'Information manquante',
|
||||
'iTopUpdate:Error:NoUpdate' => 'La mise à jour a échoué',
|
||||
'iTopUpdate:Error:FileUploadMaxSizeTooSmall' => 'La taille maximale de chargement de fichier semble trop petite pour faire la mise à jour. Veuillez changer la configuration de PHP.',
|
||||
|
||||
'iTopUpdate:UI:RestoreArchive' => 'Vous pouvez restaurer l\'application depuis \'%1$s\'',
|
||||
'iTopUpdate:UI:RestoreBackup' => 'Vous pouvez restaurer la base de données depuis \'%1$s\'',
|
||||
'iTopUpdate:UI:MaintenanceModeActive' => 'L\'application est actuellement en maintenance, aucun utilisateur n\'a accès à l\'application. Vous pouvez lancer un setup ou réinstaller l\'application depuis une archive pour retourner dans un mode normal.',
|
||||
|
||||
'iTopUpdate:UI:UpdateDone' => 'Mise à jour effectuée',
|
||||
'Menu:iTopUpdate' => 'Mise à jour de l\'application',
|
||||
'Menu:iTopUpdate+' => 'Mise à jour de l\'application',
|
||||
'iTopUpdate:Error:BadFileContent' => 'Le fichier n\'est pas une archive valide',
|
||||
'iTopUpdate:Error:BadItopProduct' => 'L\'archive n\'est pas compatible avec votre application',
|
||||
'iTopUpdate:Error:NoFile' => 'Pas d\'archive',
|
||||
'iTopUpdate:Error:UpdateFailed' => 'La mise à jour à échoué ',
|
||||
|
||||
// Missing itop entries
|
||||
'Class:ModuleInstallation/Attribute:installed' => 'Installé le',
|
||||
'Class:ModuleInstallation/Attribute:name' => 'Nom',
|
||||
'Class:ModuleInstallation/Attribute:version' => 'Version',
|
||||
'Class:ModuleInstallation/Attribute:comment' => 'Commentaire',
|
||||
));
|
||||
|
||||
|
||||
19
datamodels/2.x/itop-core-update/index.php
Normal file
19
datamodels/2.x/itop-core-update/index.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\CoreUpdate;
|
||||
|
||||
use Combodo\iTop\CoreUpdate\Controller\UpdateController;
|
||||
use ContextTag;
|
||||
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
new ContextTag('Setup');
|
||||
|
||||
$oUpdateController = new UpdateController();
|
||||
$oUpdateController->DisableInDemoMode();
|
||||
$oUpdateController->AllowOnlyAdmin();
|
||||
$oUpdateController->SetDefaultOperation('SelectUpdateFile');
|
||||
$oUpdateController->HandleOperation();
|
||||
52
datamodels/2.x/itop-core-update/module.itop-core-update.php
Normal file
52
datamodels/2.x/itop-core-update/module.itop-core-update.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
//
|
||||
// iTop module definition file
|
||||
//
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-core-update/1.0.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
'label' => 'iTop Core Update',
|
||||
'category' => 'business',
|
||||
|
||||
// Setup
|
||||
//
|
||||
'dependencies' => array(
|
||||
'itop-twig-base/1.0.0',
|
||||
'itop-files-information/1.0.0',
|
||||
'combodo-db-tools/1.0.8',
|
||||
),
|
||||
'mandatory' => false,
|
||||
'visible' => true,
|
||||
|
||||
// Components
|
||||
//
|
||||
'datamodel' => array(
|
||||
'model.itop-core-update.php',
|
||||
'src/Service/RunTimeEnvironmentCoreUpdater.php',
|
||||
'src/Service/CoreUpdater.php',
|
||||
'src/Controller/UpdateController.php',
|
||||
'src/Controller/AjaxController.php',
|
||||
),
|
||||
'webservice' => array(),
|
||||
'data.struct' => array(),
|
||||
'data.sample' => array(),
|
||||
|
||||
// Documentation
|
||||
//
|
||||
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any
|
||||
'doc.more_information' => '', // hyperlink to more information, if any
|
||||
|
||||
// Default settings
|
||||
//
|
||||
'settings' => array(),
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
namespace Combodo\iTop\CoreUpdate\Controller;
|
||||
|
||||
|
||||
use Combodo\iTop\CoreUpdate\Service\CoreUpdater;
|
||||
use Combodo\iTop\DBTools\Service\DBToolsUtils;
|
||||
use Combodo\iTop\FilesInformation\Service\FileNotExistException;
|
||||
use Combodo\iTop\FilesInformation\Service\FilesInformation;
|
||||
use Combodo\iTop\TwigBase\Controller\Controller;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use MetaModel;
|
||||
use SetupUtils;
|
||||
use utils;
|
||||
|
||||
class AjaxController extends Controller
|
||||
{
|
||||
const LOCAL_DIR = __DIR__;
|
||||
|
||||
public function OperationCanUpdateCore()
|
||||
{
|
||||
$aParams = array();
|
||||
|
||||
try
|
||||
{
|
||||
$bCanUpdateCore = FilesInformation::CanUpdateCore($sMessage);
|
||||
$aParams['bStatus'] = $bCanUpdateCore;
|
||||
if ($bCanUpdateCore)
|
||||
{
|
||||
$aParams['sMessage'] = Dict::S('iTopUpdate:UI:CanCoreUpdate:Yes');
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams['sMessage'] = Dict::Format('iTopUpdate:UI:CanCoreUpdate:No', $sMessage);
|
||||
}
|
||||
}
|
||||
catch (FileNotExistException $e)
|
||||
{
|
||||
$aParams['bStatus'] = false;
|
||||
$aParams['sMessage'] = Dict::Format('iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist', $e->getMessage());
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$aParams['bStatus'] = false;
|
||||
$aParams['sMessage'] = Dict::Format('iTopUpdate:UI:CanCoreUpdate:Error', $e->getMessage());
|
||||
}
|
||||
|
||||
$this->DisplayJSONPage($aParams);
|
||||
}
|
||||
|
||||
public function OperationGetItopDiskSpace()
|
||||
{
|
||||
$aParams = array();
|
||||
$aParams['iItopDiskSpace'] = FilesInformation::GetItopDiskSpace();
|
||||
$aParams['sItopDiskSpace'] = utils::BytesToFriendlyFormat($aParams['iItopDiskSpace']);
|
||||
$this->DisplayJSONPage($aParams);
|
||||
}
|
||||
|
||||
public function OperationGetDBDiskSpace()
|
||||
{
|
||||
$aParams = array();
|
||||
$aParams['iDBDiskSpace'] = DBToolsUtils::GetDatabaseSize();
|
||||
$aParams['sDBDiskSpace'] = utils::BytesToFriendlyFormat($aParams['iDBDiskSpace']);
|
||||
$this->DisplayJSONPage($aParams);
|
||||
}
|
||||
|
||||
public function OperationGetCurrentVersion()
|
||||
{
|
||||
$aParams = array();
|
||||
$aParams['sVersion'] = Dict::Format('UI:iTopVersion:Long', ITOP_APPLICATION, ITOP_VERSION, ITOP_REVISION, ITOP_BUILD_DATE);
|
||||
$this->DisplayJSONPage($aParams);
|
||||
}
|
||||
|
||||
public function OperationEnterMaintenance()
|
||||
{
|
||||
$aParams = array();
|
||||
try
|
||||
{
|
||||
SetupUtils::EnterReadOnlyMode(MetaModel::GetConfig());
|
||||
$iResponseCode = 200;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error("EnterMaintenance: ".$e->getMessage());
|
||||
$aParams['sError'] = $e->getMessage();
|
||||
$iResponseCode = 500;
|
||||
}
|
||||
$this->DisplayJSONPage($aParams, $iResponseCode);
|
||||
}
|
||||
|
||||
public function OperationExitMaintenance()
|
||||
{
|
||||
$aParams = array();
|
||||
try
|
||||
{
|
||||
SetupUtils::ExitReadOnlyMode();
|
||||
$iResponseCode = 200;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error("ExitMaintenance: ".$e->getMessage());
|
||||
$aParams['sError'] = $e->getMessage();
|
||||
$iResponseCode = 500;
|
||||
}
|
||||
$this->DisplayJSONPage($aParams, $iResponseCode);
|
||||
}
|
||||
|
||||
public function OperationBackup()
|
||||
{
|
||||
$aParams = array();
|
||||
try
|
||||
{
|
||||
CoreUpdater::Backup();
|
||||
$iResponseCode = 200;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error("Backup: ".$e->getMessage());
|
||||
$aParams['sError'] = $e->getMessage();
|
||||
$iResponseCode = 500;
|
||||
}
|
||||
$this->DisplayJSONPage($aParams, $iResponseCode);
|
||||
}
|
||||
|
||||
public function OperationFilesArchive()
|
||||
{
|
||||
$aParams = array();
|
||||
try
|
||||
{
|
||||
CoreUpdater::CreateItopArchive();
|
||||
$iResponseCode = 200;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error("FilesArchive: ".$e->getMessage());
|
||||
$aParams['sError'] = $e->getMessage();
|
||||
$iResponseCode = 500;
|
||||
}
|
||||
$this->DisplayJSONPage($aParams, $iResponseCode);
|
||||
}
|
||||
|
||||
public function OperationCopyFiles()
|
||||
{
|
||||
$aParams = array();
|
||||
try
|
||||
{
|
||||
CoreUpdater::CopyCoreFiles();
|
||||
$iResponseCode = 200;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error("CopyFiles: ".$e->getMessage());
|
||||
$aParams['sError'] = $e->getMessage();
|
||||
$iResponseCode = 500;
|
||||
}
|
||||
|
||||
$this->DisplayJSONPage($aParams, $iResponseCode);
|
||||
}
|
||||
|
||||
public function OperationCompile()
|
||||
{
|
||||
$aParams = array();
|
||||
try
|
||||
{
|
||||
CoreUpdater::Compile();
|
||||
$iResponseCode = 200;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error("Compile: ".$e->getMessage());
|
||||
$aParams['sError'] = $e->getMessage();
|
||||
$iResponseCode = 500;
|
||||
}
|
||||
|
||||
$this->DisplayJSONPage($aParams, $iResponseCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\CoreUpdate\Controller;
|
||||
|
||||
use Combodo\iTop\CoreUpdate\Service\CoreUpdater;
|
||||
use Combodo\iTop\DBTools\Service\DBToolsUtils;
|
||||
use Combodo\iTop\TwigBase\Controller\Controller;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use SetupUtils;
|
||||
use utils;
|
||||
|
||||
class UpdateController extends Controller
|
||||
{
|
||||
const LOCAL_DIR = __DIR__;
|
||||
|
||||
public function OperationSelectUpdateFile()
|
||||
{
|
||||
$sTransactionId = utils::GetNewTransactionId();
|
||||
$aParams = array();
|
||||
$aParams['sTransactionId'] = $sTransactionId;
|
||||
$aParams['aPreviousInstall'] = $this->GetPreviousInstallations();
|
||||
$aParams['sAjaxURL'] = utils::GetAbsoluteUrlModulePage('itop-core-update', 'ajax.php', array('maintenance' => 'true'));
|
||||
$aParams['iDiskFreeSpace'] = disk_free_space(APPROOT);
|
||||
$aParams['sDiskFreeSpace'] = utils::BytesToFriendlyFormat($aParams['iDiskFreeSpace']);
|
||||
$aParams['iFileUploadMaxSize'] = $this->GetFileUploadMaxSize();
|
||||
$aParams['sFileUploadMaxSize'] = utils::BytesToFriendlyFormat($aParams['iFileUploadMaxSize']);
|
||||
$aParams['sPostMaxSize'] = ini_get('post_max_size');
|
||||
$aParams['sUploadMaxSize'] = ini_get('upload_max_filesize');
|
||||
|
||||
$this->DisplayPage($aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function OperationConfirmUpdate()
|
||||
{
|
||||
$sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id');
|
||||
if (!utils::IsTransactionValid($sTransactionId))
|
||||
{
|
||||
throw new Exception(Dict::S('iTopUpdate:Error:InvalidToken'));
|
||||
}
|
||||
|
||||
$bDoBackup = utils::ReadPostedParam('doBackup', 0, 'integer') == 1;
|
||||
$bDoFilesArchive = utils::ReadPostedParam('doFilesArchive', 0, 'integer') == 1;
|
||||
|
||||
$sNewVersion = null;
|
||||
$sName = '';
|
||||
$sVersionToInstall = '';
|
||||
$sError = '';
|
||||
try
|
||||
{
|
||||
if (isset($_FILES['file']))
|
||||
{
|
||||
$aFileInfo = $_FILES['file'];
|
||||
$iError = $aFileInfo['error'];
|
||||
if ($iError === UPLOAD_ERR_OK)
|
||||
{
|
||||
$sDownloadDir = CoreUpdater::DOWNLOAD_DIR;
|
||||
if (is_dir($sDownloadDir))
|
||||
{
|
||||
SetupUtils::rrmdir($sDownloadDir);
|
||||
}
|
||||
SetupUtils::builddir($sDownloadDir);
|
||||
$sTmpName = $aFileInfo['tmp_name'];
|
||||
$sName = $aFileInfo['name'];
|
||||
$sNewVersion = $sDownloadDir.$sName;
|
||||
if (@move_uploaded_file($sTmpName, $sNewVersion) === false)
|
||||
{
|
||||
throw new Exception(Dict::S('iTopUpdate:Error:FileNotFound'));
|
||||
}
|
||||
CoreUpdater::ExtractDownloadedFile($sNewVersion);
|
||||
$sVersionToInstall = CoreUpdater::GetVersionToInstall();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(Dict::S('iTopUpdate:Error:NoFile'));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(Dict::S('iTopUpdate:Error:NoFile'));
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$iError = UPLOAD_ERR_NO_FILE;
|
||||
$sError = $e->getMessage();
|
||||
}
|
||||
|
||||
|
||||
$aParams = array();
|
||||
$aParams['sName'] = $sName;
|
||||
$aParams['bSuccess'] = ($iError == 0);
|
||||
$aParams['sError'] = $sError;
|
||||
|
||||
$aParams['bDoBackup'] = $bDoBackup;
|
||||
$aParams['bDoFilesArchive'] = $bDoFilesArchive;
|
||||
$aParams['sItopArchive'] = CoreUpdater::GetItopArchiveFile();
|
||||
$aParams['sBackupFile'] = CoreUpdater::GetBackupFile();
|
||||
|
||||
$sQuestion = Dict::Format('iTopUpdate:UI:ConfirmInstallFile', $sVersionToInstall);
|
||||
$aParams['sQuestion'] = $sQuestion;
|
||||
|
||||
$sTransactionId = utils::GetNewTransactionId();
|
||||
$aParams['sTransactionId'] = $sTransactionId;
|
||||
|
||||
$this->DisplayPage($aParams);
|
||||
}
|
||||
|
||||
public function OperationUpdateCoreFiles()
|
||||
{
|
||||
$sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id');
|
||||
if (!utils::IsTransactionValid($sTransactionId))
|
||||
{
|
||||
throw new Exception(Dict::S('iTopUpdate:Error:InvalidToken'));
|
||||
}
|
||||
|
||||
$sNewVersion = utils::ReadPostedParam('filename', null, 'filename');
|
||||
$bDoBackup = utils::ReadPostedParam('doBackup', 0, 'integer') == 1;
|
||||
$bDoFilesArchive = utils::ReadPostedParam('doFilesArchive', 0, 'integer') == 1;
|
||||
$sCurrentVersion = Dict::Format('UI:iTopVersion:Long', ITOP_APPLICATION, ITOP_VERSION, ITOP_REVISION, ITOP_BUILD_DATE);
|
||||
|
||||
$aParams = array(
|
||||
'sCurrentVersion' => $sCurrentVersion,
|
||||
'bDoBackup' => $bDoBackup,
|
||||
'sBackupFile' => CoreUpdater::GetBackupFile(),
|
||||
'bDoFilesArchive' => $bDoFilesArchive,
|
||||
'sItopArchive' => CoreUpdater::GetItopArchiveFile(),
|
||||
'sNewVersion' => $sNewVersion,
|
||||
'sProgressImage' => utils::GetAbsoluteUrlAppRoot().'setup/orange-progress.gif',
|
||||
'sSetupToken' => SetupUtils::CreateSetupToken(),
|
||||
'sAjaxURL' => utils::GetAbsoluteUrlModulePage('itop-core-update', 'ajax.php', array('maintenance' => 'true')),
|
||||
);
|
||||
$this->AddLinkedScript(utils::GetAbsoluteUrlAppRoot().'setup/jquery.progression.js');
|
||||
|
||||
$this->DisplayPage($aParams);
|
||||
}
|
||||
|
||||
private function GetPreviousInstallations()
|
||||
{
|
||||
return DBToolsUtils::GetPreviousInstallations();
|
||||
}
|
||||
|
||||
// Returns a file size limit in bytes based on the PHP upload_max_filesize
|
||||
// and post_max_size
|
||||
private function GetFileUploadMaxSize()
|
||||
{
|
||||
static $iMaxSize = -1;
|
||||
|
||||
if ($iMaxSize < 0)
|
||||
{
|
||||
// Start with post_max_size.
|
||||
$iPostMaxSize = $this->ParseSize(ini_get('post_max_size'));
|
||||
if ($iPostMaxSize > 0)
|
||||
{
|
||||
$iMaxSize = $iPostMaxSize;
|
||||
}
|
||||
|
||||
// If upload_max_size is less, then reduce. Except if upload_max_size is
|
||||
// zero, which indicates no limit.
|
||||
$iUploadMax = $this->ParseSize(ini_get('upload_max_filesize'));
|
||||
if ($iUploadMax > 0 && $iUploadMax < $iMaxSize)
|
||||
{
|
||||
$iMaxSize = $iUploadMax;
|
||||
}
|
||||
}
|
||||
return $iMaxSize;
|
||||
}
|
||||
|
||||
private function ParseSize($iSize)
|
||||
{
|
||||
$sUnit = preg_replace('/[^bkmgtpezy]/i', '', $iSize); // Remove the non-unit characters from the size.
|
||||
$iSize = preg_replace('/[^0-9.]/', '', $iSize); // Remove the non-numeric characters from the size.
|
||||
if ($sUnit)
|
||||
{
|
||||
// Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
|
||||
return round($iSize * pow(1024, stripos('bkmgtpezy', $sUnit[0])));
|
||||
}
|
||||
else
|
||||
{
|
||||
return round($iSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
522
datamodels/2.x/itop-core-update/src/Service/CoreUpdater.php
Normal file
522
datamodels/2.x/itop-core-update/src/Service/CoreUpdater.php
Normal file
@@ -0,0 +1,522 @@
|
||||
<?php
|
||||
/**
|
||||
* iTop
|
||||
* @copyright Copyright (C) 2010,2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\CoreUpdate\Service;
|
||||
|
||||
use Combodo\iTop\FilesInformation\Service\FilesIntegrity;
|
||||
use DBBackup;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use iTopExtension;
|
||||
use iTopExtensionsMap;
|
||||
use iTopMutex;
|
||||
use MetaModel;
|
||||
use SetupUtils;
|
||||
use utils;
|
||||
use ZipArchive;
|
||||
|
||||
require_once APPROOT.'setup/applicationinstaller.class.inc.php';
|
||||
require_once APPROOT.'setup/runtimeenv.class.inc.php';
|
||||
|
||||
final class CoreUpdater
|
||||
{
|
||||
const DOWNLOAD_DIR = APPROOT.'data/downloaded-core/';
|
||||
const UPDATE_DIR = APPROOT.'data/core-update/';
|
||||
|
||||
/**
|
||||
* @param bool $bDoBackup
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function CopyCoreFiles()
|
||||
{
|
||||
set_time_limit(600);
|
||||
|
||||
// Extract updater file from the new version if available
|
||||
if (is_file(APPROOT.'setup/appupgradecopy.php'))
|
||||
{
|
||||
// Remove previous specific updater
|
||||
@unlink(APPROOT.'setup/appupgradecopy.php');
|
||||
}
|
||||
if (is_file(self::UPDATE_DIR.'web/setup/appupgradecopy.php'))
|
||||
{
|
||||
IssueLog::Info('itop-core-update: Use updater provided in the archive');
|
||||
self::CopyFile(self::UPDATE_DIR.'web/setup/appupgradecopy.php', APPROOT.'setup/appupgradecopy.php');
|
||||
@include_once(APPROOT.'setup/appupgradecopy.php');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (function_exists('AppUpgradeCopyFiles'))
|
||||
{
|
||||
// start the update
|
||||
set_time_limit(600);
|
||||
AppUpgradeCopyFiles(self::UPDATE_DIR.'web/');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Local function for older iTop versions
|
||||
IssueLog::Info('itop-core-update: Use default updater');
|
||||
self::LocalUpdateCoreFiles(self::UPDATE_DIR.'web/');
|
||||
}
|
||||
IssueLog::Info('itop-core-update: Update done, check files integrity');
|
||||
FilesIntegrity::CheckInstallationIntegrity(APPROOT);
|
||||
IssueLog::Info('itop-core-update: Files integrity OK');
|
||||
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::error($e->getMessage());
|
||||
IssueLog::Info('itop-core-update: ended');
|
||||
throw $e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
self::RRmdir(self::UPDATE_DIR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function Compile()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Compile code
|
||||
IssueLog::Info('itop-core-update: Start compilation');
|
||||
IssueLog::Info('itop-core-update: Version Dev');
|
||||
|
||||
$sTargetEnv = 'production';
|
||||
$oRuntimeEnv = new RunTimeEnvironmentCoreUpdater($sTargetEnv);
|
||||
$oRuntimeEnv->CheckDirectories($sTargetEnv);
|
||||
$oRuntimeEnv->CompileFrom('production');
|
||||
$oConfig = $oRuntimeEnv->MakeConfigFile($sTargetEnv.' (built on '.date('Y-m-d').')');
|
||||
$oConfig->Set('access_mode', ACCESS_FULL);
|
||||
$oRuntimeEnv->WriteConfigFileSafe($oConfig);
|
||||
$oRuntimeEnv->InitDataModel($oConfig, true);
|
||||
|
||||
$aAvailableModules = $oRuntimeEnv->AnalyzeInstallation($oConfig, $oRuntimeEnv->GetBuildDir());
|
||||
$aSelectedModules = array();
|
||||
foreach ($aAvailableModules as $sModuleId => $aModule)
|
||||
{
|
||||
if (($sModuleId == ROOT_MODULE) || ($sModuleId == DATAMODEL_MODULE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aSelectedModules[] = $sModuleId;
|
||||
}
|
||||
}
|
||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'BeforeDatabaseCreation');
|
||||
$oRuntimeEnv->CreateDatabaseStructure($oConfig, 'upgrade');
|
||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseCreation');
|
||||
$oRuntimeEnv->UpdatePredefinedObjects();
|
||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseSetup');
|
||||
$oRuntimeEnv->LoadData($aAvailableModules, $aSelectedModules, false /* no sample data*/);
|
||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDataLoad');
|
||||
$sDataModelVersion = $oRuntimeEnv->GetCurrentDataModelVersion();
|
||||
$oExtensionsMap = new iTopExtensionsMap();
|
||||
// Default choices = as before
|
||||
$oExtensionsMap->LoadChoicesFromDatabase($oConfig);
|
||||
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension)
|
||||
{
|
||||
// Plus all "remote" extensions
|
||||
if ($oExtension->sSource == iTopExtension::SOURCE_REMOTE)
|
||||
{
|
||||
$oExtensionsMap->MarkAsChosen($oExtension->sCode);
|
||||
}
|
||||
}
|
||||
$aSelectedExtensionCodes = array();
|
||||
foreach ($oExtensionsMap->GetChoices() as $oExtension)
|
||||
{
|
||||
$aSelectedExtensionCodes[] = $oExtension->sCode;
|
||||
}
|
||||
$oRuntimeEnv->RecordInstallation($oConfig, $sDataModelVersion, $aSelectedModules,
|
||||
$aSelectedExtensionCodes, 'Done by the iTop Core Updater');
|
||||
|
||||
IssueLog::Info('itop-core-update: Compilation done');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::error($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sFromDir
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function LocalUpdateCoreFiles($sFromDir)
|
||||
{
|
||||
self::CopyDir($sFromDir, APPROOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sArchiveFile
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function ExtractUpdateFile($sArchiveFile)
|
||||
{
|
||||
if (!utils::EndsWith($sArchiveFile, '.zip'))
|
||||
{
|
||||
throw new Exception(Dict::S('iTopUpdate:Error:BadFileFormat'));
|
||||
}
|
||||
|
||||
$oArchive = new ZipArchive();
|
||||
$oArchive->open($sArchiveFile);
|
||||
|
||||
self::RRmdir(self::UPDATE_DIR);
|
||||
SetupUtils::builddir(self::UPDATE_DIR);
|
||||
$oArchive->extractTo(self::UPDATE_DIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function Backup()
|
||||
{
|
||||
$sBackupName = self::GetBackupName();
|
||||
$sBackupFile = self::GetBackupFile();
|
||||
if (file_exists($sBackupFile))
|
||||
{
|
||||
@unlink($sBackupFile);
|
||||
}
|
||||
|
||||
self::DoBackup($sBackupName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function CreateItopArchive()
|
||||
{
|
||||
set_time_limit(0);
|
||||
$sItopArchiveFile = self::GetItopArchiveFile();
|
||||
if (file_exists($sItopArchiveFile))
|
||||
{
|
||||
@unlink($sItopArchiveFile);
|
||||
}
|
||||
|
||||
$sTempFile = sys_get_temp_dir().'/'.basename($sItopArchiveFile);
|
||||
if (file_exists($sTempFile))
|
||||
{
|
||||
@unlink($sTempFile);
|
||||
}
|
||||
|
||||
$aPathInfo = pathInfo(realpath(APPROOT));
|
||||
$sParentPath = $aPathInfo['dirname'];
|
||||
$sDirName = $aPathInfo['basename'];
|
||||
|
||||
$oZipArchive = new ZipArchive();
|
||||
$oZipArchive->open($sTempFile, ZIPARCHIVE::CREATE);
|
||||
$oZipArchive->addEmptyDir($sDirName);
|
||||
self::ZipFolder(realpath(APPROOT), $oZipArchive, strlen("$sParentPath/"));
|
||||
$oZipArchive->close();
|
||||
|
||||
if (!file_exists($sTempFile))
|
||||
{
|
||||
IssueLog::Error("Failed to create itop archive $sTempFile");
|
||||
}
|
||||
|
||||
if (@rename($sTempFile, $sItopArchiveFile))
|
||||
{
|
||||
IssueLog::Info("Archive $sItopArchiveFile Created");
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Error("Failed to create archive $sItopArchiveFile");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $sTargetFile
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function DoBackup($sTargetFile)
|
||||
{
|
||||
// Make sure the target directory exists
|
||||
$sBackupDir = dirname($sTargetFile);
|
||||
SetupUtils::builddir($sBackupDir);
|
||||
|
||||
$oBackup = new DBBackup();
|
||||
$oBackup->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
|
||||
|
||||
$oMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment());
|
||||
$oMutex->Lock();
|
||||
try
|
||||
{
|
||||
$oBackup->CreateCompressedBackup($sTargetFile);
|
||||
IssueLog::Info('itop-core-update: Backup done: '.$sTargetFile);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oMutex->Unlock();
|
||||
throw $e;
|
||||
}
|
||||
$oMutex->Unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sSource
|
||||
* @param $sDest
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function CopyDir($sSource, $sDest)
|
||||
{
|
||||
if (is_dir($sSource))
|
||||
{
|
||||
if (!is_dir($sDest))
|
||||
{
|
||||
@mkdir($sDest, 0755);
|
||||
}
|
||||
$aFiles = scandir($sSource);
|
||||
if(sizeof($aFiles) > 0 )
|
||||
{
|
||||
foreach($aFiles as $sFile)
|
||||
{
|
||||
if ($sFile == '.' || $sFile == '..' || $sFile == '.svn' || $sFile == '.git')
|
||||
{
|
||||
// Skip
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_dir($sSource.'/'.$sFile))
|
||||
{
|
||||
// Recurse
|
||||
self::CopyDir($sSource.'/'.$sFile, $sDest.'/'.$sFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_link($sDest.'/'.$sFile))
|
||||
{
|
||||
unlink($sDest.'/'.$sFile);
|
||||
}
|
||||
self::CopyFile($sSource.'/'.$sFile, $sDest.'/'.$sFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (is_file($sSource))
|
||||
{
|
||||
self::CopyFile($sSource, $sDest);
|
||||
}
|
||||
}
|
||||
|
||||
public static function RRmdir($sDir) {
|
||||
if (is_dir($sDir))
|
||||
{
|
||||
$oDir = @opendir($sDir);
|
||||
while (false !== ($sFile = @readdir($oDir)))
|
||||
{
|
||||
if (($sFile != '.') && ($sFile != '..'))
|
||||
{
|
||||
$sFull = $sDir.'/'.$sFile;
|
||||
if (is_dir($sFull))
|
||||
{
|
||||
self::RRmdir($sFull);
|
||||
}
|
||||
else
|
||||
{
|
||||
@unlink($sFull);
|
||||
}
|
||||
}
|
||||
}
|
||||
@closedir($oDir);
|
||||
@rmdir($sDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sSource
|
||||
* @param $sDest
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function CopyFile($sSource, $sDest)
|
||||
{
|
||||
if (is_file($sSource))
|
||||
{
|
||||
if (!@copy($sSource, $sDest))
|
||||
{
|
||||
// Try changing the mode of the file
|
||||
@chmod($sDest, 0644);
|
||||
if (!@copy($sSource, $sDest))
|
||||
{
|
||||
throw new Exception(Dict::Format('iTopUpdate:Error:Copy', $sSource, $sDest));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add files and sub-directories in a folder to zip file.
|
||||
*
|
||||
* @param string $sFolder
|
||||
* @param ZipArchive $oZipArchive
|
||||
* @param int $iStrippedLength Number of text to be removed from the file path.
|
||||
*/
|
||||
private static function ZipFolder($sFolder, &$oZipArchive, $iStrippedLength) {
|
||||
$oFolder = opendir($sFolder);
|
||||
while (false !== ($sFile = readdir($oFolder))) {
|
||||
if (($sFile == '.') || ($sFile == '..'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$sFilePath = "$sFolder/$sFile";
|
||||
|
||||
$sLocalItopPath = utils::LocalPath($sFilePath);
|
||||
if ($sLocalItopPath == 'data/backups' || $sLocalItopPath == 'log')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove prefix from file path before add to zip.
|
||||
$sLocalPath = substr($sFilePath, $iStrippedLength);
|
||||
if (is_file($sFilePath)) {
|
||||
$oZipArchive->addFile($sFilePath, $sLocalPath);
|
||||
} elseif (is_dir($sFilePath)) {
|
||||
// Add sub-directory.
|
||||
$oZipArchive->addEmptyDir($sLocalPath);
|
||||
self::ZipFolder($sFilePath, $oZipArchive, $iStrippedLength);
|
||||
}
|
||||
}
|
||||
closedir($oFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private static function GetItopArchiveName()
|
||||
{
|
||||
$sItopArchiveName = APPROOT.'data/backups/itop';
|
||||
return $sItopArchiveName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function GetItopArchiveFile()
|
||||
{
|
||||
$sItopArchiveFile = self::GetItopArchiveName().'.zip';
|
||||
return $sItopArchiveFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private static function GetBackupName()
|
||||
{
|
||||
$sBackupName = APPROOT.'data/backups/manual/backup-core-update';
|
||||
return $sBackupName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function GetBackupFile()
|
||||
{
|
||||
$sBackupFile = self::GetBackupName().'.tar.gz';
|
||||
return $sBackupFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sArchiveFile
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function ExtractDownloadedFile($sArchiveFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Extract archive file
|
||||
self::ExtractUpdateFile($sArchiveFile);
|
||||
|
||||
IssueLog::Info('itop-core-update: Archive extracted, check files integrity');
|
||||
|
||||
// Check files integrity
|
||||
FilesIntegrity::CheckInstallationIntegrity(self::UPDATE_DIR.'web/');
|
||||
|
||||
IssueLog::Info('itop-core-update: Files integrity OK');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
self::RRmdir(self::UPDATE_DIR);
|
||||
throw $e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
self::RRmdir(self::DOWNLOAD_DIR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function GetVersionToInstall()
|
||||
{
|
||||
try
|
||||
{
|
||||
$sConfigFile = self::UPDATE_DIR.'web/core/config.class.inc.php';
|
||||
if (!is_file($sConfigFile))
|
||||
{
|
||||
throw new Exception(Dict::S(Dict::S('iTopUpdate:Error:BadFileContent')));
|
||||
}
|
||||
|
||||
$sContents = file_get_contents($sConfigFile);
|
||||
preg_match_all("@define\('(?<name>ITOP_[^']*)', '(?<value>[^']*)'\);@", $sContents, $aMatches);
|
||||
if (empty($aMatches))
|
||||
{
|
||||
throw new Exception(Dict::S(Dict::S('iTopUpdate:Error:BadFileContent')));
|
||||
}
|
||||
$aValues = array();
|
||||
foreach ($aMatches['name'] as $index => $sName)
|
||||
{
|
||||
$aValues[$sName] = $aMatches['value'][$index];
|
||||
}
|
||||
|
||||
if ($aValues['ITOP_APPLICATION'] != ITOP_APPLICATION)
|
||||
{
|
||||
throw new Exception(Dict::S('iTopUpdate:Error:BadItopProduct'));
|
||||
}
|
||||
|
||||
// Extract updater file from the new version if available
|
||||
if (is_file(APPROOT.'setup/appupgradecheck.php'))
|
||||
{
|
||||
// Remove previous specific updater
|
||||
@unlink(APPROOT.'setup/appupgradecheck.php');
|
||||
}
|
||||
if (is_file(self::UPDATE_DIR.'web/setup/appupgradecheck.php'))
|
||||
{
|
||||
IssueLog::Info('itop-core-update: Use updater provided in the archive');
|
||||
self::CopyFile(self::UPDATE_DIR.'web/setup/appupgradecheck.php', APPROOT.'setup/appupgradecheck.php');
|
||||
@include_once(APPROOT.'setup/appupgradecheck.php');
|
||||
}
|
||||
if (function_exists('AppUpgradeCheckInstall'))
|
||||
{
|
||||
AppUpgradeCheckInstall();
|
||||
}
|
||||
|
||||
return Dict::Format('UI:iTopVersion:Long', $aValues['ITOP_APPLICATION'], $aValues['ITOP_VERSION'], $aValues['ITOP_REVISION'], $aValues['ITOP_BUILD_DATE']);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
self::RRmdir(self::UPDATE_DIR);
|
||||
self::RRmdir(self::DOWNLOAD_DIR);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
namespace Combodo\iTop\CoreUpdate\Service;
|
||||
|
||||
require_once(APPROOT."setup/runtimeenv.class.inc.php");
|
||||
|
||||
use Config;
|
||||
use Exception;
|
||||
use RunTimeEnvironment;
|
||||
|
||||
class RunTimeEnvironmentCoreUpdater extends RunTimeEnvironment
|
||||
{
|
||||
public function CheckDirectories($sTargetEnv)
|
||||
{
|
||||
$sTargetDir = APPROOT.'env-'.$sTargetEnv;
|
||||
$sBuildDir = $sTargetDir.'-build';
|
||||
|
||||
self::CheckDirectory($sTargetDir);
|
||||
self::CheckDirectory($sBuildDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sDir
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function CheckDirectory($sDir)
|
||||
{
|
||||
if (!is_dir($sDir))
|
||||
{
|
||||
if (!@mkdir($sDir,0770))
|
||||
{
|
||||
throw new Exception('Creating directory '.$sDir.' is denied (Check access rights)');
|
||||
}
|
||||
}
|
||||
// Try create a file
|
||||
$sTempFile = $sDir.'/__itop_temp_file__';
|
||||
if (!@touch($sTempFile))
|
||||
{
|
||||
throw new Exception('Write access to '.$sDir.' is denied (Check access rights)');
|
||||
}
|
||||
@unlink($sTempFile);
|
||||
}
|
||||
|
||||
public function MakeConfigFile($sEnvironmentLabel = null)
|
||||
{
|
||||
$oConfig = $this->GetConfig();
|
||||
if (!is_null($oConfig))
|
||||
{
|
||||
// Return the existing one
|
||||
$oConfig->UpdateIncludes('env-'.$this->sTargetEnv);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clone the default 'production' config file
|
||||
//
|
||||
$oConfig = clone($this->GetConfig('production'));
|
||||
|
||||
$oConfig->UpdateIncludes('env-'.$this->sTargetEnv);
|
||||
|
||||
if (is_null($sEnvironmentLabel))
|
||||
{
|
||||
$sEnvironmentLabel = $this->sTargetEnv;
|
||||
}
|
||||
$oConfig->Set('app_env_label', $sEnvironmentLabel);
|
||||
if ($this->sFinalEnv !== 'production')
|
||||
{
|
||||
$oConfig->Set('db_name', $oConfig->Get('db_name').'_'.$this->sFinalEnv);
|
||||
}
|
||||
}
|
||||
|
||||
return $oConfig;
|
||||
}
|
||||
|
||||
protected function GetConfig($sEnvironment = null)
|
||||
{
|
||||
if (is_null($sEnvironment))
|
||||
{
|
||||
$sEnvironment = $this->sTargetEnv;
|
||||
}
|
||||
$sFile = APPCONF.$sEnvironment.'/'.ITOP_CONFIG_FILE;
|
||||
if (file_exists($sFile))
|
||||
{
|
||||
$oConfig = new Config($sFile);
|
||||
return $oConfig;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
110
datamodels/2.x/itop-core-update/view/ConfirmUpdate.html.twig
Normal file
110
datamodels/2.x/itop-core-update/view/ConfirmUpdate.html.twig
Normal file
@@ -0,0 +1,110 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<div class="page_header"><h1>{{ 'iTopUpdate:UI:PageTitle'|dict_s }}</h1></div>
|
||||
|
||||
<div class="display_block display-files">
|
||||
{% if bSuccess %}
|
||||
|
||||
<fieldset>
|
||||
<legend>{{ 'iTopUpdate:UI:Status'|dict_s }}</legend>
|
||||
<div class="header_message message_info">
|
||||
<div>
|
||||
<span>{{ sQuestion }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table style="vertical-align:top" class="one-col-details" data-mode="view">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="vertical-align:top" class="">
|
||||
{{ include('DisplayCurrentVersion.html.twig') }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{ 'iTopUpdate:UI:Backup'|dict_s }}</legend>
|
||||
{% if bDoBackup or bDoFilesArchive %}
|
||||
<table style="vertical-align:top" class="one-col-details" data-mode="view">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="vertical-align:top" class="">
|
||||
{% if bDoBackup %}
|
||||
<div class="details">
|
||||
<div class="field_container field_small">
|
||||
<div class="field_label label"><span title="">{{ 'iTopUpdate:UI:WithDBBackup'|dict_s }}</span></div>
|
||||
<div class="field_data">
|
||||
<div class="field_value">{{ sBackupFile }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if bDoFilesArchive %}
|
||||
<div class="details">
|
||||
<div class="field_container field_small">
|
||||
<div class="field_label label"><span title="">{{ 'iTopUpdate:UI:WithFilesBackup'|dict_s }}</span></div>
|
||||
<div class="field_data">
|
||||
<div class="field_value">{{ sItopArchive }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="header_message message_info">
|
||||
<div>
|
||||
<span>{{ 'iTopUpdate:UI:WithoutBackup'|dict_s }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{ 'iTopUpdate:UI:Action'|dict_s }}</legend>
|
||||
|
||||
<div>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<form enctype="multipart/form-data" method="post">
|
||||
<p><input id="cancel-update" type="submit" value="{{ 'iTopUpdate:UI:Cancel'|dict_s }}"/></p>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form enctype="multipart/form-data" method="post">
|
||||
<p><input id="do-update" type="submit" value="{{ 'iTopUpdate:UI:DoUpdate'|dict_s }}"/> <i id="submit-wait" style="display: none" class="fas fa-spinner fa-spin" aria-hidden="true"></i></p>
|
||||
<input type="hidden" name="operation" value="UpdateCoreFiles"/>
|
||||
<input type="hidden" name="transaction_id" value="{{ sTransactionId }}">
|
||||
<input type="hidden" name="filename" value="{{ sName }}">
|
||||
<input type="hidden" name="doBackup" value="{{ bDoBackup }}">
|
||||
<input type="hidden" name="doFilesArchive" value="{{ bDoFilesArchive }}">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
{% else %}
|
||||
|
||||
<fieldset>
|
||||
<div class="header_message message_error">
|
||||
<div>
|
||||
<span>{{ sError }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form enctype="multipart/form-data" method="post">
|
||||
<p><input type="submit" value="{{ 'iTopUpdate:UI:Back'|dict_s }}"/></p>
|
||||
</form>
|
||||
|
||||
</fieldset>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
$("#do-update").on("click", function() {
|
||||
$("#submit-wait").show();
|
||||
$(this).prop("disabled", true);
|
||||
$("#cancel-update").prop("disabled", true);
|
||||
$(this).parents('form').submit();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% set sVersion = 'UI:iTopVersion:Long'|dict_format(constant('ITOP_APPLICATION'), constant('ITOP_VERSION'), constant('ITOP_REVISION'), constant('ITOP_BUILD_DATE')) %}
|
||||
<div class="details">
|
||||
<div class="field_container field_small">
|
||||
<div class="field_label label"><span title="">{{ 'iTopUpdate:UI:CurrentVersion'|dict_s }}</span></div>
|
||||
<div class="field_data"><div class="field_value">{{ sVersion }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
134
datamodels/2.x/itop-core-update/view/SelectUpdateFile.html.twig
Normal file
134
datamodels/2.x/itop-core-update/view/SelectUpdateFile.html.twig
Normal file
@@ -0,0 +1,134 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<div class="page_header"><h1>{{ 'iTopUpdate:UI:PageTitle'|dict_s }}</h1></div>
|
||||
|
||||
<div class="display_block display-files">
|
||||
<fieldset>
|
||||
<legend>{{ 'iTopUpdate:UI:Status'|dict_s }}</legend>
|
||||
|
||||
<div id="header-requirements" class="header_message message_info">
|
||||
<div>
|
||||
<span id="can-core-update">{{ 'iTopUpdate:UI:CanCoreUpdate:Loading'|dict_s }} <i class="ajax-spin fas fa-spinner fa-spin" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table style="vertical-align:top" class="one-col-details" data-mode="view">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="vertical-align:top" class="">
|
||||
|
||||
{{ include('DisplayCurrentVersion.html.twig') }}
|
||||
|
||||
<div class="details">
|
||||
<div class="field_container field_small">
|
||||
<div class="field_label label"><span title="">{{ 'iTopUpdate:UI:DiskFreeSpace'|dict_s }}</span></div>
|
||||
<div class="field_data"><div class="field_value">{{ sDiskFreeSpace }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="details">
|
||||
<div class="field_container field_small">
|
||||
<div class="field_label label"><span title="">{{ 'iTopUpdate:UI:ItopDiskSpace'|dict_s }}</span></div>
|
||||
<div class="field_data"><div id="itop-disk-space" class="field_value"><i class="ajax-spin fas fa-spinner fa-spin" aria-hidden="true"></i></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="details">
|
||||
<div class="field_container field_small">
|
||||
<div class="field_label label"><span title="">{{ 'iTopUpdate:UI:DBDiskSpace'|dict_s }}</span></div>
|
||||
<div class="field_data"><div id="db-disk-space" class="field_value"><i class="ajax-spin fas fa-spinner fa-spin" aria-hidden="true"></i></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="details">
|
||||
<div class="field_container field_small">
|
||||
<div class="field_label label"><span title="">{{ 'iTopUpdate:UI:FileUploadMaxSize'|dict_s }}</span></div>
|
||||
<div class="field_data"><div class="field_value">{{ sFileUploadMaxSize }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{ 'iTopUpdate:UI:SelectUpdateFile'|dict_s }}</legend>
|
||||
|
||||
<div class="details">
|
||||
<form enctype="multipart/form-data" method="post">
|
||||
<input type="hidden" name="operation" value="ConfirmUpdate"/>
|
||||
<input type="hidden" name="transaction_id" value="{{ sTransactionId }}">
|
||||
|
||||
<div id="header-file-size" class="header_message message_error" style="display: none">
|
||||
<div>
|
||||
<span>{{ 'iTopUpdate:Error:FileUploadMaxSizeTooSmall'|dict_s }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>{{ 'iTopUpdate:UI:PostMaxSize'|dict_format(sPostMaxSize) }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>{{ 'iTopUpdate:UI:UploadMaxFileSize'|dict_format(sUploadMaxSize) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field_container field_large">
|
||||
<div class="field_data">
|
||||
<div class="upload_container">
|
||||
<input type="file" id="file" name="file"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="dobackup-warning" class="header_message message_info" style="display: none">{{ 'iTopUpdate:UI:DoBackup:Warning'|dict_s }}</div>
|
||||
<div class="field_data">
|
||||
<label id="dobackup-label" for="doBackup">{{ 'iTopUpdate:UI:DoBackup:Label'|dict_s }}</label>
|
||||
<input type="checkbox" id="doBackup" name="doBackup" checked="checked" value="1"/>
|
||||
</div>
|
||||
<div class="field_data">
|
||||
<label id="dofilesarchive-label" for="doFilesArchive">{{ 'iTopUpdate:UI:DoFilesArchive'|dict_s }}</label>
|
||||
<input type="checkbox" id="doFilesArchive" name="doFilesArchive" checked="checked" value="1"/>
|
||||
</div>
|
||||
</div>
|
||||
<p><input id="check-update" type="submit" value="{{ 'iTopUpdate:UI:CheckUpdate'|dict_s }}"/> <i id="submit-wait" style="display: none" class="fas fa-spinner fa-spin" aria-hidden="true"></i></p>
|
||||
</form>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{ 'iTopUpdate:UI:History'|dict_s }}</legend>
|
||||
|
||||
<div class="details">
|
||||
{% for aInstall in aPreviousInstall %}
|
||||
{% if loop.first %}
|
||||
<table class="listResults">
|
||||
<tr>
|
||||
<th>{{ 'Class:ModuleInstallation/Attribute:name'|dict_s }}</th>
|
||||
<th>{{ 'Class:ModuleInstallation/Attribute:version'|dict_s }}</th>
|
||||
<th>{{ 'Class:ModuleInstallation/Attribute:installed'|dict_s }}</th>
|
||||
<th>{{ 'Class:ModuleInstallation/Attribute:comment'|dict_s }}</th>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if (loop.index0 % 2) == 0 %}
|
||||
{% set sRowClass = "odd" %}
|
||||
{% else %}
|
||||
{% set sRowClass = "even" %}
|
||||
{% endif %}
|
||||
|
||||
<tr class="{{ sRowClass }}">
|
||||
<td>{{ aInstall.name }}</td>
|
||||
<td>{{ aInstall.version }}</td>
|
||||
<td>{{ aInstall.installed }}</td>
|
||||
<td>{{ aInstall.comment }}</td>
|
||||
</tr>
|
||||
|
||||
{% if loop.last %}
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,95 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
var iDiskFreeSpace = {{ iDiskFreeSpace }};
|
||||
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "{{ sAjaxURL|raw }}",
|
||||
data: {
|
||||
"operation": "CanUpdateCore"
|
||||
},
|
||||
dataType: "json",
|
||||
success: function(data)
|
||||
{
|
||||
var oRequirements = $("#header-requirements");
|
||||
var oCanCoreUpdate = $("#can-core-update");
|
||||
oCanCoreUpdate.html(data.sMessage);
|
||||
oRequirements.removeClass("message_info");
|
||||
if (data.bStatus)
|
||||
{
|
||||
oRequirements.addClass("message_ok");
|
||||
}
|
||||
else
|
||||
{
|
||||
oRequirements.addClass("message_error");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var oGetItopDiskSpace = $.ajax({
|
||||
method: "POST",
|
||||
url: "{{ sAjaxURL|raw }}",
|
||||
data: {
|
||||
"operation": "GetItopDiskSpace"
|
||||
},
|
||||
dataType: "json",
|
||||
success: function(data)
|
||||
{
|
||||
var oRequirement = $("#itop-disk-space");
|
||||
oRequirement.html(data.sItopDiskSpace);
|
||||
}
|
||||
});
|
||||
|
||||
var oGetDBDiskSpace = $.ajax({
|
||||
method: "POST",
|
||||
url: "{{ sAjaxURL|raw }}",
|
||||
data: {
|
||||
"operation": "GetDBDiskSpace"
|
||||
},
|
||||
dataType: "json",
|
||||
success: function(data)
|
||||
{
|
||||
var oRequirement = $("#db-disk-space");
|
||||
oRequirement.html(data.sDBDiskSpace);
|
||||
}
|
||||
});
|
||||
|
||||
$.when(oGetItopDiskSpace, oGetDBDiskSpace).then(
|
||||
function(data1, data2)
|
||||
{
|
||||
var iItopDiskSpace = data1[0].iItopDiskSpace;
|
||||
var iDBDiskSpace = data2[0].iDBDiskSpace;
|
||||
if ((2 * (iItopDiskSpace + iDBDiskSpace)) > iDiskFreeSpace)
|
||||
{
|
||||
$("#dobackup-warning").show();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$("#file").on("change", function(e) {
|
||||
var selectedFile = $('#file').get(0).files[0];
|
||||
var errorMsg = $("#header-file-size");
|
||||
var submitButton = $("#check-update");
|
||||
if (selectedFile)
|
||||
{
|
||||
if (selectedFile.size > {{ iFileUploadMaxSize }})
|
||||
{
|
||||
errorMsg.show();
|
||||
submitButton.prop("disabled", true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
errorMsg.hide();
|
||||
submitButton.prop("disabled", false);
|
||||
});
|
||||
|
||||
$("#check-update").on("click", function(e) {
|
||||
$("#submit-wait").show();
|
||||
$(this).prop("disabled", true);
|
||||
$(".ajax-spin").removeClass("fa-spinner").removeClass("fa-spin").addClass("fa-times");
|
||||
$(this).parents('form').submit();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
@@ -0,0 +1,90 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<div class="page_header"><h1>{{ 'iTopUpdate:UI:PageTitle'|dict_s }}</h1></div>
|
||||
|
||||
<div class="display_block display-files">
|
||||
<fieldset id="installation_progress">
|
||||
<legend>{{ 'iTopUpdate:UI:Status'|dict_s }}</legend>
|
||||
|
||||
<div id="current_version" class="header_message message_info">
|
||||
<div>
|
||||
<span id="current-version">{{ sCurrentVersion }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table style="vertical-align:top" class="one-col-details" data-mode="view">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="vertical-align:top" class="">
|
||||
{% if bDoBackup %}
|
||||
<div id="do_backup_done" class="details" style="display: none;">
|
||||
<div class="field_container field_small">
|
||||
<div class="field_label label"><span title="">{{ 'iTopUpdate:UI:WithDBBackup'|dict_s }}</span></div>
|
||||
<div class="field_data">
|
||||
<div class="field_value">{{ sBackupFile }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if bDoFilesArchive %}
|
||||
<div id="do_files_archive_done" class="details" style="display: none;">
|
||||
<div class="field_container field_small">
|
||||
<div class="field_label label"><span title="">{{ 'iTopUpdate:UI:WithFilesBackup'|dict_s }}</span></div>
|
||||
<div class="field_data">
|
||||
<div class="field_value">{{ sItopArchive }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="installation_progress">
|
||||
<legend>{{ 'iTopUpdate:UI:Progress'|dict_s }}</legend>
|
||||
|
||||
<div id="progress_content" style="height: 100px; overflow: auto; text-align: center;">
|
||||
<p class="center">
|
||||
<span id="setup_msg">{{ 'iTopUpdate:UI:SetupMessage:Ready'|dict_s }}</span>
|
||||
</p>
|
||||
<div id="progress" style="
|
||||
margin: 5px auto;
|
||||
display:block;
|
||||
border:1px solid #000000;
|
||||
width: 280px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
">0%</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<div id="setup_error_outer" class="header_message message_error" style="display: none;">
|
||||
<div>
|
||||
<span id="setup_error"></span>
|
||||
</div>
|
||||
{% if bDoFilesArchive %}
|
||||
<div>
|
||||
<span>{{ 'iTopUpdate:UI:RestoreArchive'|dict_format(sItopArchive) }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if bDoBackup %}
|
||||
<div>
|
||||
<span>{{ 'iTopUpdate:UI:RestoreBackup'|dict_format(sBackupFile) }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<span>{{ 'iTopUpdate:UI:MaintenanceModeActive'|dict_s }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form enctype="multipart/form-data" method="post">
|
||||
<p><input id="setup_continue" type="submit" value="{{ 'iTopUpdate:UI:Continue'|dict_s }}" disabled/></p>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
@@ -0,0 +1,122 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
function UpdateProgress(sMessage, iPercent) {
|
||||
$('#setup_msg').html(sMessage);
|
||||
$('#progress').progression({
|
||||
Current: iPercent,
|
||||
Maximum: 100,
|
||||
aBackgroundImg: "{{ sProgressImage }}",
|
||||
aTextColor: '#000000'
|
||||
});
|
||||
}
|
||||
|
||||
var oGetCurrentVersion = {
|
||||
method: "POST",
|
||||
url: "{{ sAjaxURL|raw }}",
|
||||
data: {
|
||||
"operation": "GetCurrentVersion"
|
||||
},
|
||||
dataType: "json",
|
||||
success: function(data)
|
||||
{
|
||||
var oCurrentVersion = $("#current-version");
|
||||
if (oCurrentVersion)
|
||||
{
|
||||
oCurrentVersion.html(data.sVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function GetAjaxRequest(sOperation)
|
||||
{
|
||||
oAjaxRequest = {
|
||||
method: "POST",
|
||||
url: "{{ sAjaxURL|raw }}",
|
||||
data: {
|
||||
"operation": sOperation,
|
||||
"authent": "{{ sSetupToken }}"
|
||||
},
|
||||
dataType: "json"
|
||||
};
|
||||
|
||||
if (sOperation === "Backup")
|
||||
{
|
||||
oAjaxRequest.success = function() {
|
||||
$("#do_backup_done").show();
|
||||
};
|
||||
}
|
||||
|
||||
if (sOperation === "FilesArchive")
|
||||
{
|
||||
oAjaxRequest.success = function() {
|
||||
$("#do_files_archive_done").show();
|
||||
};
|
||||
}
|
||||
|
||||
return oAjaxRequest;
|
||||
}
|
||||
|
||||
{% set aSteps = ['EnterMaintenance', 'Backup', 'FilesArchive', 'CopyFiles', 'Compile', 'ExitMaintenance', 'UpdateDone'] %}
|
||||
|
||||
aStepsName = [];
|
||||
|
||||
{% for sStep in aSteps %}
|
||||
aStepsName.push({{ ('iTopUpdate:UI:SetupMessage:' ~ sStep)|dict_s|json_encode|raw }});
|
||||
{% endfor %}
|
||||
|
||||
var sBackupStep;
|
||||
{% if bDoBackup %}
|
||||
sBackupStep = "Backup";
|
||||
{% endif %}
|
||||
|
||||
var sFilesArchiveStep;
|
||||
{% if bDoFilesArchive %}
|
||||
sFilesArchiveStep = "FilesArchive";
|
||||
{% endif %}
|
||||
|
||||
var aStepsAjaxOperation = ["EnterMaintenance", sBackupStep, sFilesArchiveStep, "CopyFiles", "Compile", "ExitMaintenance", null];
|
||||
|
||||
var iNextStep = 0;
|
||||
function ExecNextStep() {
|
||||
if (iNextStep < aStepsAjaxOperation.length)
|
||||
{
|
||||
var sAjaxOperation = aStepsAjaxOperation[iNextStep];
|
||||
var iPercent = (iNextStep + 1) * 100 / aStepsAjaxOperation.length;
|
||||
UpdateProgress(aStepsName[iNextStep], iPercent);
|
||||
iNextStep++;
|
||||
if (sAjaxOperation) {
|
||||
$.ajax(GetAjaxRequest(sAjaxOperation))
|
||||
.done(function () {
|
||||
setTimeout(ExecNextStep, 500);
|
||||
})
|
||||
.fail(function ( jqXHR) {
|
||||
$("#setup_continue").removeAttr("disabled");
|
||||
if (jqXHR && jqXHR.responseJSON) {
|
||||
$("#setup_error").html({{ 'iTopUpdate:Error:UpdateFailed'|dict_s|json_encode|raw }}+" "+jqXHR.responseJSON.sError);
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#setup_error").html({{ 'iTopUpdate:Error:UpdateFailed'|dict_s|json_encode|raw }});
|
||||
}
|
||||
$('.progress').css("background-image", "none").css("background-color", "#fcc");
|
||||
$("#setup_error_outer").show();
|
||||
})
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
setTimeout(ExecNextStep, 500);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$.ajax(oGetCurrentVersion);
|
||||
$("#setup_continue").removeAttr("disabled");
|
||||
$("#current_version").removeClass("message_info").addClass("message_ok");
|
||||
$('.progress').css("background-image", "none").css("background-color", "#cfc");
|
||||
}
|
||||
}
|
||||
|
||||
ExecNextStep();
|
||||
|
||||
1
datamodels/2.x/itop-files-information/README.md
Normal file
1
datamodels/2.x/itop-files-information/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# itop-files-information
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
|
||||
</itop_design>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
Dict::Add('EN US', 'English', 'English', array(
|
||||
// Errors
|
||||
'FilesInformation:Error:MissingFile' => 'Missing file: %1$s',
|
||||
'FilesInformation:Error:CorruptedFile' => 'File %1$s is corrupted',
|
||||
'FilesInformation:Error:CantWriteToFile' => 'Can not write to file %1$s',
|
||||
));
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
Dict::Add('FR FR', 'French', 'Français', array(
|
||||
// Errors
|
||||
'FilesInformation:Error:MissingFile' => 'Ficher manquant : %1$s',
|
||||
'FilesInformation:Error:CorruptedFile' => 'Le fichier %1$s est corrompu',
|
||||
'FilesInformation:Error:CantWriteToFile' => 'Impossible de modifier le fichier %1$s',
|
||||
));
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
//
|
||||
// iTop module definition file
|
||||
//
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-files-information/1.0.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
'label' => 'iTop files information',
|
||||
'category' => 'business',
|
||||
|
||||
// Setup
|
||||
//
|
||||
'dependencies' => array(
|
||||
),
|
||||
'mandatory' => false,
|
||||
'visible' => false,
|
||||
|
||||
// Components
|
||||
//
|
||||
'datamodel' => array(
|
||||
'model.itop-files-information.php',
|
||||
'src/Service/FilesInformation.php',
|
||||
'src/Service/FilesInformationException.php',
|
||||
'src/Service/FilesInformationUtils.php',
|
||||
'src/Service/FilesIntegrity.php',
|
||||
),
|
||||
'webservice' => array(),
|
||||
'data.struct' => array(),
|
||||
'data.sample' => array(),
|
||||
|
||||
// Documentation
|
||||
//
|
||||
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any
|
||||
'doc.more_information' => '', // hyperlink to more information, if any
|
||||
|
||||
// Default settings
|
||||
//
|
||||
'settings' => array(),
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
namespace Combodo\iTop\FilesInformation\Service;
|
||||
|
||||
|
||||
use Dict;
|
||||
use MetaModel;
|
||||
use utils;
|
||||
|
||||
class FilesInformation
|
||||
{
|
||||
private static $sItopOwner;
|
||||
|
||||
/**
|
||||
* Check iTop files access rights to tell if core update is possible
|
||||
*
|
||||
* @param string $sMessage
|
||||
*
|
||||
* @return bool true if core update is possible
|
||||
* @throws \Combodo\iTop\FilesInformation\Service\FileNotExistException
|
||||
*/
|
||||
public static function CanUpdateCore(&$sMessage)
|
||||
{
|
||||
self::Init();
|
||||
// Check than iTop can write everywhere
|
||||
if (!self::CanWriteRecursive('', $sMessage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sRootPath
|
||||
* @param string $sMessage
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Combodo\iTop\FilesInformation\Service\FileNotExistException
|
||||
*/
|
||||
private static function CanWriteRecursive($sRootPath = '', &$sMessage = null)
|
||||
{
|
||||
$aDirStats = FilesInformationUtils::Scan($sRootPath, false);
|
||||
foreach ($aDirStats as $sFileName => $aFileStats)
|
||||
{
|
||||
if (!self::CanWriteToFile($aFileStats))
|
||||
{
|
||||
$sMessage = Dict::Format('FilesInformation:Error:CantWriteToFile', $sRootPath.DIRECTORY_SEPARATOR.$sFileName);
|
||||
return false;
|
||||
}
|
||||
if (($sFileName != '.') && ($aFileStats['type'] == 'dir'))
|
||||
{
|
||||
if (!self::CanWriteRecursive($sRootPath.DIRECTORY_SEPARATOR.$sFileName, $sMessage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if iTop can write
|
||||
* @param string $sFilename absolute path to chack
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Combodo\iTop\FilesInformation\Service\FileNotExistException
|
||||
*/
|
||||
public static function IsWritable($sFilename)
|
||||
{
|
||||
$aFileStats = FilesInformationUtils::GetFileStat(utils::LocalPath($sFilename));
|
||||
return self::CanWriteToFile($aFileStats);
|
||||
}
|
||||
|
||||
private static function CanWriteToFile($aFileStats)
|
||||
{
|
||||
if ($aFileStats['writable'])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if ($aFileStats['file_owner'] == self::$sItopOwner)
|
||||
{
|
||||
// If iTop owns the file, no pb to write
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Combodo\iTop\FilesInformation\Service\FileNotExistException
|
||||
*/
|
||||
private static function Init()
|
||||
{
|
||||
clearstatcache();
|
||||
|
||||
$sSourceConfigFile = MetaModel::GetConfig()->GetLoadedFile();
|
||||
$sSourceConfigFile = utils::LocalPath($sSourceConfigFile);
|
||||
|
||||
$aConfigFiles = FilesInformationUtils::Scan(dirname($sSourceConfigFile));
|
||||
if (!isset($aConfigFiles[basename($sSourceConfigFile)]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
$aConfigStats = $aConfigFiles[basename($sSourceConfigFile)];
|
||||
self::$sItopOwner = $aConfigStats['file_owner'];
|
||||
}
|
||||
|
||||
public static function GetItopDiskSpace()
|
||||
{
|
||||
return FilesInformationUtils::GetDirSize(realpath(APPROOT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sLocalDirPath
|
||||
*
|
||||
* @return array
|
||||
* @throws \Combodo\iTop\FilesInformation\Service\FileNotExistException
|
||||
*/
|
||||
public static function GetDirInfo($sLocalDirPath)
|
||||
{
|
||||
if (utils::AbsolutePath($sLocalDirPath) === false)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
return FilesInformationUtils::Scan($sLocalDirPath);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
namespace Combodo\iTop\FilesInformation\Service;
|
||||
|
||||
use Exception;
|
||||
|
||||
class FilesInformationException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class FileNotExistException extends FilesInformationException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class FileIntegrityException extends FilesInformationException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\FilesInformation\Service;
|
||||
|
||||
use utils;
|
||||
|
||||
class FilesInformationUtils
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
clearstatcache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sPath
|
||||
* @param bool $bGetDirSize
|
||||
*
|
||||
* @return array
|
||||
* @throws \Combodo\iTop\FilesInformation\Service\FileNotExistException
|
||||
*/
|
||||
public static function Scan($sPath = '', $bGetDirSize = true)
|
||||
{
|
||||
$sRealRootPath = utils::AbsolutePath($sPath);
|
||||
$aFiles = scandir($sRealRootPath);
|
||||
$aFileStats = array();
|
||||
|
||||
foreach ($aFiles as $sScanFile)
|
||||
{
|
||||
if ($sScanFile == '..')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$sFile = $sRealRootPath.DIRECTORY_SEPARATOR.$sScanFile;
|
||||
$sFileName = utils::LocalPath($sFile);
|
||||
$aFileStat = self::GetFileStat($sFileName, $bGetDirSize);
|
||||
$aFileStat['basename'] = $sScanFile;
|
||||
$aFileStats[$sScanFile] = $aFileStat;
|
||||
}
|
||||
|
||||
return $aFileStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sFilename
|
||||
*
|
||||
* @param bool $bGetDirSize
|
||||
*
|
||||
* @return array
|
||||
* @throws \Combodo\iTop\FilesInformation\Service\FileNotExistException
|
||||
*/
|
||||
public static function GetFileStat($sFilename, $bGetDirSize = true)
|
||||
{
|
||||
$sFile = realpath(APPROOT.$sFilename);
|
||||
$aStats = @stat($sFile);
|
||||
if (!$aStats)
|
||||
{
|
||||
throw new FileNotExistException($sFilename);
|
||||
}
|
||||
|
||||
$aFileStats = array();
|
||||
$aFileStats['name'] = $sFilename;
|
||||
$aFileStats['size'] = $aStats['size'];
|
||||
|
||||
if (is_dir($sFile))
|
||||
{
|
||||
// Special dir case
|
||||
if ($bGetDirSize)
|
||||
{
|
||||
// The size is computed by aggregating the sizes on the children
|
||||
$aFileStats['size'] = self::GetDirSize($sFile);
|
||||
}
|
||||
}
|
||||
|
||||
$aFileStats['display_size'] = utils::BytesToFriendlyFormat($aFileStats['size']);
|
||||
|
||||
$aFileStats['perms'] = sprintf("0%o", 0777 & $aStats['mode']);
|
||||
$aFileStats['mode'] = $aStats['mode'];
|
||||
|
||||
$aTypes = array(
|
||||
0140000=>'socket',
|
||||
0120000=>'link',
|
||||
0100000=>'file',
|
||||
0060000=>'block',
|
||||
0040000=>'dir',
|
||||
0020000=>'char',
|
||||
0010000=>'fifo'
|
||||
);
|
||||
$iRawMode = $aStats['mode'];
|
||||
$iMode = decoct($iRawMode & 0170000); // File Encoding Bit
|
||||
|
||||
$sDisplayMode =(array_key_exists(octdec($iMode),$aTypes))?$aTypes[octdec($iMode)]{0}:'u';
|
||||
$sDisplayMode.=(($iRawMode&0x0100)?'r':'-').(($iRawMode&0x0080)?'w':'-');
|
||||
$sDisplayMode.=(($iRawMode&0x0040)?(($iRawMode&0x0800)?'s':'x'):(($iRawMode&0x0800)?'S':'-'));
|
||||
$sDisplayMode.=(($iRawMode&0x0020)?'r':'-').(($iRawMode&0x0010)?'w':'-');
|
||||
$sDisplayMode.=(($iRawMode&0x0008)?(($iRawMode&0x0400)?'s':'x'):(($iRawMode&0x0400)?'S':'-'));
|
||||
$sDisplayMode.=(($iRawMode&0x0004)?'r':'-').(($iRawMode&0x0002)?'w':'-');
|
||||
$sDisplayMode.=(($iRawMode&0x0001)?(($iRawMode&0x0200)?'t':'x'):(($iRawMode&0x0200)?'T':'-'));
|
||||
|
||||
$aFileStats['display_mode'] = $sDisplayMode;
|
||||
$aFileStats['type'] = $aTypes[octdec($iMode)];
|
||||
$aFileStats['readable'] = is_readable($sFile);
|
||||
$aFileStats['writable'] = is_writable($sFile);
|
||||
$aFileStats['file_owner'] = $aStats['uid'];
|
||||
$aFileStats['file_group'] = $aStats['gid'];
|
||||
if (function_exists('posix_getpwuid'))
|
||||
{
|
||||
$aPwUid = @posix_getpwuid($aStats['uid']);
|
||||
if (isset($aPwUid['name']))
|
||||
{
|
||||
$aFileStats['owner_name'] = $aPwUid['name'];
|
||||
}
|
||||
}
|
||||
if (empty($aFileStats['owner_name']))
|
||||
{
|
||||
$aFileStats['owner_name'] = '';
|
||||
}
|
||||
if (function_exists('posix_getgrgid'))
|
||||
{
|
||||
$aGrGid = @posix_getgrgid($aStats['gid']);
|
||||
if (isset($aGrGid['name']))
|
||||
{
|
||||
$aFileStats['group_name'] = $aGrGid['name'];
|
||||
}
|
||||
}
|
||||
if (empty($aFileStats['group_name']))
|
||||
{
|
||||
$aFileStats['group_name'] = '';
|
||||
}
|
||||
$aFileStats['mtime'] = date('Y-m-d H:i:s', $aStats['mtime']);
|
||||
$aFileStats['ctime'] = date('Y-m-d H:i:s', $aStats['ctime']);
|
||||
|
||||
return $aFileStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sPath relative iTop path
|
||||
*
|
||||
* @return string absolute path
|
||||
* @throws \Combodo\iTop\FilesInformation\Service\FileNotExistException
|
||||
*/
|
||||
public static function GetAbsolutePath($sPath)
|
||||
{
|
||||
$sRootPath = realpath(APPROOT);
|
||||
$sFullPath = realpath($sRootPath.DIRECTORY_SEPARATOR.$sPath);
|
||||
if (($sFullPath === false) || !utils::StartsWith($sFullPath, $sRootPath))
|
||||
{
|
||||
throw new FileNotExistException($sPath);
|
||||
}
|
||||
return $sFullPath;
|
||||
}
|
||||
|
||||
public static function GetDirSize($sRealRootPath)
|
||||
{
|
||||
$aFiles = scandir($sRealRootPath);
|
||||
$iSize = 0;
|
||||
foreach ($aFiles as $sScanFile)
|
||||
{
|
||||
if (($sScanFile == '.') || ($sScanFile == '..'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$sFile = $sRealRootPath.DIRECTORY_SEPARATOR.$sScanFile;
|
||||
if (is_dir($sFile))
|
||||
{
|
||||
$iSize += self::GetDirSize($sFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aStats = @stat($sFile);
|
||||
$iSize += $aStats['size'];
|
||||
}
|
||||
}
|
||||
return $iSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
namespace Combodo\iTop\FilesInformation\Service;
|
||||
|
||||
|
||||
use Dict;
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use DOMXPath;
|
||||
|
||||
class FilesIntegrity
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the files defined in the manifest.xml
|
||||
*
|
||||
* @param string $sManifest full path of the manifest file
|
||||
*
|
||||
* @return array|false list of file info (path, size, md5)
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function GetInstalledFiles($sManifest)
|
||||
{
|
||||
$aFiles = array();
|
||||
|
||||
$aManifestStats = @stat($sManifest);
|
||||
if ($aManifestStats === false)
|
||||
{
|
||||
// No manifest
|
||||
return false;
|
||||
}
|
||||
|
||||
$oManifestDocument = new DOMDocument('1.0', 'UTF-8');
|
||||
@$oManifestDocument->load($sManifest);
|
||||
$oXPath = new DOMXPath($oManifestDocument);
|
||||
$oNodeList = $oXPath->query('/files');
|
||||
if ($oNodeList->length == 0)
|
||||
{
|
||||
// no files
|
||||
return false;
|
||||
}
|
||||
foreach ($oNodeList as $oItems)
|
||||
{
|
||||
foreach ($oItems->childNodes as $oFileNode)
|
||||
{
|
||||
if (($oFileNode instanceof DOMNode))
|
||||
{
|
||||
if ($oFileNode->hasChildNodes())
|
||||
{
|
||||
$aFileInfo = array();
|
||||
foreach ($oFileNode->childNodes as $oFileInfo)
|
||||
{
|
||||
if ($oFileInfo instanceof DOMElement)
|
||||
{
|
||||
$aFileInfo[$oFileInfo->tagName] = $oFileInfo->textContent;
|
||||
}
|
||||
}
|
||||
$aFiles[] = $aFileInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that files present in iTop folder corresponds to the manifest
|
||||
*
|
||||
* @param string $sRootPath
|
||||
*
|
||||
* @throws \Combodo\iTop\FilesInformation\Service\FileIntegrityException
|
||||
*/
|
||||
public static function CheckInstallationIntegrity($sRootPath = APPROOT)
|
||||
{
|
||||
$aFilesInfo = FilesIntegrity::GetInstalledFiles($sRootPath.'manifest.xml');
|
||||
|
||||
if ($aFilesInfo === false)
|
||||
{
|
||||
throw new FileIntegrityException(Dict::Format('FilesInformation:Error:MissingFile', 'manifest.xml'));
|
||||
}
|
||||
|
||||
@clearstatcache();
|
||||
foreach ($aFilesInfo as $aFileInfo)
|
||||
{
|
||||
$sFile = $sRootPath.$aFileInfo['path'];
|
||||
if (is_file($sFile))
|
||||
{
|
||||
$aStats = @stat($sFile);
|
||||
$iSize = $aStats['size'];
|
||||
$sContent = file_get_contents($sFile);
|
||||
$sChecksum = md5($sContent);
|
||||
if (($iSize != $aFileInfo['size']) || ($sChecksum != $aFileInfo['md5']))
|
||||
{
|
||||
|
||||
throw new FileIntegrityException(Dict::Format('FilesInformation:Error:CorruptedFile', basename($sFile)));
|
||||
}
|
||||
}
|
||||
// Packed with missing files...
|
||||
}
|
||||
}
|
||||
|
||||
public static function IsInstallationConform($sRootPath, &$sErrorMsg)
|
||||
{
|
||||
$sErrorMsg = '';
|
||||
try
|
||||
{
|
||||
self::CheckInstallationIntegrity($sRootPath);
|
||||
return true;
|
||||
}
|
||||
catch (FileIntegrityException $e)
|
||||
{
|
||||
$sErrorMsg = $e->getMessage();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
7
datamodels/2.x/itop-twig-base/README.md
Normal file
7
datamodels/2.x/itop-twig-base/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# itop-twig-base
|
||||
|
||||
Provide Twig service to other modules.
|
||||
|
||||
BEWARE: This feature is subject to change! The API if not guaranteed future versions may break the compatibility.
|
||||
|
||||
namespace Combodo\iTop\TwigBase;
|
||||
1
datamodels/2.x/itop-twig-base/index.php
Normal file
1
datamodels/2.x/itop-twig-base/index.php
Normal file
@@ -0,0 +1 @@
|
||||
<?php
|
||||
1
datamodels/2.x/itop-twig-base/model.itop-twig-base.php
Normal file
1
datamodels/2.x/itop-twig-base/model.itop-twig-base.php
Normal file
@@ -0,0 +1 @@
|
||||
<?php
|
||||
47
datamodels/2.x/itop-twig-base/module.itop-twig-base.php
Normal file
47
datamodels/2.x/itop-twig-base/module.itop-twig-base.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
//
|
||||
// iTop module definition file
|
||||
//
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-twig-base/1.0.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
'label' => 'iTop Twig Base',
|
||||
'category' => 'business',
|
||||
|
||||
// Setup
|
||||
//
|
||||
'dependencies' => array(),
|
||||
'mandatory' => false,
|
||||
'visible' => false,
|
||||
|
||||
// Components
|
||||
//
|
||||
'datamodel' => array(
|
||||
'model.itop-twig-base.php',
|
||||
'src/Controller/Controller.php',
|
||||
'src/Twig/Extension.php',
|
||||
'src/Twig/TwigHelper.php',
|
||||
),
|
||||
'webservice' => array(),
|
||||
'data.struct' => array(),
|
||||
'data.sample' => array(),
|
||||
|
||||
// Documentation
|
||||
//
|
||||
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any
|
||||
'doc.more_information' => '', // hyperlink to more information, if any
|
||||
|
||||
// Default settings
|
||||
//
|
||||
'settings' => array(),
|
||||
)
|
||||
);
|
||||
470
datamodels/2.x/itop-twig-base/src/Controller/Controller.php
Normal file
470
datamodels/2.x/itop-twig-base/src/Controller/Controller.php
Normal file
@@ -0,0 +1,470 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\TwigBase\Controller;
|
||||
|
||||
use ajax_page;
|
||||
use ApplicationMenu;
|
||||
use Combodo\iTop\TwigBase\Twig\TwigHelper;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use iTopWebPage;
|
||||
use LoginWebPage;
|
||||
use MetaModel;
|
||||
use ReflectionClass;
|
||||
use SetupPage;
|
||||
use SetupUtils;
|
||||
use Twig_Error;
|
||||
use utils;
|
||||
use ZipArchive;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
/** @var \Twig\Environment */
|
||||
private $m_oTwig;
|
||||
/** @var string */
|
||||
private $m_sOperation;
|
||||
/** @var string */
|
||||
private $m_sModule;
|
||||
/** @var iTopWebPage|\ajax_page */
|
||||
private $m_oPage;
|
||||
/** @var bool */
|
||||
private $m_bCheckDemoMode = false;
|
||||
/** @var bool */
|
||||
private $m_bMustBeAdmin = false;
|
||||
/** @var string */
|
||||
private $m_sMenuId = null;
|
||||
/** @var string */
|
||||
private $m_sDefaultOperation = 'Default';
|
||||
private $m_aLinkedScripts;
|
||||
private $m_aLinkedStylesheets;
|
||||
private $m_aAjaxTabs;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$sModulePath = dirname(dirname($this->getDir()));
|
||||
$this->m_sModule = basename($sModulePath);
|
||||
$oTwig = TwigHelper::GetTwigEnvironment($sModulePath.'/view');
|
||||
$this->m_oTwig = $oTwig;
|
||||
$this->m_aLinkedScripts = array();
|
||||
$this->m_aLinkedStylesheets = array();
|
||||
$this->m_aAjaxTabs = array();
|
||||
}
|
||||
|
||||
private function getDir()
|
||||
{
|
||||
return dirname((new ReflectionClass(static::class))->getFileName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point to handle requests
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function HandleOperation()
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->CheckAccess();
|
||||
$this->m_sOperation = utils::ReadParam('operation', $this->m_sDefaultOperation);
|
||||
|
||||
$sMethodName = 'Operation'.$this->m_sOperation;
|
||||
if (method_exists($this, $sMethodName))
|
||||
{
|
||||
$this->$sMethodName();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->DisplayPageNotFound();
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
require_once(APPROOT."/setup/setuppage.class.inc.php");
|
||||
|
||||
http_response_code(500);
|
||||
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
|
||||
$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");
|
||||
$oP->add(get_class($e).' : '.htmlentities($e->GetMessage(), ENT_QUOTES, 'utf-8'));
|
||||
$oP->output();
|
||||
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridable "page not found" which is more an "operation not found"
|
||||
*/
|
||||
public function DisplayPageNotFound()
|
||||
{
|
||||
http_response_code(404);
|
||||
die("Page not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function CheckAccess()
|
||||
{
|
||||
if ($this->m_bCheckDemoMode && MetaModel::GetConfig()->Get('demo_mode'))
|
||||
{
|
||||
throw new Exception("Sorry, iTop is in <b>demonstration mode</b>: this feature is disabled.");
|
||||
}
|
||||
|
||||
LoginWebPage::DoLogin($this->m_bMustBeAdmin);
|
||||
if (!empty($this->m_sMenuId))
|
||||
{
|
||||
ApplicationMenu::CheckMenuIdEnabled($this->m_sMenuId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function GetDefaultParameters()
|
||||
{
|
||||
$aParams = array();
|
||||
$aParams['sIndexURL'] = utils::GetAbsoluteUrlModulePage($this->m_sModule, 'index.php');
|
||||
|
||||
return $aParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable this feature if in demo mode
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function DisableInDemoMode()
|
||||
{
|
||||
$this->m_bCheckDemoMode = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow only admin users for this feature
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function AllowOnlyAdmin()
|
||||
{
|
||||
$this->m_bMustBeAdmin = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Id of the menu to check for user access rights
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sMenuId
|
||||
*/
|
||||
public function SetMenuId($sMenuId)
|
||||
{
|
||||
$this->m_sMenuId = $sMenuId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default operation when no 'operation' parameter is given on URL
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sDefaultOperation
|
||||
*/
|
||||
public function SetDefaultOperation($sDefaultOperation)
|
||||
{
|
||||
$this->m_sDefaultOperation = $sDefaultOperation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an AJAX page (ajax_page)
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param array $aParams Params used by the twig template
|
||||
* @param null $sTemplateName Name of the twig template, ie MyTemplate for MyTemplate.html.twig
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function DisplayAjaxPage($aParams = array(), $sTemplateName = null)
|
||||
{
|
||||
$this->DisplayPage($aParams, $sTemplateName, 'ajax');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the twig page based on the name or the operation
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param array $aParams Params used by the twig template
|
||||
* @param string $sTemplateName Name of the twig template, ie MyTemplate for MyTemplate.html.twig
|
||||
* @param string $sPageType ('html' or 'ajax')
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function DisplayPage($aParams = array(), $sTemplateName = null, $sPageType = 'html')
|
||||
{
|
||||
if (empty($sTemplateName))
|
||||
{
|
||||
$sTemplateName = $this->m_sOperation;
|
||||
}
|
||||
$aParams = array_merge($this->GetDefaultParameters(), $aParams);
|
||||
$this->CreatePage($sPageType);
|
||||
$this->AddToPage($this->RenderTemplate($aParams, $sTemplateName, 'html'));
|
||||
$this->AddScriptToPage($this->RenderTemplate($aParams, $sTemplateName, 'js'));
|
||||
$this->AddReadyScriptToPage($this->RenderTemplate($aParams, $sTemplateName, 'ready.js'));
|
||||
if (!empty($this->m_aAjaxTabs))
|
||||
{
|
||||
$this->m_oPage->AddTabContainer('');
|
||||
$this->m_oPage->SetCurrentTabContainer('');
|
||||
}
|
||||
foreach ($this->m_aAjaxTabs as $aTab)
|
||||
{
|
||||
$this->AddAjaxTabToPage($aTab['label'], $aTab['url'], $aTab['cache']);
|
||||
}
|
||||
foreach ($this->m_aLinkedScripts as $sLinkedScript)
|
||||
{
|
||||
$this->AddLinkedScriptToPage($sLinkedScript);
|
||||
}
|
||||
foreach ($this->m_aLinkedStylesheets as $sLinkedStylesheet)
|
||||
{
|
||||
$this->AddLinkedStylesheetToPage($sLinkedStylesheet);
|
||||
}
|
||||
$this->OutputPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a JSON response
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param array $aParams Content of the response, will be converted to JSON
|
||||
* @param int $iResponseCode HTTP response code
|
||||
* @param array $aHeaders additional HTTP headers
|
||||
*/
|
||||
public function DisplayJSONPage($aParams = array(), $iResponseCode = 200, $aHeaders = array())
|
||||
{
|
||||
http_response_code($iResponseCode);
|
||||
header('Content-Type: application/json');
|
||||
foreach ($aHeaders as $sHeader)
|
||||
{
|
||||
header($sHeader);
|
||||
}
|
||||
echo json_encode($aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a page, zip it and propose the zipped file for download
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param array $aParams Params used by the twig template
|
||||
* @param null $sTemplateName Name of the twig template, ie MyTemplate for MyTemplate.html.twig
|
||||
*/
|
||||
public function DownloadZippedPage($aParams = array(), $sTemplateName = null)
|
||||
{
|
||||
if (empty($sTemplateName))
|
||||
{
|
||||
$sTemplateName = $this->m_sOperation;
|
||||
}
|
||||
$sReportFolder = str_replace("\\", '/', APPROOT.'log/');
|
||||
$sReportFile = 'itop-system-information-report-'.date('Y-m-d-H-i-s');
|
||||
$sHTMLReport = $sReportFolder.$sReportFile.'.html';
|
||||
$sZIPReportFile = $sReportFile;
|
||||
|
||||
file_put_contents($sHTMLReport, $this->RenderTemplate($aParams, $sTemplateName, 'html'));
|
||||
|
||||
$this->ZipDownloadRemoveFile(array($sHTMLReport), $sZIPReportFile, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an archive and launch download, remove original file and archive when done
|
||||
*
|
||||
* @param string[] $aFiles
|
||||
* @param string $sDownloadArchiveName file name to download, without the extension (.zip is automatically added)
|
||||
* @param bool $bUnlinkFiles if true then will unlink each source file
|
||||
*/
|
||||
final protected function ZipDownloadRemoveFile($aFiles, $sDownloadArchiveName, $bUnlinkFiles = false)
|
||||
{
|
||||
$sArchiveFileFullPath = tempnam(SetupUtils::GetTmpDir(), 'itop_download-').'.zip';
|
||||
$oArchive = new ZipArchive();
|
||||
$oArchive->open($sArchiveFileFullPath, ZipArchive::CREATE);
|
||||
foreach ($aFiles as $sFile)
|
||||
{
|
||||
$oArchive->addFile($sFile, basename($sFile));
|
||||
}
|
||||
$oArchive->close();
|
||||
|
||||
if ($bUnlinkFiles)
|
||||
{
|
||||
foreach ($aFiles as $sFile)
|
||||
{
|
||||
unlink($sFile);
|
||||
}
|
||||
}
|
||||
|
||||
$this->SendFileContent($sArchiveFileFullPath, $sDownloadArchiveName.'.zip', true, true);
|
||||
}
|
||||
|
||||
final protected function SendFileContent($sFilePath, $sDownloadArchiveName = null, $bFileTransfer = true, $bRemoveFile = false, $aHeaders = array())
|
||||
{
|
||||
$sFileMimeType = utils::GetFileMimeType($sFilePath);
|
||||
header('Content-Type: '.$sFileMimeType);
|
||||
|
||||
if ($bFileTransfer)
|
||||
{
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Disposition: inline; filename="'.$sDownloadArchiveName);
|
||||
}
|
||||
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
|
||||
foreach ($aHeaders as $sKey => $sValue)
|
||||
{
|
||||
header($sKey.': '.$sValue);
|
||||
}
|
||||
|
||||
header('Content-Length: '.filesize($sFilePath));
|
||||
|
||||
readfile($sFilePath);
|
||||
|
||||
if ($bRemoveFile)
|
||||
{
|
||||
unlink($sFilePath);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a linked script to the current Page
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sScript Script path to link
|
||||
*/
|
||||
public function AddLinkedScript($sScript)
|
||||
{
|
||||
$this->m_aLinkedScripts[] = $sScript;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an linked stylesheet to the current Page
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sStylesheet Stylesheet path to link
|
||||
*/
|
||||
public function AddLinkedStylesheet($sStylesheet)
|
||||
{
|
||||
$this->m_aLinkedStylesheets[] = $sStylesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an AJAX tab to the current page
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sLabel Label of the tab
|
||||
* @param string $sURL URL to call when the tab is activated
|
||||
* @param bool $bCache If true, cache the result for the current web page
|
||||
*/
|
||||
public function AddAjaxTab($sLabel, $sURL, $bCache = true)
|
||||
{
|
||||
$this->m_aAjaxTabs[] = array('label' => $sLabel, 'url' => $sURL, 'cache' => $bCache);
|
||||
}
|
||||
|
||||
private function RenderTemplate($aParams, $sName, $sTemplateFileExtension)
|
||||
{
|
||||
try
|
||||
{
|
||||
return $this->m_oTwig->render($sName.'.'.$sTemplateFileExtension.'.twig', $aParams);
|
||||
}
|
||||
catch (Twig_Error $e)
|
||||
{
|
||||
// Ignore errors
|
||||
if (!utils::StartsWith($e->getMessage(), 'Unable to find template'))
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sPageType
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function CreatePage($sPageType)
|
||||
{
|
||||
switch ($sPageType)
|
||||
{
|
||||
case 'html':
|
||||
$this->m_oPage = new iTopWebPage($this->GetOperationTitle());
|
||||
break;
|
||||
|
||||
case 'ajax':
|
||||
$this->m_oPage = new ajax_page($this->GetOperationTitle());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of the operation
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetOperationTitle()
|
||||
{
|
||||
return Dict::S($this->m_sModule.':UI:'.$this->m_sOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sContent
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function AddToPage($sContent)
|
||||
{
|
||||
$this->m_oPage->add($sContent);
|
||||
}
|
||||
|
||||
private function AddReadyScriptToPage($sScript)
|
||||
{
|
||||
$this->m_oPage->add_ready_script($sScript);
|
||||
}
|
||||
|
||||
private function AddScriptToPage($sScript)
|
||||
{
|
||||
$this->m_oPage->add_script($sScript);
|
||||
}
|
||||
|
||||
private function AddLinkedScriptToPage($sLinkedScript)
|
||||
{
|
||||
$this->m_oPage->add_linked_script($sLinkedScript);
|
||||
}
|
||||
|
||||
private function AddLinkedStylesheetToPage($sLinkedStylesheet)
|
||||
{
|
||||
$this->m_oPage->add_linked_stylesheet($sLinkedStylesheet);
|
||||
}
|
||||
|
||||
private function AddAjaxTabToPage($sLabel, $sURL, $bCache)
|
||||
{
|
||||
$this->m_oPage->AddAjaxTab($sLabel, $sURL, $bCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function OutputPage()
|
||||
{
|
||||
$this->m_oPage->output();
|
||||
}
|
||||
}
|
||||
132
datamodels/2.x/itop-twig-base/src/Twig/Extension.php
Normal file
132
datamodels/2.x/itop-twig-base/src/Twig/Extension.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\TwigBase\Twig;
|
||||
|
||||
|
||||
use AttributeDateTime;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use Twig_Environment;
|
||||
use Twig_SimpleFilter;
|
||||
use Twig_SimpleFunction;
|
||||
use utils;
|
||||
|
||||
class Extension
|
||||
{
|
||||
/**
|
||||
* Registers Twig extensions such as filters or functions.
|
||||
* It allows us to access some stuff directly in twig.
|
||||
*
|
||||
* @param \Twig_Environment $oTwigEnv
|
||||
*/
|
||||
public static function RegisterTwigExtensions(Twig_Environment &$oTwigEnv)
|
||||
{
|
||||
// Filter to translate a string via the Dict::S function
|
||||
// Usage in twig: {{ 'String:ToTranslate'|dict_s }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_s',
|
||||
function ($sStringCode, $sDefault = null, $bUserLanguageOnly = false) {
|
||||
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to format a string via the Dict::Format function
|
||||
// Usage in twig: {{ 'String:ToTranslate'|dict_format() }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_format',
|
||||
function ($sStringCode, $sParam01 = null, $sParam02 = null, $sParam03 = null, $sParam04 = null) {
|
||||
return Dict::Format($sStringCode, $sParam01, $sParam02, $sParam03, $sParam04);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to format output
|
||||
// example a DateTime is converted to user format
|
||||
// Usage in twig: {{ 'String:ToFormat'|output_format }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('date_format',
|
||||
function ($sDate) {
|
||||
try
|
||||
{
|
||||
if (preg_match('@^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$@', trim($sDate)))
|
||||
{
|
||||
return AttributeDateTime::GetFormat()->Format($sDate);
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
}
|
||||
|
||||
return $sDate;
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
// Filter to format output
|
||||
// example a DateTime is converted to user format
|
||||
// Usage in twig: {{ 'String:ToFormat'|output_format }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('size_format',
|
||||
function ($sSize) {
|
||||
return utils::BytesToFriendlyFormat($sSize);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to enable base64 encode/decode
|
||||
// Usage in twig: {{ 'String to encode'|base64_encode }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_encode', 'base64_encode'));
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_decode', 'base64_decode'));
|
||||
|
||||
// Filter to enable json decode (encode already exists)
|
||||
// Usage in twig: {{ aSomeArray|json_decode }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('json_decode', function ($sJsonString, $bAssoc = false) {
|
||||
return json_decode($sJsonString, $bAssoc);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to add itopversion to an url
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_itop_version', function ($sUrl) {
|
||||
if (strpos($sUrl, '?') === false)
|
||||
{
|
||||
$sUrl = $sUrl."?itopversion=".ITOP_VERSION;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUrl = $sUrl."&itopversion=".ITOP_VERSION;
|
||||
}
|
||||
|
||||
return $sUrl;
|
||||
}));
|
||||
|
||||
// Filter to add a module's version to an url
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_module_version', function ($sUrl, $sModuleName) {
|
||||
$sModuleVersion = utils::GetCompiledModuleVersion($sModuleName);
|
||||
|
||||
if (strpos($sUrl, '?') === false)
|
||||
{
|
||||
$sUrl = $sUrl."?moduleversion=".$sModuleVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUrl = $sUrl."&moduleversion=".$sModuleVersion;
|
||||
}
|
||||
|
||||
return $sUrl;
|
||||
}));
|
||||
|
||||
// Function to check our current environment
|
||||
// Usage in twig: {% if is_development_environment() %}
|
||||
$oTwigEnv->addFunction(new Twig_SimpleFunction('is_development_environment', function () {
|
||||
return utils::IsDevelopmentEnvironment();
|
||||
}));
|
||||
|
||||
// Function to get configuration parameter
|
||||
// Usage in twig: {{ get_config_parameter('foo') }}
|
||||
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_config_parameter', function ($sParamName) {
|
||||
$oConfig = MetaModel::GetConfig();
|
||||
|
||||
return $oConfig->Get($sParamName);
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
25
datamodels/2.x/itop-twig-base/src/Twig/TwigHelper.php
Normal file
25
datamodels/2.x/itop-twig-base/src/Twig/TwigHelper.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\TwigBase\Twig;
|
||||
|
||||
use Twig_Environment;
|
||||
use Twig_Loader_Filesystem;
|
||||
|
||||
@include_once(APPROOT.'/lib/silex/vendor/autoload.php');
|
||||
|
||||
|
||||
class TwigHelper
|
||||
{
|
||||
public static function GetTwigEnvironment($sViewPath)
|
||||
{
|
||||
$oLoader = new Twig_Loader_Filesystem($sViewPath);
|
||||
$oTwig = new Twig_Environment($oLoader);
|
||||
Extension::RegisterTwigExtensions($oTwig);
|
||||
|
||||
return $oTwig;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user