diff --git a/framework/yii/assets/yii.activeForm.js b/framework/yii/assets/yii.activeForm.js index 483df96..1a2e58d 100644 --- a/framework/yii/assets/yii.activeForm.js +++ b/framework/yii/assets/yii.activeForm.js @@ -135,12 +135,20 @@ data.submitting = true; if (!data.settings.beforeSubmit || data.settings.beforeSubmit($form)) { validate($form, function (messages) { - var hasError = false; + var errors = []; $.each(data.attributes, function () { - hasError = updateInput($form, this, messages) || hasError; + if (updateInput($form, this, messages)) { + errors.push(this.input); + } }); updateSummary($form, messages); - if (!hasError) { + if (errors.length) { + var top = $form.find(errors.join(',')).first().offset().top; + var wtop = $(window).scrollTop(); + if (top < wtop || top > wtop + $(window).height) { + $(window).scrollTop(top); + } + } else { data.validated = true; var $button = data.submitObject || $form.find(':submit:first'); // TODO: if the submission is caused by "change" event, it will not work diff --git a/framework/yii/base/View.php b/framework/yii/base/View.php index 7f965fa..219a0fb 100644 --- a/framework/yii/base/View.php +++ b/framework/yii/base/View.php @@ -235,10 +235,10 @@ class View extends Component public function renderFile($viewFile, $params = array(), $context = null) { $viewFile = Yii::getAlias($viewFile); + if ($this->theme !== null) { + $viewFile = $this->theme->applyTo($viewFile); + } if (is_file($viewFile)) { - if ($this->theme !== null) { - $viewFile = $this->theme->applyTo($viewFile); - } $viewFile = FileHelper::localize($viewFile); } else { throw new InvalidParamException("The view file does not exist: $viewFile"); diff --git a/framework/yii/bootstrap/Tabs.php b/framework/yii/bootstrap/Tabs.php index a18eef6..4a85b9a 100644 --- a/framework/yii/bootstrap/Tabs.php +++ b/framework/yii/bootstrap/Tabs.php @@ -132,7 +132,7 @@ class Tabs extends Widget $header = Html::a($label, "#", array('class' => 'dropdown-toggle', 'data-toggle' => 'dropdown')) . "\n" . Dropdown::widget(array('items' => $item['items'], 'clientOptions' => false)); } elseif (isset($item['content'])) { - $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options')); + $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', array())); $options['id'] = ArrayHelper::getValue($options, 'id', $this->options['id'] . '-tab' . $n); $this->addCssClass($options, 'tab-pane'); diff --git a/framework/yii/web/Application.php b/framework/yii/web/Application.php index a786985..12c9295 100644 --- a/framework/yii/web/Application.php +++ b/framework/yii/web/Application.php @@ -23,6 +23,26 @@ class Application extends \yii\base\Application * @var string the default route of this application. Defaults to 'site'. */ public $defaultRoute = 'site'; + /** + * @var array the configuration specifying a controller action which should handle + * all user requests. This is mainly used when the application is in maintenance mode + * and needs to handle all incoming requests via a single action. + * The configuration is an array whose first element specifies the route of the action. + * The rest of the array elements (key-value pairs) specify the parameters to be bound + * to the action. For example, + * + * ~~~ + * array( + * 'offline/notice', + * 'param1' => 'value1', + * 'param2' => 'value2', + * ) + * ~~~ + * + * Defaults to null, meaning catch-all is not effective. + */ + public $catchAll; + /** * Processes the request. @@ -34,7 +54,12 @@ class Application extends \yii\base\Application $request = $this->getRequest(); Yii::setAlias('@wwwroot', dirname($request->getScriptFile())); Yii::setAlias('@www', $request->getBaseUrl()); - list ($route, $params) = $request->resolve(); + if (empty($this->catchAll)) { + list ($route, $params) = $request->resolve(); + } else { + $route = $this->catchAll[0]; + $params = array_splice($this->catchAll, 1); + } try { return $this->runAction($route, $params); } catch (InvalidRouteException $e) { diff --git a/framework/yii/widgets/Menu.php b/framework/yii/widgets/Menu.php index 08088d3..5b5d48c 100644 --- a/framework/yii/widgets/Menu.php +++ b/framework/yii/widgets/Menu.php @@ -9,6 +9,7 @@ namespace yii\widgets; use Yii; use yii\base\Widget; +use yii\helpers\ArrayHelper; use yii\helpers\Html; /** @@ -16,15 +17,15 @@ use yii\helpers\Html; * * The main property of Menu is [[items]], which specifies the possible items in the menu. * A menu item can contain sub-items which specify the sub-menu under that menu item. - * + * * Menu checks the current route and request parameters to toggle certain menu items * with active state. - * + * * Note that Menu only renders the HTML tags about the menu. It does do any styling. * You are responsible to provide CSS styles to make it look like a real menu. * * The following example shows how to use Menu: - * + * * ~~~ * echo Menu::widget(array( * 'items' => array( @@ -40,7 +41,7 @@ use yii\helpers\Html; * ), * )); * ~~~ - * + * * @author Qiang Xue * @since 2.0 */ @@ -68,6 +69,13 @@ class Menu extends Widget */ public $items = array(); /** + * @var array list of HTML attributes for the menu container tag. This will be overwritten + * by the "options" set in individual [[items]]. The following special options are recognized: + * + * - tag: string, defaults to "li", the tag name of the item container tags. + */ + public $itemOptions = array(); + /** * @var string the template used to render the body of a menu which is a link. * In this template, the token `{url}` will be replaced with the corresponding link URL; * while `{label}` will be replaced with the link text. @@ -110,7 +118,9 @@ class Menu extends Widget */ public $hideEmptyItems = true; /** - * @var array the HTML attributes for the menu's container tag. + * @var array the HTML attributes for the menu's container tag. The following special options are recognized: + * + * - tag: string, defaults to "ul", the tag name of the item container tags. */ public $options = array(); /** @@ -125,7 +135,7 @@ class Menu extends Widget public $lastItemCssClass; /** * @var string the route used to determine if a menu item is active or not. - * If not set, it will use the route of the current request. + * If not set, it will use the route of the current request. * @see params * @see isItemActive */ @@ -151,7 +161,9 @@ class Menu extends Widget $this->params = $_GET; } $items = $this->normalizeItems($this->items, $hasActiveChild); - echo Html::tag('ul', $this->renderItems($items), $this->options); + $options = $this->options; + $tag = ArrayHelper::remove($options, 'tag', 'ul'); + echo Html::tag($tag, $this->renderItems($items), $options); } /** @@ -164,7 +176,8 @@ class Menu extends Widget $n = count($items); $lines = array(); foreach ($items as $i => $item) { - $options = isset($item['options']) ? $item['options'] : array(); + $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', array())); + $tag = ArrayHelper::remove($options, 'tag', 'li'); $class = array(); if ($item['active']) { $class[] = $this->activeCssClass; @@ -189,7 +202,7 @@ class Menu extends Widget '{items}' => $this->renderItems($item['items']), )); } - $lines[] = Html::tag('li', $menu, $options); + $lines[] = Html::tag($tag, $menu, $options); } return implode("\n", $lines); } @@ -203,13 +216,13 @@ class Menu extends Widget protected function renderItem($item) { if (isset($item['url'])) { - $template = isset($item['template']) ? $item['template'] : $this->linkTemplate; + $template = ArrayHelper::getValue($item, 'template', $this->linkTemplate); return strtr($template, array( '{url}' => Html::url($item['url']), '{label}' => $item['label'], )); } else { - $template = isset($item['template']) ? $item['template'] : $this->labelTemplate; + $template = ArrayHelper::getValue($item, 'template', $this->labelTemplate); return strtr($template, array( '{label}' => $item['label'], )); @@ -284,5 +297,4 @@ class Menu extends Widget } return false; } - } diff --git a/tests/unit/framework/i18n/FormatterTest.php b/tests/unit/framework/i18n/FormatterTest.php index f742f22..2a67422 100644 --- a/tests/unit/framework/i18n/FormatterTest.php +++ b/tests/unit/framework/i18n/FormatterTest.php @@ -83,6 +83,6 @@ class FormatterTest extends TestCase { $time = time(); $this->assertSame(date('n/j/y', $time), $this->formatter->asDate($time)); - $this->assertSame(date('M j, Y', $time), $this->formatter->asDate($time, 'long')); + $this->assertSame(date('F j, Y', $time), $this->formatter->asDate($time, 'long')); } }