]> source.dussan.org Git - nextcloud-server.git/commitdiff
[PoC] JS Combiner
authorRoeland Jago Douma <roeland@famdouma.nl>
Wed, 22 Mar 2017 13:47:07 +0000 (14:47 +0100)
committerRoeland Jago Douma <roeland@famdouma.nl>
Fri, 24 Mar 2017 09:58:10 +0000 (10:58 +0100)
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
core/Application.php
core/Controller/JsController.php [new file with mode: 0644]
core/routes.php
lib/private/Template/JSCombiner.php [new file with mode: 0644]
lib/private/Template/JSResourceLocator.php
lib/private/TemplateLayout.php

index 6621964c289bc89c08fae2d5276b95b972e7a0c4..33b7dd1b33314ccc80962d52887bee4e865e31b6 100644 (file)
@@ -30,6 +30,8 @@
 
 namespace OC\Core;
 
+use OC\AppFramework\Utility\SimpleContainer;
+use OC\Core\Controller\JsController;
 use OC\Core\Controller\OCJSController;
 use OC\Security\IdentityProof\Manager;
 use OC\Server;
@@ -87,5 +89,13 @@ class Application extends App {
                                $server->getURLGenerator()
                        );
                });
+               $container->registerService(JsController::class, function () use ($container) {
+                       return new JsController(
+                               $container->query('AppName'),
+                               $container->query(IRequest::class),
+                               $container->getServer()->getAppDataDir('js'),
+                               $container->query(ITimeFactory::class)
+                       );
+               });
        }
 }
