diff --git a/apps/advanced/.bowerrc b/apps/advanced/.bowerrc new file mode 100644 index 0000000..3f8ac64 --- /dev/null +++ b/apps/advanced/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory" : "assets" +} diff --git a/apps/advanced/backend/web/assets/.gitkeep b/apps/advanced/assets/.gitkeep similarity index 100% rename from apps/advanced/backend/web/assets/.gitkeep rename to apps/advanced/assets/.gitkeep diff --git a/apps/advanced/backend/.bowerrc b/apps/advanced/backend/.bowerrc deleted file mode 100644 index 16098e9..0000000 --- a/apps/advanced/backend/.bowerrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "directory" : "web/assets" -} \ No newline at end of file diff --git a/apps/advanced/backend/bower.json b/apps/advanced/bower.json similarity index 70% rename from apps/advanced/backend/bower.json rename to apps/advanced/bower.json index 4e165e6..63b1246 100644 --- a/apps/advanced/backend/bower.json +++ b/apps/advanced/bower.json @@ -1,5 +1,5 @@ { - "name": "yii2-advanced-backend", + "name": "yii2-advanced", "version": "1.0.0", "dependencies": { }, diff --git a/apps/advanced/environments/index.php b/apps/advanced/environments/index.php index e7ab700..1bec659 100644 --- a/apps/advanced/environments/index.php +++ b/apps/advanced/environments/index.php @@ -18,6 +18,9 @@ * 'setCookieValidationKey' => [ * // list of config files that need to be inserted with automatically generated cookie validation keys * ], + * 'createSymlink' => [ + * // list of symlinks to be created. Keys are symlinks, and values are the targets. + * ], * ], * ]; * ``` @@ -38,6 +41,10 @@ return [ 'backend/config/main-local.php', 'frontend/config/main-local.php', ], + 'createSymlink' => [ + 'frontend/web/assets' => 'assets', + 'backend/web/assets' => 'assets', + ], ], 'Production' => [ 'path' => 'prod', @@ -54,5 +61,9 @@ return [ 'backend/config/main-local.php', 'frontend/config/main-local.php', ], + 'createSymlink' => [ + 'frontend/web/assets' => 'assets', + 'backend/web/assets' => 'assets', + ], ], ]; diff --git a/apps/advanced/frontend/.bowerrc b/apps/advanced/frontend/.bowerrc deleted file mode 100644 index 16098e9..0000000 --- a/apps/advanced/frontend/.bowerrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "directory" : "web/assets" -} \ No newline at end of file diff --git a/apps/advanced/frontend/bower.json b/apps/advanced/frontend/bower.json deleted file mode 100644 index cad1f7e..0000000 --- a/apps/advanced/frontend/bower.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "yii2-advanced-frontend", - "version": "1.0.0", - "dependencies": { - }, - "devDependencies": { - } -} diff --git a/apps/advanced/frontend/web/assets/.gitkeep b/apps/advanced/frontend/web/assets/.gitkeep deleted file mode 100644 index 72e8ffc..0000000 --- a/apps/advanced/frontend/web/assets/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/apps/advanced/init b/apps/advanced/init index db9dd7e..e6278db 100755 --- a/apps/advanced/init +++ b/apps/advanced/init @@ -193,3 +193,13 @@ function setCookieValidationKey($root, $paths) file_put_contents($file, $content); } } + +function createSymlink($links) +{ + foreach ($links as $link => $target) { + echo " symlink $target as $link\n"; + if (!is_link($link)) { + symlink($target, $link); + } + } +} diff --git a/extensions/jui/DatePicker.php b/extensions/jui/DatePicker.php index fbdd836..bf2e186 100644 --- a/extensions/jui/DatePicker.php +++ b/extensions/jui/DatePicker.php @@ -78,22 +78,27 @@ class DatePicker extends InputWidget public function run() { echo $this->renderWidget() . "\n"; + $containerID = $this->inline ? $this->containerOptions['id'] : $this->options['id']; $language = $this->language ? $this->language : Yii::$app->language; + if ($language != 'en-US') { $view = $this->getView(); - DatePickerLanguageAsset::register($view); - + $bundle = DatePickerLanguageAsset::register($view); + if ($bundle->autoGenerate) { + $am = $view->getAssetManager(); + $view->registerJsFile($am->getAssetUrl("jquery-ui/ui/i18n/datepicker-$language.js"), [ + 'depends' => [JuiAsset::className()], + ]); + } $options = Json::encode($this->clientOptions); $view->registerJs("$('#{$containerID}').datepicker($.extend({}, $.datepicker.regional['{$language}'], $options));"); - - $options = $this->clientOptions; - $this->clientOptions = false; // the datepicker js widget is already registered - $this->registerWidget('datepicker', $containerID); - $this->clientOptions = $options; } else { - $this->registerWidget('datepicker', $containerID); + $this->registerClientOptions('datepicker', $containerID); } + + $this->registerClientEvents('datepicker', $containerID); + JuiAsset::register($this->getView()); } /** diff --git a/extensions/jui/DatePickerLanguageAsset.php b/extensions/jui/DatePickerLanguageAsset.php index 0c1ecf0..9f9cdf4 100644 --- a/extensions/jui/DatePickerLanguageAsset.php +++ b/extensions/jui/DatePickerLanguageAsset.php @@ -15,9 +15,15 @@ use yii\web\AssetBundle; */ class DatePickerLanguageAsset extends AssetBundle { - public $js = [ - 'jquery.ui.datepicker-i18n.js', - ]; + /** + * @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]] + * and its language settings. If this is false, you should explicitly specify the language js files via [[js]]. + */ + public $autoGenerate = true; + /** + * @inheritdoc + */ public $depends = [ 'yii\jui\JuiAsset', ]; diff --git a/framework/web/AssetBundle.php b/framework/web/AssetBundle.php index b37ed8e..0f905a3 100644 --- a/framework/web/AssetBundle.php +++ b/framework/web/AssetBundle.php @@ -9,6 +9,7 @@ namespace yii\web; use Yii; use yii\base\Object; +use yii\helpers\Url; /** * AssetBundle represents a collection of asset files, such as CSS, JS, images. @@ -115,4 +116,61 @@ class AssetBundle extends Object $this->baseUrl = rtrim(Yii::getAlias($this->baseUrl), '/'); } } + + /** + * @param View $view + */ + public function registerAssetFiles($view) + { + $manager = $view->getAssetManager(); + foreach ($this->js as $js) { + $view->registerJsFile($this->getAssetUrl($js, $manager), $this->jsOptions); + } + foreach ($this->css as $css) { + $view->registerCssFile($this->getAssetUrl($css, $manager), $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. + * @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|boolean the actual file path, or false if the asset is specified as an absolute URL + */ + public function getAssetPath($asset, $manager) + { + if (($actualAsset = $manager->resolveAsset($asset)) !== false) { + return Url::isRelative($actualAsset) ? $manager->basePath . '/' . $actualAsset : false; + } + + if (strncmp($asset, '@/', 2) === 0) { + return $manager->basePath . substr($asset, 1); + } elseif (Url::isRelative($asset)) { + return $this->basePath . '/' . $asset; + } else { + return false; + } + } } diff --git a/framework/web/AssetManager.php b/framework/web/AssetManager.php index 225a1db..925f3a2 100644 --- a/framework/web/AssetManager.php +++ b/framework/web/AssetManager.php @@ -10,7 +10,6 @@ namespace yii\web; use Yii; use yii\base\Component; use yii\base\InvalidConfigException; -use yii\helpers\Url; /** * AssetManager manages asset bundle configuration and loading. @@ -84,6 +83,8 @@ class AssetManager extends Component */ public $assetMap = []; + private $_dummyBundles = []; + /** * Initializes the component. @@ -114,7 +115,7 @@ class AssetManager extends Component public function getBundle($name) { if ($this->bundles === false) { - return null; + return $this->loadDummyBundle($name); } elseif (!isset($this->bundles[$name])) { return $this->bundles[$name] = $this->loadBundle($name); } elseif ($this->bundles[$name] instanceof AssetBundle) { @@ -122,7 +123,7 @@ class AssetManager extends Component } elseif (is_array($this->bundles[$name])) { return $this->bundles[$name] = $this->loadBundle($name, $this->bundles[$name]); } elseif ($this->bundles[$name] === false) { - return null; + return $this->loadDummyBundle($name); } else { throw new InvalidConfigException("Invalid asset bundle configuration: $name"); } @@ -143,49 +144,42 @@ class AssetManager extends Component return $bundle; } - /** - * @param View $view - * @param AssetBundle $bundle - */ - public function registerAssetFiles($view, $bundle) + protected function loadDummyBundle($name) { - foreach ($bundle->js as $js) { - $view->registerJsFile($this->getAssetUrl($bundle, $js), $bundle->jsOptions); - } - foreach ($bundle->css as $css) { - $view->registerCssFile($this->getAssetUrl($bundle, $css), $bundle->cssOptions); + if (!isset($this->_dummyBundles[$name])) { + $this->_dummyBundles[$name] = $this->loadBundle($name, [ + 'js' => [], + 'css' => [], + 'depends' => [], + ]); } + return $this->_dummyBundles[$name]; } - protected function getAssetUrl($bundle, $file) + public function resolveAsset($asset) { - if (($mappedFile = $this->mapAsset($file)) !== false) { - return Url::isRelative($mappedFile) ? $this->baseUrl . '/' . $mappedFile : $mappedFile; + if (isset($this->assetMap[$asset])) { + return $this->assetMap[$asset]; } - if (strncmp($file, '@/', 2) === 0) { - $file = $this->baseUrl . substr($file, 1); - } elseif (Url::isRelative($file)) { - $file = $bundle->baseUrl . '/' . $file; - } - - return $file; - } - - protected function mapAsset($file) - { - if (isset($this->assetMap[$file])) { - return $this->assetMap[$file]; - } - - $n = strlen($file); + $n = strlen($asset); foreach ($this->assetMap as $from => $to) { $n2 = strlen($from); - if ($n2 <= $n && substr_compare($file, $from, $n - $n2, $n2) === 0) { + if ($n2 <= $n && substr_compare($asset, $from, $n - $n2, $n2) === 0) { return $to; } } return false; } + + public function getAssetUrl($asset) + { + return $this->baseUrl . '/' . ltrim($asset, '/'); + } + + public function getAssetPath($asset) + { + return $this->basePath . '/' . ltrim($asset, '/'); + } } diff --git a/framework/web/View.php b/framework/web/View.php index c1ce75a..7733068 100644 --- a/framework/web/View.php +++ b/framework/web/View.php @@ -261,7 +261,7 @@ class View extends \yii\base\View foreach ($bundle->depends as $dep) { $this->registerAssetFiles($dep); } - $this->getAssetManager()->registerAssetFiles($this, $bundle); + $bundle->registerAssetFiles($this); } unset($this->assetBundles[$name]); } @@ -380,7 +380,7 @@ class View extends \yii\base\View } else { $this->getAssetManager()->bundles[$key] = new AssetBundle([ 'baseUrl' => '', - 'css' => [$url], + 'css' => [ltrim($url, '/')], 'cssOptions' => $options, 'depends' => (array) $depends, ]); @@ -445,7 +445,7 @@ class View extends \yii\base\View } else { $this->getAssetManager()->bundles[$key] = new AssetBundle([ 'baseUrl' => '', - 'js' => [$url], + 'js' => [ltrim($url, '/')], 'jsOptions' => $options, 'depends' => (array) $depends, ]);