Browse Source

new asset WIP: renabled asset publishing

tags/2.0.0-rc
Qiang Xue 10 years ago
parent
commit
60e07e0d2a
  1. 2
      apps/advanced/.bowerrc
  2. 1
      apps/advanced/assets/.gitkeep
  3. 2
      apps/advanced/backend/web/assets/.gitignore
  4. 8
      apps/advanced/environments/index.php
  5. 2
      apps/advanced/frontend/web/assets/.gitignore
  6. 2
      apps/basic/.bowerrc
  7. 2
      apps/basic/web/assets/.gitignore
  8. 1
      apps/basic/web/assets/.gitkeep
  9. 11
      docs/guide/concept-aliases.md
  10. 5
      extensions/authclient/widgets/AuthChoiceAsset.php
  11. 3
      extensions/bootstrap/BootstrapAsset.php
  12. 3
      extensions/bootstrap/BootstrapPluginAsset.php
  13. 3
      extensions/bootstrap/BootstrapThemeAsset.php
  14. 5
      extensions/debug/DebugAsset.php
  15. 9
      extensions/gii/GiiAsset.php
  16. 11
      extensions/gii/TypeAheadAsset.php
  17. 2
      extensions/gii/views/layouts/main.php
  18. 27
      extensions/jui/CoreAsset.php
  19. 3
      extensions/jui/DatePicker.php
  20. 1
      extensions/jui/DatePickerLanguageAsset.php
  21. 5
      extensions/jui/JuiAsset.php
  22. 2
      framework/base/Application.php
  23. 3
      framework/captcha/CaptchaAsset.php
  24. 3
      framework/grid/GridViewAsset.php
  25. 3
      framework/validators/PunycodeAsset.php
  26. 3
      framework/validators/ValidationAsset.php
  27. 106
      framework/web/AssetBundle.php
  28. 375
      framework/web/AssetManager.php
  29. 3
      framework/web/JqueryAsset.php
  30. 3
      framework/web/YiiAsset.php
  31. 3
      framework/widgets/ActiveFormAsset.php
  32. 3
      framework/widgets/MaskedInputAsset.php
  33. 3
      framework/widgets/PjaxAsset.php

2
apps/advanced/.bowerrc

@ -1,3 +1,3 @@
{ {
"directory" : "assets" "directory" : "vendor/bower"
} }

1
apps/advanced/assets/.gitkeep

@ -1 +0,0 @@
*

2
apps/advanced/backend/web/assets/.gitignore vendored

@ -0,0 +1,2 @@
*
!.gitignore

8
apps/advanced/environments/index.php

@ -41,10 +41,6 @@ return [
'backend/config/main-local.php', 'backend/config/main-local.php',
'frontend/config/main-local.php', 'frontend/config/main-local.php',
], ],
'createSymlink' => [
'frontend/web/assets' => 'assets',
'backend/web/assets' => 'assets',
],
], ],
'Production' => [ 'Production' => [
'path' => 'prod', 'path' => 'prod',
@ -61,9 +57,5 @@ return [
'backend/config/main-local.php', 'backend/config/main-local.php',
'frontend/config/main-local.php', 'frontend/config/main-local.php',
], ],
'createSymlink' => [
'frontend/web/assets' => 'assets',
'backend/web/assets' => 'assets',
],
], ],
]; ];

2
apps/advanced/frontend/web/assets/.gitignore vendored

@ -0,0 +1,2 @@
*
!.gitignore

2
apps/basic/.bowerrc

@ -1,3 +1,3 @@
{ {
"directory" : "web/assets" "directory" : "vendor/bower"
} }

2
apps/basic/web/assets/.gitignore vendored

@ -0,0 +1,2 @@
*
!.gitignore

1
apps/basic/web/assets/.gitkeep

@ -1 +0,0 @@
*

11
docs/guide/concept-aliases.md