diff --git a/core/Controller/JsController.php b/core/Controller/JsController.php
new file mode 100644 (file)
index 0000000..0770974
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OC\Core\Controller;
+
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\NotFoundResponse;
+use OCP\AppFramework\Http\FileDisplayResponse;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Files\IAppData;
+use OCP\Files\NotFoundException;
+use OCP\IRequest;
+
+class JsController extends Controller {
+
+       /** @var IAppData */
+       protected $appData;
+
+       /** @var ITimeFactory */
+       protected $timeFactory;
+
+       /**
+        * @param string $appName
+        * @param IRequest $request
+        * @param IAppData $appData
+        * @param ITimeFactory $timeFactory
+        */
+       public function __construct($appName, IRequest $request, IAppData $appData, ITimeFactory $timeFactory) {
+               parent::__construct($appName, $request);
+
+               $this->appData = $appData;
+               $this->timeFactory = $timeFactory;
+       }
+
+       /**
+        * @PublicPage
+        * @NoCSRFRequired
+        *
+        * @param string $fileName css filename with extension
+        * @param string $appName css folder name
+        * @return FileDisplayResponse|NotFoundResponse
+        */
+       public function getJs($fileName, $appName) {
+               try {
+                       $folder = $this->appData->getFolder($appName);
+                       $jsFile = $folder->getFile($fileName);
+               } catch(NotFoundException $e) {
+                       return new NotFoundResponse();
+               }
+
+               $response = new FileDisplayResponse($jsFile, Http::STATUS_OK, ['Content-Type' => 'application/javascript']);
+               $response->cacheFor(86400);
+               $expires = new \DateTime();
+               $expires->setTimestamp($this->timeFactory->getTime());
+               $expires->add(new \DateInterval('PT24H'));
+               $response->addHeader('Expires', $expires->format(\DateTime::RFC1123));
+               $response->addHeader('Pragma', 'cache');
+               return $response;
+       }
+}
index 5d61d58e03795508f7a78ccac728f92905c268d6..d3356404fd511ab3aeb40b7702ed8d3a48e0a150 100644 (file)
@@ -56,6 +56,7 @@ $application->registerRoutes($this, [
                ['name' => 'Preview#getPreview', 'url' => '/core/preview', 'verb' => 'GET'],
                ['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'],
                ['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'],
+               ['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'],
        ],
        'ocs' => [
                ['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'],
diff --git a/lib/private/Template/JSCombiner.php b/lib/private/Template/JSCombiner.php
new file mode 100644 (file)
index 0000000..10560c8
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+/**
+ * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OC\Template;
+
+use OCP\Files\IAppData;
+use OCP\Files\NotFoundException;
+use OCP\Files\NotPermittedException;
+use OCP\Files\SimpleFS\ISimpleFolder;
+use OCP\IURLGenerator;
+
+class JSCombiner {
+
+       /** @var IAppData */
+       protected $appData;
+
+       /** @var IURLGenerator */
+       protected $urlGenerator;
+
+       /**
+        * JSCombiner constructor.
+        *
+        * @param IAppData $appData
+        * @param IURLGenerator $urlGenerator
+        */
+       public function __construct(IAppData $appData,
+                                                               IURLGenerator $urlGenerator) {
+               $this->appData = $appData;
+               $this->urlGenerator = $urlGenerator;
+       }
+
+       /**
+        * @param string $root
+        * @param string $file
+        * @param string $app
+        * @return bool
+        */
+       public function process($root, $file, $app) {
+               $path = explode('/', $root . '/' . $file);
+
+               $fileName = array_pop($path);
+               $path = implode('/', $path);
+
+               try {
+                       $folder = $this->appData->getFolder($app);
+               } catch(NotFoundException $e) {
+                       // creating css appdata folder
+                       $folder = $this->appData->newFolder($app);
+               }
+
+               if($this->isCached($fileName, $folder)) {
+                       return true;
+               }
+               return $this->cache($path, $fileName, $folder);
+       }
+
+       /**
+        * @param string $fileName
+        * @param ISimpleFolder $folder
+        * @return bool
+        */
+       protected function isCached($fileName, ISimpleFolder $folder) {
+               return false;
+       }
+
+       /**
+        * @param string $path
+        * @param string $fileName
+        * @param ISimpleFolder $folder
+        * @return bool
+        */
+       protected function cache($path, $fileName, ISimpleFolder $folder) {
+               $data = json_decode(file_get_contents($path . '/' . $fileName));
+
+               $res = '';
+               $deps = [];
+               foreach ($data as $file) {
+                       $filePath = $path . '/' . $file;
+
+                       if (is_file($filePath)) {
+                               $res .= file_get_contents($path . '/' . $file);
+                               $res .= PHP_EOL . PHP_EOL;
+                               $deps[$file] = filemtime($path . '/' . $file);
+                       }
+               }
+
+               $fileName = str_replace('.json', '.js', $fileName);
+               try {
+                       $cachedfile = $folder->getFile($fileName);
+               } catch(NotFoundException $e) {
+                       $cachedfile = $folder->newFile($fileName);
+               }
+
+               try {
+                       $cachedfile->putContent($res);
+                       return true;
+               } catch (NotPermittedException $e) {
+                       return false;
+               }
+       }
+
+       /**
+        * @param string $appName
+        * @param string $fileName
+        * @return string
+        */
+       public function getCachedJS($appName, $fileName) {
+               $tmpfileLoc = explode('/', $fileName);
+               $fileName = array_pop($tmpfileLoc);
+               $fileName = str_replace('.json', '.js', $fileName);
+
+               return substr($this->urlGenerator->linkToRoute('core.Js.getJs', array('fileName' => $fileName, 'appName' => $appName)), strlen(\OC::$WEBROOT) + 1);
+       }
+}
index 724f49965b389e716f502b9288bc23a3369ba2af..6f863e859d9450f77e04f54f030744a8d1e14b31 100644 (file)
 namespace OC\Template;
 
 class JSResourceLocator extends ResourceLocator {
+
+       /** @var JSCombiner */
+       protected $jsCombiner;
+
+       public function __construct(\OCP\ILogger $logger, $theme, array $core_map, array $party_map, JSCombiner $JSCombiner) {
+               parent::__construct($logger, $theme, $core_map, $party_map);
+
+               $this->jsCombiner = $JSCombiner;
+       }
+
        /**
         * @param string $script
         */
@@ -52,8 +62,10 @@ class JSResourceLocator extends ResourceLocator {
                } else if ($this->appendIfExist($this->serverroot, $theme_dir.'apps/'.$script.'.js')
                        || $this->appendIfExist($this->serverroot, $theme_dir.$script.'.js')
                        || $this->appendIfExist($this->serverroot, $script.'.js')
+                       || $this->cacheAndAppendCombineJsonIfExist($this->serverroot, $script.'.json')
                        || $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$script.'.js')
                        || $this->appendIfExist($this->serverroot, 'core/'.$script.'.js')
+                       || $this->cacheAndAppendCombineJsonIfExist($this->serverroot, 'core/'.$script.'.json')
                ) {
                        return;
                }
@@ -68,7 +80,9 @@ class JSResourceLocator extends ResourceLocator {
                        $this->appendIfExist($app_path, $script . '.js', $app_url);
                        return;
                }
-               $this->append($app_path, $script . '.js', $app_url);
+               if (!$this->cacheAndAppendCombineJsonIfExist($app_path, $script.'.json', $app)) {
+                       $this->append($app_path, $script . '.js', $app_url);
+               }
        }
 
        /**
@@ -76,4 +90,15 @@ class JSResourceLocator extends ResourceLocator {
         */
        public function doFindTheme($script) {
        }
+
+       protected function cacheAndAppendCombineJsonIfExist($root, $file, $app = 'core') {
+               if (is_file($root.'/'.$file)) {
+                       if ($this->jsCombiner->process($root, $file, $app)) {
+                               $this->append($this->serverroot, $this->jsCombiner->getCachedJS($app, $file), false, false);
+                               return true;
+                       }
+               }
+
+               return false;
+       }
 }
index 3206a1d3ba8819b297bb9a930c3ffd6d98cb4e5f..064e590b20a1d5c090a5b0cabfc10e08e44933fb 100644 (file)
@@ -35,6 +35,7 @@
  */
 namespace OC;
 
+use OC\Template\JSCombiner;
 use OC\Template\JSConfigHelper;
 use OC\Template\SCSSCacher;
 
@@ -249,7 +250,12 @@ class TemplateLayout extends \OC_Template {
                        \OC::$server->getLogger(),
                        $theme,
                        array( \OC::$SERVERROOT => \OC::$WEBROOT ),
-                       array( \OC::$SERVERROOT => \OC::$WEBROOT ));
+                       array( \OC::$SERVERROOT => \OC::$WEBROOT ),
+                       new JSCombiner(
+                               \OC::$server->getAppDataDir('js'),
+                               \OC::$server->getURLGenerator()
+                       )
+                       );
                $locator->find($scripts);
                return $locator->getResources();
        }