mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API Implement support for public/ webroot folder (#7741)
* API Implement support for public/ webroot folder * Bugfixes and refactor based on feedback
This commit is contained in:
parent
ea50b01d32
commit
8d077203d4
@ -76,6 +76,7 @@ before_script:
|
|||||||
# Install composer dependencies
|
# Install composer dependencies
|
||||||
- export PATH=~/.composer/vendor/bin:$PATH
|
- export PATH=~/.composer/vendor/bin:$PATH
|
||||||
- composer validate
|
- composer validate
|
||||||
|
- mkdir ./public
|
||||||
- if [[ $DB == PGSQL ]]; then composer require silverstripe/postgresql:2.0.x-dev --no-update; fi
|
- if [[ $DB == PGSQL ]]; then composer require silverstripe/postgresql:2.0.x-dev --no-update; fi
|
||||||
- if [[ $DB == SQLITE ]]; then composer require silverstripe/sqlite3:2.0.x-dev --no-update; fi
|
- if [[ $DB == SQLITE ]]; then composer require silverstripe/sqlite3:2.0.x-dev --no-update; fi
|
||||||
- composer require silverstripe/recipe-core:1.1.x-dev silverstripe/admin:1.1.x-dev silverstripe/versioned:1.1.x-dev --no-update
|
- composer require silverstripe/recipe-core:1.1.x-dev silverstripe/admin:1.1.x-dev silverstripe/versioned:1.1.x-dev --no-update
|
||||||
|
@ -40,9 +40,9 @@ $ composer create-project silverstripe/installer ./silverstripe
|
|||||||
## Install and configure
|
## Install and configure
|
||||||
* Option 1: Environment file - Set up a file named `.env` file either in the webroot and setup as per the [Environment Management process](/getting_started/environment_management).
|
* Option 1: Environment file - Set up a file named `.env` file either in the webroot and setup as per the [Environment Management process](/getting_started/environment_management).
|
||||||
|
|
||||||
* Option 2: Installer - Visit `http://localhost/silverstripe` - you will see SilverStripe's installation screen.
|
* Option 2: Installer - Visit `http://localhost/silverstripe/public` - you will see SilverStripe's installation screen.
|
||||||
* You should be able to click "Install SilverStripe" and the installer will do its thing. It takes a minute or two.
|
* You should be able to click "Install SilverStripe" and the installer will do its thing. It takes a minute or two.
|
||||||
* Once the installer has finished, visit `http://localhost/silverstripe`. You should see your new SilverStripe site's
|
* Once the installer has finished, visit `http://localhost/silverstripe/public`. You should see your new SilverStripe site's
|
||||||
home page.
|
home page.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
@ -190,7 +190,7 @@ After gettng the code installed, make sure you set the folder permissions proper
|
|||||||
|
|
||||||
## Start SilverStripe installer
|
## Start SilverStripe installer
|
||||||
|
|
||||||
Open a browser and point it to `http://localhost/ss`
|
Open a browser and point it to `http://localhost/ss/public`
|
||||||
|
|
||||||
If an installation screen shows up, congratulations! We're very close now.
|
If an installation screen shows up, congratulations! We're very close now.
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
# Lightttpd
|
# Lightttpd
|
||||||
|
|
||||||
1. Lighttpd works fine so long as you provide a custom config. Add the following to lighttpd.conf **BEFORE** installing
|
1. Lighttpd works fine so long as you provide a custom config. Add the following to lighttpd.conf **BEFORE** installing
|
||||||
Silverstripe. Replace "yoursite.com" and "/home/yoursite/public_html/" below.
|
Silverstripe. Replace "yoursite.com" and "/home/yoursite/public/" below.
|
||||||
|
|
||||||
|
|
||||||
$HTTP["host"] == "yoursite.com" {
|
$HTTP["host"] == "yoursite.com" {
|
||||||
server.document-root = "/home/yoursite/public_html/"
|
server.document-root = "/home/yoursite/public/"
|
||||||
|
|
||||||
# Disable directory listings
|
# Disable directory listings
|
||||||
dir-listing.activate = "disable"
|
dir-listing.activate = "disable"
|
||||||
@ -14,16 +14,11 @@ Silverstripe. Replace "yoursite.com" and "/home/yoursite/public_html/" below.
|
|||||||
url.access-deny += ( ".ss" )
|
url.access-deny += ( ".ss" )
|
||||||
static-file.exclude-extensions += ( ".ss" )
|
static-file.exclude-extensions += ( ".ss" )
|
||||||
|
|
||||||
# Deny access to SilverStripe command-line interface
|
# Deny access to vendor
|
||||||
$HTTP["url"] =~ "^/vendor/silverstripe/framework/cli-script.php" {
|
$HTTP["url"] =~ "^/vendor" {
|
||||||
url.access-deny = ( "" )
|
url.access-deny = ( "" )
|
||||||
}
|
}
|
||||||
|
|
||||||
# Disable FastCGI in assets directory (so that PHP files are not executed)
|
|
||||||
$HTTP["url"] =~ "^/assets/" {
|
|
||||||
fastcgi.server = ()
|
|
||||||
}
|
|
||||||
|
|
||||||
# Rewrite URLs so they are nicer
|
# Rewrite URLs so they are nicer
|
||||||
url.rewrite-once = (
|
url.rewrite-once = (
|
||||||
"^/.*\.[A-Za-z0-9]+.*?$" => "$0",
|
"^/.*\.[A-Za-z0-9]+.*?$" => "$0",
|
||||||
|
@ -16,11 +16,12 @@ If you don't fully understand the configuration presented here, consult the
|
|||||||
Especially be aware of [accidental php-execution](https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/ "Don't trust the tutorials") when extending the configuration.
|
Especially be aware of [accidental php-execution](https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/ "Don't trust the tutorials") when extending the configuration.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
But enough of the disclaimer, on to the actual configuration — typically in `nginx.conf`:
|
But enough of the disclaimer, on to the actual configuration — typically in `nginx.conf`. This assumes
|
||||||
|
you are running your site configuration with a separate `public/` webroot folder.
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
root /path/to/ss/folder;
|
root /var/www/the-website/public;
|
||||||
|
|
||||||
server_name site.com www.site.com;
|
server_name site.com www.site.com;
|
||||||
|
|
||||||
@ -43,53 +44,15 @@ But enough of the disclaimer, on to the actual configuration — typically in `n
|
|||||||
sendfile on;
|
sendfile on;
|
||||||
try_files $uri index.php?$query_string;
|
try_files $uri index.php?$query_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ /framework/.*(main|rpc|tiny_mce_gzip)\.php$ {
|
|
||||||
fastcgi_keep_conn on;
|
|
||||||
fastcgi_pass 127.0.0.1:9000;
|
|
||||||
fastcgi_index index.php;
|
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
||||||
include fastcgi_params;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ /(mysite|framework|cms)/.*\.(php|php3|php4|php5|phtml|inc)$ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ /\.. {
|
location ~ /\.. {
|
||||||
deny all;
|
deny all;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ \.ss$ {
|
|
||||||
satisfy any;
|
|
||||||
allow 127.0.0.1;
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ web\.config$ {
|
location ~ web\.config$ {
|
||||||
deny all;
|
deny all;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ \.ya?ml$ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ^~ /vendor/ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* /silverstripe-cache/ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* composer\.(json|lock)$ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* /(cms|framework)/silverstripe_version$ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ \.php$ {
|
location ~ \.php$ {
|
||||||
fastcgi_keep_conn on;
|
fastcgi_keep_conn on;
|
||||||
fastcgi_pass 127.0.0.1:9000;
|
fastcgi_pass 127.0.0.1:9000;
|
||||||
|
@ -57,35 +57,12 @@ Create `/etc/nginx/silverstripe.conf` and add this configuration:
|
|||||||
location ^~ /assets/ {
|
location ^~ /assets/ {
|
||||||
try_files $uri =404;
|
try_files $uri =404;
|
||||||
}
|
}
|
||||||
location ~ /(mysite|framework|cms)/.*\.(php|php3|php4|php5|phtml|inc)$ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
location ~ /\.. {
|
location ~ /\.. {
|
||||||
deny all;
|
deny all;
|
||||||
}
|
}
|
||||||
location ~ \.ss$ {
|
|
||||||
satisfy any;
|
|
||||||
allow 127.0.0.1;
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
location ~ web\.config$ {
|
location ~ web\.config$ {
|
||||||
deny all;
|
deny all;
|
||||||
}
|
}
|
||||||
location ~ \.ya?ml$ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
location ^~ /vendor/ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
location ~* /silverstripe-cache/ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
location ~* composer\.(json|lock)$ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
location ~* /(cms|framework)/silverstripe_version$ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
|
|
||||||
The above script passes all non-static file requests to `/index.php` in the webroot which relies on
|
The above script passes all non-static file requests to `/index.php` in the webroot which relies on
|
||||||
`hhvm.conf` being included prior so that php requests are handled.
|
`hhvm.conf` being included prior so that php requests are handled.
|
||||||
@ -97,7 +74,7 @@ e.g. `/etc/nginx/sites-enabled/mysite`:
|
|||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
root /var/www/mysite;
|
root /var/www/mysite/public;
|
||||||
server_name www.mysite.com;
|
server_name www.mysite.com;
|
||||||
|
|
||||||
error_log /var/log/nginx/mysite.error.log;
|
error_log /var/log/nginx/mysite.error.log;
|
||||||
|
@ -7,11 +7,13 @@ directories is meaningful to its logic.
|
|||||||
|
|
||||||
## Core Structure
|
## Core Structure
|
||||||
|
|
||||||
Directory | Description
|
Directory | Description
|
||||||
--------- | -----------
|
--------- | -----------
|
||||||
`assets/` | Images and other files uploaded via the SilverStripe CMS. You can also place your own content inside it, and link to it from within the content area of the CMS.
|
`public/` | Webserver public webroot
|
||||||
`resources/`| Public files from modules (added automatically)
|
`public/assets/` | Images and other files uploaded via the SilverStripe CMS. You can also place your own content inside it, and link to it from within the content area of the CMS.
|
||||||
`vendor/` | SilverStripe modules and other supporting libraries (the framework is in `vendor/silverstripe/framework`)
|
`public/resources/` | Exposed public files added from modules. Folders within this parent will match that of the source root location.
|
||||||
|
`vendor/` | SilverStripe modules and other supporting libraries (the framework is in `vendor/silverstripe/framework`)
|
||||||
|
`themes/` | Standard theme installation location
|
||||||
|
|
||||||
## Custom Code Structure
|
## Custom Code Structure
|
||||||
|
|
||||||
@ -100,4 +102,4 @@ by using a `flush=1` query parameter. See the ["Manifests" documentation](/devel
|
|||||||
## Best Practices
|
## Best Practices
|
||||||
|
|
||||||
### Making /assets readonly
|
### Making /assets readonly
|
||||||
See [Secure coding](/developer_guides/security/secure_coding#filesystem)
|
See [Secure coding](/developer_guides/security/secure_coding#filesystem)
|
||||||
|
@ -6,6 +6,8 @@ This version introduces many breaking changes, which in most projects can be man
|
|||||||
of automatic upgrade processes as well as manual code review. This document reviews these changes and will
|
of automatic upgrade processes as well as manual code review. This document reviews these changes and will
|
||||||
guide developers in preparing existing 3.x code for compatibility with 4.0
|
guide developers in preparing existing 3.x code for compatibility with 4.0
|
||||||
|
|
||||||
|
For users upgrading to 4.1.0 please see the specific [4.1.0 upgrading guide](4.1.0.md).
|
||||||
|
|
||||||
## Overview {#overview}
|
## Overview {#overview}
|
||||||
|
|
||||||
* Minimum version dependencies have increased; PHP 5.5 and Internet Explorer 11 (or other modern browser)
|
* Minimum version dependencies have increased; PHP 5.5 and Internet Explorer 11 (or other modern browser)
|
||||||
|
208
docs/en/04_Changelogs/4.1.0.md
Normal file
208
docs/en/04_Changelogs/4.1.0.md
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
# 4.1.0
|
||||||
|
|
||||||
|
## Overview {#overview}
|
||||||
|
|
||||||
|
* Support for public webroot folder `public/`
|
||||||
|
* Better support for cross-platform filesystem path manipulation
|
||||||
|
|
||||||
|
## Upgrading
|
||||||
|
|
||||||
|
### Upgrade `public/` folder
|
||||||
|
|
||||||
|
This release allows the maintenance of a public webroot folder which separates all
|
||||||
|
web-accessible files from protected project files (like the vendor folder
|
||||||
|
and all of your PHP files). This makes your web hosting more secure, and
|
||||||
|
less likely to leak information through accidentally deployed files like
|
||||||
|
a project README. New projects will default to this behaviour. Existing
|
||||||
|
projects (updating from 3.x or 4.0) will continue working as-is, but we
|
||||||
|
strongly recommend switching to the public webroot structure in order to
|
||||||
|
get the security benefits.
|
||||||
|
|
||||||
|
This folder name is not configurable, but is turned on by creating this folder, and off by ensuring
|
||||||
|
this folder doesn't exist.
|
||||||
|
|
||||||
|
When separating the public webroot from the BASE_PATH it is necessary to move a few files during migration:
|
||||||
|
|
||||||
|
- Move `.htaccess` from base to `public/`
|
||||||
|
- Move `index.php` from base to `public/`
|
||||||
|
- Move `assets` folder (including the nested `assets/.protected` folder) into `public/`.
|
||||||
|
This is the only folder which needs write permissions.
|
||||||
|
- Ensure that the `public/resources` folder exists; If this folder already exists in root, you should
|
||||||
|
delete this, and re-generate it by running `composer vendor-expose` in your root path.
|
||||||
|
- Any public assets committed directly to your project intended to be served directly to the
|
||||||
|
webserver. E.g. move `mysite/javascript/script.js` to `public/javascript/script.js`.
|
||||||
|
You can then use `Requirements::css('javascript/script.js');` /
|
||||||
|
`<% require css('javascript/script.js') %>` to include this file.
|
||||||
|
- Ensure that the web-root configured for your server of choice points to the public/ folder instead of the base path.
|
||||||
|
E.g. an apache virtualhost configuration would look like:
|
||||||
|
|
||||||
|
```
|
||||||
|
<VirtualHost *:80>
|
||||||
|
ServerName mywebsite.com
|
||||||
|
ServerAlias *.mywebsite.com
|
||||||
|
VirtualDocumentRoot "/var/www/Sites/mywebsite/public/"
|
||||||
|
</VirtualHost>
|
||||||
|
```
|
||||||
|
|
||||||
|
You may also need to add various changes to your code if you reference the BASE_PATH directly:
|
||||||
|
|
||||||
|
- You should use `Director::publicFolder()` instead of `Director::baseFolder()` if referring to the
|
||||||
|
public folder.
|
||||||
|
- You can check if a public folder exists with `Director::publicDir()`
|
||||||
|
|
||||||
|
### Example `public/` folder structure
|
||||||
|
|
||||||
|
For example, this is an existing folder structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
/var/www/mysite
|
||||||
|
├── assets/
|
||||||
|
│ └── .protected/
|
||||||
|
├── mysite/
|
||||||
|
│ ├── code/
|
||||||
|
│ │ ├── Page.php
|
||||||
|
│ │ └── PageController.php
|
||||||
|
│ └── css/
|
||||||
|
│ └── projectstyle.css
|
||||||
|
├── resources/ _(auto-generated by vendor-plugin `composer vendor-expose` command)_
|
||||||
|
│ └── silverstripe/
|
||||||
|
│ └── blog/
|
||||||
|
│ └── css/ _(symlink)_
|
||||||
|
│ └── blog.css
|
||||||
|
├── themes/
|
||||||
|
│ └── mytheme/
|
||||||
|
│ ├── css/
|
||||||
|
│ │ └── theme.css
|
||||||
|
│ └── templates/
|
||||||
|
│ └── BlogPage.ss
|
||||||
|
├── vendor/
|
||||||
|
│ └── silverstripe/
|
||||||
|
│ └── blog/
|
||||||
|
│ ├── css/ _(exposed in blog composer.json)_
|
||||||
|
│ │ └── blog.css
|
||||||
|
│ └── composer.json
|
||||||
|
├── .htaccess
|
||||||
|
├── composer.json
|
||||||
|
├── favicon.ico
|
||||||
|
├── index.php
|
||||||
|
└── install.php
|
||||||
|
```
|
||||||
|
|
||||||
|
After migration the folder structure would look like:
|
||||||
|
|
||||||
|
```
|
||||||
|
/var/www/mysite
|
||||||
|
├── mysite/
|
||||||
|
│ └── code/
|
||||||
|
│ ├── Page.php
|
||||||
|
│ └── PageController.php
|
||||||
|
├── public/
|
||||||
|
│ ├── assets/
|
||||||
|
│ │ └── .protected/
|
||||||
|
│ ├── css/
|
||||||
|
│ │ └── somestyle.css
|
||||||
|
│ ├── resources/ _(auto-generated by vendor-plugin `composer vendor-expose` command)_
|
||||||
|
│ │ ├── themes/
|
||||||
|
│ │ │ └── mytheme/
|
||||||
|
│ │ │ └── css/ _(symlink)_
|
||||||
|
│ │ │ └── theme.css
|
||||||
|
│ │ └── vendor/
|
||||||
|
│ │ └── silverstripe/
|
||||||
|
│ │ └── blog/
|
||||||
|
│ │ └── css/ _(symlink)_
|
||||||
|
│ │ └── blog.css
|
||||||
|
│ ├── .htaccess
|
||||||
|
│ ├── favicon.ico
|
||||||
|
│ ├── index.php
|
||||||
|
│ └── install.php
|
||||||
|
├── themes/
|
||||||
|
│ └── mytheme/
|
||||||
|
│ ├── css/ _(exposed in root composer.json)_
|
||||||
|
│ │ └── theme.css
|
||||||
|
│ └── templates/
|
||||||
|
│ └── BlogPage.ss
|
||||||
|
├── vendor/
|
||||||
|
│ └── silverstripe/
|
||||||
|
│ └── blog/
|
||||||
|
│ ├── css/ _(exposed in blog composer.json)_
|
||||||
|
│ │ └── blog.css
|
||||||
|
│ └── composer.json
|
||||||
|
└── composer.php
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use new `$public` theme set
|
||||||
|
|
||||||
|
In addition there is a new helper pseudo-theme that you can configure to expose files in the `public/`
|
||||||
|
folder to the themed css / javascript file lookup. For instance, this is how you can prioritise those
|
||||||
|
files:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
Name: mytheme
|
||||||
|
---
|
||||||
|
SilverStripe\View\SSViewer:
|
||||||
|
themes:
|
||||||
|
- '$public'
|
||||||
|
- 'simple'
|
||||||
|
- '$default'
|
||||||
|
```
|
||||||
|
|
||||||
|
This would allow `<% require themedCSS('style.css') %>` to find a file comitted to `public/css/style.css`.
|
||||||
|
|
||||||
|
Note that `Requirements` calls will look in both the `public` folder (first) and then the base path when
|
||||||
|
resolving css or javascript files. Any files that aren't in the public folder must be exposed using
|
||||||
|
the composer.json "expose" mechanism described below.
|
||||||
|
|
||||||
|
### Expose root project files
|
||||||
|
|
||||||
|
If you have files comitted to source control outside of the `public` folder, but you need them to be available
|
||||||
|
to the web server, you can also use the composer.json `expose` directive to symlink / copy these to `public/resources/`.
|
||||||
|
|
||||||
|
**composer.json** (in project root)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"extra": {
|
||||||
|
"expose": [
|
||||||
|
"mysite/client"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run the composer helper `composer vendor-expose` in your project root. This will symlink (or copy)
|
||||||
|
the `mysite/client` directory to `public/resources/mysite/client`.
|
||||||
|
|
||||||
|
If you are using third party modules which may not have explicit `expose` directives,
|
||||||
|
you can also expose those assets manually by declaring the full path to the directory to expose.
|
||||||
|
This works the same for `silverstripe-module` and `silverstripe-vendormodule` types.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"extra": {
|
||||||
|
"expose": [
|
||||||
|
"vendor/somevendor/somemodule/css",
|
||||||
|
"anothermodule/css"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information on how vendor modules work please see the documentation on the
|
||||||
|
[vendor plugin page](https://github.com/silverstripe/vendor-plugin) or the
|
||||||
|
[publishing a module](/developer_guides/extending/how_tos/publish_a_module) documentation.
|
||||||
|
|
||||||
|
### Path manipulation helpers
|
||||||
|
|
||||||
|
The following filesystem helpers have been added in order to better support working with cross-platform
|
||||||
|
path manipulation:
|
||||||
|
|
||||||
|
* `SilverStripe\Core\Convert::slashes()` to convert directory separators to either `/` or `\`
|
||||||
|
* `SilverStripe\Core\Path::join()` which will join one or more relative or absolute paths.
|
||||||
|
* `SilverStripe\Core\Path::normalise()` which will normalise and trim directory separators in a relative or absolute path
|
||||||
|
|
||||||
|
For example: normalising `Convert::normalise('/some\\dir/')` will convert to `/some/dir`.
|
||||||
|
Setting the second arg to true will also trim leading slashes.
|
||||||
|
E.g. `Convert::normalise('/sub\\dir/', true)` will convert to `sub/dir`.
|
||||||
|
|
||||||
|
It is preferrable to use these helpers in your code instead of assuming `DIRECTORY_SEPARATOR` === `/`
|
@ -11,6 +11,7 @@ use SilverStripe\Core\Extensible;
|
|||||||
use SilverStripe\Core\Injector\Injectable;
|
use SilverStripe\Core\Injector\Injectable;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Core\Kernel;
|
use SilverStripe\Core\Kernel;
|
||||||
|
use SilverStripe\Core\Path;
|
||||||
use SilverStripe\Dev\Deprecation;
|
use SilverStripe\Dev\Deprecation;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use SilverStripe\View\Requirements;
|
use SilverStripe\View\Requirements;
|
||||||
@ -76,15 +77,13 @@ class Director implements TemplateGlobalProvider
|
|||||||
private static $alternate_base_folder;
|
private static $alternate_base_folder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force the base_url to a specific value.
|
* Override PUBLIC_DIR. Set to a non-null value to override.
|
||||||
* If assigned, default_base_url and the value in the $_SERVER
|
* Setting to an empty string will disable public dir.
|
||||||
* global is ignored.
|
|
||||||
* Supports back-ticked vars; E.g. '`SS_BASE_URL`'
|
|
||||||
*
|
*
|
||||||
* @config
|
* @config
|
||||||
* @var string
|
* @var bool|null
|
||||||
*/
|
*/
|
||||||
private static $alternate_base_url;
|
private static $alternate_public_dir = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base url to populate if cannot be determined otherwise.
|
* Base url to populate if cannot be determined otherwise.
|
||||||
@ -589,7 +588,40 @@ class Director implements TemplateGlobalProvider
|
|||||||
public static function baseFolder()
|
public static function baseFolder()
|
||||||
{
|
{
|
||||||
$alternate = Director::config()->uninherited('alternate_base_folder');
|
$alternate = Director::config()->uninherited('alternate_base_folder');
|
||||||
return ($alternate) ? $alternate : BASE_PATH;
|
return $alternate ?: BASE_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if using a seperate public dir, and if so return this directory
|
||||||
|
* name.
|
||||||
|
*
|
||||||
|
* This will be removed in 5.0 and fixed to 'public'
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function publicDir()
|
||||||
|
{
|
||||||
|
$alternate = self::config()->uninherited('alternate_public_dir');
|
||||||
|
if (isset($alternate)) {
|
||||||
|
return $alternate;
|
||||||
|
}
|
||||||
|
return PUBLIC_DIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the webroot of the project, which may be a subfolder of {@see baseFolder()}
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function publicFolder()
|
||||||
|
{
|
||||||
|
$folder = self::baseFolder();
|
||||||
|
$publicDir = self::publicDir();
|
||||||
|
if ($publicDir) {
|
||||||
|
return Path::join($folder, $publicDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -618,7 +650,7 @@ class Director implements TemplateGlobalProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove base folder or url
|
// Remove base folder or url
|
||||||
foreach ([self::baseFolder(), self::baseURL()] as $base) {
|
foreach ([self::publicFolder(), self::baseFolder(), self::baseURL()] as $base) {
|
||||||
// Ensure single / doesn't break comparison (unless it would make base empty)
|
// Ensure single / doesn't break comparison (unless it would make base empty)
|
||||||
$base = rtrim($base, '\\/') ?: $base;
|
$base = rtrim($base, '\\/') ?: $base;
|
||||||
if (stripos($url, $base) === 0) {
|
if (stripos($url, $base) === 0) {
|
||||||
@ -746,7 +778,21 @@ class Director implements TemplateGlobalProvider
|
|||||||
*/
|
*/
|
||||||
public static function getAbsFile($file)
|
public static function getAbsFile($file)
|
||||||
{
|
{
|
||||||
return self::is_absolute($file) ? $file : Director::baseFolder() . '/' . $file;
|
// If already absolute
|
||||||
|
if (self::is_absolute($file)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If path is relative to public folder search there first
|
||||||
|
if (self::publicDir()) {
|
||||||
|
$path = Path::join(self::publicFolder(), $file);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to base folder
|
||||||
|
return Path::join(self::baseFolder(), $file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,8 +4,11 @@ namespace SilverStripe\Control;
|
|||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\Core\Convert;
|
||||||
|
use SilverStripe\Core\Manifest\ManifestFileFinder;
|
||||||
use SilverStripe\Core\Manifest\ModuleResource;
|
use SilverStripe\Core\Manifest\ModuleResource;
|
||||||
use SilverStripe\Core\Manifest\ResourceURLGenerator;
|
use SilverStripe\Core\Manifest\ResourceURLGenerator;
|
||||||
|
use SilverStripe\Core\Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate URLs assuming that BASE_PATH is also the webroot
|
* Generate URLs assuming that BASE_PATH is also the webroot
|
||||||
@ -21,9 +24,7 @@ class SimpleResourceURLGenerator implements ResourceURLGenerator
|
|||||||
* @config
|
* @config
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $url_rewrites = [
|
private static $url_rewrites = [];
|
||||||
'#^vendor/#i' => 'resources/',
|
|
||||||
];
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @var string
|
* @var string
|
||||||
@ -65,40 +66,163 @@ class SimpleResourceURLGenerator implements ResourceURLGenerator
|
|||||||
*/
|
*/
|
||||||
public function urlForResource($relativePath)
|
public function urlForResource($relativePath)
|
||||||
{
|
{
|
||||||
|
$query = '';
|
||||||
if ($relativePath instanceof ModuleResource) {
|
if ($relativePath instanceof ModuleResource) {
|
||||||
// Load from module resource
|
list($exists, $absolutePath, $relativePath) = $this->resolveModuleResource($relativePath);
|
||||||
$resource = $relativePath;
|
|
||||||
$relativePath = $resource->getRelativePath();
|
|
||||||
$exists = $resource->exists();
|
|
||||||
$absolutePath = $resource->getPath();
|
|
||||||
} else {
|
} else {
|
||||||
// Use normal string
|
// Save querystring for later
|
||||||
$absolutePath = preg_replace('/\?.*/', '', Director::baseFolder() . '/' . $relativePath);
|
if (strpos($relativePath, '?') !== false) {
|
||||||
$exists = file_exists($absolutePath);
|
list($relativePath, $query) = explode('?', $relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine lookup mechanism based on existence of public/ folder.
|
||||||
|
// From 5.0 onwards only resolvePublicResource() will be used.
|
||||||
|
if (!Director::publicDir()) {
|
||||||
|
list($exists, $absolutePath, $relativePath) = $this->resolveUnsecuredResource($relativePath);
|
||||||
|
} else {
|
||||||
|
list($exists, $absolutePath, $relativePath) = $this->resolvePublicResource($relativePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!$exists) {
|
if (!$exists) {
|
||||||
trigger_error("File {$relativePath} does not exist", E_USER_NOTICE);
|
trigger_error("File {$relativePath} does not exist", E_USER_NOTICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Switch slashes for URL
|
||||||
|
$relativeURL = Convert::slashes($relativePath, '/');
|
||||||
|
|
||||||
// Apply url rewrites
|
// Apply url rewrites
|
||||||
$rules = Config::inst()->get(static::class, 'url_rewrites') ?: [];
|
$rules = Config::inst()->get(static::class, 'url_rewrites') ?: [];
|
||||||
foreach ($rules as $from => $to) {
|
foreach ($rules as $from => $to) {
|
||||||
$relativePath = preg_replace($from, $to, $relativePath);
|
$relativeURL = preg_replace($from, $to, $relativeURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply nonce
|
// Apply nonce
|
||||||
$nonce = '';
|
|
||||||
// Don't add nonce to directories
|
// Don't add nonce to directories
|
||||||
if ($this->nonceStyle && $exists && is_file($absolutePath)) {
|
if ($this->nonceStyle && $exists && is_file($absolutePath)) {
|
||||||
$nonce = (strpos($relativePath, '?') === false) ? '?' : '&';
|
|
||||||
|
|
||||||
switch ($this->nonceStyle) {
|
switch ($this->nonceStyle) {
|
||||||
case 'mtime':
|
case 'mtime':
|
||||||
$nonce .= "m=" . filemtime($absolutePath);
|
if ($query) {
|
||||||
|
$query .= '&';
|
||||||
|
}
|
||||||
|
$query .= "m=" . filemtime($absolutePath);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Director::baseURL() . $relativePath . $nonce;
|
// Add back querystring
|
||||||
|
if ($query) {
|
||||||
|
$relativeURL .= '?' . $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Director::baseURL() . $relativeURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update relative path for a module resource
|
||||||
|
*
|
||||||
|
* @param ModuleResource $resource
|
||||||
|
* @return array List of [$exists, $absolutePath, $relativePath]
|
||||||
|
*/
|
||||||
|
protected function resolveModuleResource(ModuleResource $resource)
|
||||||
|
{
|
||||||
|
// Load from module resource
|
||||||
|
$relativePath = $resource->getRelativePath();
|
||||||
|
$exists = $resource->exists();
|
||||||
|
$absolutePath = $resource->getPath();
|
||||||
|
|
||||||
|
// Rewrite to resources with public directory
|
||||||
|
if (Director::publicDir()) {
|
||||||
|
// All resources mapped directly to resources/
|
||||||
|
$relativePath = Path::join(ManifestFileFinder::RESOURCES_DIR, $relativePath);
|
||||||
|
} elseif (stripos($relativePath, ManifestFileFinder::VENDOR_DIR . DIRECTORY_SEPARATOR) === 0) {
|
||||||
|
// @todo Non-public dir support will be removed in 5.0, so remove this block there
|
||||||
|
// If there is no public folder, map to resources/ but trim leading vendor/ too (4.0 compat)
|
||||||
|
$relativePath = Path::join(
|
||||||
|
ManifestFileFinder::RESOURCES_DIR,
|
||||||
|
substr($relativePath, strlen(ManifestFileFinder::VENDOR_DIR))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [$exists, $absolutePath, $relativePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve resource in the absence of a public/ folder
|
||||||
|
*
|
||||||
|
* @deprecated 4.1.0...5.0.0 Will be removed in 5.0 when public/ folder becomes mandatory
|
||||||
|
* @param string $relativePath
|
||||||
|
* @return array List of [$exists, $absolutePath, $relativePath]
|
||||||
|
*/
|
||||||
|
protected function resolveUnsecuredResource($relativePath)
|
||||||
|
{
|
||||||
|
// Check if the path requested is public-only, but we have no public folder
|
||||||
|
$publicOnly = $this->inferPublicResourceRequired($relativePath);
|
||||||
|
if ($publicOnly) {
|
||||||
|
trigger_error('Requesting a public resource without a public folder has no effect', E_USER_WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve path to base
|
||||||
|
$absolutePath = Path::join(Director::baseFolder(), $relativePath);
|
||||||
|
$exists = file_exists($absolutePath);
|
||||||
|
|
||||||
|
// Rewrite vendor/ to resources/ folder
|
||||||
|
if (stripos($relativePath, ManifestFileFinder::VENDOR_DIR . DIRECTORY_SEPARATOR) === 0) {
|
||||||
|
$relativePath = Path::join(
|
||||||
|
ManifestFileFinder::RESOURCES_DIR,
|
||||||
|
substr($relativePath, strlen(ManifestFileFinder::VENDOR_DIR))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [$exists, $absolutePath, $relativePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the requested $relativePath requires a public-only resource.
|
||||||
|
* An error will occur if this file isn't immediately available in the public/ assets folder.
|
||||||
|
*
|
||||||
|
* @param string $relativePath Requested relative path which may have a public/ prefix.
|
||||||
|
* This prefix will be removed if exists. This path will also be normalised to match DIRECTORY_SEPARATOR
|
||||||
|
* @return bool True if the resource must be a public resource
|
||||||
|
*/
|
||||||
|
protected function inferPublicResourceRequired(&$relativePath)
|
||||||
|
{
|
||||||
|
// Normalise path
|
||||||
|
$relativePath = Path::normalise($relativePath, true);
|
||||||
|
|
||||||
|
// Detect public-only request
|
||||||
|
$publicOnly = stripos($relativePath, 'public' . DIRECTORY_SEPARATOR) === 0;
|
||||||
|
if ($publicOnly) {
|
||||||
|
$relativePath = substr($relativePath, strlen(Director::publicDir() . DIRECTORY_SEPARATOR));
|
||||||
|
}
|
||||||
|
return $publicOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a resource that may either exist in a public/ folder, or be exposed from the base path to
|
||||||
|
* public/resources/
|
||||||
|
*
|
||||||
|
* @param string $relativePath
|
||||||
|
* @return array List of [$exists, $absolutePath, $relativePath]
|
||||||
|
*/
|
||||||
|
protected function resolvePublicResource($relativePath)
|
||||||
|
{
|
||||||
|
// Determine if we should search both public and base resources, or only public
|
||||||
|
$publicOnly = $this->inferPublicResourceRequired($relativePath);
|
||||||
|
|
||||||
|
// Search public folder first, and unless `public/` is prefixed, also private base path
|
||||||
|
$publicPath = Path::join(Director::publicFolder(), $relativePath);
|
||||||
|
if (file_exists($publicPath)) {
|
||||||
|
// String is a literal url comitted directly to public folder
|
||||||
|
return [true, $publicPath, $relativePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to private path (and assume expose will make this available to resources/)
|
||||||
|
$privatePath = Path::join(Director::baseFolder(), $relativePath);
|
||||||
|
if (!$publicOnly && file_exists($privatePath)) {
|
||||||
|
// String is private but exposed to resources/, so rewrite to the symlinked base
|
||||||
|
$relativePath = Path::join(ManifestFileFinder::RESOURCES_DIR, $relativePath);
|
||||||
|
return [true, $privatePath, $relativePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
// File doesn't exist, fail
|
||||||
|
return [false, null, $relativePath];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -591,4 +591,20 @@ class Convert
|
|||||||
$num = round($bytes / pow(1024, $scale), $decimal);
|
$num = round($bytes / pow(1024, $scale), $decimal);
|
||||||
return $num . $scales[$scale];
|
return $num . $scales[$scale];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert slashes in relative or asolute filesystem path. Defaults to DIRECTORY_SEPARATOR
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param string $separator
|
||||||
|
* @param bool $multiple Collapses multiple slashes or not
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function slashes($path, $separator = DIRECTORY_SEPARATOR, $multiple = true)
|
||||||
|
{
|
||||||
|
if ($multiple) {
|
||||||
|
return preg_replace('#[/\\\\]+#', $separator, $path);
|
||||||
|
}
|
||||||
|
return str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ use SilverStripe\Dev\DebugView;
|
|||||||
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
|
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
|
||||||
use SilverStripe\Logging\ErrorHandler;
|
use SilverStripe\Logging\ErrorHandler;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
|
use SilverStripe\View\PublicThemes;
|
||||||
|
use SilverStripe\View\SSViewer;
|
||||||
use SilverStripe\View\ThemeManifest;
|
use SilverStripe\View\ThemeManifest;
|
||||||
use SilverStripe\View\ThemeResourceLoader;
|
use SilverStripe\View\ThemeResourceLoader;
|
||||||
use SilverStripe\Dev\Deprecation;
|
use SilverStripe\Dev\Deprecation;
|
||||||
@ -115,7 +117,8 @@ class CoreKernel implements Kernel
|
|||||||
|
|
||||||
// Load template manifest
|
// Load template manifest
|
||||||
$themeResourceLoader = ThemeResourceLoader::inst();
|
$themeResourceLoader = ThemeResourceLoader::inst();
|
||||||
$themeResourceLoader->addSet('$default', new ThemeManifest(
|
$themeResourceLoader->addSet(SSViewer::PUBLIC_THEME, new PublicThemes());
|
||||||
|
$themeResourceLoader->addSet(SSViewer::DEFAULT_THEME, new ThemeManifest(
|
||||||
$basePath,
|
$basePath,
|
||||||
null, // project is defined in config, and this argument is deprecated
|
null, // project is defined in config, and this argument is deprecated
|
||||||
$manifestCacheFactory
|
$manifestCacheFactory
|
||||||
@ -306,9 +309,9 @@ class CoreKernel implements Kernel
|
|||||||
protected function redirectToInstaller()
|
protected function redirectToInstaller()
|
||||||
{
|
{
|
||||||
// Error if installer not available
|
// Error if installer not available
|
||||||
if (!file_exists($this->basePath . '/install.php')) {
|
if (!file_exists(Director::publicFolder() . '/install.php')) {
|
||||||
throw new HTTPResponse_Exception(
|
throw new HTTPResponse_Exception(
|
||||||
'SilverStripe Framework requires a $databaseConfig defined.',
|
'SilverStripe Framework requires database configuration defined via .env',
|
||||||
500
|
500
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,17 @@
|
|||||||
namespace SilverStripe\Core\Manifest;
|
namespace SilverStripe\Core\Manifest;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use InvalidArgumentException;
|
||||||
use Serializable;
|
use Serializable;
|
||||||
|
use SilverStripe\Core\Path;
|
||||||
use SilverStripe\Dev\Deprecation;
|
use SilverStripe\Dev\Deprecation;
|
||||||
|
|
||||||
class Module implements Serializable
|
class Module implements Serializable
|
||||||
{
|
{
|
||||||
const TRIM_CHARS = '/\\';
|
/**
|
||||||
|
* @deprecated 4.1..5.0 Use Path::normalise() instead
|
||||||
|
*/
|
||||||
|
const TRIM_CHARS = ' /\\';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full directory path to this module with no trailing slash
|
* Full directory path to this module with no trailing slash
|
||||||
@ -42,12 +47,12 @@ class Module implements Serializable
|
|||||||
* Construct a module
|
* Construct a module
|
||||||
*
|
*
|
||||||
* @param string $path Absolute filesystem path to this module
|
* @param string $path Absolute filesystem path to this module
|
||||||
* @param string $base base url for the application this module is installed in
|
* @param string $basePath base path for the application this module is installed in
|
||||||
*/
|
*/
|
||||||
public function __construct($path, $base)
|
public function __construct($path, $basePath)
|
||||||
{
|
{
|
||||||
$this->path = rtrim($path, self::TRIM_CHARS);
|
$this->path = Path::normalise($path);
|
||||||
$this->basePath = rtrim($base, self::TRIM_CHARS);
|
$this->basePath = Path::normalise($basePath);
|
||||||
$this->loadComposer();
|
$this->loadComposer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +142,10 @@ class Module implements Serializable
|
|||||||
*/
|
*/
|
||||||
public function getRelativePath()
|
public function getRelativePath()
|
||||||
{
|
{
|
||||||
return trim(substr($this->path, strlen($this->basePath)), self::TRIM_CHARS);
|
if ($this->path === $this->basePath) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return substr($this->path, strlen($this->basePath) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function serialize()
|
public function serialize()
|
||||||
@ -188,7 +196,10 @@ class Module implements Serializable
|
|||||||
*/
|
*/
|
||||||
public function getResource($path)
|
public function getResource($path)
|
||||||
{
|
{
|
||||||
$path = trim($path, self::TRIM_CHARS);
|
$path = Path::normalise($path, true);
|
||||||
|
if (empty($path)) {
|
||||||
|
throw new InvalidArgumentException('$path is required');
|
||||||
|
}
|
||||||
if (isset($this->resources[$path])) {
|
if (isset($this->resources[$path])) {
|
||||||
return $this->resources[$path];
|
return $this->resources[$path];
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ namespace SilverStripe\Core\Manifest;
|
|||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
use SilverStripe\Core\Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This object represents a single resource file attached to a module, and can be used
|
* This object represents a single resource file attached to a module, and can be used
|
||||||
@ -39,7 +40,7 @@ class ModuleResource
|
|||||||
public function __construct(Module $module, $relativePath)
|
public function __construct(Module $module, $relativePath)
|
||||||
{
|
{
|
||||||
$this->module = $module;
|
$this->module = $module;
|
||||||
$this->relativePath = ltrim($relativePath, Module::TRIM_CHARS);
|
$this->relativePath = Path::normalise($relativePath, true);
|
||||||
if (empty($this->relativePath)) {
|
if (empty($this->relativePath)) {
|
||||||
throw new InvalidArgumentException("Resource cannot have empty path");
|
throw new InvalidArgumentException("Resource cannot have empty path");
|
||||||
}
|
}
|
||||||
@ -55,7 +56,7 @@ class ModuleResource
|
|||||||
*/
|
*/
|
||||||
public function getPath()
|
public function getPath()
|
||||||
{
|
{
|
||||||
return $this->module->getPath() . '/' . $this->relativePath;
|
return Path::join($this->module->getPath(), $this->relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,8 +69,12 @@ class ModuleResource
|
|||||||
*/
|
*/
|
||||||
public function getRelativePath()
|
public function getRelativePath()
|
||||||
{
|
{
|
||||||
$path = $this->module->getRelativePath() . '/' . $this->relativePath;
|
// Root module
|
||||||
return ltrim($path, Module::TRIM_CHARS);
|
$parent = $this->module->getRelativePath();
|
||||||
|
if (!$parent) {
|
||||||
|
return $this->relativePath;
|
||||||
|
}
|
||||||
|
return Path::join($parent, $this->relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,15 +140,8 @@ class ModuleResource
|
|||||||
*/
|
*/
|
||||||
public function getRelativeResource($path)
|
public function getRelativeResource($path)
|
||||||
{
|
{
|
||||||
// Check cache
|
// Defer to parent module
|
||||||
$path = trim($path, Module::TRIM_CHARS);
|
$relativeToModule = Path::join($this->relativePath, $path);
|
||||||
if (isset($this->resources[$path])) {
|
return $this->getModule()->getResource($relativeToModule);
|
||||||
return $this->resources[$path];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build new relative path
|
|
||||||
$relativeBase = rtrim($this->relativePath, Module::TRIM_CHARS);
|
|
||||||
$relativePath = "{$relativeBase}/{$path}";
|
|
||||||
return $this->resources[$path] = new ModuleResource($this->getModule(), $relativePath);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
61
src/Core/Path.php
Normal file
61
src/Core/Path.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Core;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path manipulation helpers
|
||||||
|
*/
|
||||||
|
class Path
|
||||||
|
{
|
||||||
|
const TRIM_CHARS = ' /\\';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins one or more paths, normalising all separators to DIRECTORY_SEPARATOR
|
||||||
|
*
|
||||||
|
* Note: Errors on collapsed `/../` for security reasons. Use realpath() if you need to
|
||||||
|
* join a trusted relative path.
|
||||||
|
* @link https://www.owasp.org/index.php/Testing_Directory_traversal/file_include_(OTG-AUTHZ-001)
|
||||||
|
* @see File::join_paths() for joining file identifiers
|
||||||
|
*
|
||||||
|
* @param array $parts
|
||||||
|
* @return string Combined path, not including trailing slash (unless it's a single slash)
|
||||||
|
*/
|
||||||
|
public static function join(...$parts)
|
||||||
|
{
|
||||||
|
// In case $parts passed as an array in first parameter
|
||||||
|
if (count($parts) === 1 && is_array($parts[0])) {
|
||||||
|
$parts = $parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup and join all parts
|
||||||
|
$parts = array_filter(array_map('trim', $parts));
|
||||||
|
$fullPath = static::normalise(implode(DIRECTORY_SEPARATOR, $parts));
|
||||||
|
|
||||||
|
// Protect against directory traversal vulnerability (OTG-AUTHZ-001)
|
||||||
|
if (strpos($fullPath, '..') !== false) {
|
||||||
|
throw new InvalidArgumentException('Can not collapse relative folders');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fullPath ?: DIRECTORY_SEPARATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalise absolute or relative filesystem path.
|
||||||
|
* Important: Single slashes are converted to empty strings (empty relative paths)
|
||||||
|
*
|
||||||
|
* @param string $path Input path
|
||||||
|
* @param bool $relative
|
||||||
|
* @return string Path with no trailing slash. If $relative is true, also trim leading slashes
|
||||||
|
*/
|
||||||
|
public static function normalise($path, $relative = false)
|
||||||
|
{
|
||||||
|
$path = trim(Convert::slashes($path));
|
||||||
|
if ($relative) {
|
||||||
|
return trim($path, self::TRIM_CHARS);
|
||||||
|
} else {
|
||||||
|
return rtrim($path, self::TRIM_CHARS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ class TempFolder
|
|||||||
$parent = static::getTempParentFolder($base);
|
$parent = static::getTempParentFolder($base);
|
||||||
|
|
||||||
// The actual temp folder is a subfolder of getTempParentFolder(), named by username
|
// The actual temp folder is a subfolder of getTempParentFolder(), named by username
|
||||||
$subfolder = $parent . DIRECTORY_SEPARATOR . static::getTempFolderUsername();
|
$subfolder = Path::join($parent, static::getTempFolderUsername());
|
||||||
|
|
||||||
if (!@file_exists($subfolder)) {
|
if (!@file_exists($subfolder)) {
|
||||||
mkdir($subfolder);
|
mkdir($subfolder);
|
||||||
@ -65,26 +65,27 @@ class TempFolder
|
|||||||
*/
|
*/
|
||||||
protected static function getTempParentFolder($base)
|
protected static function getTempParentFolder($base)
|
||||||
{
|
{
|
||||||
$base = rtrim($base, '/\\');
|
|
||||||
// first, try finding a silverstripe-cache dir built off the base path
|
// first, try finding a silverstripe-cache dir built off the base path
|
||||||
$tempPath = $base . DIRECTORY_SEPARATOR . 'silverstripe-cache';
|
$localPath = Path::join($base, 'silverstripe-cache');
|
||||||
if (@file_exists($tempPath)) {
|
if (@file_exists($localPath)) {
|
||||||
if ((fileperms($tempPath) & 0777) != 0777) {
|
if ((fileperms($localPath) & 0777) != 0777) {
|
||||||
@chmod($tempPath, 0777);
|
@chmod($localPath, 0777);
|
||||||
}
|
}
|
||||||
return $tempPath;
|
return $localPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// failing the above, try finding a namespaced silverstripe-cache dir in the system temp
|
// failing the above, try finding a namespaced silverstripe-cache dir in the system temp
|
||||||
$tempPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR .
|
$tempPath = Path::join(
|
||||||
|
sys_get_temp_dir(),
|
||||||
'silverstripe-cache-php' . preg_replace('/[^\w-\.+]+/', '-', PHP_VERSION) .
|
'silverstripe-cache-php' . preg_replace('/[^\w-\.+]+/', '-', PHP_VERSION) .
|
||||||
str_replace(array(' ', '/', ':', '\\'), '-', $base);
|
str_replace(array(' ', '/', ':', '\\'), '-', $base)
|
||||||
|
);
|
||||||
if (!@file_exists($tempPath)) {
|
if (!@file_exists($tempPath)) {
|
||||||
$oldUMask = umask(0);
|
$oldUMask = umask(0);
|
||||||
@mkdir($tempPath, 0777);
|
@mkdir($tempPath, 0777);
|
||||||
umask($oldUMask);
|
umask($oldUMask);
|
||||||
|
|
||||||
// if the folder already exists, correct perms
|
// if the folder already exists, correct perms
|
||||||
} else {
|
} else {
|
||||||
if ((fileperms($tempPath) & 0777) != 0777) {
|
if ((fileperms($tempPath) & 0777) != 0777) {
|
||||||
@chmod($tempPath, 0777);
|
@chmod($tempPath, 0777);
|
||||||
@ -95,7 +96,7 @@ class TempFolder
|
|||||||
|
|
||||||
// failing to use the system path, attempt to create a local silverstripe-cache dir
|
// failing to use the system path, attempt to create a local silverstripe-cache dir
|
||||||
if (!$worked) {
|
if (!$worked) {
|
||||||
$tempPath = $base . DIRECTORY_SEPARATOR . 'silverstripe-cache';
|
$tempPath = $localPath;
|
||||||
if (!@file_exists($tempPath)) {
|
if (!@file_exists($tempPath)) {
|
||||||
$oldUMask = umask(0);
|
$oldUMask = umask(0);
|
||||||
@mkdir($tempPath, 0777);
|
@mkdir($tempPath, 0777);
|
||||||
|
@ -7,6 +7,7 @@ use Exception;
|
|||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RecursiveDirectoryIterator;
|
use RecursiveDirectoryIterator;
|
||||||
use RecursiveIteratorIterator;
|
use RecursiveIteratorIterator;
|
||||||
|
use SilverStripe\Core\Path;
|
||||||
use SilverStripe\Core\TempFolder;
|
use SilverStripe\Core\TempFolder;
|
||||||
use SplFileInfo;
|
use SplFileInfo;
|
||||||
|
|
||||||
@ -64,9 +65,29 @@ class InstallRequirements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get base path for this installation
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public function getBaseDir()
|
public function getBaseDir()
|
||||||
{
|
{
|
||||||
return rtrim($this->baseDir, '/\\') . '/';
|
return Path::normalise($this->baseDir) . DIRECTORY_SEPARATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get path to public directory
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPublicDir()
|
||||||
|
{
|
||||||
|
$base = $this->getBaseDir();
|
||||||
|
$public = Path::join($base, 'public') . DIRECTORY_SEPARATOR;
|
||||||
|
if (file_exists($public)) {
|
||||||
|
return $public;
|
||||||
|
}
|
||||||
|
return $base;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -297,16 +318,30 @@ class InstallRequirements
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check public folder exists
|
||||||
|
$this->requireFile(
|
||||||
|
'public',
|
||||||
|
[
|
||||||
|
'File permissions',
|
||||||
|
'Is there a public/ directory?',
|
||||||
|
'It is recommended to have a separate public/ web directory',
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
// Ensure root assets dir is writable
|
// Ensure root assets dir is writable
|
||||||
$this->requireWriteable('assets', array("File permissions", "Is the assets/ directory writeable?", null));
|
$this->requireWriteable(ASSETS_PATH, array("File permissions", "Is the assets/ directory writeable?", null), true);
|
||||||
|
|
||||||
// Ensure all assets files are writable
|
// Ensure all assets files are writable
|
||||||
$assetsDir = $this->getBaseDir() . 'assets';
|
$innerIterator = new RecursiveDirectoryIterator(ASSETS_PATH, RecursiveDirectoryIterator::SKIP_DOTS);
|
||||||
$innerIterator = new RecursiveDirectoryIterator($assetsDir, RecursiveDirectoryIterator::SKIP_DOTS);
|
|
||||||
$iterator = new RecursiveIteratorIterator($innerIterator, RecursiveIteratorIterator::SELF_FIRST);
|
$iterator = new RecursiveIteratorIterator($innerIterator, RecursiveIteratorIterator::SELF_FIRST);
|
||||||
/** @var SplFileInfo $file */
|
/** @var SplFileInfo $file */
|
||||||
foreach ($iterator as $file) {
|
foreach ($iterator as $file) {
|
||||||
|
// Only report file as error if not writable
|
||||||
|
if ($file->isWritable()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$relativePath = substr($file->getPathname(), strlen($this->getBaseDir()));
|
$relativePath = substr($file->getPathname(), strlen($this->getBaseDir()));
|
||||||
$message = $file->isDir()
|
$message = $file->isDir()
|
||||||
? "Is the {$relativePath} directory writeable?"
|
? "Is the {$relativePath} directory writeable?"
|
||||||
@ -838,13 +873,23 @@ class InstallRequirements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function requireFile($filename, $testDetails)
|
public function requireFile($filename, $testDetails, $absolute = false, $error = true)
|
||||||
{
|
{
|
||||||
$this->testing($testDetails);
|
$this->testing($testDetails);
|
||||||
$filename = $this->getBaseDir() . $filename;
|
|
||||||
if (!file_exists($filename)) {
|
if ($absolute) {
|
||||||
$testDetails[2] .= " (file '$filename' not found)";
|
$filename = Path::normalise($filename);
|
||||||
|
} else {
|
||||||
|
$filename = Path::join($this->getBaseDir(), $filename);
|
||||||
|
}
|
||||||
|
if (file_exists($filename)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$testDetails[2] .= " (file '$filename' not found)";
|
||||||
|
if ($error) {
|
||||||
$this->error($testDetails);
|
$this->error($testDetails);
|
||||||
|
} else {
|
||||||
|
$this->warning($testDetails);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -853,9 +898,9 @@ class InstallRequirements
|
|||||||
$this->testing($testDetails);
|
$this->testing($testDetails);
|
||||||
|
|
||||||
if ($absolute) {
|
if ($absolute) {
|
||||||
$filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
|
$filename = Path::normalise($filename);
|
||||||
} else {
|
} else {
|
||||||
$filename = $this->getBaseDir() . str_replace('/', DIRECTORY_SEPARATOR, $filename);
|
$filename = Path::join($this->getBaseDir(), $filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_exists($filename)) {
|
if (file_exists($filename)) {
|
||||||
@ -864,47 +909,49 @@ class InstallRequirements
|
|||||||
$isWriteable = is_writeable(dirname($filename));
|
$isWriteable = is_writeable(dirname($filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$isWriteable) {
|
if ($isWriteable) {
|
||||||
if (function_exists('posix_getgroups')) {
|
return;
|
||||||
$userID = posix_geteuid();
|
}
|
||||||
$user = posix_getpwuid($userID);
|
|
||||||
|
|
||||||
$currentOwnerID = fileowner(file_exists($filename) ? $filename : dirname($filename));
|
if (function_exists('posix_getgroups')) {
|
||||||
$currentOwner = posix_getpwuid($currentOwnerID);
|
$userID = posix_geteuid();
|
||||||
|
$user = posix_getpwuid($userID);
|
||||||
|
|
||||||
$testDetails[2] .= "User '$user[name]' needs to be able to write to this file:\n$filename\n\nThe "
|
$currentOwnerID = fileowner(file_exists($filename) ? $filename : dirname($filename));
|
||||||
. "file is currently owned by '$currentOwner[name]'. ";
|
$currentOwner = posix_getpwuid($currentOwnerID);
|
||||||
|
|
||||||
if ($user['name'] == $currentOwner['name']) {
|
$testDetails[2] .= "User '$user[name]' needs to be able to write to this file:\n$filename\n\nThe "
|
||||||
$testDetails[2] .= "We recommend that you make the file writeable.";
|
. "file is currently owned by '$currentOwner[name]'. ";
|
||||||
} else {
|
|
||||||
$groups = posix_getgroups();
|
if ($user['name'] == $currentOwner['name']) {
|
||||||
$groupList = array();
|
$testDetails[2] .= "We recommend that you make the file writeable.";
|
||||||
foreach ($groups as $group) {
|
} else {
|
||||||
$groupInfo = posix_getgrgid($group);
|
$groups = posix_getgroups();
|
||||||
if (in_array($currentOwner['name'], $groupInfo['members'])) {
|
$groupList = array();
|
||||||
$groupList[] = $groupInfo['name'];
|
foreach ($groups as $group) {
|
||||||
}
|
$groupInfo = posix_getgrgid($group);
|
||||||
}
|
if (in_array($currentOwner['name'], $groupInfo['members'])) {
|
||||||
if ($groupList) {
|
$groupList[] = $groupInfo['name'];
|
||||||
$testDetails[2] .= " We recommend that you make the file group-writeable "
|
|
||||||
. "and change the group to one of these groups:\n - " . implode("\n - ", $groupList)
|
|
||||||
. "\n\nFor example:\nchmod g+w $filename\nchgrp " . $groupList[0] . " $filename";
|
|
||||||
} else {
|
|
||||||
$testDetails[2] .= " There is no user-group that contains both the web-server user and the "
|
|
||||||
. "owner of this file. Change the ownership of the file, create a new group, or "
|
|
||||||
. "temporarily make the file writeable by everyone during the install process.";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
if ($groupList) {
|
||||||
$testDetails[2] .= "The webserver user needs to be able to write to this file:\n$filename";
|
$testDetails[2] .= " We recommend that you make the file group-writeable "
|
||||||
|
. "and change the group to one of these groups:\n - " . implode("\n - ", $groupList)
|
||||||
|
. "\n\nFor example:\nchmod g+w $filename\nchgrp " . $groupList[0] . " $filename";
|
||||||
|
} else {
|
||||||
|
$testDetails[2] .= " There is no user-group that contains both the web-server user and the "
|
||||||
|
. "owner of this file. Change the ownership of the file, create a new group, or "
|
||||||
|
. "temporarily make the file writeable by everyone during the install process.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$testDetails[2] .= "The webserver user needs to be able to write to this file:\n$filename";
|
||||||
|
}
|
||||||
|
|
||||||
if ($error) {
|
if ($error) {
|
||||||
$this->error($testDetails);
|
$this->error($testDetails);
|
||||||
} else {
|
} else {
|
||||||
$this->warning($testDetails);
|
$this->warning($testDetails);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ use SilverStripe\Control\Cookie;
|
|||||||
use SilverStripe\Control\HTTPApplication;
|
use SilverStripe\Control\HTTPApplication;
|
||||||
use SilverStripe\Control\HTTPRequest;
|
use SilverStripe\Control\HTTPRequest;
|
||||||
use SilverStripe\Control\HTTPRequestBuilder;
|
use SilverStripe\Control\HTTPRequestBuilder;
|
||||||
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\Core\CoreKernel;
|
use SilverStripe\Core\CoreKernel;
|
||||||
use SilverStripe\Core\EnvironmentLoader;
|
use SilverStripe\Core\EnvironmentLoader;
|
||||||
use SilverStripe\Core\Kernel;
|
use SilverStripe\Core\Kernel;
|
||||||
@ -27,13 +28,15 @@ class Installer extends InstallRequirements
|
|||||||
|
|
||||||
protected function installHeader()
|
protected function installHeader()
|
||||||
{
|
{
|
||||||
|
$clientPath = PUBLIC_DIR
|
||||||
|
? 'resources/vendor/silverstripe/framework/src/Dev/Install/client'
|
||||||
|
: 'resources/silverstripe/framework/src/Dev/Install/client';
|
||||||
?>
|
?>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<title>Installing SilverStripe...</title>
|
<title>Installing SilverStripe...</title>
|
||||||
<link rel="stylesheet" type="text/css"
|
<link rel="stylesheet" type="text/css" href="<?=$clientPath; ?>/styles/install.css"/>
|
||||||
href="resources/silverstripe/framework/src/Dev/Install/client/styles/install.css"/>
|
|
||||||
<script src="//code.jquery.com/jquery-1.7.2.min.js"></script>
|
<script src="//code.jquery.com/jquery-1.7.2.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -209,7 +212,15 @@ use SilverStripe\Control\HTTPRequestBuilder;
|
|||||||
use SilverStripe\Core\CoreKernel;
|
use SilverStripe\Core\CoreKernel;
|
||||||
use SilverStripe\Core\Startup\ErrorControlChainMiddleware;
|
use SilverStripe\Core\Startup\ErrorControlChainMiddleware;
|
||||||
|
|
||||||
require __DIR__ . '/vendor/autoload.php';
|
// Find autoload.php
|
||||||
|
if (file_exists(__DIR__ . '/vendor/autoload.php')) {
|
||||||
|
require __DIR__ . '/vendor/autoload.php';
|
||||||
|
} elseif (file_exists(__DIR__ . '/../vendor/autoload.php')) {
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
} else {
|
||||||
|
echo "autoload.php not found";
|
||||||
|
die;
|
||||||
|
}
|
||||||
|
|
||||||
// Build request and detect flush
|
// Build request and detect flush
|
||||||
$request = HTTPRequestBuilder::createFromEnvironment();
|
$request = HTTPRequestBuilder::createFromEnvironment();
|
||||||
@ -221,7 +232,8 @@ $app->addMiddleware(new ErrorControlChainMiddleware($app));
|
|||||||
$response = $app->handle($request);
|
$response = $app->handle($request);
|
||||||
$response->output();
|
$response->output();
|
||||||
PHP;
|
PHP;
|
||||||
$this->writeToFile('index.php', $content);
|
$path = $this->getPublicDir() . 'index.php';
|
||||||
|
$this->writeToFile($path, $content, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -340,11 +352,13 @@ PHP
|
|||||||
if ($config['theme'] && $config['theme'] !== 'tutorial') {
|
if ($config['theme'] && $config['theme'] !== 'tutorial') {
|
||||||
$theme = $this->ymlString($config['theme']);
|
$theme = $this->ymlString($config['theme']);
|
||||||
$themeYML = <<<YML
|
$themeYML = <<<YML
|
||||||
|
- '\$public'
|
||||||
- '$theme'
|
- '$theme'
|
||||||
- '\$default'
|
- '\$default'
|
||||||
YML;
|
YML;
|
||||||
} else {
|
} else {
|
||||||
$themeYML = <<<YML
|
$themeYML = <<<YML
|
||||||
|
- '\$public'
|
||||||
- '\$default'
|
- '\$default'
|
||||||
YML;
|
YML;
|
||||||
}
|
}
|
||||||
@ -378,21 +392,24 @@ YML
|
|||||||
/**
|
/**
|
||||||
* Write file to given location
|
* Write file to given location
|
||||||
*
|
*
|
||||||
* @param $filename
|
* @param string $filename
|
||||||
* @param $content
|
* @param string $content
|
||||||
|
* @param bool $absolute If $filename is absolute path set to true
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function writeToFile($filename, $content)
|
public function writeToFile($filename, $content, $absolute = false)
|
||||||
{
|
{
|
||||||
$base = $this->getBaseDir();
|
$path = $absolute
|
||||||
$this->statusMessage("Setting up $base$filename");
|
? $filename
|
||||||
|
: $this->getBaseDir() . $filename;
|
||||||
|
$this->statusMessage("Setting up $path");
|
||||||
|
|
||||||
if ((@$fh = fopen($base . $filename, 'wb')) && fwrite($fh, $content) && fclose($fh)) {
|
if ((@$fh = fopen($path, 'wb')) && fwrite($fh, $content) && fclose($fh)) {
|
||||||
// Set permissions to writable
|
// Set permissions to writable
|
||||||
@chmod($base . $filename, 0775);
|
@chmod($path, 0775);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
$this->error("Couldn't write to file $base$filename");
|
$this->error("Couldn't write to file $path");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,11 +422,7 @@ YML
|
|||||||
$end = "\n### SILVERSTRIPE END ###";
|
$end = "\n### SILVERSTRIPE END ###";
|
||||||
|
|
||||||
$base = dirname($_SERVER['SCRIPT_NAME']);
|
$base = dirname($_SERVER['SCRIPT_NAME']);
|
||||||
if (defined('DIRECTORY_SEPARATOR')) {
|
$base = Convert::slashes($base, '/');
|
||||||
$base = str_replace(DIRECTORY_SEPARATOR, '/', $base);
|
|
||||||
} else {
|
|
||||||
$base = str_replace("\\", '/', $base);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($base != '.') {
|
if ($base != '.') {
|
||||||
$baseClause = "RewriteBase '$base'\n";
|
$baseClause = "RewriteBase '$base'\n";
|
||||||
@ -474,8 +487,9 @@ ErrorDocument 500 /assets/error-500.html
|
|||||||
</IfModule>
|
</IfModule>
|
||||||
TEXT;
|
TEXT;
|
||||||
|
|
||||||
if (file_exists('.htaccess')) {
|
$htaccessPath = $this->getPublicDir() . '.htaccess';
|
||||||
$htaccess = file_get_contents('.htaccess');
|
if (file_exists($htaccessPath)) {
|
||||||
|
$htaccess = file_get_contents($htaccessPath);
|
||||||
|
|
||||||
if (strpos($htaccess, '### SILVERSTRIPE START ###') === false
|
if (strpos($htaccess, '### SILVERSTRIPE START ###') === false
|
||||||
&& strpos($htaccess, '### SILVERSTRIPE END ###') === false
|
&& strpos($htaccess, '### SILVERSTRIPE END ###') === false
|
||||||
@ -492,7 +506,7 @@ TEXT;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->writeToFile('.htaccess', $start . $rewrite . $end);
|
$this->writeToFile($htaccessPath, $start . $rewrite . $end, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -509,7 +523,6 @@ TEXT;
|
|||||||
<requestFiltering>
|
<requestFiltering>
|
||||||
<hiddenSegments applyToWebDAV="false">
|
<hiddenSegments applyToWebDAV="false">
|
||||||
<add segment="silverstripe-cache" />
|
<add segment="silverstripe-cache" />
|
||||||
<add segment="vendor" />
|
|
||||||
<add segment="composer.json" />
|
<add segment="composer.json" />
|
||||||
<add segment="composer.lock" />
|
<add segment="composer.lock" />
|
||||||
</hiddenSegments>
|
</hiddenSegments>
|
||||||
@ -534,7 +547,8 @@ TEXT;
|
|||||||
</configuration>
|
</configuration>
|
||||||
TEXT;
|
TEXT;
|
||||||
|
|
||||||
$this->writeToFile('web.config', $content);
|
$path = $this->getPublicDir() . 'web.config';
|
||||||
|
$this->writeToFile($path, $content, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkRewrite()
|
public function checkRewrite()
|
||||||
@ -542,8 +556,11 @@ TEXT;
|
|||||||
$token = new ParameterConfirmationToken('flush', new HTTPRequest('GET', '/'));
|
$token = new ParameterConfirmationToken('flush', new HTTPRequest('GET', '/'));
|
||||||
$params = http_build_query($token->params());
|
$params = http_build_query($token->params());
|
||||||
|
|
||||||
$destinationURL = str_replace('install.php', '', $_SERVER['SCRIPT_NAME']) .
|
$destinationURL = BASE_URL . '/' . (
|
||||||
($this->checkModuleExists('cms') ? "home/successfullyinstalled?$params" : "?$params");
|
$this->checkModuleExists('cms')
|
||||||
|
? "home/successfullyinstalled?$params"
|
||||||
|
: "?$params"
|
||||||
|
);
|
||||||
|
|
||||||
echo <<<HTML
|
echo <<<HTML
|
||||||
<li id="ModRewriteResult">Testing...</li>
|
<li id="ModRewriteResult">Testing...</li>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
|
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
|
||||||
<head>
|
<head>
|
||||||
<title>SilverStripe CMS / Framework Installation</title>
|
<title>SilverStripe CMS / Framework Installation</title>
|
||||||
|
<base href="<?php echo htmlentities($base); ?>/" />
|
||||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||||
<script type="application/javascript" src="//code.jquery.com/jquery-1.7.2.min.js"></script>
|
<script type="application/javascript" src="//code.jquery.com/jquery-1.7.2.min.js"></script>
|
||||||
<script type="application/javascript" src="<?=$clientPath; ?>/js/install.js"></script>
|
<script type="application/javascript" src="<?=$clientPath; ?>/js/install.js"></script>
|
||||||
|
@ -105,7 +105,10 @@ if ($installFromCli && ($req->hasErrors() || $dbReq->hasErrors())) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Path to client resources (copied through silverstripe/vendor-plugin)
|
// Path to client resources (copied through silverstripe/vendor-plugin)
|
||||||
$clientPath = 'resources/silverstripe/framework/src/Dev/Install/client';
|
$base = BASE_URL;
|
||||||
|
$clientPath = PUBLIC_DIR
|
||||||
|
? 'resources/vendor/silverstripe/framework/src/Dev/Install/client'
|
||||||
|
: 'resources/silverstripe/framework/src/Dev/Install/client';
|
||||||
|
|
||||||
// If already installed, ensure the user clicked "reinstall"
|
// If already installed, ensure the user clicked "reinstall"
|
||||||
$expectedArg = $alreadyInstalled ? 'reinstall' : 'go';
|
$expectedArg = $alreadyInstalled ? 'reinstall' : 'go';
|
||||||
@ -135,6 +138,7 @@ $adminConfig = $config->getAdminConfig($_REQUEST, false);
|
|||||||
|
|
||||||
// config-form.html vars (placeholder to prevent deletion)
|
// config-form.html vars (placeholder to prevent deletion)
|
||||||
[
|
[
|
||||||
|
$base,
|
||||||
$theme,
|
$theme,
|
||||||
$clientPath,
|
$clientPath,
|
||||||
$adminConfig,
|
$adminConfig,
|
||||||
|
11
src/View/PublicThemes.php
Normal file
11
src/View/PublicThemes.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\View;
|
||||||
|
|
||||||
|
class PublicThemes implements ThemeList
|
||||||
|
{
|
||||||
|
public function getThemes()
|
||||||
|
{
|
||||||
|
return PUBLIC_DIR ? ['/' . PUBLIC_DIR] : [];
|
||||||
|
}
|
||||||
|
}
|
@ -331,13 +331,13 @@ class Requirements implements Flushable
|
|||||||
* 'framework/javascript/lang'
|
* 'framework/javascript/lang'
|
||||||
* @param bool $return Return all relative file paths rather than including them in
|
* @param bool $return Return all relative file paths rather than including them in
|
||||||
* requirements
|
* requirements
|
||||||
* @param bool $langOnly Only include language files, not the base libraries
|
* @param bool $langOnly @deprecated 4.1...5.0 as i18n.js should be included manually in your project
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function add_i18n_javascript($langDir, $return = false, $langOnly = false)
|
public static function add_i18n_javascript($langDir, $return = false, $langOnly = false)
|
||||||
{
|
{
|
||||||
return self::backend()->add_i18n_javascript($langDir, $return, $langOnly);
|
return self::backend()->add_i18n_javascript($langDir, $return);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,6 +14,7 @@ use SilverStripe\Core\Injector\Injectable;
|
|||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
||||||
use SilverStripe\Core\Manifest\ResourceURLGenerator;
|
use SilverStripe\Core\Manifest\ResourceURLGenerator;
|
||||||
|
use SilverStripe\Core\Path;
|
||||||
use SilverStripe\Dev\Debug;
|
use SilverStripe\Dev\Debug;
|
||||||
use SilverStripe\Dev\Deprecation;
|
use SilverStripe\Dev\Deprecation;
|
||||||
use SilverStripe\i18n\i18n;
|
use SilverStripe\i18n\i18n;
|
||||||
@ -603,7 +604,12 @@ class Requirements_Backend
|
|||||||
public function javascriptTemplate($file, $vars, $uniquenessID = null)
|
public function javascriptTemplate($file, $vars, $uniquenessID = null)
|
||||||
{
|
{
|
||||||
$file = ModuleResourceLoader::singleton()->resolvePath($file);
|
$file = ModuleResourceLoader::singleton()->resolvePath($file);
|
||||||
$script = file_get_contents(Director::getAbsFile($file));
|
$absolutePath = Director::getAbsFile($file);
|
||||||
|
if (!file_exists($absolutePath)) {
|
||||||
|
throw new InvalidArgumentException("Javascript template file {$file} does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
$script = file_get_contents($absolutePath);
|
||||||
$search = array();
|
$search = array();
|
||||||
$replace = array();
|
$replace = array();
|
||||||
|
|
||||||
@ -629,9 +635,9 @@ class Requirements_Backend
|
|||||||
{
|
{
|
||||||
$file = ModuleResourceLoader::singleton()->resolvePath($file);
|
$file = ModuleResourceLoader::singleton()->resolvePath($file);
|
||||||
|
|
||||||
$this->css[$file] = array(
|
$this->css[$file] = [
|
||||||
"media" => $media
|
"media" => $media
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -997,12 +1003,6 @@ class Requirements_Backend
|
|||||||
$langDir = ModuleResourceLoader::singleton()->resolvePath($langDir);
|
$langDir = ModuleResourceLoader::singleton()->resolvePath($langDir);
|
||||||
|
|
||||||
$files = array();
|
$files = array();
|
||||||
$base = Director::baseFolder() . '/';
|
|
||||||
|
|
||||||
if (substr($langDir, -1) != '/') {
|
|
||||||
$langDir .= '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
$candidates = array(
|
$candidates = array(
|
||||||
'en.js',
|
'en.js',
|
||||||
'en_US.js',
|
'en_US.js',
|
||||||
@ -1012,19 +1012,21 @@ class Requirements_Backend
|
|||||||
i18n::get_locale() . '.js',
|
i18n::get_locale() . '.js',
|
||||||
);
|
);
|
||||||
foreach ($candidates as $candidate) {
|
foreach ($candidates as $candidate) {
|
||||||
if (file_exists($base . DIRECTORY_SEPARATOR . $langDir . $candidate)) {
|
$relativePath = Path::join($langDir, $candidate);
|
||||||
$files[] = $langDir . $candidate;
|
$absolutePath = Director::getAbsFile($relativePath);
|
||||||
|
if (file_exists($absolutePath)) {
|
||||||
|
$files[] = $relativePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($return) {
|
if ($return) {
|
||||||
return $files;
|
return $files;
|
||||||
} else {
|
|
||||||
foreach ($files as $file) {
|
|
||||||
$this->javascript($file);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$this->javascript($file);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1345,9 +1347,12 @@ MESSAGE
|
|||||||
function () use ($fileList, $minify, $type) {
|
function () use ($fileList, $minify, $type) {
|
||||||
// Physically combine all file content
|
// Physically combine all file content
|
||||||
$combinedData = '';
|
$combinedData = '';
|
||||||
$base = Director::baseFolder() . '/';
|
|
||||||
foreach ($fileList as $file) {
|
foreach ($fileList as $file) {
|
||||||
$fileContent = file_get_contents($base . $file);
|
$filePath = Director::getAbsFile($file);
|
||||||
|
if (!file_exists($filePath)) {
|
||||||
|
throw new InvalidArgumentException("Combined file {$file} does not exist");
|
||||||
|
}
|
||||||
|
$fileContent = file_get_contents($filePath);
|
||||||
// Use configured minifier
|
// Use configured minifier
|
||||||
if ($minify) {
|
if ($minify) {
|
||||||
$fileContent = $this->minifier->minify($fileContent, $type, $file);
|
$fileContent = $this->minifier->minify($fileContent, $type, $file);
|
||||||
@ -1419,11 +1424,11 @@ MESSAGE
|
|||||||
protected function hashOfFiles($fileList)
|
protected function hashOfFiles($fileList)
|
||||||
{
|
{
|
||||||
// Get hash based on hash of each file
|
// Get hash based on hash of each file
|
||||||
$base = Director::baseFolder() . '/';
|
|
||||||
$hash = '';
|
$hash = '';
|
||||||
foreach ($fileList as $file) {
|
foreach ($fileList as $file) {
|
||||||
if (file_exists($base . $file)) {
|
$absolutePath = Director::getAbsFile($file);
|
||||||
$hash .= sha1_file($base . $file);
|
if (file_exists($absolutePath)) {
|
||||||
|
$hash .= sha1_file($absolutePath);
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidArgumentException("Combined file {$file} does not exist");
|
throw new InvalidArgumentException("Combined file {$file} does not exist");
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,11 @@ class SSViewer implements Flushable
|
|||||||
*/
|
*/
|
||||||
const DEFAULT_THEME = '$default';
|
const DEFAULT_THEME = '$default';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier for the public theme
|
||||||
|
*/
|
||||||
|
const PUBLIC_THEME = '$public';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list (highest priority first) of themes to use
|
* A list (highest priority first) of themes to use
|
||||||
* Only used when {@link $theme_enabled} is set to TRUE.
|
* Only used when {@link $theme_enabled} is set to TRUE.
|
||||||
@ -267,7 +272,7 @@ class SSViewer implements Flushable
|
|||||||
*/
|
*/
|
||||||
public static function get_themes()
|
public static function get_themes()
|
||||||
{
|
{
|
||||||
$default = [self::DEFAULT_THEME];
|
$default = [self::PUBLIC_THEME, self::DEFAULT_THEME];
|
||||||
|
|
||||||
if (!SSViewer::config()->uninherited('theme_enabled')) {
|
if (!SSViewer::config()->uninherited('theme_enabled')) {
|
||||||
return $default;
|
return $default;
|
||||||
@ -284,7 +289,7 @@ class SSViewer implements Flushable
|
|||||||
|
|
||||||
// Support legacy behaviour
|
// Support legacy behaviour
|
||||||
if ($theme = SSViewer::config()->uninherited('theme')) {
|
if ($theme = SSViewer::config()->uninherited('theme')) {
|
||||||
return [$theme, self::DEFAULT_THEME];
|
return [self::PUBLIC_THEME, $theme, self::DEFAULT_THEME];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $default;
|
return $default;
|
||||||
|
@ -4,6 +4,7 @@ namespace SilverStripe\View;
|
|||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use SilverStripe\Core\Manifest\ModuleLoader;
|
use SilverStripe\Core\Manifest\ModuleLoader;
|
||||||
|
use SilverStripe\Core\Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles finding templates from a stack of template manifest objects.
|
* Handles finding templates from a stack of template manifest objects.
|
||||||
@ -103,12 +104,12 @@ class ThemeResourceLoader
|
|||||||
if (count($parts) > 1) {
|
if (count($parts) > 1) {
|
||||||
throw new InvalidArgumentException("Invalid theme identifier {$identifier}");
|
throw new InvalidArgumentException("Invalid theme identifier {$identifier}");
|
||||||
}
|
}
|
||||||
return ltrim($identifier, '/');
|
return Path::normalise($identifier, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no slash / colon it's a legacy theme
|
// If there is no slash / colon it's a legacy theme
|
||||||
if ($slashPos === false && count($parts) === 1) {
|
if ($slashPos === false && count($parts) === 1) {
|
||||||
return THEMES_DIR.'/'.$identifier;
|
return Path::join(THEMES_DIR, $identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract from <vendor>/<module>:<theme> format.
|
// Extract from <vendor>/<module>:<theme> format.
|
||||||
@ -148,7 +149,7 @@ class ThemeResourceLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Join module with subpath
|
// Join module with subpath
|
||||||
return ltrim($modulePath . $subpath, '/');
|
return Path::normalise($modulePath . $subpath, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -214,7 +215,7 @@ class ThemeResourceLoader
|
|||||||
foreach ($themePaths as $themePath) {
|
foreach ($themePaths as $themePath) {
|
||||||
// Join path
|
// Join path
|
||||||
$pathParts = [ $this->base, $themePath, 'templates', $head, $type, $tail ];
|
$pathParts = [ $this->base, $themePath, 'templates', $head, $type, $tail ];
|
||||||
$path = implode('/', array_filter($pathParts)) . '.ss';
|
$path = Path::join($pathParts) . '.ss';
|
||||||
if (file_exists($path)) {
|
if (file_exists($path)) {
|
||||||
return $path;
|
return $path;
|
||||||
}
|
}
|
||||||
@ -282,16 +283,13 @@ class ThemeResourceLoader
|
|||||||
*/
|
*/
|
||||||
public function findThemedResource($resource, $themes)
|
public function findThemedResource($resource, $themes)
|
||||||
{
|
{
|
||||||
if ($resource[0] !== '/') {
|
|
||||||
$resource = '/' . $resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
$paths = $this->getThemePaths($themes);
|
$paths = $this->getThemePaths($themes);
|
||||||
|
|
||||||
foreach ($paths as $themePath) {
|
foreach ($paths as $themePath) {
|
||||||
$abspath = $this->base . '/' . $themePath;
|
$relativePath = Path::join($themePath, $resource);
|
||||||
if (file_exists($abspath . $resource)) {
|
$absolutePath = Path::join($this->base, $relativePath);
|
||||||
return $themePath . $resource;
|
if (file_exists($absolutePath)) {
|
||||||
|
return $relativePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\Core\Environment;
|
use SilverStripe\Core\Environment;
|
||||||
use SilverStripe\Core\EnvironmentLoader;
|
use SilverStripe\Core\EnvironmentLoader;
|
||||||
use SilverStripe\Core\TempFolder;
|
use SilverStripe\Core\TempFolder;
|
||||||
@ -57,6 +58,10 @@ if (!defined('BASE_PATH')) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set public webroot dir / path
|
||||||
|
define('PUBLIC_DIR', is_dir(BASE_PATH . DIRECTORY_SEPARATOR . 'public') ? 'public' : '');
|
||||||
|
define('PUBLIC_PATH', PUBLIC_DIR ? BASE_PATH . DIRECTORY_SEPARATOR . PUBLIC_DIR : BASE_PATH);
|
||||||
|
|
||||||
// Allow a first class env var to be set that disables .env file loading
|
// Allow a first class env var to be set that disables .env file loading
|
||||||
if (!Environment::getEnv('SS_IGNORE_DOT_ENV')) {
|
if (!Environment::getEnv('SS_IGNORE_DOT_ENV')) {
|
||||||
call_user_func(function () {
|
call_user_func(function () {
|
||||||
@ -102,13 +107,17 @@ if (!defined('BASE_URL')) {
|
|||||||
|
|
||||||
// Determine the base URL by comparing SCRIPT_NAME to SCRIPT_FILENAME and getting common elements
|
// Determine the base URL by comparing SCRIPT_NAME to SCRIPT_FILENAME and getting common elements
|
||||||
// This tends not to work on CLI
|
// This tends not to work on CLI
|
||||||
$path = realpath($_SERVER['SCRIPT_FILENAME']);
|
$path = Convert::slashes($_SERVER['SCRIPT_FILENAME']);
|
||||||
if (substr($path, 0, strlen(BASE_PATH)) == BASE_PATH) {
|
$scriptName = Convert::slashes($_SERVER['SCRIPT_NAME'], '/');
|
||||||
$urlSegmentToRemove = str_replace('\\', '/', substr($path, strlen(BASE_PATH)));
|
|
||||||
if (substr($_SERVER['SCRIPT_NAME'], -strlen($urlSegmentToRemove)) == $urlSegmentToRemove) {
|
// Ensure script is served from public folder (otherwise error)
|
||||||
$baseURL = substr($_SERVER['SCRIPT_NAME'], 0, -strlen($urlSegmentToRemove));
|
if (stripos($path, PUBLIC_PATH) === 0) {
|
||||||
// ltrim('.'), normalise slashes to '/', and rtrim('/')
|
// Get entire url following PUBLIC_PATH
|
||||||
return rtrim(str_replace('\\', '/', ltrim($baseURL, '.')), '/');
|
$urlSegmentToRemove = Convert::slashes(substr($path, strlen(PUBLIC_PATH)), '/');
|
||||||
|
if (substr($scriptName, -strlen($urlSegmentToRemove)) === $urlSegmentToRemove) {
|
||||||
|
// Remove this from end of SCRIPT_NAME to get url to base
|
||||||
|
$baseURL = substr($scriptName, 0, -strlen($urlSegmentToRemove));
|
||||||
|
return rtrim(ltrim($baseURL, '.'), '/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +144,14 @@ if (!defined('ASSETS_DIR')) {
|
|||||||
define('ASSETS_DIR', 'assets');
|
define('ASSETS_DIR', 'assets');
|
||||||
}
|
}
|
||||||
if (!defined('ASSETS_PATH')) {
|
if (!defined('ASSETS_PATH')) {
|
||||||
define('ASSETS_PATH', BASE_PATH . DIRECTORY_SEPARATOR . ASSETS_DIR);
|
call_user_func(function () {
|
||||||
|
$paths = [
|
||||||
|
BASE_PATH,
|
||||||
|
(PUBLIC_DIR ? PUBLIC_DIR : null),
|
||||||
|
ASSETS_DIR
|
||||||
|
];
|
||||||
|
define('ASSETS_PATH', implode(DIRECTORY_SEPARATOR, array_filter($paths)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom include path - deprecated
|
// Custom include path - deprecated
|
||||||
|
@ -338,6 +338,12 @@ class DirectorTest extends SapphireTest
|
|||||||
BASE_PATH . '/some/file.txt',
|
BASE_PATH . '/some/file.txt',
|
||||||
'some/file.txt',
|
'some/file.txt',
|
||||||
],
|
],
|
||||||
|
// public folder is found
|
||||||
|
[
|
||||||
|
'http://www.mysite.com/base/folder',
|
||||||
|
PUBLIC_PATH . '/some/file.txt',
|
||||||
|
'some/file.txt',
|
||||||
|
],
|
||||||
// querystring is protected
|
// querystring is protected
|
||||||
[
|
[
|
||||||
'http://www.mysite.com/base/folder',
|
'http://www.mysite.com/base/folder',
|
||||||
|
@ -22,6 +22,10 @@ class SimpleResourceURLGeneratorTest extends SapphireTest
|
|||||||
'alternate_base_url',
|
'alternate_base_url',
|
||||||
'http://www.mysite.com/'
|
'http://www.mysite.com/'
|
||||||
);
|
);
|
||||||
|
Director::config()->set(
|
||||||
|
'alternate_public_dir',
|
||||||
|
'public'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddMTime()
|
public function testAddMTime()
|
||||||
@ -30,7 +34,7 @@ class SimpleResourceURLGeneratorTest extends SapphireTest
|
|||||||
$generator = Injector::inst()->get(ResourceURLGenerator::class);
|
$generator = Injector::inst()->get(ResourceURLGenerator::class);
|
||||||
$mtime = filemtime(__DIR__ .'/SimpleResourceURLGeneratorTest/_fakewebroot/basemodule/client/file.js');
|
$mtime = filemtime(__DIR__ .'/SimpleResourceURLGeneratorTest/_fakewebroot/basemodule/client/file.js');
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'/basemodule/client/file.js?m='.$mtime,
|
'/resources/basemodule/client/file.js?m='.$mtime,
|
||||||
$generator->urlForResource('basemodule/client/file.js')
|
$generator->urlForResource('basemodule/client/file.js')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -43,11 +47,34 @@ class SimpleResourceURLGeneratorTest extends SapphireTest
|
|||||||
__DIR__ .'/SimpleResourceURLGeneratorTest/_fakewebroot/vendor/silverstripe/mymodule/client/style.css'
|
__DIR__ .'/SimpleResourceURLGeneratorTest/_fakewebroot/vendor/silverstripe/mymodule/client/style.css'
|
||||||
);
|
);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'/resources/silverstripe/mymodule/client/style.css?m='.$mtime,
|
'/resources/vendor/silverstripe/mymodule/client/style.css?m='.$mtime,
|
||||||
$generator->urlForResource('vendor/silverstripe/mymodule/client/style.css')
|
$generator->urlForResource('vendor/silverstripe/mymodule/client/style.css')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPublicDirResource()
|
||||||
|
{
|
||||||
|
/** @var SimpleResourceURLGenerator $generator */
|
||||||
|
$generator = Injector::inst()->get(ResourceURLGenerator::class);
|
||||||
|
$mtime = filemtime(
|
||||||
|
__DIR__ .'/SimpleResourceURLGeneratorTest/_fakewebroot/public/basemodule/css/style.css'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
'/basemodule/css/style.css?m='.$mtime,
|
||||||
|
$generator->urlForResource('public/basemodule/css/style.css')
|
||||||
|
);
|
||||||
|
|
||||||
|
$mtime = filemtime(
|
||||||
|
__DIR__ .'/SimpleResourceURLGeneratorTest/_fakewebroot/basemodule/client/file.js'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
'/resources/basemodule/client/file.js?m='.$mtime,
|
||||||
|
$generator->urlForResource('basemodule/client/file.js')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function testModuleResource()
|
public function testModuleResource()
|
||||||
{
|
{
|
||||||
/** @var SimpleResourceURLGenerator $generator */
|
/** @var SimpleResourceURLGenerator $generator */
|
||||||
@ -60,7 +87,7 @@ class SimpleResourceURLGeneratorTest extends SapphireTest
|
|||||||
__DIR__ .'/SimpleResourceURLGeneratorTest/_fakewebroot/vendor/silverstripe/mymodule/client/style.css'
|
__DIR__ .'/SimpleResourceURLGeneratorTest/_fakewebroot/vendor/silverstripe/mymodule/client/style.css'
|
||||||
);
|
);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'/resources/silverstripe/mymodule/client/style.css?m='.$mtime,
|
'/resources/vendor/silverstripe/mymodule/client/style.css?m='.$mtime,
|
||||||
$generator->urlForResource($module->getResource('client/style.css'))
|
$generator->urlForResource($module->getResource('client/style.css'))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
/* mymodule/style.css */
|
||||||
|
body {}
|
@ -3,7 +3,6 @@
|
|||||||
namespace SilverStripe\Core\Tests\Manifest;
|
namespace SilverStripe\Core\Tests\Manifest;
|
||||||
|
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Core\Manifest\ModuleLoader;
|
|
||||||
use SilverStripe\Core\Manifest\ModuleManifest;
|
use SilverStripe\Core\Manifest\ModuleManifest;
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
|
||||||
@ -27,6 +26,7 @@ class ModuleResourceTest extends SapphireTest
|
|||||||
$this->manifest = new ModuleManifest($this->base);
|
$this->manifest = new ModuleManifest($this->base);
|
||||||
$this->manifest->init();
|
$this->manifest->init();
|
||||||
Director::config()->set('alternate_base_url', 'http://www.mysite.com/basefolder/');
|
Director::config()->set('alternate_base_url', 'http://www.mysite.com/basefolder/');
|
||||||
|
Director::config()->set('alternate_public_dir', 'public');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBaseModuleResource()
|
public function testBaseModuleResource()
|
||||||
@ -42,7 +42,7 @@ class ModuleResourceTest extends SapphireTest
|
|||||||
$resource->getPath()
|
$resource->getPath()
|
||||||
);
|
);
|
||||||
$this->assertStringStartsWith(
|
$this->assertStringStartsWith(
|
||||||
'/basefolder/module/client/script.js?m=',
|
'/basefolder/resources/module/client/script.js?m=',
|
||||||
$resource->getURL()
|
$resource->getURL()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ class ModuleResourceTest extends SapphireTest
|
|||||||
$resource->getPath()
|
$resource->getPath()
|
||||||
);
|
);
|
||||||
$this->assertStringStartsWith(
|
$this->assertStringStartsWith(
|
||||||
'/basefolder/resources/silverstripe/modulec/client/script.js?m=',
|
'/basefolder/resources/vendor/silverstripe/modulec/client/script.js?m=',
|
||||||
$resource->getURL()
|
$resource->getURL()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ class ModuleResourceTest extends SapphireTest
|
|||||||
$resource->getPath()
|
$resource->getPath()
|
||||||
);
|
);
|
||||||
$this->assertStringStartsWith(
|
$this->assertStringStartsWith(
|
||||||
'/basefolder/resources/silverstripe/modulec/client/script.js?m=',
|
'/basefolder/resources/vendor/silverstripe/modulec/client/script.js?m=',
|
||||||
$resource->getURL()
|
$resource->getURL()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
118
tests/php/Core/PathTest.php
Normal file
118
tests/php/Core/PathTest.php
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Core\Tests;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use SilverStripe\Core\Path;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
|
||||||
|
class PathTest extends SapphireTest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test paths are joined
|
||||||
|
*
|
||||||
|
* @dataProvider providerTestJoinPaths
|
||||||
|
* @param array $args Arguments to pass to Path::join()
|
||||||
|
* @param string $expected Expected path
|
||||||
|
*/
|
||||||
|
public function testJoinPaths($args, $expected)
|
||||||
|
{
|
||||||
|
$joined = Path::join($args);
|
||||||
|
$this->assertEquals($expected, $joined);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of tests for testJoinPaths
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function providerTestJoinPaths()
|
||||||
|
{
|
||||||
|
$tests = [
|
||||||
|
// Single arg
|
||||||
|
[['/'], '/'],
|
||||||
|
[['\\'], '/'],
|
||||||
|
[['base'], 'base'],
|
||||||
|
[['c:/base\\'], 'c:/base'],
|
||||||
|
// Windows paths
|
||||||
|
[['c:/', 'bob'], 'c:/bob'],
|
||||||
|
[['c:/', '\\bob/'], 'c:/bob'],
|
||||||
|
[['c:\\basedir', '/bob\\'], 'c:/basedir/bob'],
|
||||||
|
// Empty-ish paths to clear out
|
||||||
|
[['/root/dir', '/', ' ', 'next/', '\\'], '/root/dir/next'],
|
||||||
|
[['/', '/', ' ', '/', '\\'], '/'],
|
||||||
|
[['/', '', '',], '/'],
|
||||||
|
[['/root', '/', ' ', '/', '\\'], '/root'],
|
||||||
|
[['', '/root', '/', ' ', '/', '\\'], '/root'],
|
||||||
|
[['', 'root', '/', ' ', '/', '\\'], 'root'],
|
||||||
|
[['\\', '', '/root', '/', ' ', '/', '\\'], '/root'],
|
||||||
|
// join blocks of paths
|
||||||
|
[['/root/dir', 'another/path\\to/join'], '/root/dir/another/path/to/join'],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Rewrite tests for other filesystems (output arg only)
|
||||||
|
if (DIRECTORY_SEPARATOR !== '/') {
|
||||||
|
foreach ($tests as $index => $test) {
|
||||||
|
$tests[$index][1] = str_replace('/', DIRECTORY_SEPARATOR, $tests[$index][1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $tests;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that joinPaths give the appropriate error
|
||||||
|
*
|
||||||
|
* @dataProvider providerTestJoinPathsErrors
|
||||||
|
* @param array $args Arguments to pass to Filesystem::joinPath()
|
||||||
|
* @param string $error Expected path
|
||||||
|
*/
|
||||||
|
public function testJoinPathsErrors($args, $error)
|
||||||
|
{
|
||||||
|
$this->expectException(InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage($error);
|
||||||
|
Path::join($args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providerTestJoinPathsErrors()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[['/base', '../passwd'], 'Can not collapse relative folders'],
|
||||||
|
[['/base/../', 'passwd/path'], 'Can not collapse relative folders'],
|
||||||
|
[['../', 'passwd/path'], 'Can not collapse relative folders'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providerTestNormalise
|
||||||
|
* @param string $input
|
||||||
|
* @param string $expected
|
||||||
|
*/
|
||||||
|
public function testNormalise($input, $expected)
|
||||||
|
{
|
||||||
|
$output = Path::normalise($input);
|
||||||
|
$this->assertEquals($expected, $output, "Expected $input to be normalised to $expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providerTestNormalise()
|
||||||
|
{
|
||||||
|
$tests = [
|
||||||
|
// Windows paths
|
||||||
|
["c:/bob", "c:/bob"],
|
||||||
|
["c://bob", "c:/bob"],
|
||||||
|
["/root/dir/", "/root/dir"],
|
||||||
|
["/root\\dir\\\\sub/", "/root/dir/sub"],
|
||||||
|
[" /some/dir/ ", "/some/dir"],
|
||||||
|
["", ""],
|
||||||
|
["/", ""],
|
||||||
|
["\\", ""],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Rewrite tests for other filesystems (output arg only)
|
||||||
|
if (DIRECTORY_SEPARATOR !== '/') {
|
||||||
|
foreach ($tests as $index => $test) {
|
||||||
|
$tests[$index][1] = str_replace('/', DIRECTORY_SEPARATOR, $tests[$index][1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $tests;
|
||||||
|
}
|
||||||
|
}
|
@ -140,7 +140,7 @@ class HTMLEditorFieldTest extends FunctionalTest
|
|||||||
= '/assets/HTMLEditorFieldTest/f5c7c2f814/example__ResizedImageWzEwLDIwXQ.jpg';
|
= '/assets/HTMLEditorFieldTest/f5c7c2f814/example__ResizedImageWzEwLDIwXQ.jpg';
|
||||||
|
|
||||||
$this->assertEquals($neededFilename, (string)$xml[0]['src'], 'Correct URL of resized image is set.');
|
$this->assertEquals($neededFilename, (string)$xml[0]['src'], 'Correct URL of resized image is set.');
|
||||||
$this->assertTrue(file_exists(BASE_PATH.DIRECTORY_SEPARATOR.$neededFilename), 'File for resized image exists');
|
$this->assertTrue(file_exists(PUBLIC_PATH.DIRECTORY_SEPARATOR.$neededFilename), 'File for resized image exists');
|
||||||
$this->assertEquals(false, $obj->HasBrokenFile, 'Referenced image file exists.');
|
$this->assertEquals(false, $obj->HasBrokenFile, 'Referenced image file exists.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ class TinyMCECombinedGeneratorTest extends SapphireTest
|
|||||||
// Set custom base_path for tinymce
|
// Set custom base_path for tinymce
|
||||||
Director::config()->set('alternate_base_folder', __DIR__ . '/TinyMCECombinedGeneratorTest');
|
Director::config()->set('alternate_base_folder', __DIR__ . '/TinyMCECombinedGeneratorTest');
|
||||||
Director::config()->set('alternate_base_url', 'http://www.mysite.com/basedir/');
|
Director::config()->set('alternate_base_url', 'http://www.mysite.com/basedir/');
|
||||||
|
Director::config()->set('alternate_public_dir', ''); // Disable public dir
|
||||||
SSViewer::config()->set('themes', [SSViewer::DEFAULT_THEME]);
|
SSViewer::config()->set('themes', [SSViewer::DEFAULT_THEME]);
|
||||||
TinyMCEConfig::config()
|
TinyMCEConfig::config()
|
||||||
->set('base_dir', 'tinymce')
|
->set('base_dir', 'tinymce')
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace SilverStripe\View\Tests;
|
namespace SilverStripe\View\Tests;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
use SilverStripe\View\Requirements;
|
use SilverStripe\View\Requirements;
|
||||||
@ -11,6 +12,8 @@ use SilverStripe\Assets\Tests\Storage\AssetStoreTest\TestAssetStore;
|
|||||||
use SilverStripe\View\Requirements_Backend;
|
use SilverStripe\View\Requirements_Backend;
|
||||||
use SilverStripe\Core\Manifest\ResourceURLGenerator;
|
use SilverStripe\Core\Manifest\ResourceURLGenerator;
|
||||||
use SilverStripe\Control\SimpleResourceURLGenerator;
|
use SilverStripe\Control\SimpleResourceURLGenerator;
|
||||||
|
use SilverStripe\View\SSViewer;
|
||||||
|
use SilverStripe\View\ThemeResourceLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Test that order of combine_files() is correct
|
* @todo Test that order of combine_files() is correct
|
||||||
@ -20,16 +23,28 @@ use SilverStripe\Control\SimpleResourceURLGenerator;
|
|||||||
class RequirementsTest extends SapphireTest
|
class RequirementsTest extends SapphireTest
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ThemeResourceLoader
|
||||||
|
*/
|
||||||
|
protected $oldThemeResourceLoader = null;
|
||||||
|
|
||||||
static $html_template = '<html><head></head><body></body></html>';
|
static $html_template = '<html><head></head><body></body></html>';
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
Director::config()->set('alternate_base_folder', __DIR__ . '/SSViewerTest');
|
||||||
|
Director::config()->set('alternate_base_url', 'http://www.mysite.com/basedir/');
|
||||||
|
Director::config()->set('alternate_public_dir', 'public'); // Enforce public dir
|
||||||
|
// Add public as a theme in itself
|
||||||
|
SSViewer::set_themes([SSViewer::PUBLIC_THEME, SSViewer::DEFAULT_THEME]);
|
||||||
TestAssetStore::activate('RequirementsTest'); // Set backend root to /RequirementsTest
|
TestAssetStore::activate('RequirementsTest'); // Set backend root to /RequirementsTest
|
||||||
|
$this->oldThemeResourceLoader = ThemeResourceLoader::inst();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown()
|
protected function tearDown()
|
||||||
{
|
{
|
||||||
|
ThemeResourceLoader::set_instance($this->oldThemeResourceLoader);
|
||||||
TestAssetStore::reset();
|
TestAssetStore::reset();
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
}
|
}
|
||||||
@ -84,20 +99,23 @@ class RequirementsTest extends SapphireTest
|
|||||||
*/
|
*/
|
||||||
protected function setupCombinedRequirements($backend)
|
protected function setupCombinedRequirements($backend)
|
||||||
{
|
{
|
||||||
$basePath = $this->getThemeRoot();
|
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
|
|
||||||
// require files normally (e.g. called from a FormField instance)
|
// require files normally (e.g. called from a FormField instance)
|
||||||
$backend->javascript($basePath . '/javascript/RequirementsTest_a.js');
|
$backend->javascript('javascript/RequirementsTest_a.js');
|
||||||
$backend->javascript($basePath . '/javascript/RequirementsTest_b.js');
|
$backend->javascript('javascript/RequirementsTest_b.js');
|
||||||
$backend->javascript($basePath . '/javascript/RequirementsTest_c.js');
|
$backend->javascript('javascript/RequirementsTest_c.js');
|
||||||
|
|
||||||
|
// Public resources may or may not be specified with `public/` prefix
|
||||||
|
$backend->javascript('javascript/RequirementsTest_d.js');
|
||||||
|
$backend->javascript('public/javascript/RequirementsTest_e.js');
|
||||||
|
|
||||||
// require two of those files as combined includes
|
// require two of those files as combined includes
|
||||||
$backend->combineFiles(
|
$backend->combineFiles(
|
||||||
'RequirementsTest_bc.js',
|
'RequirementsTest_bc.js',
|
||||||
array(
|
array(
|
||||||
$basePath . '/javascript/RequirementsTest_b.js',
|
'javascript/RequirementsTest_b.js',
|
||||||
$basePath . '/javascript/RequirementsTest_c.js'
|
'javascript/RequirementsTest_c.js'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -109,15 +127,14 @@ class RequirementsTest extends SapphireTest
|
|||||||
*/
|
*/
|
||||||
protected function setupCombinedNonrequiredRequirements($backend)
|
protected function setupCombinedNonrequiredRequirements($backend)
|
||||||
{
|
{
|
||||||
$basePath = $this->getThemeRoot();
|
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
|
|
||||||
// require files as combined includes
|
// require files as combined includes
|
||||||
$backend->combineFiles(
|
$backend->combineFiles(
|
||||||
'RequirementsTest_bc.js',
|
'RequirementsTest_bc.js',
|
||||||
array(
|
array(
|
||||||
$basePath . '/javascript/RequirementsTest_b.js',
|
'javascript/RequirementsTest_b.js',
|
||||||
$basePath . '/javascript/RequirementsTest_c.js'
|
'javascript/RequirementsTest_c.js'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -129,25 +146,24 @@ class RequirementsTest extends SapphireTest
|
|||||||
*/
|
*/
|
||||||
protected function setupCombinedRequirementsJavascriptAsyncDefer($backend, $async, $defer)
|
protected function setupCombinedRequirementsJavascriptAsyncDefer($backend, $async, $defer)
|
||||||
{
|
{
|
||||||
$basePath = $this->getThemeRoot();
|
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
|
|
||||||
// require files normally (e.g. called from a FormField instance)
|
// require files normally (e.g. called from a FormField instance)
|
||||||
$backend->javascript($basePath . '/javascript/RequirementsTest_a.js');
|
$backend->javascript('javascript/RequirementsTest_a.js');
|
||||||
$backend->javascript($basePath . '/javascript/RequirementsTest_b.js');
|
$backend->javascript('javascript/RequirementsTest_b.js');
|
||||||
$backend->javascript($basePath . '/javascript/RequirementsTest_c.js');
|
$backend->javascript('javascript/RequirementsTest_c.js');
|
||||||
|
|
||||||
// require two of those files as combined includes
|
// require two of those files as combined includes
|
||||||
$backend->combineFiles(
|
$backend->combineFiles(
|
||||||
'RequirementsTest_bc.js',
|
'RequirementsTest_bc.js',
|
||||||
array(
|
[
|
||||||
$basePath . '/javascript/RequirementsTest_b.js',
|
'javascript/RequirementsTest_b.js',
|
||||||
$basePath . '/javascript/RequirementsTest_c.js'
|
'javascript/RequirementsTest_c.js'
|
||||||
),
|
],
|
||||||
array(
|
[
|
||||||
'async' => $async,
|
'async' => $async,
|
||||||
'defer' => $defer,
|
'defer' => $defer,
|
||||||
)
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,17 +171,14 @@ class RequirementsTest extends SapphireTest
|
|||||||
{
|
{
|
||||||
/** @var Requirements_Backend $backend */
|
/** @var Requirements_Backend $backend */
|
||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$basePath = $this->getThemeRoot();
|
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
|
|
||||||
// require files normally (e.g. called from a FormField instance)
|
// require files normally (e.g. called from a FormField instance)
|
||||||
$backend->javascript(
|
$backend->javascript(
|
||||||
$basePath . '/javascript/RequirementsTest_a.js',
|
'javascript/RequirementsTest_a.js',
|
||||||
[
|
[ 'type' => 'application/json' ]
|
||||||
'type' => 'application/json'
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
$backend->javascript($basePath . '/javascript/RequirementsTest_b.js');
|
$backend->javascript('javascript/RequirementsTest_b.js');
|
||||||
$result = $backend->includeInHTML(self::$html_template);
|
$result = $backend->includeInHTML(self::$html_template);
|
||||||
$this->assertRegExp(
|
$this->assertRegExp(
|
||||||
'#<script type="application/json" src=".*/javascript/RequirementsTest_a.js#',
|
'#<script type="application/json" src=".*/javascript/RequirementsTest_a.js#',
|
||||||
@ -505,26 +518,27 @@ class RequirementsTest extends SapphireTest
|
|||||||
|
|
||||||
public function testCombinedCss()
|
public function testCombinedCss()
|
||||||
{
|
{
|
||||||
$basePath = $this->getThemeRoot();
|
|
||||||
/** @var Requirements_Backend $backend */
|
/** @var Requirements_Backend $backend */
|
||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
|
|
||||||
$backend->combineFiles(
|
$backend->combineFiles(
|
||||||
'print.css',
|
'print.css',
|
||||||
array(
|
[
|
||||||
$basePath . '/css/RequirementsTest_print_a.css',
|
'css/RequirementsTest_print_a.css',
|
||||||
$basePath . '/css/RequirementsTest_print_b.css'
|
'css/RequirementsTest_print_b.css',
|
||||||
),
|
'css/RequirementsTest_print_d.css',
|
||||||
array(
|
'public/css/RequirementsTest_print_e.css',
|
||||||
|
],
|
||||||
|
[
|
||||||
'media' => 'print'
|
'media' => 'print'
|
||||||
)
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
$html = $backend->includeInHTML(self::$html_template);
|
$html = $backend->includeInHTML(self::$html_template);
|
||||||
|
|
||||||
$this->assertRegExp(
|
$this->assertRegExp(
|
||||||
'/href=".*\/print\-94e723d\.css/',
|
'/href=".*\/print\-69ce614\.css/',
|
||||||
$html,
|
$html,
|
||||||
'Print stylesheets have been combined.'
|
'Print stylesheets have been combined.'
|
||||||
);
|
);
|
||||||
@ -540,22 +554,26 @@ class RequirementsTest extends SapphireTest
|
|||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
$backend->combineFiles(
|
$backend->combineFiles(
|
||||||
'style.css',
|
'style.css',
|
||||||
array(
|
[
|
||||||
$basePath . '/css/RequirementsTest_b.css',
|
'css/RequirementsTest_b.css',
|
||||||
$basePath . '/css/RequirementsTest_c.css'
|
'css/RequirementsTest_c.css',
|
||||||
)
|
'css/RequirementsTest_d.css',
|
||||||
|
'public/css/RequirementsTest_e.css',
|
||||||
|
]
|
||||||
);
|
);
|
||||||
$backend->combineFiles(
|
$backend->combineFiles(
|
||||||
'style.css',
|
'style.css',
|
||||||
array(
|
array(
|
||||||
$basePath . '/css/RequirementsTest_b.css',
|
'css/RequirementsTest_b.css',
|
||||||
$basePath . '/css/RequirementsTest_c.css'
|
'css/RequirementsTest_c.css',
|
||||||
|
'css/RequirementsTest_d.css',
|
||||||
|
'public/css/RequirementsTest_e.css',
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$html = $backend->includeInHTML(self::$html_template);
|
$html = $backend->includeInHTML(self::$html_template);
|
||||||
$this->assertRegExp(
|
$this->assertRegExp(
|
||||||
'/href=".*\/style\-bcd90f5\.css/',
|
'/href=".*\/style\-8011538\.css/',
|
||||||
$html,
|
$html,
|
||||||
'Stylesheets have been combined.'
|
'Stylesheets have been combined.'
|
||||||
);
|
);
|
||||||
@ -563,7 +581,6 @@ class RequirementsTest extends SapphireTest
|
|||||||
|
|
||||||
public function testBlockedCombinedJavascript()
|
public function testBlockedCombinedJavascript()
|
||||||
{
|
{
|
||||||
$basePath = $this->getThemeRoot();
|
|
||||||
/** @var Requirements_Backend $backend */
|
/** @var Requirements_Backend $backend */
|
||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$this->setupCombinedRequirements($backend);
|
$this->setupCombinedRequirements($backend);
|
||||||
@ -585,7 +602,7 @@ class RequirementsTest extends SapphireTest
|
|||||||
|
|
||||||
/* BLOCKED UNCOMBINED FILES ARE NOT INCLUDED */
|
/* BLOCKED UNCOMBINED FILES ARE NOT INCLUDED */
|
||||||
$this->setupCombinedRequirements($backend);
|
$this->setupCombinedRequirements($backend);
|
||||||
$backend->block($basePath .'/javascript/RequirementsTest_b.js');
|
$backend->block('javascript/RequirementsTest_b.js');
|
||||||
$combinedFileName2 = '/_combinedfiles/RequirementsTest_bc-3748f67.js'; // SHA1 without file b included
|
$combinedFileName2 = '/_combinedfiles/RequirementsTest_bc-3748f67.js'; // SHA1 without file b included
|
||||||
$combinedFilePath2 = TestAssetStore::base_path() . $combinedFileName2;
|
$combinedFilePath2 = TestAssetStore::base_path() . $combinedFileName2;
|
||||||
clearstatcache(); // needed to get accurate file_exists() results
|
clearstatcache(); // needed to get accurate file_exists() results
|
||||||
@ -596,7 +613,7 @@ class RequirementsTest extends SapphireTest
|
|||||||
file_get_contents($combinedFilePath2),
|
file_get_contents($combinedFilePath2),
|
||||||
'blocked uncombined files are not included'
|
'blocked uncombined files are not included'
|
||||||
);
|
);
|
||||||
$backend->unblock($basePath . '/javascript/RequirementsTest_b.js');
|
$backend->unblock('javascript/RequirementsTest_b.js');
|
||||||
|
|
||||||
/* A SINGLE FILE CAN'T BE INCLUDED IN TWO COMBINED FILES */
|
/* A SINGLE FILE CAN'T BE INCLUDED IN TWO COMBINED FILES */
|
||||||
$this->setupCombinedRequirements($backend);
|
$this->setupCombinedRequirements($backend);
|
||||||
@ -606,28 +623,26 @@ class RequirementsTest extends SapphireTest
|
|||||||
$this->expectException(InvalidArgumentException::class);
|
$this->expectException(InvalidArgumentException::class);
|
||||||
$this->expectExceptionMessage(sprintf(
|
$this->expectExceptionMessage(sprintf(
|
||||||
"Requirements_Backend::combine_files(): Already included file(s) %s in combined file '%s'",
|
"Requirements_Backend::combine_files(): Already included file(s) %s in combined file '%s'",
|
||||||
$basePath . '/javascript/RequirementsTest_c.js',
|
'javascript/RequirementsTest_c.js',
|
||||||
'RequirementsTest_bc.js'
|
'RequirementsTest_bc.js'
|
||||||
));
|
));
|
||||||
$backend->combineFiles(
|
$backend->combineFiles(
|
||||||
'RequirementsTest_ac.js',
|
'RequirementsTest_ac.js',
|
||||||
array(
|
[
|
||||||
$basePath . '/javascript/RequirementsTest_a.js',
|
'javascript/RequirementsTest_a.js',
|
||||||
$basePath . '/javascript/RequirementsTest_c.js'
|
'javascript/RequirementsTest_c.js'
|
||||||
)
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testArgsInUrls()
|
public function testArgsInUrls()
|
||||||
{
|
{
|
||||||
$basePath = $this->getThemeRoot();
|
|
||||||
|
|
||||||
/** @var Requirements_Backend $backend */
|
/** @var Requirements_Backend $backend */
|
||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
|
|
||||||
$backend->javascript($basePath . '/javascript/RequirementsTest_a.js?test=1&test=2&test=3');
|
$backend->javascript('javascript/RequirementsTest_a.js?test=1&test=2&test=3');
|
||||||
$backend->css($basePath . '/css/RequirementsTest_a.css?test=1&test=2&test=3');
|
$backend->css('css/RequirementsTest_a.css?test=1&test=2&test=3');
|
||||||
$html = $backend->includeInHTML(self::$html_template);
|
$html = $backend->includeInHTML(self::$html_template);
|
||||||
|
|
||||||
/* Javascript has correct path */
|
/* Javascript has correct path */
|
||||||
@ -647,12 +662,10 @@ class RequirementsTest extends SapphireTest
|
|||||||
|
|
||||||
public function testRequirementsBackend()
|
public function testRequirementsBackend()
|
||||||
{
|
{
|
||||||
$basePath = $this->getThemeRoot();
|
|
||||||
|
|
||||||
/** @var Requirements_Backend $backend */
|
/** @var Requirements_Backend $backend */
|
||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
$backend->javascript($basePath . '/a.js');
|
$backend->javascript('a.js');
|
||||||
|
|
||||||
$this->assertCount(
|
$this->assertCount(
|
||||||
1,
|
1,
|
||||||
@ -660,48 +673,48 @@ class RequirementsTest extends SapphireTest
|
|||||||
"There should be only 1 file included in required javascript."
|
"There should be only 1 file included in required javascript."
|
||||||
);
|
);
|
||||||
$this->assertArrayHasKey(
|
$this->assertArrayHasKey(
|
||||||
$basePath . '/a.js',
|
'a.js',
|
||||||
$backend->getJavascript(),
|
$backend->getJavascript(),
|
||||||
"a.js should be included in required javascript."
|
"a.js should be included in required javascript."
|
||||||
);
|
);
|
||||||
|
|
||||||
$backend->javascript($basePath . '/b.js');
|
$backend->javascript('b.js');
|
||||||
$this->assertCount(
|
$this->assertCount(
|
||||||
2,
|
2,
|
||||||
$backend->getJavascript(),
|
$backend->getJavascript(),
|
||||||
"There should be 2 files included in required javascript."
|
"There should be 2 files included in required javascript."
|
||||||
);
|
);
|
||||||
|
|
||||||
$backend->block($basePath . '/a.js');
|
$backend->block('a.js');
|
||||||
$this->assertCount(
|
$this->assertCount(
|
||||||
1,
|
1,
|
||||||
$backend->getJavascript(),
|
$backend->getJavascript(),
|
||||||
"There should be only 1 file included in required javascript."
|
"There should be only 1 file included in required javascript."
|
||||||
);
|
);
|
||||||
$this->assertArrayNotHasKey(
|
$this->assertArrayNotHasKey(
|
||||||
$basePath . '/a.js',
|
'a.js',
|
||||||
$backend->getJavascript(),
|
$backend->getJavascript(),
|
||||||
"a.js should not be included in required javascript after it has been blocked."
|
"a.js should not be included in required javascript after it has been blocked."
|
||||||
);
|
);
|
||||||
$this->assertArrayHasKey(
|
$this->assertArrayHasKey(
|
||||||
$basePath . '/b.js',
|
'b.js',
|
||||||
$backend->getJavascript(),
|
$backend->getJavascript(),
|
||||||
"b.js should be included in required javascript."
|
"b.js should be included in required javascript."
|
||||||
);
|
);
|
||||||
|
|
||||||
$backend->css($basePath . '/a.css');
|
$backend->css('a.css');
|
||||||
$this->assertCount(
|
$this->assertCount(
|
||||||
1,
|
1,
|
||||||
$backend->getCSS(),
|
$backend->getCSS(),
|
||||||
"There should be only 1 file included in required css."
|
"There should be only 1 file included in required css."
|
||||||
);
|
);
|
||||||
$this->assertArrayHasKey(
|
$this->assertArrayHasKey(
|
||||||
$basePath . '/a.css',
|
'a.css',
|
||||||
$backend->getCSS(),
|
$backend->getCSS(),
|
||||||
"a.css should be in required css."
|
"a.css should be in required css."
|
||||||
);
|
);
|
||||||
|
|
||||||
$backend->block($basePath . '/a.css');
|
$backend->block('a.css');
|
||||||
$this->assertCount(
|
$this->assertCount(
|
||||||
0,
|
0,
|
||||||
$backend->getCSS(),
|
$backend->getCSS(),
|
||||||
@ -711,8 +724,6 @@ class RequirementsTest extends SapphireTest
|
|||||||
|
|
||||||
public function testAppendAndBlockWithModuleResourceLoader()
|
public function testAppendAndBlockWithModuleResourceLoader()
|
||||||
{
|
{
|
||||||
$basePath = $this->getThemeRoot();
|
|
||||||
|
|
||||||
/** @var Requirements_Backend $backend */
|
/** @var Requirements_Backend $backend */
|
||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
@ -735,38 +746,40 @@ class RequirementsTest extends SapphireTest
|
|||||||
|
|
||||||
public function testConditionalTemplateRequire()
|
public function testConditionalTemplateRequire()
|
||||||
{
|
{
|
||||||
$testPath = $this->getThemeRoot();
|
// Set /SSViewerTest and /SSViewerTest/public as themes
|
||||||
|
SSViewer::set_themes([
|
||||||
|
'/',
|
||||||
|
SSViewer::PUBLIC_THEME
|
||||||
|
]);
|
||||||
|
ThemeResourceLoader::set_instance(new ThemeResourceLoader(__DIR__ . '/SSViewerTest'));
|
||||||
|
|
||||||
/** @var Requirements_Backend $backend */
|
/** @var Requirements_Backend $backend */
|
||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
$holder = Requirements::backend();
|
$holder = Requirements::backend();
|
||||||
Requirements::set_backend($backend);
|
Requirements::set_backend($backend);
|
||||||
$data = new ArrayData(
|
$data = new ArrayData([
|
||||||
array(
|
|
||||||
'FailTest' => true,
|
'FailTest' => true,
|
||||||
)
|
]);
|
||||||
);
|
|
||||||
|
|
||||||
// Note: SSViewer theme automatically registered due to 'templates' directory
|
|
||||||
$data->renderWith('RequirementsTest_Conditionals');
|
$data->renderWith('RequirementsTest_Conditionals');
|
||||||
$this->assertFileIncluded($backend, 'css', $testPath .'/css/RequirementsTest_a.css');
|
$this->assertFileIncluded($backend, 'css', 'css/RequirementsTest_a.css');
|
||||||
$this->assertFileIncluded(
|
$this->assertFileIncluded(
|
||||||
$backend,
|
$backend,
|
||||||
'js',
|
'js',
|
||||||
array(
|
[
|
||||||
$testPath .'/javascript/RequirementsTest_b.js',
|
'javascript/RequirementsTest_b.js',
|
||||||
$testPath .'/javascript/RequirementsTest_c.js'
|
'javascript/RequirementsTest_c.js'
|
||||||
)
|
]
|
||||||
);
|
);
|
||||||
$this->assertFileNotIncluded($backend, 'js', $testPath .'/javascript/RequirementsTest_a.js');
|
$this->assertFileNotIncluded($backend, 'js', 'javascript/RequirementsTest_a.js');
|
||||||
$this->assertFileNotIncluded(
|
$this->assertFileNotIncluded(
|
||||||
$backend,
|
$backend,
|
||||||
'css',
|
'css',
|
||||||
array(
|
[
|
||||||
$testPath .'/css/RequirementsTest_b.css',
|
'css/RequirementsTest_b.css',
|
||||||
$testPath .'/css/RequirementsTest_c.css'
|
'css/RequirementsTest_c.css'
|
||||||
)
|
]
|
||||||
);
|
);
|
||||||
$backend->clear();
|
$backend->clear();
|
||||||
$data = new ArrayData(
|
$data = new ArrayData(
|
||||||
@ -775,23 +788,23 @@ class RequirementsTest extends SapphireTest
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
$data->renderWith('RequirementsTest_Conditionals');
|
$data->renderWith('RequirementsTest_Conditionals');
|
||||||
$this->assertFileNotIncluded($backend, 'css', $testPath .'/css/RequirementsTest_a.css');
|
$this->assertFileNotIncluded($backend, 'css', 'css/RequirementsTest_a.css');
|
||||||
$this->assertFileNotIncluded(
|
$this->assertFileNotIncluded(
|
||||||
$backend,
|
$backend,
|
||||||
'js',
|
'js',
|
||||||
array(
|
[
|
||||||
$testPath .'/javascript/RequirementsTest_b.js',
|
'javascript/RequirementsTest_b.js',
|
||||||
$testPath .'/javascript/RequirementsTest_c.js'
|
'javascript/RequirementsTest_c.js',
|
||||||
)
|
]
|
||||||
);
|
);
|
||||||
$this->assertFileIncluded($backend, 'js', $testPath .'/javascript/RequirementsTest_a.js');
|
$this->assertFileIncluded($backend, 'js', 'javascript/RequirementsTest_a.js');
|
||||||
$this->assertFileIncluded(
|
$this->assertFileIncluded(
|
||||||
$backend,
|
$backend,
|
||||||
'css',
|
'css',
|
||||||
array(
|
[
|
||||||
$testPath .'/css/RequirementsTest_b.css',
|
'css/RequirementsTest_b.css',
|
||||||
$testPath .'/css/RequirementsTest_c.css'
|
'css/RequirementsTest_c.css',
|
||||||
)
|
]
|
||||||
);
|
);
|
||||||
Requirements::set_backend($holder);
|
Requirements::set_backend($holder);
|
||||||
}
|
}
|
||||||
@ -822,7 +835,7 @@ class RequirementsTest extends SapphireTest
|
|||||||
/** @var Requirements_Backend $backend */
|
/** @var Requirements_Backend $backend */
|
||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
$backend->javascript($this->getThemeRoot() . '/javascript/RequirementsTest_a.js');
|
$backend->javascript('javascript/RequirementsTest_a.js');
|
||||||
$html = $backend->includeInHTML($template);
|
$html = $backend->includeInHTML($template);
|
||||||
//wiping out commented-out html
|
//wiping out commented-out html
|
||||||
$html = preg_replace('/<!--(.*)-->/Uis', '', $html);
|
$html = preg_replace('/<!--(.*)-->/Uis', '', $html);
|
||||||
@ -841,7 +854,7 @@ class RequirementsTest extends SapphireTest
|
|||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
|
|
||||||
$src = $this->getThemeRoot() . '/javascript/RequirementsTest_a.js';
|
$src = 'javascript/RequirementsTest_a.js';
|
||||||
$backend->javascript($src);
|
$backend->javascript($src);
|
||||||
$html = $backend->includeInHTML($template);
|
$html = $backend->includeInHTML($template);
|
||||||
$urlSrc = $urlGenerator->urlForResource($src);
|
$urlSrc = $urlGenerator->urlForResource($src);
|
||||||
@ -923,16 +936,15 @@ EOS
|
|||||||
Injector::inst()->registerService($urlGenerator, ResourceURLGenerator::class);
|
Injector::inst()->registerService($urlGenerator, ResourceURLGenerator::class);
|
||||||
|
|
||||||
$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
|
$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
|
||||||
$basePath = $this->getThemeRoot();
|
|
||||||
|
|
||||||
/** @var Requirements_Backend $backend */
|
/** @var Requirements_Backend $backend */
|
||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
|
|
||||||
$backend->javascript($basePath .'/javascript/RequirementsTest_a.js');
|
$backend->javascript('javascript/RequirementsTest_a.js');
|
||||||
$backend->javascript($basePath .'/javascript/RequirementsTest_b.js?foo=bar&bla=blubb');
|
$backend->javascript('javascript/RequirementsTest_b.js?foo=bar&bla=blubb');
|
||||||
$backend->css($basePath .'/css/RequirementsTest_a.css');
|
$backend->css('css/RequirementsTest_a.css');
|
||||||
$backend->css($basePath .'/css/RequirementsTest_b.css?foo=bar&bla=blubb');
|
$backend->css('css/RequirementsTest_b.css?foo=bar&bla=blubb');
|
||||||
|
|
||||||
$urlGenerator->setNonceStyle('mtime');
|
$urlGenerator->setNonceStyle('mtime');
|
||||||
$html = $backend->includeInHTML($template);
|
$html = $backend->includeInHTML($template);
|
||||||
@ -957,27 +969,26 @@ EOS
|
|||||||
{
|
{
|
||||||
/** @var Requirements_Backend $backend */
|
/** @var Requirements_Backend $backend */
|
||||||
$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
|
$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
|
||||||
$basePath = $this->getThemeRoot();
|
|
||||||
|
|
||||||
// Test that provided files block subsequent files
|
// Test that provided files block subsequent files
|
||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
$backend->javascript($basePath . '/javascript/RequirementsTest_a.js');
|
$backend->javascript('javascript/RequirementsTest_a.js');
|
||||||
$backend->javascript(
|
$backend->javascript(
|
||||||
$basePath . '/javascript/RequirementsTest_b.js',
|
'javascript/RequirementsTest_b.js',
|
||||||
[
|
[
|
||||||
'provides' => [
|
'provides' => [
|
||||||
$basePath . '/javascript/RequirementsTest_a.js',
|
'javascript/RequirementsTest_a.js',
|
||||||
$basePath . '/javascript/RequirementsTest_c.js'
|
'javascript/RequirementsTest_c.js',
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$backend->javascript($basePath . '/javascript/RequirementsTest_c.js');
|
$backend->javascript('javascript/RequirementsTest_c.js');
|
||||||
// Note that _a.js isn't considered provided because it was included
|
// Note that _a.js isn't considered provided because it was included
|
||||||
// before it was marked as provided
|
// before it was marked as provided
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
$basePath . '/javascript/RequirementsTest_c.js' => $basePath . '/javascript/RequirementsTest_c.js'
|
'javascript/RequirementsTest_c.js' => 'javascript/RequirementsTest_c.js'
|
||||||
],
|
],
|
||||||
$backend->getProvidedScripts()
|
$backend->getProvidedScripts()
|
||||||
);
|
);
|
||||||
@ -989,20 +1000,20 @@ EOS
|
|||||||
// Test that provided files block subsequent combined files
|
// Test that provided files block subsequent combined files
|
||||||
$backend = Injector::inst()->create(Requirements_Backend::class);
|
$backend = Injector::inst()->create(Requirements_Backend::class);
|
||||||
$this->setupRequirements($backend);
|
$this->setupRequirements($backend);
|
||||||
$backend->combineFiles('combined_a.js', [$basePath . '/javascript/RequirementsTest_a.js']);
|
$backend->combineFiles('combined_a.js', ['javascript/RequirementsTest_a.js']);
|
||||||
$backend->javascript(
|
$backend->javascript(
|
||||||
$basePath . '/javascript/RequirementsTest_b.js',
|
'javascript/RequirementsTest_b.js',
|
||||||
[
|
[
|
||||||
'provides' => [
|
'provides' => [
|
||||||
$basePath . '/javascript/RequirementsTest_a.js',
|
'javascript/RequirementsTest_a.js',
|
||||||
$basePath . '/javascript/RequirementsTest_c.js'
|
'javascript/RequirementsTest_c.js'
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$backend->combineFiles('combined_c.js', [$basePath . '/javascript/RequirementsTest_c.js']);
|
$backend->combineFiles('combined_c.js', ['javascript/RequirementsTest_c.js']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
$basePath . '/javascript/RequirementsTest_c.js' => $basePath . '/javascript/RequirementsTest_c.js'
|
'javascript/RequirementsTest_c.js' => 'javascript/RequirementsTest_c.js'
|
||||||
],
|
],
|
||||||
$backend->getProvidedScripts()
|
$backend->getProvidedScripts()
|
||||||
);
|
);
|
||||||
@ -1098,14 +1109,4 @@ EOS
|
|||||||
}
|
}
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get base directory of theme to use for this test
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function getThemeRoot()
|
|
||||||
{
|
|
||||||
return $this->getCurrentRelativePath() . '/SSViewerTest';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -249,7 +249,7 @@ class SSViewerTest extends SapphireTest
|
|||||||
// secondly, make sure that requirements is generated, even though minification failed
|
// secondly, make sure that requirements is generated, even though minification failed
|
||||||
$testBackend->processCombinedFiles();
|
$testBackend->processCombinedFiles();
|
||||||
$js = array_keys($testBackend->getJavascript());
|
$js = array_keys($testBackend->getJavascript());
|
||||||
$combinedTestFilePath = BASE_PATH . reset($js);
|
$combinedTestFilePath = Director::publicFolder() . reset($js);
|
||||||
$this->assertContains('_combinedfiles/testRequirementsCombine-4c0e97a.js', $combinedTestFilePath);
|
$this->assertContains('_combinedfiles/testRequirementsCombine-4c0e97a.js', $combinedTestFilePath);
|
||||||
|
|
||||||
// and make sure the combined content matches the input content, i.e. no loss of functionality
|
// and make sure the combined content matches the input content, i.e. no loss of functionality
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
.c {color: #0ff;}
|
@ -0,0 +1 @@
|
|||||||
|
.c {color: #0ff;}
|
@ -0,0 +1 @@
|
|||||||
|
.c {color: #0ff;}
|
@ -0,0 +1 @@
|
|||||||
|
.c {color: #0ff;}
|
@ -0,0 +1 @@
|
|||||||
|
alert('d');
|
@ -0,0 +1 @@
|
|||||||
|
alert('e');
|
Loading…
Reference in New Issue
Block a user