diff --git a/.gitignore b/.gitignore index 90c0df18f..55b624267 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ /bootstrap/compiled.php /vendor -composer.lock composer.phar .DS_Store .idea diff --git a/CHANGELOG.md b/CHANGELOG.md index 595ec50b1..a70297697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +* **Build 305** (2015-10-17) + - The Inspector control has been dramatically improved. + * **Build 304** (2015-10-09) - Added new `where` and `whereComponent` methods for querying CMS template properties. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a8e436bb..93d982d6b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ Thank you for your contributions! ## Reporting a bug with OctoberCMS -**Please don't use the main Github for reporting bugs with plugins.** If you have found a bug in a plugin, the best place to report it is with the [plugin author](http://octobercms.com/plugins). +**Please don't use the main GitHub for reporting bugs with plugins.** If you have found a bug in a plugin, the best place to report it is with the [plugin author](http://octobercms.com/plugins). We work hard to process bugs that are reported, to assist with this please ensure the following details are always included: @@ -18,9 +18,9 @@ We work hard to process bugs that are reported, to assist with this please ensur - **Actual result**: What is the actual result on running above steps i.e. the bug behavior - **include any error messages**. -#### Here's how to report a bug on Github +#### Here's how to report a bug on GitHub -1. **Register for an account on [Github](https://github.com),** if you don't already have one. +1. **Register for an account on [GitHub](https://github.com),** if you don't already have one. 2. **Search for similar bugs.** Perhaps someone has already reported your issue! If so, please add clarification and/or more information to the **existing** bug. @@ -38,11 +38,11 @@ If you wish to contact us privately about any security exploits in OctoberCMS yo ## Feature requests -**Please don't use Github issues for suggesting a new feature.** If you have a feature idea, the best place to suggest it is the [OctoberCMS website forum](http://octobercms.com/forum/chan/feature-requests). +**Please don't use GitHub issues for suggesting a new feature.** If you have a feature idea, the best place to suggest it is the [OctoberCMS website forum](http://octobercms.com/forum/chan/feature-requests). -Only use Github if you are planning on contributing a new feature and developing it. If you want to discuss your idea first, before "officially" posting it anywhere, you can always join us on [IRC](http://octobercms.com/chat). +Only use GitHub if you are planning on contributing a new feature and developing it. If you want to discuss your idea first, before "officially" posting it anywhere, you can always join us on [IRC](http://octobercms.com/chat). -#### Github feature requests +#### GitHub feature requests Feature Requests submitted as GitHub Issues specifically mean *"I'd like to see this feature, I'm going to be working on some code to implement it."* It is more like a Pre-Pull Request, in which a developer signifies that he or she wants to see a feature implemented that they think would be really great, and they're committed to coding it. diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css index 559efe4c7..4698c3b08 100644 --- a/modules/backend/assets/css/october.css +++ b/modules/backend/assets/css/october.css @@ -88,9 +88,9 @@ @media all and (max-width:767px){.sweet-alert{width:auto;margin-left:0;margin-right:0;left:15px;right:15px} } .sweet-alert .icon{width:80px;height:80px;border:4px solid gray;border-radius:50%;margin:20px auto;position:relative;box-sizing:content-box} -.sweet-alert .icon.error{border-color:#d43f3a} +.sweet-alert .icon.error{border-color:#952518} .sweet-alert .icon.error .x-mark{position:relative;display:block} -.sweet-alert .icon.error .line{position:absolute;height:5px;width:47px;background-color:#d9534f;display:block;top:37px;border-radius:2px} +.sweet-alert .icon.error .line{position:absolute;height:5px;width:47px;background-color:#ab2a1c;display:block;top:37px;border-radius:2px} .sweet-alert .icon.error .line.left{-webkit-transform:rotate(45deg);transform:rotate(45deg);left:17px} .sweet-alert .icon.error .line.right{-webkit-transform:rotate(-45deg);transform:rotate(-45deg);right:16px} .sweet-alert .icon.warning{border-color:#eea236} @@ -99,20 +99,20 @@ .sweet-alert .icon.info{border-color:#46b8da} .sweet-alert .icon.info::before{content:"";position:absolute;width:5px;height:29px;left:50%;bottom:17px;border-radius:2px;margin-left:-2px;background-color:#5bc0de} .sweet-alert .icon.info::after{content:"";position:absolute;width:7px;height:7px;border-radius:50%;margin-left:-3px;top:19px;background-color:#5bc0de} -.sweet-alert .icon.success{border-color:#4cae4c} +.sweet-alert .icon.success{border-color:#2b9854} .sweet-alert .icon.success::before,.sweet-alert .icon.success::after{content:'';border-radius:50%;position:absolute;width:60px;height:120px;background:white;-webkit-transform:rotate(45deg);transform:rotate(45deg)} .sweet-alert .icon.success::before{border-radius:120px 0 0 120px;top:-7px;left:-33px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:60px 60px;transform-origin:60px 60px} .sweet-alert .icon.success::after{border-radius:0 120px 120px 0;top:-11px;left:30px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:0px 60px;transform-origin:0px 60px} -.sweet-alert .icon.success .placeholder{width:80px;height:80px;border:4px solid rgba(92,184,92,0.2);border-radius:50%;box-sizing:content-box;position:absolute;left:-4px;top:-4px;z-index:2} +.sweet-alert .icon.success .placeholder{width:80px;height:80px;border:4px solid rgba(49,172,95,0.2);border-radius:50%;box-sizing:content-box;position:absolute;left:-4px;top:-4px;z-index:2} .sweet-alert .icon.success .fix{width:5px;height:90px;background-color:#f9f9f9;position:absolute;left:28px;top:8px;z-index:1;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)} -.sweet-alert .icon.success .line{height:5px;background-color:#5cb85c;display:block;border-radius:2px;position:absolute;z-index:2} +.sweet-alert .icon.success .line{height:5px;background-color:#31ac5f;display:block;border-radius:2px;position:absolute;z-index:2} .sweet-alert .icon.success .line.tip{width:25px;left:14px;top:46px;-webkit-transform:rotate(45deg);transform:rotate(45deg)} .sweet-alert .icon.success .line.long{width:47px;right:8px;top:38px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)} .sweet-alert .icon.custom{background-size:contain;border-radius:0;border:none;background-position:center center;background-repeat:no-repeat} .sweet-alert .btn-default:focus{border-color:#e3e3e3;outline:0} -.sweet-alert .btn-success:focus{border-color:#4cae4c;outline:0} +.sweet-alert .btn-success:focus{border-color:#2b9854;outline:0} .sweet-alert .btn-info:focus{border-color:#46b8da;outline:0} -.sweet-alert .btn-danger:focus{border-color:#d43f3a;outline:0} +.sweet-alert .btn-danger:focus{border-color:#952518;outline:0} .sweet-alert .btn-warning:focus{border-color:#eea236;outline:0} .sweet-alert button::-moz-focus-inner{border:0} .sweet-alert{text-align:right} diff --git a/modules/backend/behaviors/RelationController.php b/modules/backend/behaviors/RelationController.php index 07d4ab4f4..a2813d876 100644 --- a/modules/backend/behaviors/RelationController.php +++ b/modules/backend/behaviors/RelationController.php @@ -912,15 +912,13 @@ class RelationController extends ControllerBehavior $sessionKey = $this->deferredBinding ? $this->relationGetSessionKey(true) : null; if ($this->viewMode == 'multi') { - - if ($this->relationType == 'hasMany') { - $newModel = $this->relationObject->create($saveData, $sessionKey); - } - elseif ($this->relationType == 'belongsToMany') { - $newModel = $this->relationObject->create($saveData, [], $sessionKey); + $newModel = $this->relationModel; + $modelsToSave = $this->prepareModelsToSave($newModel, $saveData); + foreach ($modelsToSave as $modelToSave) { + $modelToSave->save(null, $this->manageWidget->getSessionKey()); } - $newModel->commitDeferred($this->manageWidget->getSessionKey()); + $this->relationObject->add($newModel, $sessionKey); } elseif ($this->viewMode == 'single') { $newModel = $this->viewModel; @@ -961,8 +959,10 @@ class RelationController extends ControllerBehavior if ($this->viewMode == 'multi') { $model = $this->relationModel->find($this->manageId); - $model->fill($saveData); - $model->save(null, $this->manageWidget->getSessionKey()); + $modelsToSave = $this->prepareModelsToSave($model, $saveData); + foreach ($modelsToSave as $modelToSave) { + $modelToSave->save(null, $this->manageWidget->getSessionKey()); + } } elseif ($this->viewMode == 'single') { $this->viewWidget->setFormValues($saveData); diff --git a/modules/backend/behaviors/ReorderController.php b/modules/backend/behaviors/ReorderController.php index 7d50e4ce6..aa74496d7 100644 --- a/modules/backend/behaviors/ReorderController.php +++ b/modules/backend/behaviors/ReorderController.php @@ -211,22 +211,35 @@ class ReorderController extends ControllerBehavior */ protected function getRecords() { - $model = $this->controller->reorderGetModel(); $records = null; + $model = $this->controller->reorderGetModel(); + $query = $model->newQuery(); + + $this->controller->reorderExtendQuery($query); if ($this->sortMode == 'simple') { - $records = $model + $records = $query ->orderBy($model->getSortOrderColumn()) ->get() ; } elseif ($this->sortMode == 'nested') { - $records = $model->getEagerRoot(); + $records = $query->getNested(); } return $records; } + /** + * Extend the query used for finding reorder records. Extra conditions + * can be applied to the query, for example, $query->withTrashed(); + * @param October\Rain\Database\Builder $query + * @return void + */ + public function reorderExtendQuery($query) + { + } + // // Widgets // diff --git a/modules/backend/classes/FormField.php b/modules/backend/classes/FormField.php index 4ef1aeac4..5f37697dc 100644 --- a/modules/backend/classes/FormField.php +++ b/modules/backend/classes/FormField.php @@ -254,6 +254,10 @@ class FormField */ protected function evalConfig($config) { + if (is_null($config)) { + $config = []; + } + /* * Standard config:property values */ @@ -543,12 +547,65 @@ class FormField * Returns this fields value from a supplied data set, which can be * an array or a model or another generic collection. * @param mixed $data + * @param mixed $default * @return mixed */ public function getValueFromData($data, $default = null) { $fieldName = $this->valueFrom ?: $this->fieldName; + return $this->getFieldNameFromData($fieldName, $data, $default); + } + /** + * Returns the default value for this field, the supplied data is used + * to source data when defaultFrom is specified. + * @param mixed $data + * @return mixed + */ + public function getDefaultFromData($data) + { + if ($this->defaultFrom) { + return $this->getFieldNameFromData($this->defaultFrom, $data); + } + + if ($this->defaults !== '') { + return $this->defaults; + } + + return null; + } + + /** + * Returns the final model and attribute name of a nested attribute. + * Eg: list($model, $attribute) = $this->resolveAttribute('person[phone]'); + * @param string $attribute. + * @return array + */ + public function resolveModelAttribute($model, $attribute = null) + { + if ($attribute === null) { + $attribute = $this->valueFrom ?: $this->fieldName; + } + + $parts = is_array($attribute) ? $attribute : HtmlHelper::nameToArray($attribute); + $last = array_pop($parts); + + foreach ($parts as $part) { + $model = $model->{$part}; + } + + return [$model, $last]; + } + + /** + * Internal method to extract the value of a field name from a data set. + * @param string $fieldName + * @param mixed $data + * @param mixed $default + * @return mixed + */ + protected function getFieldNameFromData($fieldName, $data, $default = null) + { /* * Array field name, eg: field[key][key2][key3] */ @@ -588,26 +645,4 @@ class FormField return $result; } - - /** - * Returns the final model and attribute name of a nested attribute. - * Eg: list($model, $attribute) = $this->resolveAttribute('person[phone]'); - * @param string $attribute. - * @return array - */ - public function resolveModelAttribute($model, $attribute = null) - { - if ($attribute === null) { - $attribute = $this->valueFrom ?: $this->fieldName; - } - - $parts = is_array($attribute) ? $attribute : HtmlHelper::nameToArray($attribute); - $last = array_pop($parts); - - foreach ($parts as $part) { - $model = $model->{$part}; - } - - return [$model, $last]; - } } diff --git a/modules/backend/classes/FormWidgetBase.php b/modules/backend/classes/FormWidgetBase.php index 716b84dd0..97182613b 100644 --- a/modules/backend/classes/FormWidgetBase.php +++ b/modules/backend/classes/FormWidgetBase.php @@ -113,7 +113,11 @@ abstract class FormWidgetBase extends WidgetBase */ public function getLoadValue() { - return $this->formField->getValueFromData($this->data ?: $this->model); + $defaultValue = !$this->model->exists + ? $this->formField->getDefaultFromData($this->data ?: $this->model) + : null; + + return $this->formField->getValueFromData($this->data ?: $this->model, $defaultValue); } /** diff --git a/modules/backend/formwidgets/DatePicker.php b/modules/backend/formwidgets/DatePicker.php index 390be8a2f..0196f3072 100644 --- a/modules/backend/formwidgets/DatePicker.php +++ b/modules/backend/formwidgets/DatePicker.php @@ -1,5 +1,6 @@ mode = strtolower($this->mode); + + $this->minDate = is_integer($this->minDate) + ? Carbon::createFromTimestamp($this->minDate) + : Carbon::parse($this->minDate); + + $this->maxDate = is_integer($this->maxDate) + ? Carbon::createFromTimestamp($this->maxDate) + : Carbon::parse($this->maxDate); } /** diff --git a/modules/backend/formwidgets/FileUpload.php b/modules/backend/formwidgets/FileUpload.php index f6470830a..7bcc47601 100644 --- a/modules/backend/formwidgets/FileUpload.php +++ b/modules/backend/formwidgets/FileUpload.php @@ -141,7 +141,7 @@ class FileUpload extends FormWidgetBase /* * Decorate each file with thumb and custom download path */ - $list->each(function($file){ + $list->each(function($file) { $this->decorateFileAttributes($file); }); diff --git a/modules/backend/formwidgets/fileupload/assets/css/fileupload.css b/modules/backend/formwidgets/fileupload/assets/css/fileupload.css index dd3c07c6c..71a0bb55d 100644 --- a/modules/backend/formwidgets/fileupload/assets/css/fileupload.css +++ b/modules/backend/formwidgets/fileupload/assets/css/fileupload.css @@ -43,9 +43,9 @@ .field-fileupload.style-image-multi .upload-button .upload-button-icon{position:absolute;width:22px;height:22px;top:50%;left:50%;margin-top:-11px;margin-left:-11px} .field-fileupload.style-image-multi .upload-button .upload-button-icon:before{text-align:center;display:block;font-size:22px;height:22px;width:22px;line-height:22px;color:rgba(0,0,0,0.1)} .field-fileupload.style-image-multi .upload-button:hover{border:2px dotted rgba(0,0,0,0.2)} -.field-fileupload.style-image-multi .upload-button:hover .upload-button-icon:before{color:#5cb85c;color:rgba(0,0,0,0.2)} +.field-fileupload.style-image-multi .upload-button:hover .upload-button-icon:before{color:#31ac5f;color:rgba(0,0,0,0.2)} .field-fileupload.style-image-multi .upload-button:focus{border:2px solid rgba(0,0,0,0.3);background:transparent} -.field-fileupload.style-image-multi .upload-button:focus .upload-button-icon:before{color:#5cb85c;color:rgba(0,0,0,0.2)} +.field-fileupload.style-image-multi .upload-button:focus .upload-button-icon:before{color:#31ac5f;color:rgba(0,0,0,0.2)} .field-fileupload.style-image-multi .upload-files-container{margin-left:90px} .field-fileupload.style-image-multi .upload-object{background:#fff;border:1px solid #ecf0f1;width:260px} .field-fileupload.style-image-multi .upload-object .progress-bar{display:block;width:100%;overflow:hidden;height:5px;background-color:#f5f5f5;border-radius:2px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);position:absolute;bottom:10px;left:0} @@ -77,9 +77,9 @@ .field-fileupload.style-image-single .upload-button .upload-button-icon{position:absolute;width:22px;height:22px;top:50%;left:50%;margin-top:-11px;margin-left:-11px} .field-fileupload.style-image-single .upload-button .upload-button-icon:before{text-align:center;display:block;font-size:22px;height:22px;width:22px;line-height:22px;color:rgba(0,0,0,0.1)} .field-fileupload.style-image-single .upload-button:hover{border:2px dotted rgba(0,0,0,0.2)} -.field-fileupload.style-image-single .upload-button:hover .upload-button-icon:before{color:#5cb85c;color:rgba(0,0,0,0.2)} +.field-fileupload.style-image-single .upload-button:hover .upload-button-icon:before{color:#31ac5f;color:rgba(0,0,0,0.2)} .field-fileupload.style-image-single .upload-button:focus{border:2px solid rgba(0,0,0,0.3);background:transparent} -.field-fileupload.style-image-single .upload-button:focus .upload-button-icon:before{color:#5cb85c;color:rgba(0,0,0,0.2)} +.field-fileupload.style-image-single .upload-button:focus .upload-button-icon:before{color:#31ac5f;color:rgba(0,0,0,0.2)} .field-fileupload.style-image-single .upload-object{padding-bottom:66px} .field-fileupload.style-image-single .upload-object .icon-container{border:1px solid #f6f8f9;background:rgba(255,255,255,0.5)} .field-fileupload.style-image-single .upload-object .icon-container.image img{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;display:block;max-width:100%;height:auto;min-height:100px;min-width:100px} diff --git a/modules/backend/formwidgets/fileupload/partials/_file_multi.htm b/modules/backend/formwidgets/fileupload/partials/_file_multi.htm index 6ae8c2d6b..23a49033f 100644 --- a/modules/backend/formwidgets/fileupload/partials/_file_multi.htm +++ b/modules/backend/formwidgets/fileupload/partials/_file_multi.htm @@ -48,7 +48,7 @@