| Current Path : /var/www/html/maiwp.bak/backend/modules/mmngr/controllers/ |
| Current File : /var/www/html/maiwp.bak/backend/modules/mmngr/controllers/ModuleImportController.php |
<?php
namespace backend\modules\mmngr\controllers;
use Yii;
use yii\web\Controller;
use backend\modules\mmngr\models\ModuleImportSearch;
use backend\modules\mmngr\models\ModuleImport;
use yii\web\UploadedFile;
use ZipArchive;
use Exception;
/**
* Module for import external module trough zip file.
*/
class ModuleImportController extends Controller
{
public $container;
public $modulePath = '/../../backend/modules';
public $moduleClass = 'backend\modules';
public $moduleClassName = 'Module';
public $metaFileName = 'meta.json';
public $moduleIdName = 'module_id';
public $metaModuleName = 'Module.php';
public $imageDir = 'images/modules';
public function actionIndex()
{
$searchModel = new ModuleImportSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'imageDir' => $this->getImgDir()
]);
}
public function actionImport()
{
$m = new ModuleImport();
$meta = null;
if (Yii::$app->request->isPost) {
try {
$m->file = UploadedFile::getInstance($m, 'file');
$zip = new ZipArchive();
$status = $zip->open($m->file->tempName);
if ($status !== TRUE) {
throw new Exception($this->ZipStatusString($status));
}
$filearr = null;
if ($zip->numFiles > 0) {
for ($i = 0; $i < $zip->numFiles; $i ++) {
// get container name
if ($i === 0) {
// fix problem with zip without container
list($zipname) = explode('/', $zip->getNameIndex($i));
$this->container = $zipname . '/';
}
$filename = $zip->getNameIndex($i);
$stat = $zip->statName($filename);
if (trim(strtolower($filename)) === trim(strtolower($this->container . $this->metaFileName))) {
$content = $zip->getFromName($filename);
$meta = $this->checkMetaContent($content);
}
// get an array of filename
if (! empty($filename) && $stat['size'] > 0) {
$filearr[] = trim(strtolower($filename));
}
}
if (! empty($filearr)) {
$this->checkRequiredFile($filearr);
}
if (! empty($meta)) {
$version = $this->saveMeta($meta, $zip);
}
$zip->extractTo(Yii::getAlias('@webroot') . $this->modulePath);
$zip->close();
} else {
throw new Exception('Empty Zip file');
}
if ($version) {
Yii::$app->session->setFlash('success', 'Module Has been successfully Upgrade to ' . $version);
} else {
Yii::$app->session->setFlash('success', 'Module Has been successfully imported!. You may check it in listing below.');
}
return $this->asJson([
'success' => true,
'message' => 'ok!'
]);
} catch (Exception $e) {
Yii::$app->response->statusCode = 500;
return $this->asJson([
'success' => false,
'message' => $e->getMessage()
]);
}
}
}
public function actionRemove(int $id)
{
$m = ModuleImport::findOne($id);
//run down sql
$data = json_decode($m->data);
if (!empty($data->sql->down)) {
$this->downSql($data->sql->down);
}
//remove folder
try {
$this->deleteDir( Yii::getAlias('@webroot') . $this->modulePath . '/' . $m->module_id);
$m->delete();
Yii::$app->session->setFlash('success', 'Module Has Been removed');
return $this->redirect(['/mmngr/module-import/']);
} catch (\Exception $e) {
Yii::$app->session->setFlash('danger', $e->getMessage());
return $this->redirect(['/mmngr/module-import/']);
}
}
private function checkMetaContent($content)
{
$meta = json_decode($content);
if (json_last_error()) {
throw new Exception('Error When Parsing Json! ' . json_last_error_msg());
}
if (! property_exists($meta, $this->moduleIdName)) {
throw new Exception('Missing required meta attribute id');
}
if (! property_exists($meta, 'version')) {
throw new Exception('Missing required meta attribute version');
}
if (trim(strtolower($meta->{$this->moduleIdName})) !== trim(strtolower($this->container), '/')) {
throw new Exception('Module id must be the same with module container name');
}
if (preg_match('/[^a-zA-Z0-9\']/', trim(strtolower($meta->{$this->moduleIdName})))) {
throw new Exception('Invalide Module Id, module id must alphanumeric not symbol allow');
}
$m = ModuleImport::checkIdExist(trim(strtolower($meta->module_id)), trim(strtolower($meta->version)));
if (! $m) {
throw new Exception('This version less than the current version');
}
return $meta;
}
/**
* Process meta from meta file
* @param string $meta Json string for meta
* @param object $zip
* @throws Exception
* @return string | boolean return version on upgrade
*/
private function saveMeta($meta, $zip)
{
$upgrade = $this->replaceMeta($meta->{$this->moduleIdName});
$m = new ModuleImport();
foreach (array_keys($m->attributeLabels()) as $key) {
if (property_exists($meta, $key)) {
$item = $meta->{$key};
if (is_string($item)) {
$m->{$key} = $item;
} else if (is_array($item)) {
$m->{$key} = json_encode($item);
} else if (is_object($item)) {
$m->{$key} = json_encode($item);
}
}
}
if (property_exists($meta, 'image_path')) {
if (is_array($meta->image_path)) {
$img = $this->saveImage($meta->image_path, $zip);
if (! empty($img)) {
$m->image_path = json_encode($img);
}
}
}
$m->class = "{$this->moduleClass}\\{$meta->module_id}\\{$this->moduleClassName}";
if (property_exists($meta, 'data') && property_exists($meta->data, 'sql')) {
$sql = $meta->data->sql;
if (! empty($sql->up)) {
$query = $this->upSql($sql->up, $zip);
} else {
throw new Exception('Up properties missing for run SQL up on Sql Meta');
}
}
Yii::$app->db->transaction(function($db) use ($query, $m) {
try {
$db->createCommand($query)->execute();
} catch(\Throwable $t) {
throw new Exception('Error Saving Module From Meta :-\n' . $t->getMessage());
}
if (! $m->save()) {
throw new Exception('Error Saving Module From Meta' . json_encode($m->errors));
}
});
if ($upgrade) {
return $meta->version;
} else {
return false;
}
}
/***
* Replace Meta for module import
* @param string $moduleId
* @return boolean true if module is upgrade
*/
private function replaceMeta($moduleId){
$m = ModuleImport::find()->where(['module_id' => $moduleId])->one();
if (!empty($m)) {
$data = json_decode($m->data);
if (!empty($data->sql->down)) {
$this->downSql($data->sql->down);
}
$m->delete();
return true;
}
return false;
}
private function upSql($filename, $zip)
{
$sql = $zip->getFromName($filename);
if (! empty($sql)) {
return $sql;
} else {
throw new \Exception('Cannot read sql file');
}
}
private function downSql($data){
try {
$down = file_get_contents(Yii::getAlias('@webroot') . $this->modulePath . '/' . $data);
Yii::$app->db->createCommand($down)->execute();
} catch (\Exception $e){
Yii::$app->session->setFlash('danger', $e->getMessage());
return $this->redirect(['/mmngr/module-import/']);
}
}
private function saveImage($images, $zip)
{
$dir = Yii::getAlias('@webroot') . '/' . $this->imageDir;
if (! is_dir($dir)) {
mkdir($dir, 0777, true);
}
if (is_array($images)) {
$imgarr = [];
foreach ($images as $image) {
$img = $zip->getFromName($image);
$im = imagecreatefromstring($img);
$imgrand = preg_replace('/[^a-zA-Z0-9\']/', '_', $this->container . microtime());
$imgarr[] = $imgrand . '.jpg';
imagejpeg($im, $dir . '/' . $imgrand . '.jpg');
}
return $imgarr;
}
}
private function checkRequiredFile(array $filearr)
{
if (! in_array(trim(strtolower($this->container . $this->metaFileName)), $filearr)) {
throw new Exception('Missing meta.json file!');
}
if (! in_array(trim(strtolower($this->container . $this->metaModuleName)), $filearr)) {
throw new Exception('Missing Module.php file!');
}
}
private function ZipStatusString($status)
{
switch ((int) $status) {
case ZipArchive::ER_OK:
return 'N No error';
case ZipArchive::ER_MULTIDISK:
return 'N Multi-disk zip archives not supported';
case ZipArchive::ER_RENAME:
return 'S Renaming temporary file failed';
case ZipArchive::ER_CLOSE:
return 'S Closing zip archive failed';
case ZipArchive::ER_SEEK:
return 'S Seek error';
case ZipArchive::ER_READ:
return 'S Read error';
case ZipArchive::ER_WRITE:
return 'S Write error';
case ZipArchive::ER_CRC:
return 'N CRC error';
case ZipArchive::ER_ZIPCLOSED:
return 'N Containing zip archive was closed';
case ZipArchive::ER_NOENT:
return 'N No such file';
case ZipArchive::ER_EXISTS:
return 'N File already exists';
case ZipArchive::ER_OPEN:
return 'S Can\'t open file';
case ZipArchive::ER_TMPOPEN:
return 'S Failure to create temporary file';
case ZipArchive::ER_ZLIB:
return 'Z Zlib error';
case ZipArchive::ER_MEMORY:
return 'N Malloc failure';
case ZipArchive::ER_CHANGED:
return 'N Entry has been changed';
case ZipArchive::ER_COMPNOTSUPP:
return 'N Compression method not supported';
case ZipArchive::ER_EOF:
return 'N Premature EOF';
case ZipArchive::ER_INVAL:
return 'N Invalid argument';
case ZipArchive::ER_NOZIP:
return 'N Not a zip archive';
case ZipArchive::ER_INTERNAL:
return 'N Internal error';
case ZipArchive::ER_INCONS:
return 'N Zip archive inconsistent';
case ZipArchive::ER_REMOVE:
return 'S Can\'t remove file';
case ZipArchive::ER_DELETED:
return 'N Entry has been deleted';
default:
return sprintf('Unknown status %s', $status);
}
}
private function deleteDir($dirPath)
{
if (! is_dir($dirPath)) {
throw new Exception("$dirPath must be a directory");
}
if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
$dirPath .= '/';
}
$files = scandir($dirPath);
foreach ($files as $file) {
if ($file === '.' || $file === '..')
continue;
if (is_dir($dirPath . $file)) {
$this->deleteDir($dirPath . $file);
} else {
if ($dirPath . $file !== __FILE__) {
unlink($dirPath . $file);
}
}
}
rmdir($dirPath);
}
public function getImgDir() {
return Yii::getAlias('@web') . '/' . $this->imageDir;
}
}