@ -106,10 +106,13 @@ Yii predefines a set of aliases to easily reference commonly used file paths and
- `@yii`, the directory where the `BaseYii.php` file is located (also called the framework directory) - `@yii`, the directory where the `BaseYii.php` file is located (also called the framework directory)
- `@app`, the [[yii\base\Application::basePath|base path]] of the currently running application - `@app`, the [[yii\base\Application::basePath|base path]] of the currently running application
- `@runtime`, the [[yii\base\Application::runtimePath|runtime path]] of the currently running application - `@runtime`, the [[yii\base\Application::runtimePath|runtime path]] of the currently running application. Defaults to `@app/runtime`.
- `@vendor`, the [[yii\base\Application::vendorPath|Composer vendor directory]] - `@webroot`, the Web root directory of the currently running Web application. It is determined based on the directory
- `@webroot`, the Web root directory of the currently running Web application containing the entry script.
- `@web`, the base URL of the currently running Web application - `@web`, the base URL of the currently running Web application. It has the same value as [[yii\web\Request::baseUrl]].
- `@vendor`, the [[yii\base\Application::vendorPath|Composer vendor directory]]. Defaults to `@app/vendor`.
- `@bower`, the root directory that contains [bower packages](http://bower.io/). Defaults to `@vendor/bower`.
- `@npm`, the root directory that contains [npm packages](https://www.npmjs.org/). Defaults to `@vendor/npm`.
The `@yii` alias is defined when you include the `Yii.php` file in your [entry script](structure-entry-scripts.md). The rest of the aliases are defined in the application constructor when applying the application The `@yii` alias is defined when you include the `Yii.php` file in your [entry script](structure-entry-scripts.md). The rest of the aliases are defined in the application constructor when applying the application
[configuration](concept-configurations.md). [configuration](concept-configurations.md).

5
extensions/authclient/widgets/AuthChoiceAsset.php

@ -17,11 +17,12 @@ use yii\web\AssetBundle;
*/ */
class AuthChoiceAsset extends AssetBundle class AuthChoiceAsset extends AssetBundle
{ {
public $sourcePath = '@vendor/yii2-authclient/assets';
public $js = [ public $js = [
'yii2-authclient/assets/authchoice.js', 'authchoice.js',
]; ];
public $css = [ public $css = [
'yii2-authclient/assets/authchoice.css', 'authchoice.css',
]; ];
public $depends = [ public $depends = [
'yii\web\YiiAsset', 'yii\web\YiiAsset',

3
extensions/bootstrap/BootstrapAsset.php

@ -17,7 +17,8 @@ use yii\web\AssetBundle;
*/ */
class BootstrapAsset extends AssetBundle class BootstrapAsset extends AssetBundle
{ {
public $sourcePath = '@bower/bootstrap';
public $css = [ public $css = [
'bootstrap/dist/css/bootstrap.css', 'dist/css/bootstrap.css',
]; ];
} }

3
extensions/bootstrap/BootstrapPluginAsset.php

@ -17,8 +17,9 @@ use yii\web\AssetBundle;
*/ */
class BootstrapPluginAsset extends AssetBundle class BootstrapPluginAsset extends AssetBundle
{ {
public $sourcePath = '@bower/bootstrap';
public $js = [ public $js = [
'bootstrap/dist/js/bootstrap.js', 'dist/js/bootstrap.js',
]; ];
public $depends = [ public $depends = [
'yii\web\JqueryAsset', 'yii\web\JqueryAsset',

3
extensions/bootstrap/BootstrapThemeAsset.php

@ -17,8 +17,9 @@ use yii\web\AssetBundle;
*/ */
class BootstrapThemeAsset extends AssetBundle class BootstrapThemeAsset extends AssetBundle
{ {
public $sourcePath = '@bower/bootstrap';
public $css = [ public $css = [
'bootstrap/dist/css/bootstrap-theme.css', 'dist/css/bootstrap-theme.css',
]; ];
public $depends = [ public $depends = [
'yii\bootstrap\BootstrapAsset', 'yii\bootstrap\BootstrapAsset',

5
extensions/debug/DebugAsset.php

@ -17,9 +17,10 @@ use yii\web\AssetBundle;
*/ */
class DebugAsset extends AssetBundle class DebugAsset extends AssetBundle
{ {
public $sourcePath = '@yii/debug/assets';
public $css = [ public $css = [
'yii2-debug/assets/main.css', 'main.css',
'yii2-debug/assets/toolbar.css', 'toolbar.css',
]; ];
public $depends = [ public $depends = [
'yii\web\YiiAsset', 'yii\web\YiiAsset',

9
extensions/gii/GiiAsset.php

@ -17,18 +17,17 @@ use yii\web\AssetBundle;
*/ */
class GiiAsset extends AssetBundle class GiiAsset extends AssetBundle
{ {
public $sourcePath = '@yii/gii/assets';
public $css = [ public $css = [
'yii2-gii/assets/main.css', 'main.css',
]; ];
public $js = [ public $js = [
'yii2-gii/assets/gii.js', 'gii.js',
'typeahead.js/dist/typeahead.bundle.js',
]; ];
public $depends = [ public $depends = [
'yii\web\YiiAsset', 'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset', 'yii\bootstrap\BootstrapAsset',
'yii\bootstrap\BootstrapPluginAsset', 'yii\bootstrap\BootstrapPluginAsset',
'yii\gii\TypeAheadAsset',
]; ];
} }

11
extensions/jui/ButtonAsset.php → extensions/gii/TypeAheadAsset.php

@ -5,20 +5,23 @@
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\jui; namespace yii\gii;
use yii\web\AssetBundle; use yii\web\AssetBundle;
/** /**
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class ButtonAsset extends AssetBundle class TypeAheadAsset extends AssetBundle
{ {
public $sourcePath = '@bower/typeahead.js';
public $js = [ public $js = [
'jquery.ui/ui/button.js', 'dist/typeahead.bundle.js',
]; ];
public $depends = [ public $depends = [
'yii\jui\CoreAsset', 'yii\bootstrap\BootstrapAsset',
'yii\bootstrap\BootstrapPluginAsset',
]; ];
} }

2
extensions/gii/views/layouts/main.php

@ -22,7 +22,7 @@ $asset = yii\gii\GiiAsset::register($this);
<?php $this->beginBody() ?> <?php $this->beginBody() ?>
<?php <?php
NavBar::begin([ NavBar::begin([
'brandLabel' => Html::img('@web/assets/yii2-gii/assets/logo.png'), 'brandLabel' => Html::img($asset->baseUrl . '/logo.png'),
'brandUrl' => ['default/index'], 'brandUrl' => ['default/index'],
'options' => ['class' => 'navbar-inverse navbar-fixed-top'], 'options' => ['class' => 'navbar-inverse navbar-fixed-top'],
]); ]);

27
extensions/jui/CoreAsset.php

@ -1,27 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\jui;
use yii\web\AssetBundle;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class CoreAsset extends AssetBundle
{
public $js = [
'jquery.ui/ui/core.js',
'jquery.ui/ui/widget.js',
'jquery.ui/ui/position.js',
'jquery.ui/ui/mouse.js',
];
public $depends = [
'yii\web\JqueryAsset',
];
}

3
extensions/jui/DatePicker.php

@ -86,8 +86,7 @@ class DatePicker extends InputWidget
$view = $this->getView(); $view = $this->getView();
$bundle = DatePickerLanguageAsset::register($view); $bundle = DatePickerLanguageAsset::register($view);
if ($bundle->autoGenerate) { if ($bundle->autoGenerate) {
$am = $view->getAssetManager(); $view->registerJsFile($bundle->baseUrl . "/ui/i18n/datepicker-$language.js", [
$view->registerJsFile($am->getAssetUrl("jquery-ui/ui/i18n/datepicker-$language.js"), [
'depends' => [JuiAsset::className()], 'depends' => [JuiAsset::className()],
]); ]);
} }

1
extensions/jui/DatePickerLanguageAsset.php

@ -15,6 +15,7 @@ use yii\web\AssetBundle;
*/ */
class DatePickerLanguageAsset extends AssetBundle class DatePickerLanguageAsset extends AssetBundle
{ {
public $sourcePath = '@bower/jquery-ui';
/** /**
* @var boolean whether to automatically generate the needed language js files. * @var boolean whether to automatically generate the needed language js files.
* If this is true, the language js files will be determined based on the actual usage of [[DatePicker]] * If this is true, the language js files will be determined based on the actual usage of [[DatePicker]]

5
extensions/jui/JuiAsset.php

@ -15,11 +15,12 @@ use yii\web\AssetBundle;
*/ */
class JuiAsset extends AssetBundle class JuiAsset extends AssetBundle
{ {
public $sourcePath = '@bower/jquery-ui';
public $js = [ public $js = [
'jquery-ui/jquery-ui.js', 'jquery-ui.js',
]; ];
public $css = [ public $css = [
'jquery-ui/themes/smoothness/jquery-ui.css', 'themes/smoothness/jquery-ui.css',
]; ];
public $depends = [ public $depends = [
'yii\web\JqueryAsset', 'yii\web\JqueryAsset',

2
framework/base/Application.php

@ -449,6 +449,8 @@ abstract class Application extends Module
{ {
$this->_vendorPath = Yii::getAlias($path); $this->_vendorPath = Yii::getAlias($path);
Yii::setAlias('@vendor', $this->_vendorPath); Yii::setAlias('@vendor', $this->_vendorPath);
Yii::setAlias('@bower', $this->_vendorPath . DIRECTORY_SEPARATOR . 'bower');
Yii::setAlias('@npm', $this->_vendorPath . DIRECTORY_SEPARATOR . 'npm');
} }
/** /**

3
framework/captcha/CaptchaAsset.php

@ -17,8 +17,9 @@ use yii\web\AssetBundle;
*/ */
class CaptchaAsset extends AssetBundle class CaptchaAsset extends AssetBundle
{ {
public $sourcePath = '@yii/assets';
public $js = [ public $js = [
'yii2/assets/yii.captcha.js', 'yii.captcha.js',
]; ];
public $depends = [ public $depends = [
'yii\web\YiiAsset', 'yii\web\YiiAsset',

3
framework/grid/GridViewAsset.php

@ -17,8 +17,9 @@ use yii\web\AssetBundle;
*/ */
class GridViewAsset extends AssetBundle class GridViewAsset extends AssetBundle
{ {
public $sourcePath = '@yii/assets';
public $js = [ public $js = [
'yii2/assets/yii.gridView.js', 'yii.gridView.js',
]; ];
public $depends = [ public $depends = [
'yii\web\YiiAsset', 'yii\web\YiiAsset',

3
framework/validators/PunycodeAsset.php

@ -17,7 +17,8 @@ use yii\web\AssetBundle;
*/ */
class PunycodeAsset extends AssetBundle class PunycodeAsset extends AssetBundle
{ {
public $sourcePath = '@bower/punycode';
public $js = [ public $js = [
'punycode/punycode.js', 'punycode.js',
]; ];
} }

3
framework/validators/ValidationAsset.php

@ -17,8 +17,9 @@ use yii\web\AssetBundle;
*/ */
class ValidationAsset extends AssetBundle class ValidationAsset extends AssetBundle
{ {
public $sourcePath = '@yii/assets';
public $js = [ public $js = [
'yii2/assets/yii.validation.js', 'yii.validation.js',
]; ];
public $depends = [ public $depends = [
'yii\web\YiiAsset', 'yii\web\YiiAsset',

106
framework/web/AssetBundle.php

@ -7,9 +7,9 @@
namespace yii\web; namespace yii\web;
use Yii;
use yii\base\Object; use yii\base\Object;
use yii\helpers\Url; use yii\helpers\Url;
use Yii;
/** /**
* AssetBundle represents a collection of asset files, such as CSS, JS, images. * AssetBundle represents a collection of asset files, such as CSS, JS, images.
@ -27,11 +27,23 @@ use yii\helpers\Url;
class AssetBundle extends Object class AssetBundle extends Object
{ {
/** /**
* @var string the directory that contains the asset files in this bundle. * @var string the directory that contains the source asset files for this asset bundle.
* A source asset file is a file that is part of your source code repository of your Web application.
*
* You must set this property if the directory containing the source asset files is not Web accessible.
* By setting this property, [[AssetManager]] will publish the source asset files
* to a Web-accessible directory automatically when the asset bundle is registered on a page.
*
* If you do not set this property, it means the source asset files are located under [[basePath]].
*
* You can use either a directory or an alias of the directory.
*/
public $sourcePath;
/**
* @var string the Web-accessible directory that contains the asset files in this bundle.
* *
* The value of this property can be prefixed to every relative asset file path listed in [[js]] and [[css]] * If [[sourcePath]] is set, this property will be *overwritten* by [[AssetManager]]
* to form an absolute file path. If this property is null (meaning not set), it will be filled with the value of * when it publishes the asset files from [[sourcePath]].
* [[AssetManager::basePath]] when the bundle is being loaded by [[AssetManager::getBundle()]].
* *
* You can use either a directory or an alias of the directory. * You can use either a directory or an alias of the directory.
*/ */
@ -39,10 +51,8 @@ class AssetBundle extends Object
/** /**
* @var string the base URL for the relative asset files listed in [[js]] and [[css]]. * @var string the base URL for the relative asset files listed in [[js]] and [[css]].
* *
* The value of this property will be prefixed to every relative asset file path listed in [[js]] and [[css]] * If [[sourcePath]] is set, this property will be *overwritten* by [[AssetManager]]
* when they are being registered in a view so that they can be Web accessible. * when it publishes the asset files from [[sourcePath]].
* If this property is null (meaning not set), it will be filled with the value of
* [[AssetManager::baseUrl]] when the bundle is being loaded by [[AssetManager::getBundle()]].
* *
* You can use either a URL or an alias of the URL. * You can use either a URL or an alias of the URL.
*/ */
@ -65,11 +75,11 @@ class AssetBundle extends Object
* specified in one of the following formats: * specified in one of the following formats:
* *
* - an absolute URL representing an external asset. For example, * - an absolute URL representing an external asset. For example,
* `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` or * `http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` or
* `http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`. * `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`.
* - a path relative to [[basePath]] and [[baseUrl]]: for example, `js/main.js`. There should be no leading slash. * - a relative path representing a local asset (e.g. `js/main.js`). The actual file path of a local
* - a path relative to [[AssetManager::basePath]] and [[AssetManager::baseUrl]]: for example, * asset can be determined by prefixing [[basePath]] to the relative path, and the actual URL
* `@/jquery/dist/jquery.js`. The path must begin with `@/`. * of the asset can be determined by prefixing [[baseUrl]] to the relative path.
* *
* Note that only forward slash "/" should be used as directory separators. * Note that only forward slash "/" should be used as directory separators.
*/ */
@ -91,6 +101,11 @@ class AssetBundle extends Object
* when registering the CSS files in this bundle. * when registering the CSS files in this bundle.
*/ */
public $cssOptions = []; public $cssOptions = [];
/**
* @var array the options to be passed to [[AssetManager::publish()]] when the asset bundle
* is being published. This property is used only when [[sourcePath]] is set.
*/
public $publishOptions = [];
/** /**
@ -109,6 +124,9 @@ class AssetBundle extends Object
*/ */
public function init() public function init()
{ {
if ($this->sourcePath !== null) {
$this->sourcePath = rtrim(Yii::getAlias($this->sourcePath), '/\\');
}
if ($this->basePath !== null) { if ($this->basePath !== null) {
$this->basePath = rtrim(Yii::getAlias($this->basePath), '/\\'); $this->basePath = rtrim(Yii::getAlias($this->basePath), '/\\');
} }
@ -118,59 +136,43 @@ class AssetBundle extends Object
} }
/** /**
* @param View $view * Registers the CSS and JS files with the given view.
* @param \yii\web\View $view the view that the asset files are to be registered with.
*/ */
public function registerAssetFiles($view) public function registerAssetFiles($view)
{ {
$manager = $view->getAssetManager(); $manager = $view->getAssetManager();
foreach ($this->js as $js) { foreach ($this->js as $js) {
$view->registerJsFile($this->getAssetUrl($js, $manager), $this->jsOptions); $view->registerJsFile($manager->getAssetUrl($this, $js), $this->jsOptions);
} }
foreach ($this->css as $css) { foreach ($this->css as $css) {
$view->registerCssFile($this->getAssetUrl($css, $manager), $this->cssOptions); $view->registerCssFile($manager->getAssetUrl($this, $css), $this->cssOptions);
}
}
/**
* Returns the actual URL for the specified asset.
* The actual URL is obtained by prepending either [[baseUrl]] or [[AssetManager::baseUrl]] to the given asset path.
* @param string $asset the asset path. This should be one of the assets listed in [[js]] or [[css]].
* @param AssetManager $manager the asset manager
* @return string the actual URL for the specified asset.
*/
protected function getAssetUrl($asset, $manager)
{
if (($actualAsset = $manager->resolveAsset($asset)) !== false) {
return Url::isRelative($actualAsset) ? $manager->baseUrl . '/' . $actualAsset : $actualAsset;
}
if (strncmp($asset, '@/', 2) === 0) {
return $manager->baseUrl . substr($asset, 1);
} elseif (Url::isRelative($asset)) {
return $this->baseUrl . '/' . $asset;
} else {
return $asset;
} }
} }
/** /**
* Returns the actual file path for the specified asset. * Publishes the asset bundle if its source code is not under Web-accessible directory.
* @param string $asset the asset path. This should be one of the assets listed in [[js]] or [[css]]. * It will also try to convert non-CSS or JS files (e.g. LESS, Sass) into the corresponding
* @param AssetManager $manager the asset manager * CSS or JS files using [[AssetManager::converter|asset converter]].
* @return string|boolean the actual file path, or false if the asset is specified as an absolute URL * @param AssetManager $am the asset manager to perform the asset publishing
*/ */
public function getAssetPath($asset, $manager) public function publish($am)
{ {
if (($actualAsset = $manager->resolveAsset($asset)) !== false) { if ($this->sourcePath !== null && !isset($this->basePath, $this->baseUrl)) {
return Url::isRelative($actualAsset) ? $manager->basePath . '/' . $actualAsset : false; list ($this->basePath, $this->baseUrl) = $am->publish($this->sourcePath, $this->publishOptions);
} }
if (strncmp($asset, '@/', 2) === 0) { if (isset($this->basePath, $this->baseUrl) && ($converter = $am->getConverter()) !== null) {
return $manager->basePath . substr($asset, 1); foreach ($this->js as $i => $js) {
} elseif (Url::isRelative($asset)) { if (Url::isRelative($js)) {
return $this->basePath . '/' . $asset; $this->js[$i] = $converter->convert($js, $this->basePath);
} else { }
return false; }
foreach ($this->css as $i => $css) {
if (Url::isRelative($css)) {
$this->css[$i] = $converter->convert($css, $this->basePath);
}
}
} }
} }
} }

375
framework/web/AssetManager.php

@ -10,6 +10,9 @@ namespace yii\web;
use Yii; use Yii;
use yii\base\Component; use yii\base\Component;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
use yii\helpers\FileHelper;
use yii\helpers\Url;
/** /**
* AssetManager manages asset bundle configuration and loading. * AssetManager manages asset bundle configuration and loading.
@ -67,13 +70,18 @@ class AssetManager extends Component
public $baseUrl = '@web/assets'; public $baseUrl = '@web/assets';
/** /**
* @var array mapping from source asset files (keys) to target asset files (values). * @var array mapping from source asset files (keys) to target asset files (values).
* When an asset bundle is being loaded by [[getBundle()]], each of its asset files (listed in either *
* [[AssetBundle::css]] or [[AssetBundle::js]] will be examined to see if it matches any key * This property is provided to support fixing incorrect asset file paths in some asset bundles.
* in this map. If so, the corresponding value will be used to replace the asset file. * When an asset bundle is registered with a view, each relative asset file in its [[AssetBundle::css|css]]
* and [[AssetBundle::js|js]] arrays will be examined against this map. If any of the keys is found
* to be the last part of an asset file (which is prefixed with [[AssetBundle::sourcePath]] if available),
* the corresponding value will replace the asset and be registered with the view.
* For example, an asset file `my/path/to/jquery.js` matches a key `jquery.js`.
* *
* Note that the target asset files should be either absolute URLs or paths relative to [[baseUrl]] and [[basePath]]. * Note that the target asset files should be either absolute URLs or paths relative to [[baseUrl]] and [[basePath]].
* *
* In the following example, any occurrence of `jquery.min.js` will be replaced with `jquery/dist/jquery.js`. * In the following example, any assets ending with `jquery.min.js` will be replaced with `jquery/dist/jquery.js`
* which is relative to [[baseUrl]] and [[basePath]].
* *
* ```php * ```php
* [ * [
@ -82,6 +90,63 @@ class AssetManager extends Component
* ``` * ```
*/ */
public $assetMap = []; public $assetMap = [];
/**
* @var boolean whether to use symbolic link to publish asset files. Defaults to false, meaning
* asset files are copied to [[basePath]]. Using symbolic links has the benefit that the published
* assets will always be consistent with the source assets and there is no copy operation required.
* This is especially useful during development.
*
* However, there are special requirements for hosting environments in order to use symbolic links.
* In particular, symbolic links are supported only on Linux/Unix, and Windows Vista/2008 or greater.
*
* Moreover, some Web servers need to be properly configured so that the linked assets are accessible
* to Web users. For example, for Apache Web server, the following configuration directive should be added
* for the Web folder:
*
* ~~~
* Options FollowSymLinks
* ~~~
*/
public $linkAssets = false;
/**
* @var integer the permission to be set for newly published asset files.
* This value will be used by PHP chmod() function. No umask will be applied.
* If not set, the permission will be determined by the current environment.
*/
public $fileMode;
/**
* @var integer the permission to be set for newly generated asset directories.
* This value will be used by PHP chmod() function. No umask will be applied.
* Defaults to 0775, meaning the directory is read-writable by owner and group,
* but read-only for other users.
*/
public $dirMode = 0775;
/**
* @var callback a PHP callback that is called before copying each sub-directory or file.
* This option is used only when publishing a directory. If the callback returns false, the copy
* operation for the sub-directory or file will be cancelled.
*
* The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or
* file to be copied from, while `$to` is the copy target.
*
* This is passed as a parameter `beforeCopy` to [[\yii\helpers\FileHelper::copyDirectory()]].
*/
public $beforeCopy;
/**
* @var callback a PHP callback that is called after a sub-directory or file is successfully copied.
* This option is used only when publishing a directory. The signature of the callback is the same as
* for [[beforeCopy]].
* This is passed as a parameter `afterCopy` to [[\yii\helpers\FileHelper::copyDirectory()]].
*/
public $afterCopy;
/**
* @var boolean whether the directory being published should be copied even if
* it is found in the target directory. This option is used only when publishing a directory.
* You may want to set this to be `true` during the development stage to make sure the published
* directory is always up-to-date. Do not set this to true on production servers as it will
* significantly degrade the performance.
*/
public $forceCopy = false;
private $_dummyBundles = []; private $_dummyBundles = [];
@ -94,10 +159,12 @@ class AssetManager extends Component
{ {
parent::init(); parent::init();
$this->basePath = Yii::getAlias($this->basePath); $this->basePath = Yii::getAlias($this->basePath);
if (is_dir($this->basePath)) { if (!is_dir($this->basePath)) {
$this->basePath = realpath($this->basePath);
} else {
throw new InvalidConfigException("The directory does not exist: {$this->basePath}"); throw new InvalidConfigException("The directory does not exist: {$this->basePath}");
} elseif (!is_writable($this->basePath)) {
throw new InvalidConfigException("The directory is not writable by the Web process: {$this->basePath}");
} else {
$this->basePath = realpath($this->basePath);
} }
$this->baseUrl = rtrim(Yii::getAlias($this->baseUrl), '/'); $this->baseUrl = rtrim(Yii::getAlias($this->baseUrl), '/');
} }
@ -109,19 +176,21 @@ class AssetManager extends Component
* it will treat `$name` as the class of the asset bundle and create a new instance of it. * it will treat `$name` as the class of the asset bundle and create a new instance of it.
* *
* @param string $name the class name of the asset bundle * @param string $name the class name of the asset bundle
* @param boolean $publish whether to publish the asset files in the asset bundle before it is returned.
* If you set this false, you must manually call `AssetBundle::publish()` to publish the asset files.
* @return AssetBundle the asset bundle instance * @return AssetBundle the asset bundle instance
* @throws InvalidConfigException if $name does not refer to a valid asset bundle * @throws InvalidConfigException if $name does not refer to a valid asset bundle
*/ */
public function getBundle($name) public function getBundle($name, $publish = true)
{ {
if ($this->bundles === false) { if ($this->bundles === false) {
return $this->loadDummyBundle($name); return $this->loadDummyBundle($name);
} elseif (!isset($this->bundles[$name])) { } elseif (!isset($this->bundles[$name])) {
return $this->bundles[$name] = $this->loadBundle($name); return $this->bundles[$name] = $this->loadBundle($name, [], $publish);
} elseif ($this->bundles[$name] instanceof AssetBundle) { } elseif ($this->bundles[$name] instanceof AssetBundle) {
return $this->bundles[$name]; return $this->bundles[$name];
} elseif (is_array($this->bundles[$name])) { } elseif (is_array($this->bundles[$name])) {
return $this->bundles[$name] = $this->loadBundle($name, $this->bundles[$name]); return $this->bundles[$name] = $this->loadBundle($name, $this->bundles[$name], $publish);
} elseif ($this->bundles[$name] === false) { } elseif ($this->bundles[$name] === false) {
return $this->loadDummyBundle($name); return $this->loadDummyBundle($name);
} else { } else {
@ -129,17 +198,15 @@ class AssetManager extends Component
} }
} }
protected function loadBundle($name, $config = []) protected function loadBundle($name, $config = [], $publish = true)
{ {
if (!isset($config['class'])) { if (!isset($config['class'])) {
$config['class'] = $name; $config['class'] = $name;
} }
/* @var $bundle AssetBundle */
$bundle = Yii::createObject($config); $bundle = Yii::createObject($config);
if ($bundle->basePath === null) { if ($publish) {
$bundle->basePath = $this->basePath; $bundle->publish($this);
}
if ($bundle->baseUrl === null) {
$bundle->baseUrl = $this->baseUrl;
} }
return $bundle; return $bundle;
} }
@ -156,11 +223,50 @@ class AssetManager extends Component
return $this->_dummyBundles[$name]; return $this->_dummyBundles[$name];
} }
public function resolveAsset($asset) /**
* Returns the actual URL for the specified asset.
* The actual URL is obtained by prepending either [[baseUrl]] or [[AssetManager::baseUrl]] to the given asset path.
* @param AssetBundle $bundle the asset bundle which the asset file belongs to
* @param string $asset the asset path. This should be one of the assets listed in [[js]] or [[css]].
* @return string the actual URL for the specified asset.
*/
public function getAssetUrl($bundle, $asset)
{
if (($actualAsset = $this->resolveAsset($bundle, $asset)) !== false) {
return Url::isRelative($actualAsset) ? $this->baseUrl . '/' . $actualAsset : $actualAsset;
} else {
return Url::isRelative($asset) ? $bundle->baseUrl . '/' . $asset : $asset;
}
}
/**
* Returns the actual file path for the specified asset.
* @param AssetBundle $bundle the asset bundle which the asset file belongs to
* @param string $asset the asset path. This should be one of the assets listed in [[js]] or [[css]].
* @return string|boolean the actual file path, or false if the asset is specified as an absolute URL
*/
public function getAssetPath($bundle, $asset)
{
if (($actualAsset = $this->resolveAsset($bundle, $asset)) !== false) {
return Url::isRelative($actualAsset) ? $this->basePath . '/' . $actualAsset : false;
} else {
return Url::isRelative($asset) ? $bundle->basePath . '/' . $asset : false;
}
}
/**
* @param AssetBundle $bundle
* @param string $asset
* @return string|boolean
*/
protected function resolveAsset($bundle, $asset)
{ {
if (isset($this->assetMap[$asset])) { if (isset($this->assetMap[$asset])) {
return $this->assetMap[$asset]; return $this->assetMap[$asset];
} }
if ($bundle->sourcePath !== null && Url::isRelative($asset)) {
$asset = $bundle->sourcePath . '/' . $asset;
}
$n = strlen($asset); $n = strlen($asset);
foreach ($this->assetMap as $from => $to) { foreach ($this->assetMap as $from => $to) {
@ -173,13 +279,240 @@ class AssetManager extends Component
return false; return false;
} }
public function getAssetUrl($asset) private $_converter;
/**
* Returns the asset converter.
* @return AssetConverterInterface the asset converter.
*/
public function getConverter()
{
if ($this->_converter === null) {
$this->_converter = Yii::createObject(AssetConverter::className());
} elseif (is_array($this->_converter) || is_string($this->_converter)) {
if (is_array($this->_converter) && !isset($this->_converter['class'])) {
$this->_converter['class'] = AssetConverter::className();
}
$this->_converter = Yii::createObject($this->_converter);
}
return $this->_converter;
}
/**
* Sets the asset converter.
* @param array|AssetConverterInterface $value the asset converter. This can be either
* an object implementing the [[AssetConverterInterface]], or a configuration
* array that can be used to create the asset converter object.
*/
public function setConverter($value)
{
$this->_converter = $value;
}
/**
* @var array published assets
*/
private $_published = [];
/**
* Publishes a file or a directory.
*
* This method will copy the specified file or directory to [[basePath]] so that
* it can be accessed via the Web server.
*
* If the asset is a file, its file modification time will be checked to avoid
* unnecessary file copying.
*
* If the asset is a directory, all files and subdirectories under it will be published recursively.
* Note, in case $forceCopy is false the method only checks the existence of the target
* directory to avoid repetitive copying (which is very expensive).
*
* By default, when publishing a directory, subdirectories and files whose name starts with a dot "."
* will NOT be published. If you want to change this behavior, you may specify the "beforeCopy" option
* as explained in the `$options` parameter.
*
* Note: On rare scenario, a race condition can develop that will lead to a
* one-time-manifestation of a non-critical problem in the creation of the directory
* that holds the published assets. This problem can be avoided altogether by 'requesting'
* in advance all the resources that are supposed to trigger a 'publish()' call, and doing
* that in the application deployment phase, before system goes live. See more in the following
* discussion: http://code.google.com/p/yii/issues/detail?id=2579
*
* @param string $path the asset (file or directory) to be published
* @param array $options the options to be applied when publishing a directory.
* The following options are supported:
*
* - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.
* This overrides [[beforeCopy]] if set.
* - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied.
* This overrides [[afterCopy]] if set.
* - forceCopy: boolean, whether the directory being published should be copied even if
* it is found in the target directory. This option is used only when publishing a directory.
* This overrides [[forceCopy]] if set.
*
* @return array the path (directory or file path) and the URL that the asset is published as.
* @throws InvalidParamException if the asset to be published does not exist.
*/
public function publish($path, $options = [])
{
$path = Yii::getAlias($path);
if (isset($this->_published[$path])) {
return $this->_published[$path];
}
if (!is_string($path) || ($src = realpath($path)) === false) {
throw new InvalidParamException("The file or directory to be published does not exist: $path");
}
if (is_file($src)) {
return $this->_published[$path] = $this->publishFile($src);
} else {
return $this->_published[$path] = $this->publishDirectory($src, $options);
}
}
/**
* Publishes a file.
* @param string $src the asset file to be published
* @return array the path and the URL that the asset is published as.
* @throws InvalidParamException if the asset to be published does not exist.
*/
protected function publishFile($src)
{ {
return $this->baseUrl . '/' . ltrim($asset, '/'); $dir = $this->hash(dirname($src) . filemtime($src));
$fileName = basename($src);
$dstDir = $this->basePath . DIRECTORY_SEPARATOR . $dir;
$dstFile = $dstDir . DIRECTORY_SEPARATOR . $fileName;
if (!is_dir($dstDir)) {
FileHelper::createDirectory($dstDir, $this->dirMode, true);
}
if ($this->linkAssets) {
if (!is_file($dstFile)) {
symlink($src, $dstFile);
}
} elseif (@filemtime($dstFile) < @filemtime($src)) {
copy($src, $dstFile);
if ($this->fileMode !== null) {
@chmod($dstFile, $this->fileMode);
}
}
return [$dstFile, $this->baseUrl . "/$dir/$fileName"];
} }
public function getAssetPath($asset) /**
* Publishes a directory.
* @param string $src the asset directory to be published
* @param array $options the options to be applied when publishing a directory.
* The following options are supported:
*
* - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.
* This overrides [[beforeCopy]] if set.
* - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied.
* This overrides [[afterCopy]] if set.
* - forceCopy: boolean, whether the directory being published should be copied even if
* it is found in the target directory. This option is used only when publishing a directory.
* This overrides [[forceCopy]] if set.
*
* @return array the path directory and the URL that the asset is published as.
* @throws InvalidParamException if the asset to be published does not exist.
*/
protected function publishDirectory($src, $options)
{
$dir = $this->hash($src . filemtime($src));
$dstDir = $this->basePath . DIRECTORY_SEPARATOR . $dir;
if ($this->linkAssets) {
if (!is_dir($dstDir)) {
symlink($src, $dstDir);
}
} elseif (!is_dir($dstDir) || !empty($options['forceCopy']) || (!isset($options['forceCopy']) && $this->forceCopy)) {
$opts = [
'dirMode' => $this->dirMode,
'fileMode' => $this->fileMode,
];
if (isset($options['beforeCopy'])) {
$opts['beforeCopy'] = $options['beforeCopy'];
} elseif ($this->beforeCopy !== null) {
$opts['beforeCopy'] = $this->beforeCopy;
} else {
$opts['beforeCopy'] = function ($from, $to) {
return strncmp(basename($from), '.', 1) !== 0;
};
}
if (isset($options['afterCopy'])) {
$opts['afterCopy'] = $options['afterCopy'];
} elseif ($this->afterCopy !== null) {
$opts['afterCopy'] = $this->afterCopy;
}
FileHelper::copyDirectory($src, $dstDir, $opts);
}
return [$dstDir, $this->baseUrl . '/' . $dir];
}
/**
* Returns the published path of a file path.
* This method does not perform any publishing. It merely tells you
* if the file or directory is published, where it will go.
* @param string $path directory or file path being published
* @return string the published file path. False if the file or directory does not exist
*/
public function getPublishedPath($path)
{
$path = Yii::getAlias($path);
if (isset($this->_published[$path])) {
return $this->_published[$path][0];
}
if (is_string($path) && ($path = realpath($path)) !== false) {
$base = $this->basePath . DIRECTORY_SEPARATOR;
if (is_file($path)) {
return $base . $this->hash(dirname($path) . filemtime($path)) . DIRECTORY_SEPARATOR . basename($path);
} else {
return $base . $this->hash($path . filemtime($path));
}
} else {
return false;
}
}
/**
* Returns the URL of a published file path.
* This method does not perform any publishing. It merely tells you
* if the file path is published, what the URL will be to access it.
* @param string $path directory or file path being published
* @return string the published URL for the file or directory. False if the file or directory does not exist.
*/
public function getPublishedUrl($path)
{
$path = Yii::getAlias($path);
if (isset($this->_published[$path])) {
return $this->_published[$path][1];
}
if (is_string($path) && ($path = realpath($path)) !== false) {
if (is_file($path)) {
return $this->baseUrl . '/' . $this->hash(dirname($path) . filemtime($path)) . '/' . basename($path);
} else {
return $this->baseUrl . '/' . $this->hash($path . filemtime($path));
}
} else {
return false;
}
}
/**
* Generate a CRC32 hash for the directory path. Collisions are higher
* than MD5 but generates a much smaller hash string.
* @param string $path string to be hashed.
* @return string hashed string.
*/
protected function hash($path)
{ {
return $this->basePath . '/' . ltrim($asset, '/'); return sprintf('%x', crc32($path . Yii::getVersion()));
} }
} }

3
framework/web/JqueryAsset.php

@ -15,7 +15,8 @@ namespace yii\web;
*/ */
class JqueryAsset extends AssetBundle class JqueryAsset extends AssetBundle
{ {
public $sourcePath = '@bower/jquery';
public $js = [ public $js = [
'jquery/dist/jquery.js', 'dist/jquery.js',
]; ];
} }

3
framework/web/YiiAsset.php

@ -15,8 +15,9 @@ namespace yii\web;
*/ */
class YiiAsset extends AssetBundle class YiiAsset extends AssetBundle
{ {
public $sourcePath = '@yii/assets';
public $js = [ public $js = [
'yii2/assets/yii.js', 'yii.js',
]; ];
public $depends = [ public $depends = [
'yii\web\JqueryAsset', 'yii\web\JqueryAsset',

3
framework/widgets/ActiveFormAsset.php

@ -15,8 +15,9 @@ use yii\web\AssetBundle;
*/ */
class ActiveFormAsset extends AssetBundle class ActiveFormAsset extends AssetBundle
{ {
public $sourcePath = '@yii/assets';
public $js = [ public $js = [
'yii2/assets/yii.activeForm.js', 'yii.activeForm.js',
]; ];
public $depends = [ public $depends = [
'yii\web\YiiAsset', 'yii\web\YiiAsset',

3
framework/widgets/MaskedInputAsset.php

@ -19,8 +19,9 @@ use yii\web\AssetBundle;
*/ */
class MaskedInputAsset extends AssetBundle class MaskedInputAsset extends AssetBundle
{ {
public $sourcePath = '@bower/jquery.inputmask';
public $js = [ public $js = [
'jquery.inputmask/dist/jquery.inputmask.bundle.js' 'dist/jquery.inputmask.bundle.js'
]; ];
public $depends = [ public $depends = [
'yii\web\YiiAsset' 'yii\web\YiiAsset'

3
framework/widgets/PjaxAsset.php

@ -17,8 +17,9 @@ use yii\web\AssetBundle;
*/ */
class PjaxAsset extends AssetBundle class PjaxAsset extends AssetBundle
{ {
public $sourcePath = '@bower/yii2-pjax';
public $js = [ public $js = [
'yii2-pjax/jquery.pjax.js', 'jquery.pjax.js',
]; ];
public $depends = [ public $depends = [
'yii\web\YiiAsset', 'yii\web\YiiAsset',

Loading…
Cancel
Save