antaresproject/core

View on GitHub
src/foundation/src/Processor/Extension/ModuleConfigure.php

Summary

Maintainability
C
1 day
Test Coverage
<?php

/**
 * Part of the Antares package.
 *
 * NOTICE OF LICENSE
 *
 * Licensed under the 3-clause BSD License.
 *
 * This source file is subject to the 3-clause BSD License that is
 * bundled with this package in the LICENSE file.
 *
 * @package    Antares Core
 * @version    0.9.0
 * @author     Original Orchestral https://github.com/orchestral
 * @author     Antares Team
 * @license    BSD License (3-clause)
 * @copyright  (c) 2017, Antares
 * @link       http://antaresproject.io
 */


namespace Antares\Foundation\Processor\Extension;

use Illuminate\Support\Fluent;
use Illuminate\Support\Facades\Event;
use Antares\Support\Facades\Extension;
use Antares\Support\Facades\Foundation;
use Antares\Foundation\Processor\Processor;
use Antares\Foundation\Validation\Module as ModuleValidator;
use Antares\Contracts\Extension\Command\Configure as Command;
use Antares\Foundation\Http\Presenters\Module as Presenter;
use Antares\Contracts\Extension\Listener\Configure as Listener;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Antares\Foundation\Validation\CustomModule;
use Illuminate\Support\Facades\Log;

class ModuleConfigure extends Processor implements Command
{

    /**
     * module category
     * 
     * @var String
     */
    protected $category = null;

    /**
     * Create a new processor instance.
     *
     * @param  \Antares\Foundation\Http\Presenters\Extension  $presenter
     * @param  \Antares\Foundation\Validation\Extension  $validator
     */
    public function __construct(Presenter $presenter, ModuleValidator $validator)
    {
        $this->presenter = $presenter;
        $this->validator = $validator;
    }

    /**
     * sets module category
     * 
     * @param String $category
     * @return \Antares\Foundation\Processor\Extension\ModuleConfigure
     */
    public function setCategory($category)
    {
        $this->category = $category;
        return $this;
    }

    /**
     * View edit extension configuration page.
     *
     * @param  \Antares\Contracts\Extension\Listener\Configure  $listener
     * @param  \Illuminate\Support\Fluent  $extension
     *
     * @return mixed
     */
    public function configure(Listener $listener, Fluent $extension)
    {
        if (!Extension::started($extension->get('name'))) {
            return $listener->abortWhenRequirementMismatched();
        }
        $this->presenter->setCategory($this->category);

        $memory = Foundation::memory();

        $activeConfig = (array) $memory->get("extensions.active.{$extension->get('name')}.config", []);
        $baseConfig   = (array) $memory->get("extension_{$extension->get('name')}", []);


        $eloquent = new Fluent(array_merge($activeConfig, $baseConfig));

        $form = $this->presenter->configure($eloquent, $extension->get('name'));

        Event::fire("antares.form: extension.{$extension->get('name')}", [$eloquent, $form]);

        return $listener->showConfigurationChanger(compact('eloquent', 'form', 'extension'));
    }

    /**
     * Update an extension configuration.
     *
     * @param  \Antares\Contracts\Extension\Listener\Configure  $listener
     * @param  \Illuminate\Support\Fluent  $extension
     * @param  array  $input
     *
     * @return mixed
     */
    public function update(Listener $listener, Fluent $extension, array $input)
    {

        if (!Extension::started($extension->get('name'))) {
            return $listener->suspend(404);
        }

        $validation = $this->validator->with($input, ["antares.validate: extension.{$extension->get('name')}"]);

        if ($validation->fails()) {
            return $listener->updateConfigurationFailedValidation($validation->getMessageBag(), $extension->uid);
        }

        $memory = Foundation::memory();
        $config = (array) $memory->get("extension.active.{$extension->get('name')}.config", []);
        $input  = new Fluent(array_merge($config, $input));

        unset($input['_token']);

        Event::fire("antares.saving: extension.{$extension->get('name')}", [& $input]);

        $memory->put("extensions.active.{$extension->get('name')}.config", $input->getAttributes());
        $memory->put("extension_{$extension->get('name')}", $input->getAttributes());

        Event::fire("antares.saved: extension.{$extension->get('name')}", [$input]);

        return $listener->configurationUpdated($extension);
    }

