Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 17 additions & 36 deletions lib/private/Template/CSSResourceLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Kyle Fazzari <kyrofa@ubuntu.com>
*
* @license AGPL-3.0
*
Expand Down Expand Up @@ -122,45 +123,25 @@ public function append($root, $file, $webRoot = null, $throw = true, $scss = fal
parent::append($root, $file, $webRoot, $throw);
} else {
if (!$webRoot) {
$tmpRoot = realpath($root);
/*
* traverse the potential web roots upwards in the path
*
* example:
* - root: /srv/www/apps/myapp
* - available mappings: ['/srv/www']
*
* First we check if a mapping for /srv/www/apps/myapp is available,
* then /srv/www/apps, /srv/www/apps, /srv/www, ... until we find a
* valid web root
*/
do {
if (isset($this->mapping[$tmpRoot])) {
$webRoot = $this->mapping[$tmpRoot];
break;
$webRoot = $this->findWebRoot($root);

if ($webRoot === null) {
$webRoot = '';
$this->logger->error('ResourceLocator can not find a web root (root: {root}, file: {file}, webRoot: {webRoot}, throw: {throw})', [
'app' => 'lib',
'root' => $root,
'file' => $file,
'webRoot' => $webRoot,
'throw' => $throw ? 'true' : 'false'
]);

if ($throw && $root === '/') {
throw new ResourceNotFoundException($file, $webRoot);
}

if ($tmpRoot === '/') {
$webRoot = '';
$this->logger->error('ResourceLocator can not find a web root (root: {root}, file: {file}, webRoot: {webRoot}, throw: {throw})', [
'app' => 'lib',
'root' => $root,
'file' => $file,
'webRoot' => $webRoot,
'throw' => $throw ? 'true' : 'false'
]);
break;
}
$tmpRoot = dirname($tmpRoot);
} while(true);

}

if ($throw && $tmpRoot === '/') {
throw new ResourceNotFoundException($file, $webRoot);
}
}

$this->resources[] = array($tmpRoot, $webRoot, $file);
$this->resources[] = array($webRoot? : '/', $webRoot, $file);
}
}
}
90 changes: 57 additions & 33 deletions lib/private/Template/ResourceLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin McCorkell <robin@mccorkell.me.uk>
* @author Kyle Fazzari <kyrofa@ubuntu.com>
*
* @license AGPL-3.0
*
Expand Down Expand Up @@ -106,6 +107,50 @@ protected function appendIfExist($root, $file, $webRoot = null) {
return false;
}

/**
* Attempt to find the webRoot
*
* traverse the potential web roots upwards in the path
*
* example:
* - root: /srv/www/apps/myapp
* - available mappings: ['/srv/www']
*
* First we check if a mapping for /srv/www/apps/myapp is available,
* then /srv/www/apps, /srv/www/apps, /srv/www, ... until we find a
* valid web root
*
* @param string $root
* @return string|null The web root or null on failure
*/
protected function findWebRoot($root) {
$webRoot = null;
$tmpRoot = $root;

while ($webRoot === null) {
if (isset($this->mapping[$tmpRoot])) {
$webRoot = $this->mapping[$tmpRoot];
break;
}

if ($tmpRoot === '/') {
break;
}

$tmpRoot = dirname($tmpRoot);
}

if ($webRoot === null) {
$realpath = realpath($root);

if ($realpath && ($realpath !== $root)) {
return $this->findWebRoot($realpath);
}
}

return $webRoot;
}

