Merge pull request #7418 from open-sausages/pulls/4/vendorise-me-baby

Expose as vendor module
This commit is contained in:
Damian Mooyman 2017-10-03 16:08:19 +13:00 committed by GitHub
commit 4cbaf7cccb
34 changed files with 374 additions and 244 deletions

View File

@ -1,9 +0,0 @@
<FilesMatch "\.(php|php3|php4|php5|phtml|inc)$">
Deny from all
</FilesMatch>
<FilesMatch "(main|rpc|tiny_mce_gzip)\.php$">
Allow from all
</FilesMatch>
<FilesMatch "silverstripe_version$">
Deny from all
</FilesMatch>

View File

@ -1,6 +1,6 @@
{
"name": "silverstripe/framework",
"type": "silverstripe-module",
"type": "silverstripe-vendormodule",
"description": "The SilverStripe framework",
"homepage": "http://silverstripe.org",
"license": "BSD-3-Clause",
@ -44,7 +44,8 @@
"ext-hash": "*",
"ext-session": "*",
"psr/container-implementation": "1.0.0",
"psr/container": "1.0.0"
"psr/container": "1.0.0",
"silverstripe/vendor-plugin": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
@ -60,7 +61,12 @@
"branch-alias": {
"4.x-dev": "4.0.x-dev",
"dev-master": "5.x-dev"
}
},
"expose": [
"client/images",
"client/styles",
"src/Dev/Install/client"
]
},
"autoload": {
"psr-4": {

View File

@ -62,7 +62,7 @@ control.
3. Apache rewrite (mod_rewrite) isn't working and it's installed (prior to SilverStripe 3.1.11)
Due to some changes to `mod_dir` in [Apache 2.4](http://httpd.apache.org/docs/current/mod/mod_dir.html#DirectoryCheckHandler) (precedence of handlers), index.php gets added to the URLs as soon as you navigate to the homepage of your site. Further requests are then handled by index.php rather than `mod_rewrite` (framework/main.php). To fix this place the following within the `mod_rewrite` section of your .htaccess file:
Due to some changes to `mod_dir` in [Apache 2.4](http://httpd.apache.org/docs/current/mod/mod_dir.html#DirectoryCheckHandler) (precedence of handlers), index.php gets added to the URLs as soon as you navigate to the homepage of your site. Further requests are then handled by index.php rather than `mod_rewrite` (`main.php`). To fix this place the following within the `mod_rewrite` section of your .htaccess file:
```
<IfModule mod_rewrite.c>

View File

@ -22,7 +22,10 @@ On "live" environments, the `?isDev=1` solution is preferred, as it means that y
## mod_rewrite isn't working but it's installed (prior to SilverStripe 3.1.11)
Due to some changes to `mod_dir` in [Apache 2.4](http://httpd.apache.org/docs/current/mod/mod_dir.html#DirectoryCheckHandler) (precedence of handlers), index.php gets added to the URLs as soon as you navigate to the homepage of your site. Further requests are then handled by index.php rather than `mod_rewrite` (framework/main.php). To fix this place the following within the `mod_rewrite` section of your .htaccess file:
Due to some changes to `mod_dir` in [Apache 2.4](http://httpd.apache.org/docs/current/mod/mod_dir.html#DirectoryCheckHandler)
(precedence of handlers), index.php gets added to the URLs as soon as you navigate to the homepage of your site.
Further requests are then handled by index.php rather than `mod_rewrite` (`main.php`).
To fix this place the following within the `mod_rewrite` section of your .htaccess file:
```
<IfModule mod_rewrite.c>

View File

@ -15,7 +15,7 @@ Silverstripe. Replace "yoursite.com" and "/home/yoursite/public_html/" below.
static-file.exclude-extensions += ( ".ss" )
# Deny access to SilverStripe command-line interface
$HTTP["url"] =~ "^/framework/cli-script.php" {
$HTTP["url"] =~ "^/vendor/silverstripe/framework/cli-script.php" {
url.access-deny = ( "" )
}
@ -27,11 +27,11 @@ Silverstripe. Replace "yoursite.com" and "/home/yoursite/public_html/" below.
# Rewrite URLs so they are nicer
url.rewrite-once = (
"^/.*\.[A-Za-z0-9]+.*?$" => "$0",
"^/(.*?)(\?|$)(.*)" => "/framework/main.php?url=$1&$3"
"^/(.*?)(\?|$)(.*)" => "/vendor/silverstripe/framework/main.php?url=$1&$3"
)
# Show SilverStripe error page
server.error-handler-404 = "/framework/main.php"
server.error-handler-404 = "/vendor/silverstripe/framework/main.php"
}
@ -53,14 +53,14 @@ of Silverstripe on the same host, you can use something like this (be warned, it
url.rewrite-once = (
"(?i)(/copy1/.*\.([A-Za-z0-9]+))(.*?)$" => "$0",
"(?i)(/copy2/.*\.([A-Za-z0-9]+))(.*?)$" => "$0",
"^/copy1/(.*?)(\?|$)(.*)" => "/copy1/framework/main.php?url=$1&$3",
"^/copy2/(.*?)(\?|$)(.*)" => "/copy2/framework/main.php?url=$1&$3"
"^/copy1/(.*?)(\?|$)(.*)" => "/copy1/vendor/silverstripe/framework/main.php?url=$1&$3",
"^/copy2/(.*?)(\?|$)(.*)" => "/copy2/vendor/silverstripe/framework/main.php?url=$1&$3"
)
$HTTP["url"] =~ "^/copy1/" {
server.error-handler-404 = "/copy1/framework/main.php"
server.error-handler-404 = "/copy1/vendor/silverstripe/framework/main.php"
}
$HTTP["url"] =~ "^/copy2/" {
server.error-handler-404 = "/copy2/framework/main.php"
server.error-handler-404 = "/copy2/vendor/silverstripe/framework/main.php"
}
}

View File

@ -30,7 +30,7 @@ But enough of the disclaimer, on to the actual configuration — typically in `n
}
location / {
try_files $uri /framework/main.php?url=$uri&$query_string;
try_files $uri vendor/silverstripe/framework/main.php?url=$uri&$query_string;
}
error_page 404 /assets/error-404.html;
@ -41,7 +41,7 @@ But enough of the disclaimer, on to the actual configuration — typically in `n
deny all;
}
sendfile on;
try_files $uri /framework/main.php?url=$uri&$query_string;
try_files $uri vendor/silverstripe/framework/main.php?url=$uri&$query_string;
}
location ~ /framework/.*(main|rpc|tiny_mce_gzip)\.php$ {

View File

@ -48,7 +48,7 @@ Create `/etc/nginx/silverstripe.conf` and add this configuration:
fastcgi_buffers 4 32k;
location / {
try_files $uri /framework/main.php?url=$uri&$query_string;
try_files $uri /vendor/silverstripe/framework/main.php?url=$uri&$query_string;
}
error_page 404 /assets/error-404.html;

View File

@ -9,9 +9,9 @@ directories is meaningful to its logic.
Directory | Description
--------- | -----------
`assets/` | Contains 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.
`cms/` | Contains all the files that form the CMS area of your site. Its structure is similar to the mysite/ directory, so if you find something interesting, it should be easy enough to look inside and see how it was built.
`framework/` | The framework that builds both your own site and as the CMS that powers it. Youll be utilising files in this directory often, both directly and indirectly.
`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.
`resources/`| Public files from modules (added automatically)
`vendor/` | SilverStripe modules and other supporting libraries (the framework is in `vendor/silverstripe/framework`)
## Custom Code Structure
@ -46,20 +46,22 @@ on folder and file naming in SilverStripe core modules.
See [themes](/developer_guides/templates/themes)
## Module Structure {#module_structure}
## Module Structure {#module_structure}
Modules are currently top-level folders that have a `_config.php` file or a `_config/` directory present.
They should follow the same conventions as posed in "Custom Site Structure"
Modules are commonly stored as composer packages in the `vendor/` folder.
They need to have a `_config.php` file or a `_config/` directory present,
and should follow the same conventions as posed in "Custom Site Structure".
Example Forum:
| Directory | Description |
| --------- | ----------- |
| `forum/` | This directory contains all of your code that defines your website. |
| `forum/code` | PHP code for model and controller (subdirectories are optional) |
| `vendor/silverstripe/blog/`| This directory contains all of your code that defines your website. |
| `vendor/silverstripe/blog/code` | PHP code for model and controller (subdirectories are optional) |
| ... | ... |
![](../_images/modules_folder.jpg)
Note: Before SilverStripe 4.x, modules were living as top-level folders in the webroot itself.
Some modules might not have been upgraded to support placement in `vendor/`
### Module documentation
@ -68,7 +70,7 @@ plain text files inside a 'docs' folder located in the module folder. These file
can be written with the Markdown syntax (See [Contributing Documentation](/contributing/documentation))
and include media such as images or videos.
Inside the docs folder, developers should organise the markdown files into each
Inside the `docs/` folder, developers should organise the markdown files into each
separate language they wish to write documentation for (usually just `en`). Inside
each languages' subfolder, developers then have freedom to create whatever structure
they wish for organising the documentation they wish.
@ -77,14 +79,14 @@ Example Forum Documentation:
| Directory | Description |
| --------- | ----------- |
| `forum/docs` | The docs folder will be picked up by the documentation viewer. |
| `forum/docs/_manifest_exclude` | Empty file to signify that SilverStripe does not need to load classes from this folder |
| `forum/docs/en/` | English documentation |
| `forum/docs/en/index.md` | Documentation homepage. Should provide an introduction and links to remaining docs |
| `forum/docs/en/Getting_Started.md` | Documentation page. Naming convention is Uppercase and underscores. |
| `forum/docs/en/_images/` | Folder to store any images or media |
| `forum/docs/en/Some_Topic/` | You can organise documentation into nested folders. Naming convention is Uppercase and underscores. |
|`forum/docs/en/04_Some_Topic/00_Getting_Started.md`|Structure is created by use of numbered prefixes. This applies to nested folders and documentations pages, index.md should not have a prefix.|
| `blog/docs` | |
| `blog/docs/_manifest_exclude` | Empty file to signify that SilverStripe does not need to load classes from this folder |
| `blog/docs/en/` | English documentation |
| `blog/docs/en/index.md` | Documentation homepage. Should provide an introduction and links to remaining docs |
| `blog/docs/en/Getting_Started.md` | Documentation page. Naming convention is Uppercase and underscores. |
| `blog/docs/en/_images/` | Folder to store any images or media |
| `blog/docs/en/Some_Topic/` | You can organise documentation into nested folders. Naming convention is Uppercase and underscores. |
| `blog/docs/en/04_Some_Topic/00_Getting_Started.md`|Structure is created by use of numbered prefixes. This applies to nested folders and documentations pages, index.md should not have a prefix.|
## Autoloading

View File

@ -369,6 +369,26 @@ If the Javascript files are preferred to be placed in the `<head>` tag rather th
Requirements::set_write_js_to_body(false);
```
## Direct resource urls
In templates you can use the `$resourcePath()` or `$resourceURL()` helper methods to inject links to
resources directly. If you want to link to resources within a specific module you can use
the `vendor/module:some/path/to/file.jpg` syntax.
E.g.
```ss
<div class="loading">
<img src="$resourceURL('silverstripe/admin:client/dist/images/spinner.gif')" />
</div>
```
In PHP you can directly resolve these urls using the `ModuleResourceLoader` helper.
```php
$file = ModuleResourceLoader::singleton()
->resolveURL('silverstripe/admin:client/dist/images/spinner.gif');
```
## API Documentation

View File

@ -344,25 +344,25 @@ The extension can also be globally disabled by removing it at the root level:
If the default server configuration is not appropriate for your specific environment, then you can
further customise the .htaccess or web.config by editing one or more of the below:
* `Assets_HTAccess.ss`: Template for public permissions on the Apache server.
* `Assets_WebConfig.ss`: Template for public permissions on the IIS server.
* `Protected_HTAccess.ss`: Template for the protected store on the Apache server (should deny all requests).
* `Protected_WebConfig.ss`: Template for the protected store on the IIS server (should deny all requests).
* `PublicAssetAdapter_HTAccess.ss`: Template for public permissions on the Apache server.
* `PublicAssetAdapter_WebConfig.ss`: Template for public permissions on the IIS server.
* `ProtectedAssetAdapter_HTAccess.ss`: Template for the protected store on the Apache server (should deny all requests).
* `ProtectedAssetAdapter_WebConfig.ss`: Template for the protected store on the IIS server (should deny all requests).
Each of these files will be regenerated on ?flush, so it is important to ensure that these files are
overridden at the template level, not via manually generated configuration files.
#### Configuring Web Server: Apache server
In order to ensure that public files are served correctly, you should check that your ./assets
.htaccess bypasses PHP requests for files that do exist. The default template
(declared by `Assets_HTAccess.ss`) has the following section, which may be customised in your project:
In order to ensure that public files are served correctly, you should check that your `assets/.htaccess`
bypasses PHP requests for files that do exist. The default template
(declared by `PublicAssetAdapter_HTAccess.ss`) has the following section, which may be customised in your project:
```
# Non existant files passed to requesthandler
RewriteCond %{REQUEST_URI} ^(.*)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* ../framework/main.php?url=%1 [QSA]
# Non existant files passed to requesthandler
RewriteCond %{REQUEST_URI} ^(.*)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* ../vendor/silverstripe/framework/main.php?url=%1 [QSA]
```
You will need to ensure that your core apache configuration has the necessary `AllowOverride`
@ -377,13 +377,14 @@ while ensuring non-existent files are processed via the Framework.
The default rule for IIS is as below (only partial configuration displayed):
```
<rule name="Protected and 404 File rewrite" stopProcessing="true">
<match url="^(.*)$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Rewrite" url="../framework/main.php?url={R:1}" appendQueryString="true" />
</rule>
<rule name="Secure and 404 File rewrite" stopProcessing="true">
<match url="^(.*)$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="../vendor/silverstripe/framework/main.php" matchType="IsFile" />
</conditions>
<action type="Rewrite" url="../vendor/silverstripe/framework/main.php?url={R:1}" appendQueryString="true" />
</rule>
```
You will need to make sure that the `allowOverride` property of your root web.config is not set
@ -401,6 +402,6 @@ dynamic requests are processed via the Framework:
```
location ^~ /assets/ {
sendfile on;
try_files $uri /framework/main.php?url=$uri&$query_string;
try_files $uri vendor/silverstripe/framework/main.php?url=$uri&$query_string;
}
```

View File

@ -54,12 +54,12 @@ If no file can be directly matched, control is handed off to `framework/main.php
RewriteRule silverstripe-cache(/|$) - [F,L,NC]
RewriteRule composer\.(json|lock) - [F,L,NC]
# Process through SilverStripe if no file with the requested name exists.
# Pass through the original path as a query parameter, and retain the existing parameters.
# Non existant files passed to requesthandler
# Try finding the module in the vendor folder first
RewriteCond %{REQUEST_URI} ^(.*)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* framework/main.php?url=%1 [QSA]
RewriteRule .* ../vendor/silverstripe/framework/main.php?url=%1 [QSA]
# If requesting the main script directly, rewrite to the installer
RewriteCond %{REQUEST_URI} ^(.*)/framework/main.php$
RewriteCond %{REQUEST_FILENAME} !-f
@ -86,7 +86,7 @@ tasks silently in the background.
[configuration file](/getting_started/environment_management) in the webroot.
* Sets constants based on the filesystem structure (e.g. `BASE_URL`, `BASE_PATH` and `TEMP_FOLDER`)
All requests go through `framework/main.php`, which sets up the core [Kernel](api:SilverStripe\Core\Kernel) and [HTTPApplication](api:SilverStripe\Control\HTTPApplication)
All requests go through `main.php`, which sets up the core [Kernel](api:SilverStripe\Core\Kernel) and [HTTPApplication](api:SilverStripe\Control\HTTPApplication)
objects. See [/developer_guides/execution_pipeline/app_object_and_kernel] for details on this.
The main process follows:

View File

@ -6,12 +6,12 @@ SilverStripe can call [Controllers](../controllers) through a command line inter
web browser. This functionality can be used to automate tasks with cron jobs, run unit tests, or anything else that
needs to interface over the command line.
The main entry point for any command line execution is `framework/cli-script.php`. For example, to run a database
rebuild from the command line, use this command:
The main entry point for any command line execution is `cli-script.php` in the framework module.
For example, to run a database rebuild from the command line, use this command:
```bash
cd your-webroot/
php framework/cli-script.php dev/build
php vendor/bin/framework/cli-script.php dev/build
```
<div class="notice">
@ -159,4 +159,4 @@ The following will run `MyTask` every minute.
```bash
* * * * * /your/site/folder/sake dev/tasks/MyTask
```
```

View File

@ -64,8 +64,7 @@ guide developers in preparing existing 3.x code for compatibility with 4.0
[intervention/image](https://github.com/intervention/image) library to power manipualations.
* Dependencies can managed via [recipe-plugin](https://github.com/silverstripe/recipe-plugin). See [recipe-core](https://github.com/silverstripe/recipe-core) and [recipe-cms](https://github.com/silverstripe/recipe-cms) as examples.
* Authentication has been upgraded to a modular approach using re-usable interfaces and easier to hook in to LoginHandlers.
* Support for modules installed in vendor folder. See [/developer_guides/extending/how_tos/publish_a_module](the
module publishing guide) for more information.
* Core modules are installed in the `vendor/` folder by default (other modules can opt-in, see [guide](/developer_guides/extending/how_tos/publish_a_module))
## <a name="upgrading"></a>Upgrading
@ -78,6 +77,14 @@ some automated processes that users can run to
This section describes the processes which every project upgrading to 4.0 should follow. This should be followed
as a standard first point of upgrade.
#### Upgrade your rewrite rules
The location of SilverStripe's "entry file" has changed. Your project and server environment will need
to adjust the path to this file from `framework/main.php` to `vendor/silverstripe/framework/main.php`.
If you are running Apache, adjust your `.htaccess` file. For other webservers,
please consult the [installation guides](getting_started/installation/).
#### Upgrade references to renamed and namespaced classes
Nearly all core PHP classes have been namespaced. For example, `DataObject` is now called `SilverStripe\ORM\DataObject`.
@ -285,6 +292,32 @@ To ensure consistency, we've also deprecated support for path constants:
* `THIRDPARTY_DIR` and `THIRDPARTY_PATH`
* `CMS_DIR` and `CMS_PATH`
* `THEMES_DIR` and `THEMES_PATH`
#### Adapt tooling to modules in vendor folder
SilverStripe modules can now be installed like any other composer package: In the `vendor/` folder
instead of the webroot. Modules need to opt in to this behaviour after they've ensured
that no hardcoded path references exist (see "Upgrade module paths in file references").
All core modules have been moved over already:
```diff
-framework/
-cms/
...
+vendor/silverstripe/framework
+vendor/silverstripe/cms
```
Since the `vendor/` folder isn't publicly accessible, modules need to declare
which files need to be exposed via the new [vendor-plugin](https://github.com/silverstripe/vendor-plugin)
(e.g. images or CSS/JS files). These files will be either symlinked or copied into a new `resources/`
folder automatically on `composer install`.
If your deployment process relies on `composer install` on the production environment,
and this environment supports symlinks, you don't need to change anything.
If you deploy release archives, either ensure those archives can correctly extract symlinks,
or explicitly switch to the "copy" mode to avoid symlinks.
### <a name="upgrading-specifics"></a>API Specific Upgrades

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,53 +0,0 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
(function webpackMissingModule() { throw new Error("Cannot find module \"watch\""); }());
(function webpackMissingModule() { throw new Error("Cannot find module \"profile\""); }());
/***/ }
/******/ ]);
/*# sourceMappingURL=main.css.map*/

View File

@ -0,0 +1,102 @@
<?php
namespace SilverStripe\Core\Manifest;
use InvalidArgumentException;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\View\TemplateGlobalProvider;
/**
* Helper for mapping module resources to paths / urls
*/
class ModuleResourceLoader implements TemplateGlobalProvider
{
use Injectable;
/**
* Convert a file of the form "vendor/package:resource" into a BASE_PATH-relative file
* For other files, return original value
*
* @param string $resource
* @return string
*/
public function resolvePath($resource)
{
$resourceObj = $this->resolveModuleResource($resource);
if ($resourceObj) {
return $resourceObj->getRelativePath();
}
return $resource;
}
/**
* Resolves resource specifier to the given url.
*
* @param string $resource
* @return string
*/
public function resolveURL($resource)
{
$resourceObj = $this->resolveModuleResource($resource);
if ($resourceObj) {
return $resourceObj->getURL();
}
return $resource;
}
/**
* Template wrapper for resolvePath
*
* @param string $resource
* @return string
*/
public static function resourcePath($resource)
{
return static::singleton()->resolvePath($resource);
}
/**
* Template wrapper for resolveURL
*
* @param string $resource
* @return string
*/
public static function resourceURL($resource)
{
return static::singleton()->resolveURL($resource);
}
public static function get_template_global_variables()
{
return [
'resourcePath',
'resourceURL'
];
}
/**
* Return module resource for the given path, if specified as one.
* Returns null if not a module resource.
*
* @param string $resource
* @return ModuleResource|null
*/
protected function resolveModuleResource($resource)
{
// String of the form vendor/package:resource. Excludes "http://bla" as that's an absolute URL
if (!preg_match('#^ *(?<module>[^/: ]+/[^/: ]+) *: *(?<resource>[^ ]*)$#', $resource, $matches)) {
return null;
}
$module = $matches['module'];
$resource = $matches['resource'];
$moduleObj = ModuleLoader::getModule($module);
if (!$moduleObj) {
throw new InvalidArgumentException("Can't find module '$module'");
}
$resourceObj = $moduleObj->getResource($resource);
if (!$resourceObj->exists()) {
throw new InvalidArgumentException("Module '$module' does not have specified resource '$resource'");
}
return $resourceObj;
}
}

View File

@ -9,6 +9,8 @@ use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Convert;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Manifest\ModuleLoader;
use SilverStripe\Core\Manifest\ModuleResourceLoader;
/**
* A basic HTML wrapper for stylish rendering of a developement info view.
@ -217,12 +219,8 @@ class DebugView
'UTF-8'
);
$debugCSS = Controller::join_links(
Director::absoluteBaseURL(),
FRAMEWORK_DIR,
'client/styles/debug.css'
);
$debugCSS = ModuleResourceLoader::singleton()
->resolveURL('silverstripe/framework:client/styles/debug.css');
$output = '<!DOCTYPE html><html><head><title>' . $url . '</title>';
$output .= '<link rel="stylesheet" type="text/css" href="'. $debugCSS .'" />';
$output .= '</head>';

View File

@ -110,26 +110,36 @@ class DatabaseAdapterRegistry
}
/**
* Detects all _register_database.php files and invokes them
* Detects all _register_database.php files and invokes them.
* Searches through vendor/ folder only,
* does not support "legacy" folder location in webroot
*/
public static function autodiscover()
{
foreach (glob(__DIR__ . '/../../../../*', GLOB_ONLYDIR) as $directory) {
if (file_exists($directory . '/_register_database.php')) {
include_once($directory . '/_register_database.php');
// Search through all composer packages in vendor
foreach (glob(BASE_PATH . '/vendor/*', GLOB_ONLYDIR) as $vendor) {
foreach (glob($vendor . '/*', GLOB_ONLYDIR) as $directory) {
if (file_exists($directory . '/_register_database.php')) {
include_once($directory . '/_register_database.php');
}
}
}
}
/**
* Detects all _configure_database.php files and invokes them
* Called by ConfigureFromEnv.php
* Called by ConfigureFromEnv.php.
* Searches through vendor/ folder only,
* does not support "legacy" folder location in webroot
*/
public static function autoconfigure()
{
foreach (glob(__DIR__ . '/../../../../*', GLOB_ONLYDIR) as $directory) {
if (file_exists($directory . '/_configure_database.php')) {
include_once($directory . '/_configure_database.php');
// Search through all composer packages in vendor
foreach (glob(BASE_PATH . '/vendor/*', GLOB_ONLYDIR) as $vendor) {
foreach (glob($vendor . '/*', GLOB_ONLYDIR) as $directory) {
if (file_exists($directory . '/_configure_database.php')) {
include_once($directory . '/_configure_database.php');
}
}
}
}

View File

@ -199,7 +199,7 @@ class InstallRequirements
));
// Check that we can identify the root folder successfully
$this->requireFile('framework/src/Dev/Install/config-form.html', array(
$this->requireFile('vendor/silverstripe/framework/src/Dev/Install/config-form.html', array(
"File permissions",
"Does the webserver know where files are stored?",
"The webserver isn't letting me identify where files are stored.",
@ -211,9 +211,9 @@ class InstallRequirements
"mysite/ directory exists?",
''
));
$this->requireModule('framework', array(
$this->requireModule('vendor/silverstripe/framework', array(
"File permissions",
"framework/ directory exists?",
"vendor/silverstripe/framework/ directory exists?",
'',
));

View File

@ -34,7 +34,7 @@ class Installer extends InstallRequirements
<meta charset="utf-8"/>
<title>Installing SilverStripe...</title>
<link rel="stylesheet" type="text/css"
href="framework/src/Dev/Install/client/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>
</head>
<body>
@ -417,7 +417,7 @@ YML
</Files>
# Deny access to YAML configuration files which might include sensitive information
<Files *.yml>
<Files ~ "\.ya?ml$">
Order allow,deny
Deny from all
</Files>
@ -431,6 +431,7 @@ ErrorDocument 500 /assets/error-500.html
# Turn off index.php handling requests to the homepage fixes issue in apache >=2.4
<IfModule mod_dir.c>
DirectoryIndex disabled
DirectorySlash Off
</IfModule>
SetEnv HTTP_MOD_REWRITE On
@ -438,16 +439,23 @@ ErrorDocument 500 /assets/error-500.html
$baseClause
$cgiClause
# Deny access to potentially sensitive files and folders
# Deny access to vendor, unless you're requesting main.php
# Not restricting to the start of the path to support RewriteBase
RewriteCond %{REQUEST_URI} !^/vendor/silverstripe/framework/main\.php
RewriteRule ^vendor(/|$) - [F,L,NC]
# Deny access to potentially sensitive files and folders
RewriteRule ^\.env - [F,L,NC]
RewriteRule silverstripe-cache(/|$) - [F,L,NC]
RewriteRule composer\.(json|lock) - [F,L,NC]
RewriteRule (error|silverstripe|debug)\.log - [F,L,NC]
# Process through SilverStripe if no file with the requested name exists.
# Pass through the original path as a query parameter, and retain the existing parameters.
# Try finding framework in the vendor folder first
RewriteCond %{REQUEST_URI} ^(.*)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* framework/main.php?url=%1 [QSA]
RewriteRule .* vendor/silverstripe/framework/main.php?url=%1 [QSA]
</IfModule>
TEXT;
@ -502,8 +510,9 @@ TEXT;
<match url="^(.*)$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="vendor/silverstripe/framework/main.php" matchType="IsFile" />
</conditions>
<action type="Rewrite" url="framework/main.php?url={R:1}" appendQueryString="true" />
<action type="Rewrite" url="vendor/silverstripe/framework/main.php?url={R:1}" appendQueryString="true" />
</rule>
</rules>
</rewrite>

View File

@ -104,8 +104,8 @@ if ($installFromCli && ($req->hasErrors() || $dbReq->hasErrors())) {
exit(1);
}
// Path to client resources
$clientPath = (FRAMEWORK_DIR ? FRAMEWORK_DIR . '/' : '') . 'src/Dev/Install/client';
// Path to client resources (copied through silverstripe/vendor-plugin)
$clientPath = 'resources/silverstripe/framework/src/Dev/Install/client';
// config-form.html vars (placeholder to prevent deletion)

View File

@ -8,6 +8,7 @@ use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Convert;
use SilverStripe\Core\Flushable;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Manifest\ModuleResource;
/**
* Generates tinymce config using a combined file generated via a standard
@ -90,9 +91,11 @@ class TinyMCECombinedGenerator implements TinyMCEScriptGenerator, Flushable
foreach ($config->getPlugins() as $plugin => $path) {
// Add external plugin
if ($path) {
// Convert URLS to relative paths
if (Director::is_absolute_url($path) || strpos($path, '/') === 0) {
// De-absolute site urls
if ($path instanceof ModuleResource) {
// Resolve path / url later
$files[] = $path;
} elseif (Director::is_absolute_url($path) || strpos($path, '/') === 0) {
// Convert URLS to relative paths
$path = Director::makeRelative($path);
if ($path) {
$files[] = $path;
@ -122,6 +125,9 @@ class TinyMCECombinedGenerator implements TinyMCEScriptGenerator, Flushable
// Process source files
$files = array_filter(array_map(function ($file) {
if ($file instanceof ModuleResource) {
return $file;
}
// Prioritise preferred paths
$paths = [
$file,
@ -162,11 +168,25 @@ SCRIPT;
SCRIPT;
// Load all tinymce script files into buffer
foreach ($files as $file) {
$buffer[] = $this->getFileContents(Director::baseFolder() . '/' . $file);
foreach ($files as $path) {
if ($path instanceof ModuleResource) {
$path = $path->getPath();
} else {
$path = Director::baseFolder() . '/' . $path;
}
$buffer[] = $this->getFileContents($path);
}
$filesList = Convert::raw2js(implode(',', $files));
// Generate urls for all files
$fileURLS = array_map(function ($path) {
if ($path instanceof ModuleResource) {
return $path->getURL();
}
return $path;
}, $files);
// Join list of paths
$filesList = Convert::raw2js(implode(',', $fileURLS));
// Mark all themes, plugins and languages as done
$buffer[] = "window.tinymce.each('$filesList'.split(','),".
"function(f){tinymce.ScriptLoader.markDone(baseURL+f);});";
@ -175,7 +195,6 @@ SCRIPT;
return implode("\n", $buffer) . "\n";
}
/**
* Returns the contents of the script file if it exists and removes the UTF-8 BOM header if it exists.
*

View File

@ -9,6 +9,7 @@ use SilverStripe\Core\Convert;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Manifest\Module;
use SilverStripe\Core\Manifest\ModuleLoader;
use SilverStripe\Core\Manifest\ModuleResourceLoader;
use SilverStripe\Dev\Deprecation;
use SilverStripe\i18n\i18n;
use SilverStripe\View\Requirements;
@ -352,6 +353,7 @@ class TinyMCEConfig extends HTMLEditorConfig
* - null - Will be treated as a stardard plugin in the standard location
* - relative path - Will be treated as a relative url
* - absolute url - Some url to an external plugin
* - An instance of ModuleResource object containing the plugin
*
* @param string|array $plugin,... a string, or several strings, or a single array of strings - The plugins to enable
* @return $this
@ -394,7 +396,8 @@ class TinyMCEConfig extends HTMLEditorConfig
/**
* Gets the list of all enabled plugins as an associative array.
* Array keys are the plugin names, and values are potentially the plugin location
* Array keys are the plugin names, and values are potentially the plugin location,
* or ModuleResource object
*
* @return array
*/
@ -567,7 +570,7 @@ class TinyMCEConfig extends HTMLEditorConfig
$settings['document_base_url'] = Director::absoluteBaseURL();
// https://www.tinymce.com/docs/api/class/tinymce.editormanager/#baseURL
$tinyMCEBaseURL = Controller::join_links(Director::baseURL(), $this->getTinyMCEPath());
$tinyMCEBaseURL = Controller::join_links(Director::baseURL(), $this->getTinyMCEResourcePath());
$settings['baseURL'] = $tinyMCEBaseURL;
// map all plugins to absolute urls for loading
@ -630,7 +633,9 @@ class TinyMCEConfig extends HTMLEditorConfig
$editorCSSFiles = $this->config()->get('editor_css');
if ($editorCSSFiles) {
foreach ($editorCSSFiles as $editorCSS) {
$editor[] = Director::absoluteURL($this->resolvePath($editorCSS));
$path = ModuleResourceLoader::singleton()
->resolveURL($editorCSS);
$editor[] = Director::absoluteURL($path);
}
}
@ -681,6 +686,27 @@ class TinyMCEConfig extends HTMLEditorConfig
return 'en';
}
/**
* Returns the base path to TinyMCE resources (which could be different from the original tinymce
* location in the module).
*
* @return string
* @throws Exception
*/
public function getTinyMCEResourcePath()
{
$configDir = static::config()->get('base_dir');
if ($configDir) {
return ModuleResourceLoader::singleton()
->resolveURL($configDir);
}
throw new Exception(sprintf(
'If the silverstripe/admin module is not installed you must set the TinyMCE path in %s.base_dir',
__CLASS__
));
}
/**
* @return string
* @throws Exception
@ -689,7 +715,8 @@ class TinyMCEConfig extends HTMLEditorConfig
{
$configDir = static::config()->get('base_dir');
if ($configDir) {
return $this->resolvePath($configDir);
return ModuleResourceLoader::singleton()
->resolveURL($configDir);
}
throw new Exception(sprintf(
@ -707,21 +734,4 @@ class TinyMCEConfig extends HTMLEditorConfig
Deprecation::notice('5.0', 'Set base_dir or editor_css config instead');
return ModuleLoader::getModule('silverstripe/admin');
}
/**
* Expand resource path to a relative filesystem path
*
* @param string $path
* @return string
*/
protected function resolvePath($path)
{
if (preg_match('#(?<module>[^/]+/[^/]+)\s*:\s*(?<path>[^:]+)#', $path, $results)) {
$module = ModuleLoader::getModule($results['module']);
if ($module) {
return $module->getRelativeResourcePath($results['path']);
}
}
return $path;
}
}

View File

@ -33,10 +33,11 @@ class TinyMCEGZIPGenerator implements TinyMCEScriptGenerator
// If gzip is disabled just return core script url
$useGzip = HTMLEditorField::config()->get('use_gzip');
if (!$useGzip) {
return $config->getTinyMCEPath() . '/tinymce.min.js';
return $config->getTinyMCEResourcePath() . '/tinymce.min.js';
}
// tinyMCE JS requirement
// tinyMCE JS requirement - use the original module path,
// don't assume the PHP file is copied alongside the resources
$gzipPath = BASE_PATH . '/' . $config->getTinyMCEPath() . '/tiny_mce_gzip.php';
if (!file_exists($gzipPath)) {
throw new Exception("HTMLEditorField.use_gzip enabled, but file $gzipPath does not exist!");

View File

@ -12,8 +12,8 @@ use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Convert;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Manifest\ModuleResourceLoader;
use SilverStripe\Core\Manifest\ResourceURLGenerator;
use SilverStripe\Core\Manifest\ModuleLoader;
use SilverStripe\Dev\Debug;
use SilverStripe\Dev\Deprecation;
use SilverStripe\i18n\i18n;
@ -401,7 +401,7 @@ class Requirements_Backend
*/
public function javascript($file, $options = array())
{
$file = $this->parseModuleResourceReference($file);
$file = ModuleResourceLoader::singleton()->resolvePath($file);
// Get type
$type = null;
@ -626,34 +626,13 @@ class Requirements_Backend
*/
public function css($file, $media = null)
{
$file = $this->parseModuleResourceReference($file);
$file = ModuleResourceLoader::singleton()->resolvePath($file);
$this->css[$file] = array(
"media" => $media
);
}
/**
* Convert a file of the form "vendor/package:resource" into a BASE_PATH-relative file
* For other files, reutrn original value
*
* @param string $file
* @return string
*/
protected function parseModuleResourceReference($file)
{
// String of the form vendor/package:resource. Excludes "http://bla" as that's an absolute URL
if (preg_match('#([^\/\/][^ /]*\/[^ /]*) *: *([^ ]*)#', $file, $matches)) {
list(, $module, $resource) = $matches;
$moduleObj = ModuleLoader::getModule($module);
if (!$moduleObj) {
throw new \InvalidArgumentException("Can't find module '$module'");
}
return $moduleObj->getRelativeResourcePath($resource);
}
return $file;
}
/**
* Remove a css requirement
*
@ -1462,7 +1441,7 @@ MESSAGE
if ($path) {
$this->css($path, $media);
} else {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
"The css file doesn't exist. Please check if the file $name.css exists in any context or search for "
. "themedCSS references calling this file in your templates."
);
@ -1490,7 +1469,7 @@ MESSAGE
}
$this->javascript($path, $opts);
} else {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
"The javascript file doesn't exist. Please check if the file $name.js exists in any "
. "context or search for themedJavascript references calling this file in your templates."
);

View File

@ -3,7 +3,6 @@
namespace SilverStripe\View;
use InvalidArgumentException;
use RuntimeException;
use SilverStripe\Core\Manifest\ModuleLoader;
/**
@ -279,6 +278,7 @@ class ThemeResourceLoader
*
* @param string $resource A file path relative to the root folder of a theme
* @param array $themes An order listed of themes to search
* @return string
*/
public function findThemedResource($resource, $themes)
{

View File

@ -3,8 +3,11 @@
// Init composer autoload
call_user_func(function () {
$candidates = [
// module in vendor
__DIR__ . '/../../../../autoload.php',
// module itself is webroot (usually during CI installs)
__DIR__ . '/../../vendor/autoload.php',
__DIR__ . '/../../../vendor/autoload.php',
// fallback
getcwd() . '/vendor/autoload.php',
];
foreach ($candidates as $candidate) {

View File

@ -201,16 +201,16 @@ class HTTPTest extends FunctionalTest
*/
public function testGetMimeType()
{
$this->assertEquals('text/plain', HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.csv'));
$this->assertEquals('image/gif', HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.gif'));
$this->assertEquals('text/html', HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.html'));
$this->assertEquals('image/jpeg', HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.jpg'));
$this->assertEquals('image/png', HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.png'));
$this->assertEquals('text/plain', HTTP::get_mime_type('file.csv'));
$this->assertEquals('image/gif', HTTP::get_mime_type('file.gif'));
$this->assertEquals('text/html', HTTP::get_mime_type('file.html'));
$this->assertEquals('image/jpeg', HTTP::get_mime_type('file.jpg'));
$this->assertEquals('image/png', HTTP::get_mime_type('file.png'));
$this->assertEquals(
'image/vnd.adobe.photoshop',
HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.psd')
HTTP::get_mime_type('file.psd')
);
$this->assertEquals('audio/x-wav', HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.wav'));
$this->assertEquals('audio/x-wav', HTTP::get_mime_type('file.wav'));
}
/**

View File

@ -71,7 +71,7 @@ class DeprecationTest extends SapphireTest
public function testMatchingModuleNotifcationVersionAffectsNotice()
{
Deprecation::notification_version('1.0.0');
Deprecation::notification_version('3.0.0', FRAMEWORK_DIR);
Deprecation::notification_version('3.0.0', 'silverstripe/framework');
$this->callThatOriginatesFromFramework();
}

View File

@ -4,7 +4,7 @@ namespace SilverStripe\Forms\Tests\HTMLEditor;
use SilverStripe\Control\Director;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Manifest\ModuleLoader;
use SilverStripe\Core\Manifest\Module;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\HTMLEditor\HTMLEditorConfig;
use SilverStripe\Forms\HTMLEditor\TinyMCECombinedGenerator;
@ -21,7 +21,9 @@ class TinyMCECombinedGeneratorTest extends SapphireTest
Director::config()->set('alternate_base_folder', __DIR__ . '/TinyMCECombinedGeneratorTest');
Director::config()->set('alternate_base_url', 'http://www.mysite.com/basedir/');
SSViewer::config()->set('themes', [SSViewer::DEFAULT_THEME]);
TinyMCEConfig::config()->set('base_dir', 'tinymce');
TinyMCEConfig::config()
->set('base_dir', 'tinymce')
->set('editor_css', [ 'mycode/editor.css' ]);
}
protected function tearDown()
@ -33,6 +35,7 @@ class TinyMCECombinedGeneratorTest extends SapphireTest
public function testConfig()
{
$module = new Module(Director::baseFolder().'/mycode', Director::baseFolder());
// Disable nonces
$c = new TinyMCEConfig();
$c->setTheme('testtheme');
@ -47,6 +50,7 @@ class TinyMCECombinedGeneratorTest extends SapphireTest
'plugin5' => null,
'plugin6' => '/basedir/mycode/plugin6.js',
'plugin7' => '/basedir/mycode/plugin7.js',
'plugin8' => $module->getResource('plugin8.js'),
)
);
HTMLEditorConfig::set_config('testconfig', $c);
@ -70,6 +74,8 @@ class TinyMCECombinedGeneratorTest extends SapphireTest
$this->assertContains("/* plugin4/langs/en.js */\n", $content);
$this->assertContains("/* plugin5.js */\n", $content);
$this->assertContains("/* plugin6.js */\n", $content);
// module-resource plugin
$this->assertContains("/* plugin8.js */\n", $content);
// Exclude non-local plugins
$this->assertNotContains('plugin2.js', $content);
$this->assertNotContains('plugin3.js', $content);
@ -80,10 +86,19 @@ class TinyMCECombinedGeneratorTest extends SapphireTest
$this->assertContains("/* theme.js */\n", $content);
$this->assertContains("/* testtheme/langs/en.js */\n", $content);
// Register done scripts
// Check plugin links included
$this->assertContains(
<<<EOS
tinymce.each('tinymce/langs/en.js,mycode/plugin1.js,tinymce/plugins/plugin4/plugin.min.js,tinymce/plugins/plugin4/langs/en.js,tinymce/plugins/plugin5/plugin.js,mycode/plugin6.js,tinymce/themes/testtheme/theme.js,tinymce/themes/testtheme/langs/en.js'.split(','),function(f){tinymce.ScriptLoader.markDone(baseURL+f);});
tinymce.each('tinymce/langs/en.js,mycode/plugin1.js,tinymce/plugins/plugin4/plugin.min.js,tinymce/plugins/plugin4/langs/en.js,tinymce/plugins/plugin5/plugin.js,mycode/plugin6.js,/basedir/mycode/plugin8.js?m=
EOS
,
$content
);
// Check theme links included
$this->assertContains(
<<<EOS
tinymce/themes/testtheme/theme.js,tinymce/themes/testtheme/langs/en.js'.split(','),function(f){tinymce.ScriptLoader.markDone(baseURL+f);});
EOS
,
$content

View File

@ -0,0 +1 @@
/* editor.css */

View File

@ -0,0 +1 @@
/* plugin8.js */

View File

@ -818,20 +818,19 @@ class RequirementsTest extends SapphireTest
public function testCommentedOutScriptTagIsIgnored()
{
/// Disable nonce
Injector::inst()->registerService(new SimpleResourceURLGenerator(), ResourceURLGenerator::class);
$urlGenerator = new SimpleResourceURLGenerator();
Injector::inst()->registerService($urlGenerator, ResourceURLGenerator::class);
$template = '<html><head></head><body><!--<script>alert("commented out");</script>-->'
. '<h1>more content</h1></body></html>';
/**
* @var Requirements_Backend $backend
*/
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$src = $this->getThemeRoot() . '/javascript/RequirementsTest_a.js';
$urlSrc = Controller::join_links(Director::baseURL(), $src);
$backend->javascript($src);
$html = $backend->includeInHTML($template);
$urlSrc = $urlGenerator->urlForResource($src);
$this->assertEquals(
'<html><head></head><body><!--<script>alert("commented out");</script>-->'
. '<h1>more content</h1><script type="application/javascript" src="' . $urlSrc

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Block Scripts" stopProcessing="true">
<match url="([^\\/]+)\.(php|php3|php4|php5|phtml|inc)$" />
<conditions trackAllCaptures="true">
<add input="{REQUEST_FILENAME}" pattern="\b(main|rpc|tiny_mce_gzip)\.php$" negate="true" />
</conditions>
<action type="AbortRequest" />
</rule>
<rule name="Block Version" stopProcessing="true">
<match url="\bsilverstripe_version$" />
<action type="AbortRequest" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>