    /**
     * resolving module category name by module real name
     * 
     * @param Fluent $module
     * 
     * @return String
     */
    public function resolveModuleCategoryName(Fluent $module)
    {
        return head(explode('/', $module->name));
    }

    /**
     * create module form
     * 
     * @param String $category
     * @return type
     */
    public function create(Listener $listener, $category = null)
    {
        $eloquent = Foundation::make('antares.component');
        $form     = $this->presenter->create($eloquent, $category, $this->validator);
        Event::fire("antares.form: extension.create", [$eloquent, $form]);

        return $listener->showModuleCreator(compact('form', 'category'));
    }

    /**
     * upload file temporary resolver
     * 
     * @param \Symfony\Component\HttpFoundation\File\UploadedFile $file
     * @return type
     */
    protected function resolveTempFileName(UploadedFile $file)
    {
        $name      = $file->getClientOriginalName();
        $extension = File::extension($name);
        $subdir    = sha1(time());
        $directory = storage_path() . '/app/uploads/' . $subdir;
        $filename  = sha1(time() . time()) . ".{$extension}";
        return ['directory' => $directory, 'subdir' => $subdir, 'filename' => $filename];
    }

    /**
     * upload and validate module compressed file
     * 
     * @param array $input
     * @return \Illuminate\Support\Facades\Response
     */
    public function upload(array $input)
    {
        $file                     = $this->resolveTempFileName($input['file']);
        $input['file']->directory = $file['directory'];
        $input['file']->filename  = $file['filename'];
        Validator::resolver(function($translator, $data, $rules, $messages) {
            return new CustomModule($translator, $data, $rules, $messages);
        });

        $validation = $this->validator->on('upload')->with($input, [], [
            'source' => trans('antares/foundation::response.modules.validator.invalid-structure')
        ]);


        return ($validation->fails()) ? Response::make($validation->getMessageBag()->first(), 400) :
                Response::json([
                    'html' => $this->manifestDecorator($validation->getManifest()),
                    'path' => $file['subdir'] . DIRECTORY_SEPARATOR . $file['filename']], 200);
    }

    /**
     * generate view of manifest description
     * 
     * @param array $manifest
     * @return String
     */
    protected function manifestDecorator(array $manifest)
    {
        return view('antares/foundation::modules.manifest', ['manifest' => $manifest])->render();
    }

    /**
     * unziping module package
     * 
     * @param String $source
     * @param String $destination
     * @return boolean
     * @throws \Exception
     */
    protected function unzip($source, $destination)
    {
        @mkdir($destination, 0777, true);
        $zip = new \ZipArchive;
        if ($zip->open($source) === true) {
            $zip->extractTo($destination);
            $zip->close();
            return true;
        } else {
            throw new \Exception('Unable to open module package file');
        }
    }

    /**
     * extraction of module package
     * 
     * @param Listener $listener
     * @param String $category
     * @param array $input
     */
    public function extract(Listener $listener, $category = null, array $input)
    {

        try {
            $subdir = !is_null($category) ? $category : 'addons';
            $target = realpath(app_path() . "/../src/modules/{$subdir}");
            if (!is_dir($target)) {
                throw new \Exception('Invalid path of module provided');
            }

            $name = $input['name'];
            $path = storage_path() . '/app/uploads/' . $name;
            if (!file_exists($path)) {
                throw new \Exception('Invalid path of module package');
            }

            if ($this->unzip($path, $target)) {
                return $listener->moduleExtracted($category);
            }
            return $listener->moduleExtractionError($category);
        } catch (\Exception $e) {
            Log::emergency($e);
            return $listener->moduleExtractionError($category);
        }
    }

}