/**
* append the $file resource at $root
*
Expand All @@ -116,7 +161,6 @@ protected function appendIfExist($root, $file, $webRoot = null) {
* @throws ResourceNotFoundException Only thrown when $throw is true and the resource is missing
*/
protected function append($root, $file, $webRoot = null, $throw = true) {

if (!is_string($root)) {
if ($throw) {
throw new ResourceNotFoundException($file, $webRoot);
Expand All @@ -125,38 +169,18 @@ protected function append($root, $file, $webRoot = null, $throw = true) {
}

if (!$webRoot) {
$tmpRoot = realpath($root);
/*
* traverse the potential web roots upwards in the path
*
* example:
* - root: /srv/www/apps/myapp
* - available mappings: ['/srv/www']
*
* First we check if a mapping for /srv/www/apps/myapp is available,
* then /srv/www/apps, /srv/www/apps, /srv/www, ... until we find a
* valid web root
*/
do {
if (isset($this->mapping[$tmpRoot])) {
$webRoot = $this->mapping[$tmpRoot];
break;
}

if ($tmpRoot === '/') {
$webRoot = '';
$this->logger->error('ResourceLocator can not find a web root (root: {root}, file: {file}, webRoot: {webRoot}, throw: {throw})', [
'app' => 'lib',
'root' => $root,
'file' => $file,
'webRoot' => $webRoot,
'throw' => $throw ? 'true' : 'false'
]);
break;
}
$tmpRoot = dirname($tmpRoot);
} while(true);

$webRoot = $this->findWebRoot($root);

if ($webRoot === null) {
$webRoot = '';
$this->logger->error('ResourceLocator can not find a web root (root: {root}, file: {file}, webRoot: {webRoot}, throw: {throw})', [
'app' => 'lib',
'root' => $root,
'file' => $file,
'webRoot' => $webRoot,
'throw' => $throw ? 'true' : 'false'
]);
}
}
$this->resources[] = array($root, $webRoot, $file);

Expand Down
112 changes: 89 additions & 23 deletions tests/lib/Template/CSSResourceLocatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
namespace Test\Template;

use OC\Files\AppData\Factory;
use OCP\Files\IAppData;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\ILogger;
use OCP\IURLGenerator;
use OCP\IConfig;
Expand All @@ -45,6 +48,10 @@ class CSSResourceLocatorTest extends \Test\TestCase {
protected $depsCache;
/** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */
protected $logger;
protected $appname;
protected $appdir;
protected $appdirLink;
protected $appurl;

protected function setUp() {
parent::setUp();
Expand All @@ -55,6 +62,20 @@ protected function setUp() {
$this->config = $this->createMock(IConfig::class);
$this->depsCache = $this->createMock(ICache::class);
$this->themingDefaults = $this->createMock(ThemingDefaults::class);

$this->appdir = null;
$this->themingDefaults
->expects($this->any())
->method('getScssVariables')
->willReturn([]);
}

protected function tearDown() {
if (!is_null($this->appdir)) {
array_pop(\OC::$APPSROOTS);
unlink($this->appdirLink);
$this->rrmdir($this->appdir);
}
}

private function cssResourceLocator() {
Expand Down Expand Up @@ -95,36 +116,58 @@ private function randomString() {
return sha1(uniqid(mt_rand(), true));
}

public function testConstructor() {
$locator = $this->cssResourceLocator();
$this->assertAttributeEquals('theme', 'theme', $locator);
$this->assertAttributeEquals('core', 'serverroot', $locator);
$this->assertAttributeEquals(array('core'=>'map','3rd'=>'party'), 'mapping', $locator);
$this->assertAttributeEquals('3rd', 'thirdpartyroot', $locator);
$this->assertAttributeEquals('map', 'webroot', $locator);
$this->assertAttributeEquals(array(), 'resources', $locator);
}
private function setupAppDir() {
$this->appname = 'test-app-'.$this->randomString();
$folder = $this->createMock(ISimpleFolder::class);
$this->appData->method('getFolder')
->with($this->appname)
->willReturn($folder);

$file = $this->createMock(ISimpleFile::class);
$folder->method('getFile')
->will($this->returnCallback(function($path) use ($file) {
return $file;
}));

$this->urlGenerator
->method('linkToRoute')
->willReturn(\OC::$WEBROOT . '/test-file');

public function testFindWithAppPathSymlink() {
// First create new apps path, and a symlink to it
$apps_dirname = $this->randomString();
$new_apps_path = sys_get_temp_dir() . '/' . $apps_dirname;
$new_apps_path_symlink = $new_apps_path . '_link';
mkdir($new_apps_path);
symlink($apps_dirname, $new_apps_path_symlink);
$this->appdir = sys_get_temp_dir() . '/' . $apps_dirname;
$this->appdirLink = $this->appdir . '_link';
mkdir($this->appdir);
symlink($apps_dirname, $this->appdirLink);

// Create an app within that path
mkdir($new_apps_path . '/' . 'test-css-app');
mkdir($this->appdir . '/' . $this->appname);

$this->appurl = 'css-apps-test';

// Use the symlink as the app path
\OC::$APPSROOTS[] = [
'path' => $new_apps_path_symlink,
'url' => '/css-apps-test',
'path' => $this->appdirLink,
'url' => '/' . $this->appurl,
'writable' => false,
];
}

public function testConstructor() {
$locator = $this->cssResourceLocator();
$this->assertAttributeEquals('theme', 'theme', $locator);
$this->assertAttributeEquals('core', 'serverroot', $locator);
$this->assertAttributeEquals(array('core'=>'map','3rd'=>'party'), 'mapping', $locator);
$this->assertAttributeEquals('3rd', 'thirdpartyroot', $locator);
$this->assertAttributeEquals('map', 'webroot', $locator);
$this->assertAttributeEquals(array(), 'resources', $locator);
}

public function testFindCSSWithAppPathSymlink() {
$this->setupAppDir();

$locator = $this->cssResourceLocator();
$locator->find(array('test-css-app/test-file'));
$locator->find(array($this->appname . '/test-file'));

$resources = $locator->getResources();
$this->assertCount(1, $resources);
Expand All @@ -134,17 +177,40 @@ public function testFindWithAppPathSymlink() {
$webRoot = $resource[1];
$file = $resource[2];

$expectedRoot = $new_apps_path . '/test-css-app';
$expectedWebRoot = \OC::$WEBROOT . '/css-apps-test/test-css-app';
$expectedRoot = $this->appdir . '/' . $this->appname;
$expectedWebRoot = \OC::$WEBROOT . '/' . $this->appurl . '/' . $this->appname;
$expectedFile = 'test-file.css';

$this->assertEquals($expectedRoot, $root,
'Ensure the app path symlink is resolved into the real path');
$this->assertEquals($expectedWebRoot, $webRoot);
$this->assertEquals($expectedFile, $file);
}

array_pop(\OC::$APPSROOTS);
unlink($new_apps_path_symlink);
$this->rrmdir($new_apps_path);
public function testFindSCSSWithAppPathSymlink() {
$this->setupAppDir();

// Create an SCSS file there
touch($this->appdir . '/' . $this->appname . '/test-file.scss');

$locator = $this->cssResourceLocator();
$locator->find(array($this->appname . '/test-file'));

$resources = $locator->getResources();
$this->assertCount(1, $resources);
$resource = $resources[0];
$this->assertCount(3, $resource);
$root = $resource[0];
$webRoot = $resource[1];
$file = $resource[2];

$expectedRoot = '/';
$expectedWebRoot = '';
$expectedFile = 'test-file';

$this->assertEquals($expectedRoot, $root,
'Ensure the app path symlink is resolved into the real path');
$this->assertEquals($expectedWebRoot, $webRoot);
$this->assertEquals($expectedFile, $file);
}
}