Merge branch 'develop' into table-widget
24
CHANGELOG.md
|
|
@ -1,3 +1,27 @@
|
|||
* **Build 17x** (2014-12-xx)
|
||||
- Improved asset caching (`cms.enableAssetCache`), when enabled the server will send a *304 Not Modified* header.
|
||||
|
||||
* **Build 171** (2014-12-17)
|
||||
- Add new methods `propertyName()` and `paramName()` to Component base class for accessing names of external properties.
|
||||
|
||||
* **Build 169** (2014-12-16)
|
||||
- Native `alert` and `confirm` functions have been styled in the back-end.
|
||||
- Back-end user groups have a new description field, this is shown in the group list and when creating a new administrator.
|
||||
- Back-end user groups can now be marked to add new administrators by default. This affects when a group is created (if checked, all administrators are added to the group) and creating a new administrator (the default groups are checked by default).
|
||||
|
||||
* **Build 168** (2014-12-14)
|
||||
- Config item `cms.customErrorPage` is deprecated, the setting `app.debug` should be used instead.
|
||||
- Config item `cms.enableAssetMinify` can now be set to **null**, in which case assets are only minified if debug mode (`app.debug`) is disabled.
|
||||
|
||||
* **Build 167** (2014-12-06)
|
||||
- Settings pages now have a *Reset to default* button.
|
||||
- The field `authorUrl` has been renamed to `homepage` in theme.yaml files.
|
||||
- Adds Theme customization feature (see Themes > Development docs).
|
||||
|
||||
* **Build 166** (2014-11-27)
|
||||
- Plugin details method now support "homepage" property (see Plugins > Registration & Versions docs).
|
||||
- Fixes a bug in the Datepicker using `time` mode.
|
||||
|
||||
* **Build 162** (2014-11-10)
|
||||
- Fixes an issue where the *Pages* tab is shown in the CMS when permission is denied.
|
||||
- Updates are no longer shown on the Dashboard if permission is denied.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ return array(
|
|||
| stack traces will be shown on every error that occurs within your
|
||||
| application. If disabled, a simple generic error page is shown.
|
||||
|
|
||||
| You can create a CMS page with route "/error" to set the contents
|
||||
| of this page. Otherwise a default error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => true,
|
||||
|
|
|
|||
|
|
@ -111,19 +111,6 @@ return array(
|
|||
|
||||
'urlCacheTtl' => 10,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Determines if a friendly error page should be used.
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If this value is set to true, a friendly error page is used when an
|
||||
| exception is encountered. You must create a CMS page with route "/error"
|
||||
| to set the contents of this page. Otherwise the default error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'customErrorPage' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Determines if the asset caching is enabled.
|
||||
|
|
@ -144,12 +131,13 @@ return array(
|
|||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If the minification is enabled, combined assets are compressed (minified).
|
||||
| It is recommended to disable the minification during the development, and
|
||||
| enable it in the production mode.
|
||||
| It is recommended to disable the minification during development, and
|
||||
| enable it in production mode. If set to null, assets are minified
|
||||
| when debug mode (app.debug) is disabled.
|
||||
|
|
||||
*/
|
||||
|
||||
'enableAssetMinify' => false,
|
||||
'enableAssetMinify' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -75,19 +75,6 @@ return array(
|
|||
|
||||
'urlCacheTtl' => 1,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Determines if a friendly error page should be used.
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If this value is set to true, a friendly error page is used when an
|
||||
| exception is encountered. You must create a CMS page with route "/error"
|
||||
| to set the contents of this page. Otherwise the default error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'customErrorPage' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Determines if the asset caching is enabled.
|
||||
|
|
|
|||
|
|
@ -36,16 +36,7 @@
|
|||
"tests/UiTestCase.php"
|
||||
]
|
||||
},
|
||||
"___scripts": {
|
||||
"post-install-cmd": [
|
||||
"php artisan optimize"
|
||||
],
|
||||
"pre-update-cmd": [
|
||||
"php artisan clear-compiled"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"php artisan optimize"
|
||||
],
|
||||
"scripts": {
|
||||
"post-create-project-cmd": [
|
||||
"php artisan key:generate"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ class ServiceProvider extends ModuleServiceProvider
|
|||
'label' => 'Date picker',
|
||||
'code' => 'datepicker'
|
||||
]);
|
||||
$manager->registerFormWidget('Backend\FormWidgets\TimePicker', [
|
||||
'label' => 'Time picker',
|
||||
'code' => 'timepicker'
|
||||
]);
|
||||
$manager->registerFormWidget('Backend\FormWidgets\ColorPicker', [
|
||||
'label' => 'Color picker',
|
||||
'code' => 'colorpicker'
|
||||
|
|
|
|||
|
|
@ -8865,6 +8865,9 @@ label {
|
|||
.form-preview > .form-group:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.form-preview.form-flush {
|
||||
border-top: none;
|
||||
}
|
||||
.form-elements:before,
|
||||
.form-tabless-fields:before,
|
||||
.form-elements:after,
|
||||
|
|
@ -15012,3 +15015,570 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover {
|
|||
display: block !important;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes showSweetAlert {
|
||||
0% {
|
||||
transform: scale(0.7);
|
||||
-webkit-transform: scale(0.7);
|
||||
}
|
||||
45% {
|
||||
transform: scale(1.05);
|
||||
-webkit-transform: scale(1.05);
|
||||
}
|
||||
80% {
|
||||
transform: scale(0.95);
|
||||
-webkit-tranform: scale(0.95);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes showSweetAlert {
|
||||
0% {
|
||||
transform: scale(0.7);
|
||||
-webkit-transform: scale(0.7);
|
||||
}
|
||||
45% {
|
||||
transform: scale(1.05);
|
||||
-webkit-transform: scale(1.05);
|
||||
}
|
||||
80% {
|
||||
transform: scale(0.95);
|
||||
-webkit-tranform: scale(0.95);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes hideSweetAlert {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.5);
|
||||
-webkit-transform: scale(0.5);
|
||||
}
|
||||
}
|
||||
@keyframes hideSweetAlert {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.5);
|
||||
-webkit-transform: scale(0.5);
|
||||
}
|
||||
}
|
||||
.showSweetAlert {
|
||||
-webkit-animation: showSweetAlert 0.3s;
|
||||
animation: showSweetAlert 0.3s;
|
||||
}
|
||||
.hideSweetAlert {
|
||||
-webkit-animation: hideSweetAlert 0.2s;
|
||||
animation: hideSweetAlert 0.2s;
|
||||
}
|
||||
@-webkit-keyframes animateSuccessTip {
|
||||
0% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px;
|
||||
}
|
||||
54% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px;
|
||||
}
|
||||
70% {
|
||||
width: 50px;
|
||||
left: -8px;
|
||||
top: 37px;
|
||||
}
|
||||
84% {
|
||||
width: 17px;
|
||||
left: 21px;
|
||||
top: 48px;
|
||||
}
|
||||
100% {
|
||||
width: 25px;
|
||||
left: 14px;
|
||||
top: 45px;
|
||||
}
|
||||
}
|
||||
@keyframes animateSuccessTip {
|
||||
0% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px;
|
||||
}
|
||||
54% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px;
|
||||
}
|
||||
70% {
|
||||
width: 50px;
|
||||
left: -8px;
|
||||
top: 37px;
|
||||
}
|
||||
84% {
|
||||
width: 17px;
|
||||
left: 21px;
|
||||
top: 48px;
|
||||
}
|
||||
100% {
|
||||
width: 25px;
|
||||
left: 14px;
|
||||
top: 45px;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes animateSuccessLong {
|
||||
0% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px;
|
||||
}
|
||||
65% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px;
|
||||
}
|
||||
84% {
|
||||
width: 55px;
|
||||
right: 0px;
|
||||
top: 35px;
|
||||
}
|
||||
100% {
|
||||
width: 47px;
|
||||
right: 8px;
|
||||
top: 38px;
|
||||
}
|
||||
}
|
||||
@keyframes animateSuccessLong {
|
||||
0% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px;
|
||||
}
|
||||
65% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px;
|
||||
}
|
||||
84% {
|
||||
width: 55px;
|
||||
right: 0px;
|
||||
top: 35px;
|
||||
}
|
||||
100% {
|
||||
width: 47px;
|
||||
right: 8px;
|
||||
top: 38px;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes rotatePlaceholder {
|
||||
0% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
5% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
12% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg);
|
||||
}
|
||||
}
|
||||
@keyframes rotatePlaceholder {
|
||||
0% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
5% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
12% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg);
|
||||
}
|
||||
}
|
||||
.animateSuccessTip {
|
||||
-webkit-animation: animateSuccessTip 0.75s;
|
||||
animation: animateSuccessTip 0.75s;
|
||||
}
|
||||
.animateSuccessLong {
|
||||
-webkit-animation: animateSuccessLong 0.75s;
|
||||
animation: animateSuccessLong 0.75s;
|
||||
}
|
||||
.icon.success.animate::after {
|
||||
-webkit-animation: rotatePlaceholder 4.25s ease-in;
|
||||
animation: rotatePlaceholder 4.25s ease-in;
|
||||
}
|
||||
@-webkit-keyframes animateErrorIcon {
|
||||
0% {
|
||||
transform: rotateX(100deg);
|
||||
-webkit-transform: rotateX(100deg);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(0deg);
|
||||
-webkit-transform: rotateX(0deg);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes animateErrorIcon {
|
||||
0% {
|
||||
transform: rotateX(100deg);
|
||||
-webkit-transform: rotateX(100deg);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(0deg);
|
||||
-webkit-transform: rotateX(0deg);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.animateErrorIcon {
|
||||
-webkit-animation: animateErrorIcon 0.5s;
|
||||
animation: animateErrorIcon 0.5s;
|
||||
}
|
||||
@-webkit-keyframes animateXMark {
|
||||
0% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0;
|
||||
}
|
||||
80% {
|
||||
transform: scale(1.15);
|
||||
-webkit-transform: scale(1.15);
|
||||
margin-top: -6px;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
margin-top: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes animateXMark {
|
||||
0% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0;
|
||||
}
|
||||
80% {
|
||||
transform: scale(1.15);
|
||||
-webkit-transform: scale(1.15);
|
||||
margin-top: -6px;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
margin-top: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.animateXMark {
|
||||
-webkit-animation: animateXMark 0.5s;
|
||||
animation: animateXMark 0.5s;
|
||||
}
|
||||
@-webkit-keyframes pulseWarning {
|
||||
0% {
|
||||
border-color: #F8D486;
|
||||
}
|
||||
100% {
|
||||
border-color: #F8BB86;
|
||||
}
|
||||
}
|
||||
@keyframes pulseWarning {
|
||||
0% {
|
||||
border-color: #F8D486;
|
||||
}
|
||||
100% {
|
||||
border-color: #F8BB86;
|
||||
}
|
||||
}
|
||||
.pulseWarning {
|
||||
-webkit-animation: pulseWarning 0.75s infinite alternate;
|
||||
animation: pulseWarning 0.75s infinite alternate;
|
||||
}
|
||||
@-webkit-keyframes pulseWarningIns {
|
||||
0% {
|
||||
background-color: #F8D486;
|
||||
}
|
||||
100% {
|
||||
background-color: #F8BB86;
|
||||
}
|
||||
}
|
||||
@keyframes pulseWarningIns {
|
||||
0% {
|
||||
background-color: #F8D486;
|
||||
}
|
||||
100% {
|
||||
background-color: #F8BB86;
|
||||
}
|
||||
}
|
||||
.pulseWarningIns {
|
||||
-webkit-animation: pulseWarningIns 0.75s infinite alternate;
|
||||
animation: pulseWarningIns 0.75s infinite alternate;
|
||||
}
|
||||
.sweet-overlay {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
z-index: 8050;
|
||||
}
|
||||
.sweet-alert {
|
||||
background-color: #ffffff;
|
||||
width: 478px;
|
||||
padding: 17px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -256px;
|
||||
margin-top: -200px;
|
||||
overflow: hidden;
|
||||
display: none;
|
||||
z-index: 9050;
|
||||
}
|
||||
.sweet-alert h4 {
|
||||
margin: 30px 0;
|
||||
}
|
||||
@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 .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.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;
|
||||
}
|
||||
.sweet-alert .icon.warning .body {
|
||||
position: absolute;
|
||||
width: 5px;
|
||||
height: 47px;
|
||||
left: 50%;
|
||||
top: 10px;
|
||||
border-radius: 2px;
|
||||
margin-left: -2px;
|
||||
background-color: #f0ad4e;
|
||||
}
|
||||
.sweet-alert .icon.warning .dot {
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
margin-left: -3px;
|
||||
left: 50%;
|
||||
bottom: 10px;
|
||||
background-color: #f0ad4e;
|
||||
}
|
||||
.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::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 .fix {
|
||||
width: 5px;
|
||||
height: 90px;
|
||||
background-color: #ffffff;
|
||||
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.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;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(227, 227, 227, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(227, 227, 227, 0.6);
|
||||
}
|
||||
.sweet-alert .btn-success:focus {
|
||||
border-color: #4cae4c;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(76, 174, 76, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(76, 174, 76, 0.6);
|
||||
}
|
||||
.sweet-alert .btn-info:focus {
|
||||
border-color: #46b8da;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(70, 184, 218, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(70, 184, 218, 0.6);
|
||||
}
|
||||
.sweet-alert .btn-danger:focus {
|
||||
border-color: #d43f3a;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(212, 63, 58, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(212, 63, 58, 0.6);
|
||||
}
|
||||
.sweet-alert .btn-warning:focus {
|
||||
border-color: #eea236;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(238, 162, 54, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(238, 162, 54, 0.6);
|
||||
}
|
||||
.sweet-alert button::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 584 B |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 224 B After Width: | Height: | Size: 188 B |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.3 KiB |
|
|
@ -29,7 +29,7 @@
|
|||
* - placeholder - placholder text, for text and dropdown properties
|
||||
* - depends - a list of properties the property depend on, for dropdown lists
|
||||
* - options - an option list for dropdown lists, optional. If not provided the options are loaded with AJAX.
|
||||
* - showExternalParameter - specifies the visibility of the external parameter feature for the property. Default: true.
|
||||
* - showExternalParam - specifies the visibility of the external parameter feature for the property. Default: true.
|
||||
* Example of the configuration string (a single property):
|
||||
* [{"property":"max-width","title":"Max width","type":"string"}]
|
||||
*
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
*
|
||||
* Any HTML element that wraps the inspectable element can have the data-inspector-external-parameters property that enables the external
|
||||
* parameters support. External parameters saved with the special syntax {{ paramName }}. The external parameter feature can be toggled
|
||||
* on per-property basis with the showExternalParameter option, visible by default.
|
||||
* on per-property basis with the showExternalParam option, visible by default.
|
||||
*
|
||||
* Events
|
||||
* - change - the event is triggered on the inspectable element when it's properties are updated.
|
||||
|
|
@ -161,7 +161,7 @@
|
|||
if (this.itemType == 'property' && this.groupIndex !== undefined)
|
||||
result += self.groupExpanded(this.group) ? ' expanded' : ' collapsed'
|
||||
|
||||
if (this.itemType == 'property' && !this.showExternalParameter)
|
||||
if (this.itemType == 'property' && !this.showExternalParam)
|
||||
result += ' no-external-parameter'
|
||||
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@
|
|||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
this.error(jqXHR, textStatus, errorThrown).done(function(){
|
||||
alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText)
|
||||
self.hide()
|
||||
self.triggerEvent('popupError') // Deprecated
|
||||
self.triggerEvent('error.oc.popup')
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
tr.each(function(){
|
||||
|
||||
var link = $(this).find(options.target).filter(function(){
|
||||
return !$(this).closest('td').hasClass(options.excludeClass)
|
||||
return !$(this).closest('td').hasClass(options.excludeClass) && !$(this).hasClass(options.excludeClass)
|
||||
}).first()
|
||||
|
||||
if (!link.length) return
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
SidePanelTab.prototype.init = function() {
|
||||
var self = this
|
||||
this.tabOpenDelay = 200
|
||||
this.tabOpenTimeout = undefined
|
||||
this.$sideNavItems = $('#layout-sidenav ul li')
|
||||
this.$sidePanelItems = $('[data-content-id]', this.$el)
|
||||
this.sideNavWidth = $('#layout-sidenav ul li').outerWidth()
|
||||
|
|
@ -51,10 +53,19 @@
|
|||
})
|
||||
|
||||
self.$sideNavItems.mouseenter(function(){
|
||||
if ($(window).width() < self.options.breakpoint || !self.panelFixed())
|
||||
self.displayTab(this)
|
||||
if ($(window).width() < self.options.breakpoint || !self.panelFixed()) {
|
||||
var _this = this
|
||||
self.tabOpenTimeout = setTimeout(function () {
|
||||
self.displayTab(_this)
|
||||
}, self.tabOpenDelay)
|
||||
}
|
||||
})
|
||||
|
||||
self.$sideNavItems.mouseleave(function (){
|
||||
clearTimeout(self.tabOpenTimeout)
|
||||
})
|
||||
|
||||
|
||||
$(window).resize(function() {
|
||||
self.updatePanelPosition()
|
||||
self.updateActiveTab()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,42 @@
|
|||
* October General Utilities
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implement "Sweet Alert"
|
||||
*/
|
||||
|
||||
$(window).on('ajaxErrorMessage', function(event, message){
|
||||
|
||||
swal({
|
||||
title: message,
|
||||
// type: 'error',
|
||||
confirmButtonClass: 'btn-default'
|
||||
})
|
||||
|
||||
// Prevent the default alert() message
|
||||
event.preventDefault()
|
||||
|
||||
})
|
||||
|
||||
$(window).on('ajaxConfirmMessage', function(event, message){
|
||||
|
||||
swal({
|
||||
title: message,
|
||||
// type: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonClass: 'btn-primary'
|
||||
}, function(isConfirm){
|
||||
isConfirm
|
||||
? event.promise.resolve()
|
||||
: event.promise.reject()
|
||||
})
|
||||
|
||||
// Prevent the default confirm() message
|
||||
event.preventDefault()
|
||||
return true
|
||||
|
||||
})
|
||||
|
||||
/*
|
||||
* Path helpers
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -36,8 +36,14 @@ label {
|
|||
>.form-group:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
// Form to sit flush to the element above
|
||||
&.form-flush {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Nice forms
|
||||
//
|
||||
|
|
|
|||
|
|
@ -45,3 +45,6 @@
|
|||
@import "controls/treeview.less";
|
||||
@import "controls/callout.less";
|
||||
@import "controls/sidenav-tree.less";
|
||||
|
||||
// Vendor
|
||||
@import "../vendor/sweet-alert/sweet-alert.less";
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
@-webkit-keyframes showSweetAlert {
|
||||
0% {
|
||||
transform: scale(0.7);
|
||||
-webkit-transform: scale(0.7); }
|
||||
45% {
|
||||
transform: scale(1.05);
|
||||
-webkit-transform: scale(1.05); }
|
||||
80% {
|
||||
transform: scale(0.95);
|
||||
-webkit-tranform: scale(0.95); }
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1); } }
|
||||
@keyframes showSweetAlert {
|
||||
0% {
|
||||
transform: scale(0.7);
|
||||
-webkit-transform: scale(0.7); }
|
||||
45% {
|
||||
transform: scale(1.05);
|
||||
-webkit-transform: scale(1.05); }
|
||||
80% {
|
||||
transform: scale(0.95);
|
||||
-webkit-tranform: scale(0.95); }
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1); } }
|
||||
@-webkit-keyframes hideSweetAlert {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1); }
|
||||
100% {
|
||||
transform: scale(0.5);
|
||||
-webkit-transform: scale(0.5); } }
|
||||
@keyframes hideSweetAlert {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1); }
|
||||
100% {
|
||||
transform: scale(0.5);
|
||||
-webkit-transform: scale(0.5); } }
|
||||
.showSweetAlert {
|
||||
-webkit-animation: showSweetAlert 0.3s;
|
||||
animation: showSweetAlert 0.3s; }
|
||||
|
||||
.hideSweetAlert {
|
||||
-webkit-animation: hideSweetAlert 0.2s;
|
||||
animation: hideSweetAlert 0.2s; }
|
||||
|
||||
@-webkit-keyframes animateSuccessTip {
|
||||
0% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px; }
|
||||
54% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px; }
|
||||
70% {
|
||||
width: 50px;
|
||||
left: -8px;
|
||||
top: 37px; }
|
||||
84% {
|
||||
width: 17px;
|
||||
left: 21px;
|
||||
top: 48px; }
|
||||
100% {
|
||||
width: 25px;
|
||||
left: 14px;
|
||||
top: 45px; } }
|
||||
@keyframes animateSuccessTip {
|
||||
0% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px; }
|
||||
54% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px; }
|
||||
70% {
|
||||
width: 50px;
|
||||
left: -8px;
|
||||
top: 37px; }
|
||||
84% {
|
||||
width: 17px;
|
||||
left: 21px;
|
||||
top: 48px; }
|
||||
100% {
|
||||
width: 25px;
|
||||
left: 14px;
|
||||
top: 45px; } }
|
||||
@-webkit-keyframes animateSuccessLong {
|
||||
0% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px; }
|
||||
65% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px; }
|
||||
84% {
|
||||
width: 55px;
|
||||
right: 0px;
|
||||
top: 35px; }
|
||||
100% {
|
||||
width: 47px;
|
||||
right: 8px;
|
||||
top: 38px; } }
|
||||
@keyframes animateSuccessLong {
|
||||
0% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px; }
|
||||
65% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px; }
|
||||
84% {
|
||||
width: 55px;
|
||||
right: 0px;
|
||||
top: 35px; }
|
||||
100% {
|
||||
width: 47px;
|
||||
right: 8px;
|
||||
top: 38px; } }
|
||||
@-webkit-keyframes rotatePlaceholder {
|
||||
0% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg); }
|
||||
5% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg); }
|
||||
12% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg); }
|
||||
100% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg); } }
|
||||
@keyframes rotatePlaceholder {
|
||||
0% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg); }
|
||||
5% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg); }
|
||||
12% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg); }
|
||||
100% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg); } }
|
||||
.animateSuccessTip {
|
||||
-webkit-animation: animateSuccessTip 0.75s;
|
||||
animation: animateSuccessTip 0.75s; }
|
||||
|
||||
.animateSuccessLong {
|
||||
-webkit-animation: animateSuccessLong 0.75s;
|
||||
animation: animateSuccessLong 0.75s; }
|
||||
|
||||
.icon.success.animate::after {
|
||||
-webkit-animation: rotatePlaceholder 4.25s ease-in;
|
||||
animation: rotatePlaceholder 4.25s ease-in; }
|
||||
|
||||
@-webkit-keyframes animateErrorIcon {
|
||||
0% {
|
||||
transform: rotateX(100deg);
|
||||
-webkit-transform: rotateX(100deg);
|
||||
opacity: 0; }
|
||||
100% {
|
||||
transform: rotateX(0deg);
|
||||
-webkit-transform: rotateX(0deg);
|
||||
opacity: 1; } }
|
||||
@keyframes animateErrorIcon {
|
||||
0% {
|
||||
transform: rotateX(100deg);
|
||||
-webkit-transform: rotateX(100deg);
|
||||
opacity: 0; }
|
||||
100% {
|
||||
transform: rotateX(0deg);
|
||||
-webkit-transform: rotateX(0deg);
|
||||
opacity: 1; } }
|
||||
.animateErrorIcon {
|
||||
-webkit-animation: animateErrorIcon 0.5s;
|
||||
animation: animateErrorIcon 0.5s; }
|
||||
|
||||
@-webkit-keyframes animateXMark {
|
||||
0% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0; }
|
||||
50% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0; }
|
||||
80% {
|
||||
transform: scale(1.15);
|
||||
-webkit-transform: scale(1.15);
|
||||
margin-top: -6px; }
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
margin-top: 0;
|
||||
opacity: 1; } }
|
||||
@keyframes animateXMark {
|
||||
0% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0; }
|
||||
50% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0; }
|
||||
80% {
|
||||
transform: scale(1.15);
|
||||
-webkit-transform: scale(1.15);
|
||||
margin-top: -6px; }
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
margin-top: 0;
|
||||
opacity: 1; } }
|
||||
.animateXMark {
|
||||
-webkit-animation: animateXMark 0.5s;
|
||||
animation: animateXMark 0.5s; }
|
||||
|
||||
@-webkit-keyframes pulseWarning {
|
||||
0% {
|
||||
border-color: #F8D486; }
|
||||
100% {
|
||||
border-color: #F8BB86; } }
|
||||
@keyframes pulseWarning {
|
||||
0% {
|
||||
border-color: #F8D486; }
|
||||
100% {
|
||||
border-color: #F8BB86; } }
|
||||
.pulseWarning {
|
||||
-webkit-animation: pulseWarning 0.75s infinite alternate;
|
||||
animation: pulseWarning 0.75s infinite alternate; }
|
||||
|
||||
@-webkit-keyframes pulseWarningIns {
|
||||
0% {
|
||||
background-color: #F8D486; }
|
||||
100% {
|
||||
background-color: #F8BB86; } }
|
||||
@keyframes pulseWarningIns {
|
||||
0% {
|
||||
background-color: #F8D486; }
|
||||
100% {
|
||||
background-color: #F8BB86; } }
|
||||
.pulseWarningIns {
|
||||
-webkit-animation: pulseWarningIns 0.75s infinite alternate;
|
||||
animation: pulseWarningIns 0.75s infinite alternate; }
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
SweetAlert for Bootstrap
|
||||
https://github.com/lipis/bootstrap-sweetalert
|
||||
*/
|
||||
@import "../../../../system/assets/vendor/bootstrap/variables";
|
||||
@import "../../../../system/assets/vendor/bootstrap/mixins";
|
||||
@import "sweet-alert";
|
||||
|
|
@ -0,0 +1,564 @@
|
|||
@-webkit-keyframes showSweetAlert {
|
||||
0% {
|
||||
transform: scale(0.7);
|
||||
-webkit-transform: scale(0.7);
|
||||
}
|
||||
45% {
|
||||
transform: scale(1.05);
|
||||
-webkit-transform: scale(1.05);
|
||||
}
|
||||
80% {
|
||||
transform: scale(0.95);
|
||||
-webkit-tranform: scale(0.95);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes showSweetAlert {
|
||||
0% {
|
||||
transform: scale(0.7);
|
||||
-webkit-transform: scale(0.7);
|
||||
}
|
||||
45% {
|
||||
transform: scale(1.05);
|
||||
-webkit-transform: scale(1.05);
|
||||
}
|
||||
80% {
|
||||
transform: scale(0.95);
|
||||
-webkit-tranform: scale(0.95);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes hideSweetAlert {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.5);
|
||||
-webkit-transform: scale(0.5);
|
||||
}
|
||||
}
|
||||
@keyframes hideSweetAlert {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.5);
|
||||
-webkit-transform: scale(0.5);
|
||||
}
|
||||
}
|
||||
.showSweetAlert {
|
||||
-webkit-animation: showSweetAlert 0.3s;
|
||||
animation: showSweetAlert 0.3s;
|
||||
}
|
||||
.hideSweetAlert {
|
||||
-webkit-animation: hideSweetAlert 0.2s;
|
||||
animation: hideSweetAlert 0.2s;
|
||||
}
|
||||
@-webkit-keyframes animateSuccessTip {
|
||||
0% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px;
|
||||
}
|
||||
54% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px;
|
||||
}
|
||||
70% {
|
||||
width: 50px;
|
||||
left: -8px;
|
||||
top: 37px;
|
||||
}
|
||||
84% {
|
||||
width: 17px;
|
||||
left: 21px;
|
||||
top: 48px;
|
||||
}
|
||||
100% {
|
||||
width: 25px;
|
||||
left: 14px;
|
||||
top: 45px;
|
||||
}
|
||||
}
|
||||
@keyframes animateSuccessTip {
|
||||
0% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px;
|
||||
}
|
||||
54% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px;
|
||||
}
|
||||
70% {
|
||||
width: 50px;
|
||||
left: -8px;
|
||||
top: 37px;
|
||||
}
|
||||
84% {
|
||||
width: 17px;
|
||||
left: 21px;
|
||||
top: 48px;
|
||||
}
|
||||
100% {
|
||||
width: 25px;
|
||||
left: 14px;
|
||||
top: 45px;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes animateSuccessLong {
|
||||
0% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px;
|
||||
}
|
||||
65% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px;
|
||||
}
|
||||
84% {
|
||||
width: 55px;
|
||||
right: 0px;
|
||||
top: 35px;
|
||||
}
|
||||
100% {
|
||||
width: 47px;
|
||||
right: 8px;
|
||||
top: 38px;
|
||||
}
|
||||
}
|
||||
@keyframes animateSuccessLong {
|
||||
0% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px;
|
||||
}
|
||||
65% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px;
|
||||
}
|
||||
84% {
|
||||
width: 55px;
|
||||
right: 0px;
|
||||
top: 35px;
|
||||
}
|
||||
100% {
|
||||
width: 47px;
|
||||
right: 8px;
|
||||
top: 38px;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes rotatePlaceholder {
|
||||
0% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
5% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
12% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg);
|
||||
}
|
||||
}
|
||||
@keyframes rotatePlaceholder {
|
||||
0% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
5% {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
12% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(-405deg);
|
||||
-webkit-transform: rotate(-405deg);
|
||||
}
|
||||
}
|
||||
.animateSuccessTip {
|
||||
-webkit-animation: animateSuccessTip 0.75s;
|
||||
animation: animateSuccessTip 0.75s;
|
||||
}
|
||||
.animateSuccessLong {
|
||||
-webkit-animation: animateSuccessLong 0.75s;
|
||||
animation: animateSuccessLong 0.75s;
|
||||
}
|
||||
.icon.success.animate::after {
|
||||
-webkit-animation: rotatePlaceholder 4.25s ease-in;
|
||||
animation: rotatePlaceholder 4.25s ease-in;
|
||||
}
|
||||
@-webkit-keyframes animateErrorIcon {
|
||||
0% {
|
||||
transform: rotateX(100deg);
|
||||
-webkit-transform: rotateX(100deg);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(0deg);
|
||||
-webkit-transform: rotateX(0deg);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes animateErrorIcon {
|
||||
0% {
|
||||
transform: rotateX(100deg);
|
||||
-webkit-transform: rotateX(100deg);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(0deg);
|
||||
-webkit-transform: rotateX(0deg);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.animateErrorIcon {
|
||||
-webkit-animation: animateErrorIcon 0.5s;
|
||||
animation: animateErrorIcon 0.5s;
|
||||
}
|
||||
@-webkit-keyframes animateXMark {
|
||||
0% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0;
|
||||
}
|
||||
80% {
|
||||
transform: scale(1.15);
|
||||
-webkit-transform: scale(1.15);
|
||||
margin-top: -6px;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
margin-top: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes animateXMark {
|
||||
0% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.4);
|
||||
-webkit-transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0;
|
||||
}
|
||||
80% {
|
||||
transform: scale(1.15);
|
||||
-webkit-transform: scale(1.15);
|
||||
margin-top: -6px;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
-webkit-transform: scale(1);
|
||||
margin-top: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.animateXMark {
|
||||
-webkit-animation: animateXMark 0.5s;
|
||||
animation: animateXMark 0.5s;
|
||||
}
|
||||
@-webkit-keyframes pulseWarning {
|
||||
0% {
|
||||
border-color: #F8D486;
|
||||
}
|
||||
100% {
|
||||
border-color: #F8BB86;
|
||||
}
|
||||
}
|
||||
@keyframes pulseWarning {
|
||||
0% {
|
||||
border-color: #F8D486;
|
||||
}
|
||||
100% {
|
||||
border-color: #F8BB86;
|
||||
}
|
||||
}
|
||||
.pulseWarning {
|
||||
-webkit-animation: pulseWarning 0.75s infinite alternate;
|
||||
animation: pulseWarning 0.75s infinite alternate;
|
||||
}
|
||||
@-webkit-keyframes pulseWarningIns {
|
||||
0% {
|
||||
background-color: #F8D486;
|
||||
}
|
||||
100% {
|
||||
background-color: #F8BB86;
|
||||
}
|
||||
}
|
||||
@keyframes pulseWarningIns {
|
||||
0% {
|
||||
background-color: #F8D486;
|
||||
}
|
||||
100% {
|
||||
background-color: #F8BB86;
|
||||
}
|
||||
}
|
||||
.pulseWarningIns {
|
||||
-webkit-animation: pulseWarningIns 0.75s infinite alternate;
|
||||
animation: pulseWarningIns 0.75s infinite alternate;
|
||||
}
|
||||
.sweet-overlay {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
z-index: 1040;
|
||||
}
|
||||
.sweet-alert {
|
||||
background-color: #ffffff;
|
||||
width: 478px;
|
||||
padding: 17px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -256px;
|
||||
margin-top: -200px;
|
||||
overflow: hidden;
|
||||
display: none;
|
||||
z-index: 2000;
|
||||
}
|
||||
@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 .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.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;
|
||||
}
|
||||
.sweet-alert .icon.warning .body {
|
||||
position: absolute;
|
||||
width: 5px;
|
||||
height: 47px;
|
||||
left: 50%;
|
||||
top: 10px;
|
||||
border-radius: 2px;
|
||||
margin-left: -2px;
|
||||
background-color: #f0ad4e;
|
||||
}
|
||||
.sweet-alert .icon.warning .dot {
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
margin-left: -3px;
|
||||
left: 50%;
|
||||
bottom: 10px;
|
||||
background-color: #f0ad4e;
|
||||
}
|
||||
.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::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 .fix {
|
||||
width: 5px;
|
||||
height: 90px;
|
||||
background-color: #ffffff;
|
||||
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.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: #cccccc;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(204, 204, 204, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(204, 204, 204, 0.6);
|
||||
}
|
||||
.sweet-alert .btn-success:focus {
|
||||
border-color: #4cae4c;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(76, 174, 76, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(76, 174, 76, 0.6);
|
||||
}
|
||||
.sweet-alert .btn-info:focus {
|
||||
border-color: #46b8da;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(70, 184, 218, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(70, 184, 218, 0.6);
|
||||
}
|
||||
.sweet-alert .btn-danger:focus {
|
||||
border-color: #d43f3a;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(212, 63, 58, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(212, 63, 58, 0.6);
|
||||
}
|
||||
.sweet-alert .btn-warning:focus {
|
||||
border-color: #eea236;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(238, 162, 54, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(238, 162, 54, 0.6);
|
||||
}
|
||||
.sweet-alert button::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<!-- Note: this file is only intended for development use! -->
|
||||
|
||||
<div class="sweet-overlay"></div>
|
||||
|
||||
|
||||
<!-- SweetAlert box -->
|
||||
<div class="sweet-alert">
|
||||
|
||||
<div class="icon error">
|
||||
<span class="x-mark">
|
||||
<span class="line left"></span>
|
||||
<span class="line right"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="icon warning">
|
||||
<span class="body"></span>
|
||||
<span class="dot"></span>
|
||||
</div>
|
||||
|
||||
<div class="icon info"></div>
|
||||
|
||||
<div class="icon success">
|
||||
<span class="line tip"></span>
|
||||
<span class="line long"></span>
|
||||
<div class="placeholder"></div>
|
||||
<div class="fix"></div>
|
||||
</div>
|
||||
|
||||
<div class="icon custom"></div>
|
||||
|
||||
|
||||
<h2>Title</h2>
|
||||
<p class="text-muted">Text</p>
|
||||
<p>
|
||||
<button class="cancel btn btn-lg btn-default">Cancel</button>
|
||||
<button class="confirm btn btn-lg">OK</button>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,711 @@
|
|||
// SweetAlert
|
||||
// 2014 (c) - Tristan Edwards
|
||||
// github.com/t4t5/sweetalert
|
||||
(function(window, document) {
|
||||
|
||||
var modalClass = '.sweet-alert',
|
||||
overlayClass = '.sweet-overlay',
|
||||
alertTypes = ['error', 'warning', 'info', 'success'],
|
||||
defaultParams = {
|
||||
title: '',
|
||||
text: '',
|
||||
type: null,
|
||||
allowOutsideClick: false,
|
||||
showCancelButton: false,
|
||||
closeOnConfirm: true,
|
||||
closeOnCancel: true,
|
||||
confirmButtonText: 'OK',
|
||||
confirmButtonClass: 'btn-primary',
|
||||
cancelButtonText: 'Cancel',
|
||||
cancelButtonClass: 'btn-default',
|
||||
imageUrl: null,
|
||||
imageSize: null,
|
||||
timer: null
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Manipulate DOM
|
||||
*/
|
||||
|
||||
var getModal = function() {
|
||||
return document.querySelector(modalClass);
|
||||
},
|
||||
getOverlay = function() {
|
||||
return document.querySelector(overlayClass);
|
||||
},
|
||||
hasClass = function(elem, className) {
|
||||
return new RegExp(' ' + className + ' ').test(' ' + elem.className + ' ');
|
||||
},
|
||||
addClass = function(elem, className) {
|
||||
if (!hasClass(elem, className)) {
|
||||
elem.className += ' ' + className;
|
||||
}
|
||||
},
|
||||
removeClass = function(elem, className) {
|
||||
var newClass = ' ' + elem.className.replace(/[\t\r\n]/g, ' ') + ' ';
|
||||
if (hasClass(elem, className)) {
|
||||
while (newClass.indexOf(' ' + className + ' ') >= 0) {
|
||||
newClass = newClass.replace(' ' + className + ' ', ' ');
|
||||
}
|
||||
elem.className = newClass.replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
},
|
||||
escapeHtml = function(str) {
|
||||
var div = document.createElement('div');
|
||||
div.appendChild(document.createTextNode(str));
|
||||
return div.innerHTML;
|
||||
},
|
||||
_show = function(elem) {
|
||||
elem.style.opacity = '';
|
||||
elem.style.display = 'block';
|
||||
},
|
||||
show = function(elems) {
|
||||
if (elems && !elems.length) {
|
||||
return _show(elems);
|
||||
}
|
||||
for (var i = 0; i < elems.length; ++i) {
|
||||
_show(elems[i]);
|
||||
}
|
||||
},
|
||||
_hide = function(elem) {
|
||||
elem.style.opacity = '';
|
||||
elem.style.display = 'none';
|
||||
},
|
||||
hide = function(elems) {
|
||||
if (elems && !elems.length) {
|
||||
return _hide(elems);
|
||||
}
|
||||
for (var i = 0; i < elems.length; ++i) {
|
||||
_hide(elems[i]);
|
||||
}
|
||||
},
|
||||
isDescendant = function(parent, child) {
|
||||
var node = child.parentNode;
|
||||
while (node !== null) {
|
||||
if (node === parent) {
|
||||
return true;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getTopMargin = function(elem) {
|
||||
elem.style.left = '-9999px';
|
||||
elem.style.display = 'block';
|
||||
|
||||
var height = elem.clientHeight;
|
||||
var padding = parseInt(getComputedStyle(elem).getPropertyValue('padding'), 10);
|
||||
|
||||
elem.style.left = '';
|
||||
elem.style.display = 'none';
|
||||
return ('-' + parseInt(height / 2 + padding) + 'px');
|
||||
},
|
||||
fadeIn = function(elem, interval) {
|
||||
if(+elem.style.opacity < 1) {
|
||||
interval = interval || 16;
|
||||
elem.style.opacity = 0;
|
||||
elem.style.display = 'block';
|
||||
var last = +new Date();
|
||||
var tick = function() {
|
||||
elem.style.opacity = +elem.style.opacity + (new Date() - last) / 100;
|
||||
last = +new Date();
|
||||
|
||||
if (+elem.style.opacity < 1) {
|
||||
setTimeout(tick, interval);
|
||||
}
|
||||
};
|
||||
tick();
|
||||
}
|
||||
},
|
||||
fadeOut = function(elem, interval) {
|
||||
interval = interval || 16;
|
||||
elem.style.opacity = 1;
|
||||
var last = +new Date();
|
||||
var tick = function() {
|
||||
elem.style.opacity = +elem.style.opacity - (new Date() - last) / 100;
|
||||
last = +new Date();
|
||||
|
||||
if (+elem.style.opacity > 0) {
|
||||
setTimeout(tick, interval);
|
||||
} else {
|
||||
elem.style.display = 'none';
|
||||
}
|
||||
};
|
||||
tick();
|
||||
},
|
||||
fireClick = function(node) {
|
||||
// Taken from http://www.nonobtrusive.com/2011/11/29/programatically-fire-crossbrowser-click-event-with-javascript/
|
||||
// Then fixed for today's Chrome browser.
|
||||
if (MouseEvent) {
|
||||
// Up-to-date approach
|
||||
var mevt = new MouseEvent('click', {
|
||||
view: window,
|
||||
bubbles: false,
|
||||
cancelable: true
|
||||
});
|
||||
node.dispatchEvent(mevt);
|
||||
} else if ( document.createEvent ) {
|
||||
// Fallback
|
||||
var evt = document.createEvent('MouseEvents');
|
||||
evt.initEvent('click', false, false);
|
||||
node.dispatchEvent(evt);
|
||||
} else if( document.createEventObject ) {
|
||||
node.fireEvent('onclick') ;
|
||||
} else if (typeof node.onclick === 'function' ) {
|
||||
node.onclick();
|
||||
}
|
||||
},
|
||||
stopEventPropagation = function(e) {
|
||||
// In particular, make sure the space bar doesn't scroll the main window.
|
||||
if (typeof e.stopPropagation === 'function') {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
} else if (window.event && window.event.hasOwnProperty('cancelBubble')) {
|
||||
window.event.cancelBubble = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Remember state in cases where opening and handling a modal will fiddle with it.
|
||||
var previousActiveElement,
|
||||
previousDocumentClick,
|
||||
previousWindowKeyDown,
|
||||
lastFocusedButton;
|
||||
|
||||
/*
|
||||
* Add modal + overlay to DOM
|
||||
*/
|
||||
|
||||
window.sweetAlertInitialize = function() {
|
||||
var sweetHTML = '<div class="sweet-overlay" tabIndex="-1"></div><div class="sweet-alert" tabIndex="-1"><div class="icon error"><span class="x-mark"><span class="line left"></span><span class="line right"></span></span></div><div class="icon warning"> <span class="body"></span> <span class="dot"></span> </div> <div class="icon info"></div> <div class="icon success"> <span class="line tip"></span> <span class="line long"></span> <div class="placeholder"></div> <div class="fix"></div> </div> <div class="icon custom"></div> <h4>Title</h4><p class="text-muted">Text</p><p><button class="cancel btn" tabIndex="2">Cancel</button> <button class="confirm btn" tabIndex="1">OK</button></p></div>',
|
||||
sweetWrap = document.createElement('div');
|
||||
|
||||
sweetWrap.innerHTML = sweetHTML;
|
||||
|
||||
// For readability: check sweet-alert.html
|
||||
document.body.appendChild(sweetWrap);
|
||||
|
||||
// For development use only!
|
||||
/*jQuery.ajax({
|
||||
url: '../lib/sweet-alert.html', // Change path depending on file location
|
||||
dataType: 'html'
|
||||
})
|
||||
.done(function(html) {
|
||||
jQuery('body').append(html);
|
||||
});*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Global sweetAlert function
|
||||
*/
|
||||
|
||||
window.sweetAlert = window.swal = function() {
|
||||
if (arguments[0] === undefined) {
|
||||
window.console.error('sweetAlert expects at least 1 attribute!');
|
||||
return false;
|
||||
}
|
||||
|
||||
var params = extend({}, defaultParams);
|
||||
|
||||
switch (typeof arguments[0]) {
|
||||
|
||||
case 'string':
|
||||
params.title = arguments[0];
|
||||
params.text = arguments[1] || '';
|
||||
params.type = arguments[2] || '';
|
||||
|
||||
break;
|
||||
|
||||
case 'object':
|
||||
if (arguments[0].title === undefined) {
|
||||
window.console.error('Missing "title" argument!');
|
||||
return false;
|
||||
}
|
||||
|
||||
params.title = arguments[0].title;
|
||||
params.text = arguments[0].text || defaultParams.text;
|
||||
params.type = arguments[0].type || defaultParams.type;
|
||||
params.allowOutsideClick = arguments[0].allowOutsideClick || defaultParams.allowOutsideClick;
|
||||
params.showCancelButton = arguments[0].showCancelButton !== undefined ? arguments[0].showCancelButton : defaultParams.showCancelButton;
|
||||
params.closeOnConfirm = arguments[0].closeOnConfirm !== undefined ? arguments[0].closeOnConfirm : defaultParams.closeOnConfirm;
|
||||
params.closeOnCancel = arguments[0].closeOnCancel !== undefined ? arguments[0].closeOnCancel : defaultParams.closeOnCancel;
|
||||
params.timer = arguments[0].timer || defaultParams.timer;
|
||||
|
||||
// Show "Confirm" instead of "OK" if cancel button is visible
|
||||
params.confirmButtonText = (defaultParams.showCancelButton) ? 'Confirm' : defaultParams.confirmButtonText;
|
||||
params.confirmButtonText = arguments[0].confirmButtonText || defaultParams.confirmButtonText;
|
||||
params.confirmButtonClass = arguments[0].confirmButtonClass || defaultParams.confirmButtonClass;
|
||||
params.cancelButtonText = arguments[0].cancelButtonText || defaultParams.cancelButtonText;
|
||||
params.cancelButtonClass = arguments[0].cancelButtonClass || defaultParams.cancelButtonClass;
|
||||
params.imageUrl = arguments[0].imageUrl || defaultParams.imageUrl;
|
||||
params.imageSize = arguments[0].imageSize || defaultParams.imageSize;
|
||||
params.doneFunction = arguments[1] || null;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
window.console.error('Unexpected type of argument! Expected "string" or "object", got ' + typeof arguments[0]);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
setParameters(params);
|
||||
fixVerticalPosition();
|
||||
openModal();
|
||||
|
||||
|
||||
// Modal interactions
|
||||
var modal = getModal();
|
||||
|
||||
// Mouse interactions
|
||||
var onButtonEvent = function(e) {
|
||||
|
||||
var target = e.target || e.srcElement,
|
||||
targetedConfirm = (target.className.indexOf('confirm') > -1),
|
||||
modalIsVisible = hasClass(modal, 'visible'),
|
||||
doneFunctionExists = (params.doneFunction && modal.getAttribute('data-has-done-function') === 'true');
|
||||
|
||||
switch (e.type) {
|
||||
case ("click"):
|
||||
if (targetedConfirm && doneFunctionExists && modalIsVisible) { // Clicked "confirm"
|
||||
|
||||
params.doneFunction(true);
|
||||
|
||||
if (params.closeOnConfirm) {
|
||||
closeModal();
|
||||
}
|
||||
} else if (doneFunctionExists && modalIsVisible) { // Clicked "cancel"
|
||||
|
||||
// Check if callback function expects a parameter (to track cancel actions)
|
||||
var functionAsStr = String(params.doneFunction).replace(/\s/g, '');
|
||||
var functionHandlesCancel = functionAsStr.substring(0, 9) === "function(" && functionAsStr.substring(9, 10) !== ")";
|
||||
|
||||
if (functionHandlesCancel) {
|
||||
params.doneFunction(false);
|
||||
}
|
||||
|
||||
if (params.closeOnCancel) {
|
||||
closeModal();
|
||||
}
|
||||
} else {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
var $buttons = modal.querySelectorAll('button');
|
||||
for (var i = 0; i < $buttons.length; i++) {
|
||||
$buttons[i].onclick = onButtonEvent;
|
||||
}
|
||||
|
||||
// Remember the current document.onclick event.
|
||||
previousDocumentClick = document.onclick;
|
||||
document.onclick = function(e) {
|
||||
var target = e.target || e.srcElement;
|
||||
|
||||
var clickedOnModal = (modal === target),
|
||||
clickedOnModalChild = isDescendant(modal, e.target),
|
||||
modalIsVisible = hasClass(modal, 'visible'),
|
||||
outsideClickIsAllowed = modal.getAttribute('data-allow-ouside-click') === 'true';
|
||||
|
||||
if (!clickedOnModal && !clickedOnModalChild && modalIsVisible && outsideClickIsAllowed) {
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Keyboard interactions
|
||||
var $okButton = modal.querySelector('button.confirm'),
|
||||
$cancelButton = modal.querySelector('button.cancel'),
|
||||
$modalButtons = modal.querySelectorAll('button:not([type=hidden])');
|
||||
|
||||
|
||||
function handleKeyDown(e) {
|
||||
var keyCode = e.keyCode || e.which;
|
||||
|
||||
if ([9,13,32,27].indexOf(keyCode) === -1) {
|
||||
// Don't do work on keys we don't care about.
|
||||
return;
|
||||
}
|
||||
|
||||
var $targetElement = e.target || e.srcElement;
|
||||
|
||||
var btnIndex = -1; // Find the button - note, this is a nodelist, not an array.
|
||||
for (var i = 0; i < $modalButtons.length; i++) {
|
||||
if ($targetElement === $modalButtons[i]) {
|
||||
btnIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCode === 9) {
|
||||
// TAB
|
||||
if (btnIndex === -1) {
|
||||
// No button focused. Jump to the confirm button.
|
||||
$targetElement = $okButton;
|
||||
} else {
|
||||
// Cycle to the next button
|
||||
if (btnIndex === $modalButtons.length - 1) {
|
||||
$targetElement = $modalButtons[0];
|
||||
} else {
|
||||
$targetElement = $modalButtons[btnIndex + 1];
|
||||
}
|
||||
}
|
||||
|
||||
stopEventPropagation(e);
|
||||
$targetElement.focus();
|
||||
|
||||
} else {
|
||||
if (keyCode === 13 || keyCode === 32) {
|
||||
if (btnIndex === -1) {
|
||||
// ENTER/SPACE clicked outside of a button.
|
||||
$targetElement = $okButton;
|
||||
} else {
|
||||
// Do nothing - let the browser handle it.
|
||||
$targetElement = undefined;
|
||||
}
|
||||
} else if (keyCode === 27 && !($cancelButton.hidden || $cancelButton.style.display === 'none')) {
|
||||
// ESC to cancel only if there's a cancel button displayed (like the alert() window).
|
||||
$targetElement = $cancelButton;
|
||||
} else {
|
||||
// Fallback - let the browser handle it.
|
||||
$targetElement = undefined;
|
||||
}
|
||||
|
||||
if ($targetElement !== undefined) {
|
||||
fireClick($targetElement, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
previousWindowKeyDown = window.onkeydown;
|
||||
window.onkeydown = handleKeyDown;
|
||||
|
||||
function handleOnBlur(e) {
|
||||
var $targetElement = e.target || e.srcElement,
|
||||
$focusElement = e.relatedTarget,
|
||||
modalIsVisible = hasClass(modal, 'visible');
|
||||
|
||||
if (modalIsVisible) {
|
||||
var btnIndex = -1; // Find the button - note, this is a nodelist, not an array.
|
||||
|
||||
if ($focusElement !== null) {
|
||||
// If we picked something in the DOM to focus to, let's see if it was a button.
|
||||
for (var i = 0; i < $modalButtons.length; i++) {
|
||||
if ($focusElement === $modalButtons[i]) {
|
||||
btnIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (btnIndex === -1) {
|
||||
// Something in the dom, but not a visible button. Focus back on the button.
|
||||
$targetElement.focus();
|
||||
}
|
||||
} else {
|
||||
// Exiting the DOM (e.g. clicked in the URL bar);
|
||||
lastFocusedButton = $targetElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$okButton.onblur = handleOnBlur;
|
||||
$cancelButton.onblur = handleOnBlur;
|
||||
|
||||
window.onfocus = function() {
|
||||
// When the user has focused away and focused back from the whole window.
|
||||
window.setTimeout(function() {
|
||||
// Put in a timeout to jump out of the event sequence. Calling focus() in the event
|
||||
// sequence confuses things.
|
||||
if (lastFocusedButton !== undefined) {
|
||||
lastFocusedButton.focus();
|
||||
lastFocusedButton = undefined;
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Set default params for each popup
|
||||
* @param {Object} userParams
|
||||
*/
|
||||
window.swal.setDefaults = function(userParams) {
|
||||
if (!userParams) {
|
||||
throw new Error('userParams is required');
|
||||
}
|
||||
if (typeof userParams !== 'object') {
|
||||
throw new Error('userParams has to be a object');
|
||||
}
|
||||
|
||||
extend(defaultParams, userParams);
|
||||
};
|
||||
|
||||
/*
|
||||
* Set type, text and actions on modal
|
||||
*/
|
||||
|
||||
function setParameters(params) {
|
||||
var modal = getModal();
|
||||
|
||||
var $title = modal.querySelector('h4'),
|
||||
$text = modal.querySelector('p'),
|
||||
$cancelBtn = modal.querySelector('button.cancel'),
|
||||
$confirmBtn = modal.querySelector('button.confirm');
|
||||
|
||||
// Title
|
||||
$title.innerHTML = escapeHtml(params.title).split("\n").join("<br>");
|
||||
|
||||
// Text
|
||||
$text.innerHTML = escapeHtml(params.text || '').split("\n").join("<br>");
|
||||
if (params.text) {
|
||||
show($text);
|
||||
}
|
||||
|
||||
// Icon
|
||||
hide(modal.querySelectorAll('.icon'));
|
||||
if (params.type) {
|
||||
var validType = false;
|
||||
for (var i = 0; i < alertTypes.length; i++) {
|
||||
if (params.type === alertTypes[i]) {
|
||||
validType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!validType) {
|
||||
window.console.error('Unknown alert type: ' + params.type);
|
||||
return false;
|
||||
}
|
||||
var $icon = modal.querySelector('.icon.' + params.type);
|
||||
show($icon);
|
||||
|
||||
// Animate icon
|
||||
switch (params.type) {
|
||||
case "success":
|
||||
addClass($icon, 'animate');
|
||||
addClass($icon.querySelector('.tip'), 'animateSuccessTip');
|
||||
addClass($icon.querySelector('.long'), 'animateSuccessLong');
|
||||
break;
|
||||
case "error":
|
||||
addClass($icon, 'animateErrorIcon');
|
||||
addClass($icon.querySelector('.x-mark'), 'animateXMark');
|
||||
break;
|
||||
case "warning":
|
||||
addClass($icon, 'pulseWarning');
|
||||
addClass($icon.querySelector('.body'), 'pulseWarningIns');
|
||||
addClass($icon.querySelector('.dot'), 'pulseWarningIns');
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Custom image
|
||||
if (params.imageUrl) {
|
||||
var $customIcon = modal.querySelector('.icon.custom');
|
||||
|
||||
$customIcon.style.backgroundImage = 'url(' + params.imageUrl + ')';
|
||||
show($customIcon);
|
||||
|
||||
var _imgWidth = 80,
|
||||
_imgHeight = 80;
|
||||
|
||||
if (params.imageSize) {
|
||||
var imgWidth = params.imageSize.split('x')[0];
|
||||
var imgHeight = params.imageSize.split('x')[1];
|
||||
|
||||
if (!imgWidth || !imgHeight) {
|
||||
window.console.error("Parameter imageSize expects value with format WIDTHxHEIGHT, got " + params.imageSize);
|
||||
} else {
|
||||
_imgWidth = imgWidth;
|
||||
_imgHeight = imgHeight;
|
||||
|
||||
$customIcon.css({
|
||||
'width': imgWidth + 'px',
|
||||
'height': imgHeight + 'px'
|
||||
});
|
||||
}
|
||||
}
|
||||
$customIcon.setAttribute('style', $customIcon.getAttribute('style') + 'width:' + _imgWidth + 'px; height:' + _imgHeight + 'px');
|
||||
}
|
||||
|
||||
// Cancel button
|
||||
modal.setAttribute('data-has-cancel-button', params.showCancelButton);
|
||||
if (params.showCancelButton) {
|
||||
$cancelBtn.style.display = 'inline-block';
|
||||
} else {
|
||||
hide($cancelBtn);
|
||||
}
|
||||
|
||||
// Edit text on cancel and confirm buttons
|
||||
if (params.cancelButtonText) {
|
||||
$cancelBtn.innerHTML = escapeHtml(params.cancelButtonText);
|
||||
}
|
||||
if (params.confirmButtonText) {
|
||||
$confirmBtn.innerHTML = escapeHtml(params.confirmButtonText);
|
||||
}
|
||||
|
||||
// Reset confirm buttons to default class (Ugly fix)
|
||||
$confirmBtn.className = 'confirm btn'
|
||||
|
||||
// Set confirm button to selected class
|
||||
addClass($confirmBtn, params.confirmButtonClass);
|
||||
|
||||
// Set cancel button to selected class
|
||||
addClass($cancelBtn, params.cancelButtonClass);
|
||||
|
||||
// Allow outside click?
|
||||
modal.setAttribute('data-allow-ouside-click', params.allowOutsideClick);
|
||||
|
||||
// Done-function
|
||||
var hasDoneFunction = (params.doneFunction) ? true : false;
|
||||
modal.setAttribute('data-has-done-function', hasDoneFunction);
|
||||
|
||||
// Close timer
|
||||
modal.setAttribute('data-timer', params.timer);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set hover, active and focus-states for buttons (source: http://www.sitepoint.com/javascript-generate-lighter-darker-color)
|
||||
*/
|
||||
|
||||
function colorLuminance(hex, lum) {
|
||||
// Validate hex string
|
||||
hex = String(hex).replace(/[^0-9a-f]/gi, '');
|
||||
if (hex.length < 6) {
|
||||
hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
|
||||
}
|
||||
lum = lum || 0;
|
||||
|
||||
// Convert to decimal and change luminosity
|
||||
var rgb = "#", c, i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
c = parseInt(hex.substr(i*2,2), 16);
|
||||
c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
|
||||
rgb += ("00"+c).substr(c.length);
|
||||
}
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
function extend(a, b){
|
||||
for (var key in b) {
|
||||
if (b.hasOwnProperty(key)) {
|
||||
a[key] = b[key];
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
function hexToRgb(hex) {
|
||||
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result ? parseInt(result[1], 16) + ', ' + parseInt(result[2], 16) + ', ' + parseInt(result[3], 16) : null;
|
||||
}
|
||||
|
||||
// Add box-shadow style to button (depending on its chosen bg-color)
|
||||
function setFocusStyle($button, bgColor) {
|
||||
var rgbColor = hexToRgb(bgColor);
|
||||
$button.style.boxShadow = '0 0 2px rgba(' + rgbColor +', 0.8), inset 0 0 0 1px rgba(0, 0, 0, 0.05)';
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Animations
|
||||
*/
|
||||
|
||||
function openModal() {
|
||||
var modal = getModal();
|
||||
fadeIn(getOverlay(), 10);
|
||||
show(modal);
|
||||
addClass(modal, 'showSweetAlert');
|
||||
removeClass(modal, 'hideSweetAlert');
|
||||
|
||||
previousActiveElement = document.activeElement;
|
||||
var $okButton = modal.querySelector('button.confirm');
|
||||
$okButton.focus();
|
||||
|
||||
setTimeout(function() {
|
||||
addClass(modal, 'visible');
|
||||
}, 500);
|
||||
|
||||
var timer = modal.getAttribute('data-timer');
|
||||
if (timer !== "null" && timer !== "") {
|
||||
setTimeout(function() {
|
||||
closeModal();
|
||||
}, timer);
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
var modal = getModal();
|
||||
fadeOut(getOverlay(), 5);
|
||||
fadeOut(modal, 5);
|
||||
removeClass(modal, 'showSweetAlert');
|
||||
addClass(modal, 'hideSweetAlert');
|
||||
removeClass(modal, 'visible');
|
||||
|
||||
|
||||
// Reset icon animations
|
||||
|
||||
var $successIcon = modal.querySelector('.icon.success');
|
||||
removeClass($successIcon, 'animate');
|
||||
removeClass($successIcon.querySelector('.tip'), 'animateSuccessTip');
|
||||
removeClass($successIcon.querySelector('.long'), 'animateSuccessLong');
|
||||
|
||||
var $errorIcon = modal.querySelector('.icon.error');
|
||||
removeClass($errorIcon, 'animateErrorIcon');
|
||||
removeClass($errorIcon.querySelector('.x-mark'), 'animateXMark');
|
||||
|
||||
var $warningIcon = modal.querySelector('.icon.warning');
|
||||
removeClass($warningIcon, 'pulseWarning');
|
||||
removeClass($warningIcon.querySelector('.body'), 'pulseWarningIns');
|
||||
removeClass($warningIcon.querySelector('.dot'), 'pulseWarningIns');
|
||||
|
||||
|
||||
// Reset the page to its previous state
|
||||
window.onkeydown = previousWindowKeyDown;
|
||||
document.onclick = previousDocumentClick;
|
||||
if (previousActiveElement) {
|
||||
previousActiveElement.focus();
|
||||
}
|
||||
lastFocusedButton = undefined;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set "margin-top"-property on modal based on its computed height
|
||||
*/
|
||||
|
||||
function fixVerticalPosition() {
|
||||
var modal = getModal();
|
||||
modal.style.marginTop = getTopMargin(getModal());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If library is injected after page has loaded
|
||||
*/
|
||||
|
||||
(function () {
|
||||
if (document.readyState === "complete" || document.readyState === "interactive" && document.body) {
|
||||
sweetAlertInitialize();
|
||||
} else {
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener('DOMContentLoaded', function factorial() {
|
||||
document.removeEventListener('DOMContentLoaded', arguments.callee, false);
|
||||
sweetAlertInitialize();
|
||||
}, false);
|
||||
} else if (document.attachEvent) {
|
||||
document.attachEvent('onreadystatechange', function() {
|
||||
if (document.readyState === 'complete') {
|
||||
document.detachEvent('onreadystatechange', arguments.callee);
|
||||
sweetAlertInitialize();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
})(window, document);
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
// SweetAlert
|
||||
// 2014 (c) - Tristan Edwards
|
||||
// github.com/t4t5/sweetalert
|
||||
|
||||
@import "sweet-alert-animations";
|
||||
|
||||
.sweet-overlay {
|
||||
background-color: fade(#000, 40%);
|
||||
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
display: none;
|
||||
z-index: @zindex-modal + 7000;
|
||||
}
|
||||
|
||||
.sweet-alert {
|
||||
@width: 478px;
|
||||
@padding: 17px;
|
||||
|
||||
background-color: @body-bg;
|
||||
width: @width;
|
||||
padding: @padding;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -(@width / 2 + @padding);
|
||||
margin-top: -200px;
|
||||
|
||||
overflow: hidden;
|
||||
display: none;
|
||||
z-index: @zindex-modal + 8000;
|
||||
|
||||
h4 {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
@media all and (max-width: @screen-xs-max) {
|
||||
width: auto;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
|
||||
left: (@grid-gutter-width / 2);
|
||||
right: (@grid-gutter-width / 2);
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 4px solid gray;
|
||||
border-radius: 50%;
|
||||
margin: 20px auto;
|
||||
position: relative;
|
||||
box-sizing: content-box;
|
||||
|
||||
&.error {
|
||||
border-color: @btn-danger-border;
|
||||
|
||||
.x-mark {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.line {
|
||||
position: absolute;
|
||||
height: 5px;
|
||||
width: 47px;
|
||||
background-color: @btn-danger-bg;
|
||||
display: block;
|
||||
top: 37px;
|
||||
border-radius: 2px;
|
||||
|
||||
&.left {
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
left: 17px;
|
||||
}
|
||||
&.right {
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.warning {
|
||||
border-color: @btn-warning-border;
|
||||
|
||||
.body { // Exclamation mark body
|
||||
position: absolute;
|
||||
width: 5px;
|
||||
height: 47px;
|
||||
left: 50%;
|
||||
top: 10px;
|
||||
border-radius: 2px;
|
||||
margin-left: -2px;
|
||||
background-color: @btn-warning-bg;
|
||||
}
|
||||
.dot { // Exclamation mark dot
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
margin-left: -3px;
|
||||
left: 50%;
|
||||
bottom: 10px;
|
||||
background-color: @btn-warning-bg;
|
||||
}
|
||||
}
|
||||
&.info {
|
||||
border-color: @btn-info-border;
|
||||
|
||||
&::before { // i-letter body
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 5px;
|
||||
height: 29px;
|
||||
left: 50%;
|
||||
bottom: 17px;
|
||||
border-radius: 2px;
|
||||
margin-left: -2px;
|
||||
background-color: @btn-info-bg;
|
||||
}
|
||||
&::after { // i-letter dot
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
margin-left: -3px;
|
||||
top: 19px;
|
||||
background-color: @btn-info-bg;
|
||||
}
|
||||
}
|
||||
&.success {
|
||||
border-color: @btn-success-border;
|
||||
|
||||
&::before, &::after { // Emulate moving circular line
|
||||
content: '';
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
height: 120px;
|
||||
background: white;
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
&::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;
|
||||
}
|
||||
&::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;
|
||||
}
|
||||
|
||||
.placeholder { // Ring
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 4px solid fade(@brand-success, 20%);
|
||||
border-radius: 50%;
|
||||
box-sizing: content-box;
|
||||
|
||||
position: absolute;
|
||||
left: -4px;
|
||||
top: -4px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.fix { // Hide corners left from animation
|
||||
width: 5px;
|
||||
height: 90px;
|
||||
background-color: @body-bg;
|
||||
|
||||
position: absolute;
|
||||
left: 28px;
|
||||
top: 8px;
|
||||
z-index: 1;
|
||||
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.line {
|
||||
height: 5px;
|
||||
background-color: @btn-success-bg;
|
||||
display: block;
|
||||
border-radius: 2px;
|
||||
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
|
||||
&.tip {
|
||||
width: 25px;
|
||||
|
||||
left: 14px;
|
||||
top: 46px;
|
||||
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
&.long {
|
||||
width: 47px;
|
||||
|
||||
right: 8px;
|
||||
top: 38px;
|
||||
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.custom {
|
||||
background-size: contain;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
.form-control-focus(@btn-default-border);
|
||||
}
|
||||
.btn-success {
|
||||
.form-control-focus(@btn-success-border);
|
||||
}
|
||||
.btn-info {
|
||||
.form-control-focus(@btn-info-border);
|
||||
}
|
||||
.btn-danger {
|
||||
.form-control-focus(@btn-danger-border);
|
||||
}
|
||||
.btn-warning {
|
||||
.form-control-focus(@btn-warning-border);
|
||||
}
|
||||
|
||||
button::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ use Event;
|
|||
use Input;
|
||||
use Redirect;
|
||||
use Backend;
|
||||
use Backend\Classes\FormField;
|
||||
use Backend\Classes\ControllerBehavior;
|
||||
use October\Rain\Support\Util;
|
||||
use October\Rain\Router\Helper as RouterHelper;
|
||||
|
|
@ -22,6 +23,21 @@ use Exception;
|
|||
*/
|
||||
class FormController extends ControllerBehavior
|
||||
{
|
||||
/**
|
||||
* @var string Default context for "create" pages.
|
||||
*/
|
||||
const CONTEXT_CREATE = 'create';
|
||||
|
||||
/**
|
||||
* @var string Default context for "update" pages.
|
||||
*/
|
||||
const CONTEXT_UPDATE = 'update';
|
||||
|
||||
/**
|
||||
* @var string Default context for "preview" pages.
|
||||
*/
|
||||
const CONTEXT_PREVIEW = 'preview';
|
||||
|
||||
/**
|
||||
* @var Backend\Classes\WidgetBase Reference to the widget object.
|
||||
*/
|
||||
|
|
@ -49,6 +65,11 @@ class FormController extends ControllerBehavior
|
|||
*/
|
||||
protected $modelsToSave = [];
|
||||
|
||||
/**
|
||||
* @var Model The initialized model used by the form.
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* Behavior constructor
|
||||
* @param Backend\Classes\Controller $controller
|
||||
|
|
@ -74,7 +95,22 @@ class FormController extends ControllerBehavior
|
|||
{
|
||||
$context = $this->formGetContext();
|
||||
|
||||
$config = $this->makeConfig($this->config->form);
|
||||
/*
|
||||
* Each page can supply a unique form definition, if desired
|
||||
*/
|
||||
$formFields = $this->config->form;
|
||||
|
||||
if ($context == self::CONTEXT_CREATE) {
|
||||
$formFields = $this->getConfig('create[form]', $formFields);
|
||||
}
|
||||
elseif ($context == self::CONTEXT_UPDATE) {
|
||||
$formFields = $this->getConfig('update[form]', $formFields);
|
||||
}
|
||||
elseif ($context == self::CONTEXT_PREVIEW) {
|
||||
$formFields = $this->getConfig('preview[form]', $formFields);
|
||||
}
|
||||
|
||||
$config = $this->makeConfig($formFields);
|
||||
$config->model = $model;
|
||||
$config->arrayName = class_basename($model);
|
||||
$config->context = $context;
|
||||
|
|
@ -110,6 +146,7 @@ class FormController extends ControllerBehavior
|
|||
}
|
||||
|
||||
$this->prepareVars($model);
|
||||
$this->model = $model;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -133,14 +170,14 @@ class FormController extends ControllerBehavior
|
|||
public function create($context = null)
|
||||
{
|
||||
try {
|
||||
$this->context = strlen($context) ? $context : $this->getConfig('create[context]', 'create');
|
||||
$this->context = strlen($context) ? $context : $this->getConfig('create[context]', self::CONTEXT_CREATE);
|
||||
$this->controller->pageTitle = $this->controller->pageTitle ?: $this->getLang(
|
||||
'create[title]',
|
||||
'backend::lang.form.create_title'
|
||||
);
|
||||
|
||||
$model = $this->controller->formCreateModelObject();
|
||||
$this->initForm($model);
|
||||
$this->controller->vars['formModel'] = $model;
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->controller->handleError($ex);
|
||||
|
|
@ -153,7 +190,7 @@ class FormController extends ControllerBehavior
|
|||
*/
|
||||
public function create_onSave($context = null)
|
||||
{
|
||||
$this->context = strlen($context) ? $context : $this->getConfig('create[context]', 'create');
|
||||
$this->context = strlen($context) ? $context : $this->getConfig('create[context]', self::CONTEXT_CREATE);
|
||||
$model = $this->controller->formCreateModelObject();
|
||||
$this->initForm($model);
|
||||
|
||||
|
|
@ -188,15 +225,14 @@ class FormController extends ControllerBehavior
|
|||
public function update($recordId = null, $context = null)
|
||||
{
|
||||
try {
|
||||
$this->context = strlen($context) ? $context : $this->getConfig('update[context]', 'update');
|
||||
$this->context = strlen($context) ? $context : $this->getConfig('update[context]', self::CONTEXT_UPDATE);
|
||||
$this->controller->pageTitle = $this->controller->pageTitle ?: $this->getLang(
|
||||
'update[title]',
|
||||
'backend::lang.form.update_title'
|
||||
);
|
||||
|
||||
$model = $this->controller->formFindModelObject($recordId);
|
||||
$this->initForm($model);
|
||||
|
||||
$this->controller->vars['formModel'] = $model;
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->controller->handleError($ex);
|
||||
|
|
@ -210,14 +246,14 @@ class FormController extends ControllerBehavior
|
|||
*/
|
||||
public function update_onSave($recordId = null, $context = null)
|
||||
{
|
||||
$this->context = strlen($context) ? $context : $this->getConfig('update[context]', 'update');
|
||||
$this->context = strlen($context) ? $context : $this->getConfig('update[context]', self::CONTEXT_UPDATE);
|
||||
$model = $this->controller->formFindModelObject($recordId);
|
||||
$this->initForm($model);
|
||||
|
||||
$this->controller->formBeforeSave($model);
|
||||
$this->controller->formBeforeUpdate($model);
|
||||
|
||||
$modelsToSave =$this->prepareModelsToSave($model, $this->formWidget->getSaveData());
|
||||
$modelsToSave = $this->prepareModelsToSave($model, $this->formWidget->getSaveData());
|
||||
foreach ($modelsToSave as $modelToSave) {
|
||||
$modelToSave->save(null, $this->formWidget->getSessionKey());
|
||||
}
|
||||
|
|
@ -239,7 +275,7 @@ class FormController extends ControllerBehavior
|
|||
*/
|
||||
public function update_onDelete($recordId = null)
|
||||
{
|
||||
$this->context = $this->getConfig('update[context]', 'update');
|
||||
$this->context = $this->getConfig('update[context]', self::CONTEXT_UPDATE);
|
||||
$model = $this->controller->formFindModelObject($recordId);
|
||||
$this->initForm($model);
|
||||
|
||||
|
|
@ -267,15 +303,14 @@ class FormController extends ControllerBehavior
|
|||
public function preview($recordId = null, $context = null)
|
||||
{
|
||||
try {
|
||||
$this->context = strlen($context) ? $context : $this->getConfig('preview[context]', 'preview');
|
||||
$this->context = strlen($context) ? $context : $this->getConfig('preview[context]', self::CONTEXT_PREVIEW);
|
||||
$this->controller->pageTitle = $this->controller->pageTitle ?: $this->getLang(
|
||||
'preview[title]',
|
||||
'backend::lang.form.preview_title'
|
||||
);
|
||||
|
||||
$model = $this->controller->formFindModelObject($recordId);
|
||||
$this->initForm($model);
|
||||
|
||||
$this->controller->vars['formModel'] = $model;
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->controller->handleError($ex);
|
||||
|
|
@ -300,6 +335,15 @@ class FormController extends ControllerBehavior
|
|||
return $this->formWidget->render($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the model initialized by this form behavior.
|
||||
* @return Model
|
||||
*/
|
||||
public function formGetModel()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the form context from the postback or configuration.
|
||||
* @return string
|
||||
|
|
@ -674,7 +718,7 @@ class FormController extends ControllerBehavior
|
|||
) {
|
||||
$this->setModelAttributes($model->{$attribute}, $value);
|
||||
}
|
||||
else {
|
||||
elseif ($value !== FormField::NO_SAVE_DATA) {
|
||||
$model->{$attribute} = $value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -450,6 +450,21 @@ class RelationController extends ControllerBehavior
|
|||
|
||||
return $results->lists($foreignKeyName);
|
||||
}
|
||||
|
||||
//
|
||||
// Overrides
|
||||
//
|
||||
|
||||
/**
|
||||
* Controller override: Extend the query used for populating the list
|
||||
* after the default query is processed.
|
||||
* @param October\Rain\Database\Builder $query
|
||||
* @param string $field
|
||||
* @param string $manageMode
|
||||
*/
|
||||
public function relationExtendQuery($query, $field, $manageMode)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// AJAX
|
||||
|
|
@ -505,8 +520,9 @@ class RelationController extends ControllerBehavior
|
|||
$this->beforeAjax();
|
||||
|
||||
if (($checkedIds = post('checked')) && is_array($checkedIds)) {
|
||||
foreach ($checkedIds as $relationId) {
|
||||
if (!$obj = $this->relationObject->find($relationId)) {
|
||||
$relatedModel = $this->relationObject->getRelated();
|
||||
foreach ($checkedIds as $relationId) {
|
||||
if (!$obj = $relatedModel->find($relationId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -588,7 +604,7 @@ class RelationController extends ControllerBehavior
|
|||
/*
|
||||
* Check for existing relation
|
||||
*/
|
||||
$foreignKeyName = $this->relationModel->getKeyName();
|
||||
$foreignKeyName = $this->relationModel->getQualifiedKeyName();
|
||||
$existing = $this->relationObject->where($foreignKeyName, $foreignId)->count();
|
||||
|
||||
if (!$existing) {
|
||||
|
|
@ -625,25 +641,42 @@ class RelationController extends ControllerBehavior
|
|||
|
||||
protected function makeToolbarWidget()
|
||||
{
|
||||
if ($this->readOnly) {
|
||||
return;
|
||||
$defaultConfig = [];
|
||||
|
||||
/*
|
||||
* Add buttons to toolbar
|
||||
*/
|
||||
$defaultButtons = null;
|
||||
|
||||
if (!$this->readOnly) {
|
||||
$defaultButtons = '~/modules/backend/behaviors/relationcontroller/partials/_toolbar.htm';
|
||||
}
|
||||
|
||||
$defaultConfig = [
|
||||
'buttons' => '@/modules/backend/behaviors/relationcontroller/partials/_toolbar.htm',
|
||||
];
|
||||
$defaultConfig['buttons'] = $this->getConfig('view[toolbarButtons]', $defaultButtons);
|
||||
|
||||
/*
|
||||
* Make config
|
||||
*/
|
||||
$toolbarConfig = $this->makeConfig($this->getConfig('toolbar', $defaultConfig));
|
||||
$toolbarConfig->alias = $this->alias . 'Toolbar';
|
||||
|
||||
/*
|
||||
* Add search to toolbar
|
||||
*/
|
||||
if ($this->viewMode == 'multi' && $this->getConfig('view[showSearch]')) {
|
||||
$useSearch = $this->viewMode == 'multi' && $this->getConfig('view[showSearch]');
|
||||
|
||||
if ($useSearch) {
|
||||
$toolbarConfig->search = [
|
||||
'prompt' => 'backend::lang.list.search_prompt'
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
* No buttons, no search should mean no toolbar
|
||||
*/
|
||||
if (empty($toolbarConfig->search) && empty($toolbarConfig->buttons))
|
||||
return;
|
||||
|
||||
$toolbarWidget = $this->makeWidget('Backend\Widgets\Toolbar', $toolbarConfig);
|
||||
$toolbarWidget->cssClasses[] = 'list-header';
|
||||
|
||||
|
|
@ -662,15 +695,16 @@ class RelationController extends ControllerBehavior
|
|||
$config->showSorting = $this->getConfig('view[showSorting]', true);
|
||||
$config->defaultSort = $this->getConfig('view[defaultSort]');
|
||||
$config->recordsPerPage = $this->getConfig('view[recordsPerPage]');
|
||||
$config->showCheckboxes = $this->getConfig('view[showCheckboxes]', !$this->readOnly);
|
||||
|
||||
if (!$this->readOnly) {
|
||||
$config->recordOnClick = sprintf(
|
||||
"$.oc.relationBehavior.clickManageListRecord(:id, '%s', '%s')",
|
||||
$this->field,
|
||||
$this->relationGetSessionKey()
|
||||
);
|
||||
$config->showCheckboxes = true;
|
||||
}
|
||||
$defaultOnClick = sprintf(
|
||||
"$.oc.relationBehavior.clickManageListRecord(:id, '%s', '%s')",
|
||||
$this->field,
|
||||
$this->relationGetSessionKey()
|
||||
);
|
||||
|
||||
$config->recordOnClick = $this->getConfig('view[recordOnClick]', $defaultOnClick);
|
||||
$config->recordUrl = $this->getConfig('view[recordUrl]', null);
|
||||
|
||||
if ($emptyMessage = $this->getConfig('emptyMessage')) {
|
||||
$config->noRecordsMessage = $emptyMessage;
|
||||
|
|
@ -681,6 +715,8 @@ class RelationController extends ControllerBehavior
|
|||
*/
|
||||
$widget = $this->makeWidget('Backend\Widgets\Lists', $config);
|
||||
$widget->bindEvent('list.extendQuery', function ($query) {
|
||||
$this->controller->relationExtendQuery($query, $this->field, $this->manageMode);
|
||||
|
||||
$this->relationObject->setQuery($query);
|
||||
if ($this->model->exists) {
|
||||
$this->relationObject->addConstraints();
|
||||
|
|
@ -800,6 +836,7 @@ class RelationController extends ControllerBehavior
|
|||
*/
|
||||
if ($this->manageMode == 'pivot' || $this->manageMode == 'list') {
|
||||
$widget->bindEvent('list.extendQuery', function ($query) {
|
||||
$this->controller->relationExtendQuery($query, $this->field, $this->manageMode);
|
||||
|
||||
/*
|
||||
* Where not in the current list of related records
|
||||
|
|
|
|||
|
|
@ -10,27 +10,41 @@
|
|||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title">
|
||||
<?= e(trans('backend::lang.relation.create_name', ['name'=>$relationLabel])) ?>
|
||||
<?php if ($this->readOnly): ?>
|
||||
<?= e(trans('backend::lang.relation.preview_name', ['name'=>$relationLabel])) ?>
|
||||
<?php else: ?>
|
||||
<?= e(trans('backend::lang.relation.update_name', ['name'=>$relationLabel])) ?>
|
||||
<?php endif ?>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<?= $relationManageWidget->render() ?>
|
||||
|
||||
<?= $relationManageWidget->render(['preview' => $this->readOnly]) ?>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.relation.update')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.relation.cancel')) ?>
|
||||
</button>
|
||||
<?php if ($this->readOnly): ?>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.relation.close')) ?>
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.relation.update')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.relation.cancel')) ?>
|
||||
</button>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
||||
<?= Form::close() ?>
|
||||
|
||||
<?php else: ?>
|
||||
|
|
@ -67,4 +81,4 @@
|
|||
</div>
|
||||
<?= Form::close() ?>
|
||||
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
|
|
|
|||
|
|
@ -418,14 +418,8 @@ class Controller extends Extendable
|
|||
500
|
||||
);
|
||||
}
|
||||
catch (ApplicationException $ex) {
|
||||
return Response::make($ex->getMessage(), 500);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
return Response::make(
|
||||
sprintf('"%s" on line %s of %s', $ex->getMessage(), $ex->getLine(), $ex->getFile()),
|
||||
500
|
||||
);
|
||||
return Response::make(ApplicationException::getDetailedMessage($ex));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -527,8 +521,9 @@ class Controller extends Extendable
|
|||
*/
|
||||
public function handleError($exception)
|
||||
{
|
||||
$this->fatalError = $exception->getMessage();
|
||||
$this->vars['fatalError'] = $exception->getMessage();
|
||||
$errorMessage = ApplicationException::getDetailedMessage($exception);
|
||||
$this->fatalError = $errorMessage;
|
||||
$this->vars['fatalError'] = $errorMessage;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@ use HTML;
|
|||
*/
|
||||
class FormField
|
||||
{
|
||||
/**
|
||||
* @var int Value returned when the form field should not contribute any save data.
|
||||
*/
|
||||
const NO_SAVE_DATA = -1;
|
||||
|
||||
/**
|
||||
* @var string Form field name.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -42,11 +42,6 @@ abstract class FormWidgetBase extends WidgetBase
|
|||
*/
|
||||
public $previewMode = false;
|
||||
|
||||
/**
|
||||
* @var int Value returned when the widget should not contribute any save data.
|
||||
*/
|
||||
const NO_SAVE_DATA = -1;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param $controller Controller Active controller object.
|
||||
|
|
@ -110,7 +105,12 @@ abstract class FormWidgetBase extends WidgetBase
|
|||
public function getLoadData()
|
||||
{
|
||||
list($model, $attribute) = $this->getModelArrayAttribute($this->valueFrom);
|
||||
return $model->{$attribute};
|
||||
|
||||
if (!is_null($model)) {
|
||||
return $model->{$attribute};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use Backend\Classes\Controller;
|
|||
* @author Alexey Bobkov, Samuel Georges
|
||||
*
|
||||
*/
|
||||
class Groups extends Controller
|
||||
class UserGroups extends Controller
|
||||
{
|
||||
public $implement = [
|
||||
'Backend.Behaviors.FormController',
|
||||
|
|
@ -4,6 +4,7 @@ use Backend;
|
|||
use Redirect;
|
||||
use BackendMenu;
|
||||
use BackendAuth;
|
||||
use Backend\Models\UserGroup;
|
||||
use Backend\Classes\Controller;
|
||||
use System\Classes\SettingsManager;
|
||||
|
||||
|
|
@ -85,6 +86,7 @@ class Users extends Controller
|
|||
|
||||
/**
|
||||
* Add available permission fields to the User form.
|
||||
* Mark default groups as checked for new Users.
|
||||
*/
|
||||
protected function formExtendFields($form)
|
||||
{
|
||||
|
|
@ -121,5 +123,15 @@ class Users extends Controller
|
|||
}
|
||||
|
||||
$form->addTabFields($permissionFields);
|
||||
|
||||
/*
|
||||
* Mark default groups
|
||||
*/
|
||||
if (!$form->model->exists) {
|
||||
$defaultGroupIds = UserGroup::where('is_new_user_default', true)->lists('id');
|
||||
|
||||
$groupField = $form->getField('groups');
|
||||
$groupField->value = $defaultGroupIds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
<div data-control="toolbar">
|
||||
<a href="<?= Backend::url('backend/groups/create') ?>" class="btn btn-primary oc-icon-plus"><?= e(trans('backend::lang.user.group.new')) ?></a>
|
||||
</div>
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# ===================================
|
||||
# Form Behavior Config
|
||||
# ===================================
|
||||
|
||||
name: backend::lang.user.group.name
|
||||
form: @/modules/backend/models/group/fields.yaml
|
||||
modelClass: Backend\Models\UserGroup
|
||||
defaultRedirect: backend/groups
|
||||
|
||||
create:
|
||||
redirect: backend/groups/update/:id
|
||||
redirectClose: backend/groups
|
||||
|
||||
update:
|
||||
redirect: backend/groups
|
||||
redirectClose: backend/groups
|
||||
|
|
@ -21,6 +21,7 @@ $requiredExtensions = [
|
|||
'fileinfo' => extension_loaded('fileinfo'),
|
||||
'Zip' => class_exists('ZipArchive'),
|
||||
'cURL' => function_exists('curl_init') && defined('CURLOPT_FOLLOWLOCATION'),
|
||||
'OpenSSL' => function_exists('openssl_random_pseudo_bytes'),
|
||||
];
|
||||
|
||||
foreach ($writablePaths as $path) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<div data-control="toolbar">
|
||||
<a href="<?= Backend::url('backend/usergroups/create') ?>" class="btn btn-primary oc-icon-plus"><?= e(trans('backend::lang.user.group.new')) ?></a>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# ===================================
|
||||
# Form Behavior Config
|
||||
# ===================================
|
||||
|
||||
name: backend::lang.user.group.name
|
||||
form: @/modules/backend/models/usergroup/fields.yaml
|
||||
modelClass: Backend\Models\UserGroup
|
||||
defaultRedirect: backend/usergroups
|
||||
|
||||
create:
|
||||
redirect: backend/usergroups/update/:id
|
||||
redirectClose: backend/usergroups
|
||||
|
||||
update:
|
||||
redirect: backend/usergroups
|
||||
redirectClose: backend/usergroups
|
||||
|
|
@ -3,9 +3,9 @@
|
|||
# ===================================
|
||||
|
||||
title: backend::lang.user.group.list_title
|
||||
list: @/modules/backend/models/group/columns.yaml
|
||||
list: @/modules/backend/models/usergroup/columns.yaml
|
||||
modelClass: Backend\Models\UserGroup
|
||||
recordUrl: backend/groups/update/:id
|
||||
recordUrl: backend/usergroups/update/:id
|
||||
noRecordsMessage: backend::lang.list.no_records
|
||||
recordsPerPage: 5
|
||||
showCheckboxes: true
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('backend/users') ?>"><?= e(trans('backend::lang.user.menu_label')) ?></a></li>
|
||||
<li><a href="<?= Backend::url('backend/groups') ?>"><?= e(trans('backend::lang.user.group.menu_label')) ?></a></li>
|
||||
<li><a href="<?= Backend::url('backend/usergroups') ?>"><?= e(trans('backend::lang.user.group.menu_label')) ?></a></li>
|
||||
<li><?= e(trans($this->pageTitle)) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
|
@ -40,5 +40,5 @@
|
|||
|
||||
<?php else: ?>
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
<p><a href="<?= Backend::url('backend/groups') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.group.return')) ?></a></p>
|
||||
<p><a href="<?= Backend::url('backend/usergroups') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.group.return')) ?></a></p>
|
||||
<?php endif ?>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('backend/users') ?>"><?= e(trans('backend::lang.user.menu_label')) ?></a></li>
|
||||
<li><a href="<?= Backend::url('backend/groups') ?>"><?= e(trans('backend::lang.user.group.menu_label')) ?></a></li>
|
||||
<li><a href="<?= Backend::url('backend/usergroups') ?>"><?= e(trans('backend::lang.user.group.menu_label')) ?></a></li>
|
||||
<li><?= e(trans($this->pageTitle)) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
|
@ -49,5 +49,5 @@
|
|||
|
||||
<?php else: ?>
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
<p><a href="<?= Backend::url('backend/groups') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.group.return')) ?></a></p>
|
||||
<p><a href="<?= Backend::url('backend/usergroups') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.group.return')) ?></a></p>
|
||||
<?php endif ?>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<div data-control="toolbar">
|
||||
<a href="<?= Backend::url('backend/users/create') ?>" class="btn btn-primary oc-icon-plus"><?= e(trans('backend::lang.user.new')) ?></a>
|
||||
<a href="<?= Backend::url('backend/groups') ?>" class="btn btn-default oc-icon-group"><?= e(trans('backend::lang.user.group.list_title')) ?></a>
|
||||
<a href="<?= Backend::url('backend/usergroups') ?>" class="btn btn-default oc-icon-group"><?= e(trans('backend::lang.user.group.list_title')) ?></a>
|
||||
<?php /* @todo
|
||||
<div class="btn-group">
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class DbBackendAddDescriptionField extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::table('backend_user_groups', function (Blueprint $table) {
|
||||
$table->string('code')->nullable()->index();
|
||||
$table->text('description')->nullable();
|
||||
$table->boolean('is_new_user_default')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::table('backend_user_groups', function (Blueprint $table) {
|
||||
$table->dropColumn('code');
|
||||
$table->dropColumn('description');
|
||||
$table->dropColumn('is_new_user_default');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,10 @@ class SeedSetupAdmin extends Seeder
|
|||
public function run()
|
||||
{
|
||||
$group = UserGroup::create([
|
||||
'name' => 'Admins'
|
||||
'name' => 'Admins',
|
||||
'code' => 'admins',
|
||||
'description' => 'Default group for administrators',
|
||||
'is_new_user_default' => true
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
|
|
@ -42,4 +45,5 @@ class SeedSetupAdmin extends Seeder
|
|||
|
||||
$user->addGroup($group);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ use Backend\Classes\FormWidgetBase;
|
|||
*/
|
||||
class DatePicker extends FormWidgetBase
|
||||
{
|
||||
const TIME_PREFIX = '___time_';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
@ -36,7 +38,7 @@ class DatePicker extends FormWidgetBase
|
|||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->mode = $this->getConfig('mode', $this->mode);
|
||||
$this->mode = strtolower($this->getConfig('mode', $this->mode));
|
||||
$this->minDate = $this->getConfig('minDate', $this->minDate);
|
||||
$this->maxDate = $this->getConfig('maxDate', $this->maxDate);
|
||||
}
|
||||
|
|
@ -57,19 +59,44 @@ class DatePicker extends FormWidgetBase
|
|||
{
|
||||
$this->vars['name'] = $this->formField->getName();
|
||||
|
||||
$value = $this->getLoadData();
|
||||
$this->vars['timeName'] = self::TIME_PREFIX.$this->formField->getName(false);
|
||||
$this->vars['timeValue'] = null;
|
||||
|
||||
if ($this->mode != 'datetime' && $value) {
|
||||
if (is_string($value)) {
|
||||
$value = substr($value, 0, 10);
|
||||
if ($value = $this->getLoadData()) {
|
||||
|
||||
/*
|
||||
* Date / Time
|
||||
*/
|
||||
if ($this->mode == 'datetime') {
|
||||
if (is_object($value)) {
|
||||
$value = $value->toDateTimeString();
|
||||
}
|
||||
|
||||
$dateTime = explode(' ', $value);
|
||||
$value = $dateTime[0];
|
||||
$this->vars['timeValue'] = isset($dateTime[1]) ? substr($dateTime[1], 0, 5) : '';
|
||||
}
|
||||
elseif (is_object($value)) {
|
||||
$value = $value->toDateString();
|
||||
/*
|
||||
* Date
|
||||
*/
|
||||
elseif ($this->mode == 'date') {
|
||||
if (is_string($value)) {
|
||||
$value = substr($value, 0, 10);
|
||||
}
|
||||
elseif (is_object($value)) {
|
||||
$value = $value->toDateString();
|
||||
}
|
||||
}
|
||||
elseif ($this->mode == 'time') {
|
||||
if (is_object($value)) {
|
||||
$value = $value->toTimeString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->vars['value'] = $value ?: '';
|
||||
$this->vars['showTime'] = $this->mode == 'datetime' || $this->mode == 'time';
|
||||
$this->vars['mode'] = $this->mode;
|
||||
$this->vars['minDate'] = $this->minDate;
|
||||
$this->vars['maxDate'] = $this->maxDate;
|
||||
}
|
||||
|
|
@ -80,11 +107,14 @@ class DatePicker extends FormWidgetBase
|
|||
public function loadAssets()
|
||||
{
|
||||
$this->addCss('vendor/pikaday/css/pikaday.css', 'core');
|
||||
$this->addCss('vendor/clockpicker/css/jquery-clockpicker.css', 'core');
|
||||
$this->addCss('css/datepicker.css', 'core');
|
||||
$this->addJs('vendor/moment/moment.js', 'core');
|
||||
$this->addJs('vendor/pikaday/js/pikaday.js', 'core');
|
||||
$this->addJs('vendor/pikaday/js/pikaday.jquery.js', 'core');
|
||||
$this->addJs('vendor/clockpicker/js/jquery-clockpicker.js', 'core');
|
||||
$this->addJs('js/datepicker.js', 'core');
|
||||
$this->addJs('js/timepicker.js', 'core');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -92,6 +122,17 @@ class DatePicker extends FormWidgetBase
|
|||
*/
|
||||
public function getSaveData($value)
|
||||
{
|
||||
return strlen($value) ? $value : null;
|
||||
if (!strlen($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->mode == 'datetime') {
|
||||
$value .= ' ' . post(self::TIME_PREFIX.$this->formField->getName(false)) . ':00';
|
||||
}
|
||||
elseif ($this->mode == 'time') {
|
||||
$value .= ':00';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use Input;
|
|||
use Validator;
|
||||
use System\Models\File;
|
||||
use System\Classes\SystemException;
|
||||
use Backend\Classes\FormField;
|
||||
use Backend\Classes\FormWidgetBase;
|
||||
use October\Rain\Support\ValidationException;
|
||||
use Exception;
|
||||
|
|
@ -196,7 +197,7 @@ class FileUpload extends FormWidgetBase
|
|||
*/
|
||||
public function getSaveData($value)
|
||||
{
|
||||
return FormWidgetBase::NO_SAVE_DATA;
|
||||
return FormField::NO_SAVE_DATA;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class Relation extends FormWidgetBase
|
|||
if (!$this->model->hasRelation($this->relationName)) {
|
||||
throw new SystemException(Lang::get(
|
||||
'backend::lang.model.missing_relation',
|
||||
['class'=>get_class($this->controller), 'relation'=>$this->relationName]
|
||||
['class'=>get_class($this->model), 'relation'=>$this->relationName]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
*
|
||||
* Data attributes:
|
||||
* - data-control="datepicker" - enables the plugin on an element
|
||||
* - data-show-time="value" - allow the time to be chosen
|
||||
* - data-min-date="value" - minimum date to allow
|
||||
* - data-max-date="value" - maximum date to allow
|
||||
* - data-year-range="value" - range of years to display
|
||||
|
|
@ -11,7 +10,7 @@
|
|||
* JavaScript API:
|
||||
* $('a#someElement').datePicker({ option: 'value' })
|
||||
*
|
||||
* Dependences:
|
||||
* Dependences:
|
||||
* - Pikaday plugin (pikaday.js)
|
||||
* - Pikaday jQuery addon (pikaday.jquery.js)
|
||||
*/
|
||||
|
|
@ -36,7 +35,6 @@
|
|||
changeMonitor.pause()
|
||||
|
||||
this.$input.pikaday({
|
||||
showTime: options.showTime,
|
||||
minDate: new Date(options.minDate),
|
||||
maxDate: new Date(options.maxDate),
|
||||
yearRange: options.yearRange,
|
||||
|
|
@ -56,7 +54,6 @@
|
|||
}
|
||||
|
||||
DatePicker.DEFAULTS = {
|
||||
showTime: false,
|
||||
minDate: '2000-01-01',
|
||||
maxDate: '2020-12-31',
|
||||
yearRange: 10
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* TimePicker plugin
|
||||
*
|
||||
* Data attributes:
|
||||
* - data-control="timepicker" - enables the plugin on an element
|
||||
*
|
||||
* JavaScript API:
|
||||
* $('a#someElement').timePicker({ option: 'value' })
|
||||
*
|
||||
* Dependences:
|
||||
* - Clockpicker plugin (jquery-clockpicker.js)
|
||||
*/
|
||||
|
||||
+function ($) { "use strict";
|
||||
|
||||
// DATEPICKER CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var TimePicker = function(element, options) {
|
||||
var self = this
|
||||
this.options = options
|
||||
this.$el = $(element)
|
||||
this.$input = this.$el.find('input:first')
|
||||
|
||||
// Init
|
||||
|
||||
var $form = this.$el.closest('form'),
|
||||
changeMonitor = $form.data('oc.changeMonitor')
|
||||
|
||||
if (changeMonitor !== undefined)
|
||||
changeMonitor.pause()
|
||||
|
||||
this.$input.clockpicker()
|
||||
|
||||
if (changeMonitor !== undefined)
|
||||
changeMonitor.resume()
|
||||
}
|
||||
|
||||
TimePicker.DEFAULTS = {
|
||||
}
|
||||
|
||||
// DATEPICKER PLUGIN DEFINITION
|
||||
// ============================
|
||||
|
||||
var old = $.fn.timePicker
|
||||
|
||||
$.fn.timePicker = function (option) {
|
||||
var args = Array.prototype.slice.call(arguments, 1)
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('oc.timepicker')
|
||||
var options = $.extend({}, TimePicker.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
if (!data) $this.data('oc.timepicker', (data = new TimePicker(this, options)))
|
||||
else if (typeof option == 'string') data[option].apply(data, args)
|
||||
})
|
||||
}
|
||||
|
||||
$.fn.timePicker.Constructor = TimePicker
|
||||
|
||||
// DATEPICKER NO CONFLICT
|
||||
// =================
|
||||
|
||||
$.fn.timePicker.noConflict = function () {
|
||||
$.fn.timePicker = old
|
||||
return this
|
||||
}
|
||||
|
||||
// DATEPICKER DATA-API
|
||||
// ===============
|
||||
|
||||
$(document).on('render', function() {
|
||||
$('[data-control="timepicker"]').timePicker()
|
||||
});
|
||||
|
||||
}(window.jQuery);
|
||||
316
modules/backend/formwidgets/datepicker/assets/vendor/clockpicker/css/jquery-clockpicker.css
vendored
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
/*!
|
||||
* ClockPicker v0.0.7 for jQuery (http://weareoutman.github.io/clockpicker/)
|
||||
* Copyright 2014 Wang Shenwei.
|
||||
* Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
|
||||
*
|
||||
* Bootstrap v3.1.1 (http://getbootstrap.com)
|
||||
* Copyright 2011-2014 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
/* Picked from bootstrap: .popover, .btn, .text-primary */
|
||||
|
||||
.clockpicker-popover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1010;
|
||||
display: none;
|
||||
max-width: 276px;
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
white-space: normal;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid rgba(0, 0, 0, .2);
|
||||
border-radius: 6px;
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
|
||||
}
|
||||
.clockpicker-popover.top {
|
||||
margin-top: -10px;
|
||||
}
|
||||
.clockpicker-popover.right {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.clockpicker-popover.bottom {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.clockpicker-popover.left {
|
||||
margin-left: -10px;
|
||||
}
|
||||
.clockpicker-popover .popover-title {
|
||||
padding: 8px 14px;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 18px;
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
.clockpicker-popover .popover-content {
|
||||
padding: 9px 14px;
|
||||
}
|
||||
.clockpicker-popover > .arrow,
|
||||
.clockpicker-popover > .arrow:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
/* The following are set in WordPress (wp-admin/css/revisions.css) - reset them to initial values */
|
||||
overflow:visible;
|
||||
margin:0;
|
||||
padding:0;
|
||||
z-index:auto;
|
||||
background-color:transparent;
|
||||
-webkit-box-shadow:none;
|
||||
box-shadow:none;
|
||||
bottom:auto;
|
||||
left:auto;
|
||||
right:auto;
|
||||
top:auto;
|
||||
-webkit-transform:none;
|
||||
-ms-transform:none;
|
||||
transform:none;
|
||||
}
|
||||
.clockpicker-popover > .arrow {
|
||||
border-width: 11px;
|
||||
}
|
||||
.clockpicker-popover > .arrow:after {
|
||||
content: "";
|
||||
border-width: 10px;
|
||||
}
|
||||
.clockpicker-popover.top > .arrow {
|
||||
bottom: -11px;
|
||||
left: 50%;
|
||||
margin-left: -11px;
|
||||
border-top-color: #999;
|
||||
border-top-color: rgba(0, 0, 0, .25);
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.clockpicker-popover.top > .arrow:after {
|
||||
bottom: 1px;
|
||||
margin-left: -10px;
|
||||
content: " ";
|
||||
border-top-color: #fff;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.clockpicker-popover.right > .arrow {
|
||||
top: 50%;
|
||||
left: -11px;
|
||||
margin-top: -11px;
|
||||
border-right-color: #999;
|
||||
border-right-color: rgba(0, 0, 0, .25);
|
||||
border-left-width: 0;
|
||||
}
|
||||
.clockpicker-popover.right > .arrow:after {
|
||||
bottom: -10px;
|
||||
left: 1px;
|
||||
content: " ";
|
||||
border-right-color: #fff;
|
||||
border-left-width: 0;
|
||||
}
|
||||
.clockpicker-popover.bottom > .arrow {
|
||||
top: -11px;
|
||||
left: 50%;
|
||||
margin-left: -11px;
|
||||
border-top-width: 0;
|
||||
border-bottom-color: #999;
|
||||
border-bottom-color: rgba(0, 0, 0, .25);
|
||||
}
|
||||
.clockpicker-popover.bottom > .arrow:after {
|
||||
top: 1px;
|
||||
margin-left: -10px;
|
||||
content: " ";
|
||||
border-top-width: 0;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.clockpicker-popover.left > .arrow {
|
||||
top: 50%;
|
||||
right: -11px;
|
||||
margin-top: -11px;
|
||||
border-right-width: 0;
|
||||
border-left-color: #999;
|
||||
border-left-color: rgba(0, 0, 0, .25);
|
||||
}
|
||||
.clockpicker-popover.left > .arrow:after {
|
||||
right: 1px;
|
||||
bottom: -10px;
|
||||
content: " ";
|
||||
border-right-width: 0;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
|
||||
/*!
|
||||
* ClockPicker v{package.version} for Bootstrap (http://weareoutman.github.io/clockpicker/)
|
||||
* Copyright 2014 Wang Shenwei.
|
||||
* Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
|
||||
*/
|
||||
|
||||
.clockpicker .input-group-addon {
|
||||
cursor: pointer;
|
||||
}
|
||||
.clockpicker-moving {
|
||||
cursor: move;
|
||||
}
|
||||
.clockpicker-align-left.popover > .arrow {
|
||||
left: 25px;
|
||||
}
|
||||
.clockpicker-align-top.popover > .arrow {
|
||||
top: 17px;
|
||||
}
|
||||
.clockpicker-align-right.popover > .arrow {
|
||||
left: auto;
|
||||
right: 25px;
|
||||
}
|
||||
.clockpicker-align-bottom.popover > .arrow {
|
||||
top: auto;
|
||||
bottom: 6px;
|
||||
}
|
||||
.clockpicker-popover .popover-title {
|
||||
background-color: #fff;
|
||||
color: #999;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
.clockpicker-popover .popover-title span {
|
||||
cursor: pointer;
|
||||
}
|
||||
.clockpicker-popover .popover-content {
|
||||
background-color: #f8f8f8;
|
||||
padding: 12px;
|
||||
}
|
||||
.popover-content:last-child {
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
.clockpicker-plate {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 50%;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
/* Disable text selection highlighting. Thanks to Hermanya */
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.clockpicker-canvas,
|
||||
.clockpicker-dial {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
.clockpicker-minutes {
|
||||
visibility: hidden;
|
||||
}
|
||||
.clockpicker-tick {
|
||||
border-radius: 50%;
|
||||
color: #666;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
.clockpicker-tick.active,
|
||||
.clockpicker-tick:hover {
|
||||
background-color: rgb(192, 229, 247);
|
||||
background-color: rgba(0, 149, 221, .25);
|
||||
}
|
||||
.clockpicker-button {
|
||||
background-image: none;
|
||||
background-color: #fff;
|
||||
border-width: 1px 0 0;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.clockpicker-button:hover {
|
||||
background-image: none;
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
.clockpicker-button:focus {
|
||||
outline: none!important;
|
||||
}
|
||||
.clockpicker-dial {
|
||||
-webkit-transition: -webkit-transform 350ms, opacity 350ms;
|
||||
-moz-transition: -moz-transform 350ms, opacity 350ms;
|
||||
-ms-transition: -ms-transform 350ms, opacity 350ms;
|
||||
-o-transition: -o-transform 350ms, opacity 350ms;
|
||||
transition: transform 350ms, opacity 350ms;
|
||||
}
|
||||
.clockpicker-dial-out {
|
||||
opacity: 0;
|
||||
}
|
||||
.clockpicker-hours.clockpicker-dial-out {
|
||||
-webkit-transform: scale(1.2, 1.2);
|
||||
-moz-transform: scale(1.2, 1.2);
|
||||
-ms-transform: scale(1.2, 1.2);
|
||||
-o-transform: scale(1.2, 1.2);
|
||||
transform: scale(1.2, 1.2);
|
||||
}
|
||||
.clockpicker-minutes.clockpicker-dial-out {
|
||||
-webkit-transform: scale(.8, .8);
|
||||
-moz-transform: scale(.8, .8);
|
||||
-ms-transform: scale(.8, .8);
|
||||
-o-transform: scale(.8, .8);
|
||||
transform: scale(.8, .8);
|
||||
}
|
||||
.clockpicker-canvas {
|
||||
-webkit-transition: opacity 175ms;
|
||||
-moz-transition: opacity 175ms;
|
||||
-ms-transition: opacity 175ms;
|
||||
-o-transition: opacity 175ms;
|
||||
transition: opacity 175ms;
|
||||
}
|
||||
.clockpicker-canvas-out {
|
||||
opacity: 0.25;
|
||||
}
|
||||
.clockpicker-canvas-bearing,
|
||||
.clockpicker-canvas-fg {
|
||||
stroke: none;
|
||||
fill: rgb(0, 149, 221);
|
||||
}
|
||||
.clockpicker-canvas-bg {
|
||||
stroke: none;
|
||||
fill: rgb(192, 229, 247);
|
||||
}
|
||||
.clockpicker-canvas-bg-trans {
|
||||
fill: rgba(0, 149, 221, .25);
|
||||
}
|
||||
.clockpicker-canvas line {
|
||||
stroke: rgb(0, 149, 221);
|
||||
stroke-width: 1;
|
||||
stroke-linecap: round;
|
||||
/*shape-rendering: crispEdges;*/
|
||||
}
|
||||
.clockpicker-button.am-button {
|
||||
margin: 1px;
|
||||
padding: 5px;
|
||||
border: 1px solid rgba(0, 0, 0, .2);
|
||||
border-radius: 4px;
|
||||
|
||||
}
|
||||
.clockpicker-button.pm-button {
|
||||
margin: 1px 1px 1px 136px;
|
||||
padding: 5px;
|
||||
border: 1px solid rgba(0, 0, 0, .2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
729
modules/backend/formwidgets/datepicker/assets/vendor/clockpicker/js/jquery-clockpicker.js
vendored
Normal file
|
|
@ -0,0 +1,729 @@
|
|||
/*!
|
||||
* ClockPicker v0.0.7 (http://weareoutman.github.io/clockpicker/)
|
||||
* Copyright 2014 Wang Shenwei.
|
||||
* Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
|
||||
*/
|
||||
|
||||
;(function(){
|
||||
var $ = window.jQuery,
|
||||
$win = $(window),
|
||||
$doc = $(document),
|
||||
$body;
|
||||
|
||||
// Can I use inline svg ?
|
||||
var svgNS = 'http://www.w3.org/2000/svg',
|
||||
svgSupported = 'SVGAngle' in window && (function(){
|
||||
var supported,
|
||||
el = document.createElement('div');
|
||||
el.innerHTML = '<svg/>';
|
||||
supported = (el.firstChild && el.firstChild.namespaceURI) == svgNS;
|
||||
el.innerHTML = '';
|
||||
return supported;
|
||||
})();
|
||||
|
||||
// Can I use transition ?
|
||||
var transitionSupported = (function(){
|
||||
var style = document.createElement('div').style;
|
||||
return 'transition' in style ||
|
||||
'WebkitTransition' in style ||
|
||||
'MozTransition' in style ||
|
||||
'msTransition' in style ||
|
||||
'OTransition' in style;
|
||||
})();
|
||||
|
||||
// Listen touch events in touch screen device, instead of mouse events in desktop.
|
||||
var touchSupported = 'ontouchstart' in window,
|
||||
mousedownEvent = 'mousedown' + ( touchSupported ? ' touchstart' : ''),
|
||||
mousemoveEvent = 'mousemove.clockpicker' + ( touchSupported ? ' touchmove.clockpicker' : ''),
|
||||
mouseupEvent = 'mouseup.clockpicker' + ( touchSupported ? ' touchend.clockpicker' : '');
|
||||
|
||||
// Vibrate the device if supported
|
||||
var vibrate = navigator.vibrate ? 'vibrate' : navigator.webkitVibrate ? 'webkitVibrate' : null;
|
||||
|
||||
function createSvgElement(name) {
|
||||
return document.createElementNS(svgNS, name);
|
||||
}
|
||||
|
||||
function leadingZero(num) {
|
||||
return (num < 10 ? '0' : '') + num;
|
||||
}
|
||||
|
||||
// Get a unique id
|
||||
var idCounter = 0;
|
||||
function uniqueId(prefix) {
|
||||
var id = ++idCounter + '';
|
||||
return prefix ? prefix + id : id;
|
||||
}
|
||||
|
||||
// Clock size
|
||||
var dialRadius = 100,
|
||||
outerRadius = 80,
|
||||
// innerRadius = 80 on 12 hour clock
|
||||
innerRadius = 54,
|
||||
tickRadius = 13,
|
||||
diameter = dialRadius * 2,
|
||||
duration = transitionSupported ? 350 : 1;
|
||||
|
||||
// Popover template
|
||||
var tpl = [
|
||||
'<div class="popover clockpicker-popover">',
|
||||
'<div class="arrow"></div>',
|
||||
'<div class="popover-title">',
|
||||
'<span class="clockpicker-span-hours text-primary"></span>',
|
||||
' : ',
|
||||
'<span class="clockpicker-span-minutes"></span>',
|
||||
'<span class="clockpicker-span-am-pm"></span>',
|
||||
'</div>',
|
||||
'<div class="popover-content">',
|
||||
'<div class="clockpicker-plate">',
|
||||
'<div class="clockpicker-canvas"></div>',
|
||||
'<div class="clockpicker-dial clockpicker-hours"></div>',
|
||||
'<div class="clockpicker-dial clockpicker-minutes clockpicker-dial-out"></div>',
|
||||
'</div>',
|
||||
'<span class="clockpicker-am-pm-block">',
|
||||
'</span>',
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join('');
|
||||
|
||||
// ClockPicker
|
||||
function ClockPicker(element, options) {
|
||||
var popover = $(tpl),
|
||||
plate = popover.find('.clockpicker-plate'),
|
||||
hoursView = popover.find('.clockpicker-hours'),
|
||||
minutesView = popover.find('.clockpicker-minutes'),
|
||||
amPmBlock = popover.find('.clockpicker-am-pm-block'),
|
||||
isInput = element.prop('tagName') === 'INPUT',
|
||||
input = isInput ? element : element.find('input'),
|
||||
addon = element.find('.input-group-addon'),
|
||||
self = this,
|
||||
timer;
|
||||
|
||||
this.id = uniqueId('cp');
|
||||
this.element = element;
|
||||
this.options = options;
|
||||
this.isAppended = false;
|
||||
this.isShown = false;
|
||||
this.currentView = 'hours';
|
||||
this.isInput = isInput;
|
||||
this.input = input;
|
||||
this.addon = addon;
|
||||
this.popover = popover;
|
||||
this.plate = plate;
|
||||
this.hoursView = hoursView;
|
||||
this.minutesView = minutesView;
|
||||
this.amPmBlock = amPmBlock;
|
||||
this.spanHours = popover.find('.clockpicker-span-hours');
|
||||
this.spanMinutes = popover.find('.clockpicker-span-minutes');
|
||||
this.spanAmPm = popover.find('.clockpicker-span-am-pm');
|
||||
this.amOrPm = "PM";
|
||||
|
||||
// Setup for for 12 hour clock if option is selected
|
||||
if (options.twelvehour) {
|
||||
|
||||
var amPmButtonsTemplate = ['<div class="clockpicker-am-pm-block">',
|
||||
'<button type="button" class="btn btn-sm btn-default clockpicker-button clockpicker-am-button">',
|
||||
'AM</button>',
|
||||
'<button type="button" class="btn btn-sm btn-default clockpicker-button clockpicker-pm-button">',
|
||||
'PM</button>',
|
||||
'</div>'].join('');
|
||||
|
||||
var amPmButtons = $(amPmButtonsTemplate);
|
||||
//amPmButtons.appendTo(plate);
|
||||
|
||||
////Not working b/c they are not shown when this runs
|
||||
//$('clockpicker-am-button')
|
||||
// .on("click", function() {
|
||||
// self.amOrPm = "AM";
|
||||
// $('.clockpicker-span-am-pm').empty().append('AM');
|
||||
// });
|
||||
//
|
||||
//$('clockpicker-pm-button')
|
||||
// .on("click", function() {
|
||||
// self.amOrPm = "PM";
|
||||
// $('.clockpicker-span-am-pm').empty().append('PM');
|
||||
// });
|
||||
|
||||
$('<button type="button" class="btn btn-sm btn-default clockpicker-button am-button">' + "AM" + '</button>')
|
||||
.on("click", function() {
|
||||
self.amOrPm = "AM";
|
||||
$('.clockpicker-span-am-pm').empty().append('AM');
|
||||
}).appendTo(this.amPmBlock);
|
||||
|
||||
|
||||
$('<button type="button" class="btn btn-sm btn-default clockpicker-button pm-button">' + "PM" + '</button>')
|
||||
.on("click", function() {
|
||||
self.amOrPm = 'PM';
|
||||
$('.clockpicker-span-am-pm').empty().append('PM');
|
||||
}).appendTo(this.amPmBlock);
|
||||
|
||||
}
|
||||
|
||||
if (! options.autoclose) {
|
||||
// If autoclose is not setted, append a button
|
||||
$('<button type="button" class="btn btn-sm btn-default btn-block clockpicker-button">' + options.donetext + '</button>')
|
||||
.click($.proxy(this.done, this))
|
||||
.appendTo(popover);
|
||||
}
|
||||
|
||||
// Placement and arrow align - make sure they make sense.
|
||||
if ((options.placement === 'top' || options.placement === 'bottom') && (options.align === 'top' || options.align === 'bottom')) options.align = 'left';
|
||||
if ((options.placement === 'left' || options.placement === 'right') && (options.align === 'left' || options.align === 'right')) options.align = 'top';
|
||||
|
||||
popover.addClass(options.placement);
|
||||
popover.addClass('clockpicker-align-' + options.align);
|
||||
|
||||
this.spanHours.click($.proxy(this.toggleView, this, 'hours'));
|
||||
this.spanMinutes.click($.proxy(this.toggleView, this, 'minutes'));
|
||||
|
||||
// Show or toggle
|
||||
input.on('focus.clockpicker click.clockpicker', $.proxy(this.show, this));
|
||||
addon.on('click.clockpicker', $.proxy(this.toggle, this));
|
||||
|
||||
// Build ticks
|
||||
var tickTpl = $('<div class="clockpicker-tick"></div>'),
|
||||
i, tick, radian, radius;
|
||||
|
||||
// Hours view
|
||||
if (options.twelvehour) {
|
||||
for (i = 1; i < 13; i += 1) {
|
||||
tick = tickTpl.clone();
|
||||
radian = i / 6 * Math.PI;
|
||||
radius = outerRadius;
|
||||
tick.css('font-size', '120%');
|
||||
tick.css({
|
||||
left: dialRadius + Math.sin(radian) * radius - tickRadius,
|
||||
top: dialRadius - Math.cos(radian) * radius - tickRadius
|
||||
});
|
||||
tick.html(i === 0 ? '00' : i);
|
||||
hoursView.append(tick);
|
||||
tick.on(mousedownEvent, mousedown);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 24; i += 1) {
|
||||
tick = tickTpl.clone();
|
||||
radian = i / 6 * Math.PI;
|
||||
var inner = i > 0 && i < 13;
|
||||
radius = inner ? innerRadius : outerRadius;
|
||||
tick.css({
|
||||
left: dialRadius + Math.sin(radian) * radius - tickRadius,
|
||||
top: dialRadius - Math.cos(radian) * radius - tickRadius
|
||||
});
|
||||
if (inner) {
|
||||
tick.css('font-size', '120%');
|
||||
}
|
||||
tick.html(i === 0 ? '00' : i);
|
||||
hoursView.append(tick);
|
||||
tick.on(mousedownEvent, mousedown);
|
||||
}
|
||||
}
|
||||
|
||||
// Minutes view
|
||||
for (i = 0; i < 60; i += 5) {
|
||||
tick = tickTpl.clone();
|
||||
radian = i / 30 * Math.PI;
|
||||
tick.css({
|
||||
left: dialRadius + Math.sin(radian) * outerRadius - tickRadius,
|
||||
top: dialRadius - Math.cos(radian) * outerRadius - tickRadius
|
||||
});
|
||||
tick.css('font-size', '120%');
|
||||
tick.html(leadingZero(i));
|
||||
minutesView.append(tick);
|
||||
tick.on(mousedownEvent, mousedown);
|
||||
}
|
||||
|
||||
// Clicking on minutes view space
|
||||
plate.on(mousedownEvent, function(e){
|
||||
if ($(e.target).closest('.clockpicker-tick').length === 0) {
|
||||
mousedown(e, true);
|
||||
}
|
||||
});
|
||||
|
||||
// Mousedown or touchstart
|
||||
function mousedown(e, space) {
|
||||
var offset = plate.offset(),
|
||||
isTouch = /^touch/.test(e.type),
|
||||
x0 = offset.left + dialRadius,
|
||||
y0 = offset.top + dialRadius,
|
||||
dx = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
|
||||
dy = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0,
|
||||
z = Math.sqrt(dx * dx + dy * dy),
|
||||
moved = false;
|
||||
|
||||
// When clicking on minutes view space, check the mouse position
|
||||
if (space && (z < outerRadius - tickRadius || z > outerRadius + tickRadius)) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
// Set cursor style of body after 200ms
|
||||
var movingTimer = setTimeout(function(){
|
||||
$body.addClass('clockpicker-moving');
|
||||
}, 200);
|
||||
|
||||
// Place the canvas to top
|
||||
if (svgSupported) {
|
||||
plate.append(self.canvas);
|
||||
}
|
||||
|
||||
// Clock
|
||||
self.setHand(dx, dy, ! space, true);
|
||||
|
||||
// Mousemove on document
|
||||
$doc.off(mousemoveEvent).on(mousemoveEvent, function(e){
|
||||
e.preventDefault();
|
||||
var isTouch = /^touch/.test(e.type),
|
||||
x = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
|
||||
y = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0;
|
||||
if (! moved && x === dx && y === dy) {
|
||||
// Clicking in chrome on windows will trigger a mousemove event
|
||||
return;
|
||||
}
|
||||
moved = true;
|
||||
self.setHand(x, y, false, true);
|
||||
});
|
||||
|
||||
// Mouseup on document
|
||||
$doc.off(mouseupEvent).on(mouseupEvent, function(e){
|
||||
$doc.off(mouseupEvent);
|
||||
e.preventDefault();
|
||||
var isTouch = /^touch/.test(e.type),
|
||||
x = (isTouch ? e.originalEvent.changedTouches[0] : e).pageX - x0,
|
||||
y = (isTouch ? e.originalEvent.changedTouches[0] : e).pageY - y0;
|
||||
if ((space || moved) && x === dx && y === dy) {
|
||||
self.setHand(x, y);
|
||||
}
|
||||
if (self.currentView === 'hours') {
|
||||
self.toggleView('minutes', duration / 2);
|
||||
} else {
|
||||
if (options.autoclose) {
|
||||
self.minutesView.addClass('clockpicker-dial-out');
|
||||
setTimeout(function(){
|
||||
self.done();
|
||||
}, duration / 2);
|
||||
}
|
||||
}
|
||||
plate.prepend(canvas);
|
||||
|
||||
// Reset cursor style of body
|
||||
clearTimeout(movingTimer);
|
||||
$body.removeClass('clockpicker-moving');
|
||||
|
||||
// Unbind mousemove event
|
||||
$doc.off(mousemoveEvent);
|
||||
});
|
||||
}
|
||||
|
||||
if (svgSupported) {
|
||||
// Draw clock hands and others
|
||||
var canvas = popover.find('.clockpicker-canvas'),
|
||||
svg = createSvgElement('svg');
|
||||
svg.setAttribute('class', 'clockpicker-svg');
|
||||
svg.setAttribute('width', diameter);
|
||||
svg.setAttribute('height', diameter);
|
||||
var g = createSvgElement('g');
|
||||
g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')');
|
||||
var bearing = createSvgElement('circle');
|
||||
bearing.setAttribute('class', 'clockpicker-canvas-bearing');
|
||||
bearing.setAttribute('cx', 0);
|
||||
bearing.setAttribute('cy', 0);
|
||||
bearing.setAttribute('r', 2);
|
||||
var hand = createSvgElement('line');
|
||||
hand.setAttribute('x1', 0);
|
||||
hand.setAttribute('y1', 0);
|
||||
var bg = createSvgElement('circle');
|
||||
bg.setAttribute('class', 'clockpicker-canvas-bg');
|
||||
bg.setAttribute('r', tickRadius);
|
||||
var fg = createSvgElement('circle');
|
||||
fg.setAttribute('class', 'clockpicker-canvas-fg');
|
||||
fg.setAttribute('r', 3.5);
|
||||
g.appendChild(hand);
|
||||
g.appendChild(bg);
|
||||
g.appendChild(fg);
|
||||
g.appendChild(bearing);
|
||||
svg.appendChild(g);
|
||||
canvas.append(svg);
|
||||
|
||||
this.hand = hand;
|
||||
this.bg = bg;
|
||||
this.fg = fg;
|
||||
this.bearing = bearing;
|
||||
this.g = g;
|
||||
this.canvas = canvas;
|
||||
}
|
||||
|
||||
raiseCallback(this.options.init);
|
||||
}
|
||||
|
||||
function raiseCallback(callbackFunction) {
|
||||
if (callbackFunction && typeof callbackFunction === "function") {
|
||||
callbackFunction();
|
||||
}
|
||||
}
|
||||
|
||||
// Default options
|
||||
ClockPicker.DEFAULTS = {
|
||||
'default': '', // default time, 'now' or '13:14' e.g.
|
||||
fromnow: 0, // set default time to * milliseconds from now (using with default = 'now')
|
||||
placement: 'bottom', // clock popover placement
|
||||
align: 'left', // popover arrow align
|
||||
donetext: '完成', // done button text
|
||||
autoclose: false, // auto close when minute is selected
|
||||
twelvehour: false, // change to 12 hour AM/PM clock from 24 hour
|
||||
vibrate: true // vibrate the device when dragging clock hand
|
||||
};
|
||||
|
||||
// Show or hide popover
|
||||
ClockPicker.prototype.toggle = function(){
|
||||
this[this.isShown ? 'hide' : 'show']();
|
||||
};
|
||||
|
||||
// Set popover position
|
||||
ClockPicker.prototype.locate = function(){
|
||||
var element = this.element,
|
||||
popover = this.popover,
|
||||
offset = element.offset(),
|
||||
width = element.outerWidth(),
|
||||
height = element.outerHeight(),
|
||||
placement = this.options.placement,
|
||||
align = this.options.align,
|
||||
styles = {},
|
||||
self = this;
|
||||
|
||||
popover.show();
|
||||
|
||||
// Place the popover
|
||||
switch (placement) {
|
||||
case 'bottom':
|
||||
styles.top = offset.top + height;
|
||||
break;
|
||||
case 'right':
|
||||
styles.left = offset.left + width;
|
||||
break;
|
||||
case 'top':
|
||||
styles.top = offset.top - popover.outerHeight();
|
||||
break;
|
||||
case 'left':
|
||||
styles.left = offset.left - popover.outerWidth();
|
||||
break;
|
||||
}
|
||||
|
||||
// Align the popover arrow
|
||||
switch (align) {
|
||||
case 'left':
|
||||
styles.left = offset.left;
|
||||
break;
|
||||
case 'right':
|
||||
styles.left = offset.left + width - popover.outerWidth();
|
||||
break;
|
||||
case 'top':
|
||||
styles.top = offset.top;
|
||||
break;
|
||||
case 'bottom':
|
||||
styles.top = offset.top + height - popover.outerHeight();
|
||||
break;
|
||||
}
|
||||
|
||||
popover.css(styles);
|
||||
};
|
||||
|
||||
// Show popover
|
||||
ClockPicker.prototype.show = function(e){
|
||||
// Not show again
|
||||
if (this.isShown) {
|
||||
return;
|
||||
}
|
||||
|
||||
raiseCallback(this.options.beforeShow);
|
||||
|
||||
var self = this;
|
||||
|
||||
// Initialize
|
||||
if (! this.isAppended) {
|
||||
// Append popover to body
|
||||
$body = $(document.body).append(this.popover);
|
||||
|
||||
// Reset position when resize
|
||||
$win.on('resize.clockpicker' + this.id, function(){
|
||||
if (self.isShown) {
|
||||
self.locate();
|
||||
}
|
||||
});
|
||||
|
||||
this.isAppended = true;
|
||||
}
|
||||
|
||||
// Get the time
|
||||
var value = ((this.input.prop('value') || this.options['default'] || '') + '').split(':');
|
||||
if (value[0] === 'now') {
|
||||
var now = new Date(+ new Date() + this.options.fromnow);
|
||||
value = [
|
||||
now.getHours(),
|
||||
now.getMinutes()
|
||||
];
|
||||
}
|
||||
this.hours = + value[0] || 0;
|
||||
this.minutes = + value[1] || 0;
|
||||
this.spanHours.html(leadingZero(this.hours));
|
||||
this.spanMinutes.html(leadingZero(this.minutes));
|
||||
|
||||
// Toggle to hours view
|
||||
this.toggleView('hours');
|
||||
|
||||
// Set position
|
||||
this.locate();
|
||||
|
||||
this.isShown = true;
|
||||
|
||||
// Hide when clicking or tabbing on any element except the clock, input and addon
|
||||
$doc.on('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id, function(e){
|
||||
var target = $(e.target);
|
||||
if (target.closest(self.popover).length === 0 &&
|
||||
target.closest(self.addon).length === 0 &&
|
||||
target.closest(self.input).length === 0) {
|
||||
self.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Hide when ESC is pressed
|
||||
$doc.on('keyup.clockpicker.' + this.id, function(e){
|
||||
if (e.keyCode === 27) {
|
||||
self.hide();
|
||||
}
|
||||
});
|
||||
|
||||
raiseCallback(this.options.afterShow);
|
||||
};
|
||||
|
||||
// Hide popover
|
||||
ClockPicker.prototype.hide = function(){
|
||||
raiseCallback(this.options.beforeHide);
|
||||
|
||||
this.isShown = false;
|
||||
|
||||
// Unbinding events on document
|
||||
$doc.off('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id);
|
||||
$doc.off('keyup.clockpicker.' + this.id);
|
||||
|
||||
this.popover.hide();
|
||||
|
||||
raiseCallback(this.options.afterHide);
|
||||
};
|
||||
|
||||
// Toggle to hours or minutes view
|
||||
ClockPicker.prototype.toggleView = function(view, delay){
|
||||
var raiseAfterHourSelect = false;
|
||||
if (view === 'minutes' && $(this.hoursView).css("visibility") === "visible") {
|
||||
raiseCallback(this.options.beforeHourSelect);
|
||||
raiseAfterHourSelect = true;
|
||||
}
|
||||
var isHours = view === 'hours',
|
||||
nextView = isHours ? this.hoursView : this.minutesView,
|
||||
hideView = isHours ? this.minutesView : this.hoursView;
|
||||
|
||||
this.currentView = view;
|
||||
|
||||
this.spanHours.toggleClass('text-primary', isHours);
|
||||
this.spanMinutes.toggleClass('text-primary', ! isHours);
|
||||
|
||||
// Let's make transitions
|
||||
hideView.addClass('clockpicker-dial-out');
|
||||
nextView.css('visibility', 'visible').removeClass('clockpicker-dial-out');
|
||||
|
||||
// Reset clock hand
|
||||
this.resetClock(delay);
|
||||
|
||||
// After transitions ended
|
||||
clearTimeout(this.toggleViewTimer);
|
||||
this.toggleViewTimer = setTimeout(function(){
|
||||
hideView.css('visibility', 'hidden');
|
||||
}, duration);
|
||||
|
||||
if (raiseAfterHourSelect) {
|
||||
raiseCallback(this.options.afterHourSelect);
|
||||
}
|
||||
};
|
||||
|
||||
// Reset clock hand
|
||||
ClockPicker.prototype.resetClock = function(delay){
|
||||
var view = this.currentView,
|
||||
value = this[view],
|
||||
isHours = view === 'hours',
|
||||
unit = Math.PI / (isHours ? 6 : 30),
|
||||
radian = value * unit,
|
||||
radius = isHours && value > 0 && value < 13 ? innerRadius : outerRadius,
|
||||
x = Math.sin(radian) * radius,
|
||||
y = - Math.cos(radian) * radius,
|
||||
self = this;
|
||||
if (svgSupported && delay) {
|
||||
self.canvas.addClass('clockpicker-canvas-out');
|
||||
setTimeout(function(){
|
||||
self.canvas.removeClass('clockpicker-canvas-out');
|
||||
self.setHand(x, y);
|
||||
}, delay);
|
||||
} else {
|
||||
this.setHand(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
// Set clock hand to (x, y)
|
||||
ClockPicker.prototype.setHand = function(x, y, roundBy5, dragging){
|
||||
var radian = Math.atan2(x, - y),
|
||||
isHours = this.currentView === 'hours',
|
||||
unit = Math.PI / (isHours || roundBy5 ? 6 : 30),
|
||||
z = Math.sqrt(x * x + y * y),
|
||||
options = this.options,
|
||||
inner = isHours && z < (outerRadius + innerRadius) / 2,
|
||||
radius = inner ? innerRadius : outerRadius,
|
||||
value;
|
||||
|
||||
if (options.twelvehour) {
|
||||
radius = outerRadius;
|
||||
}
|
||||
|
||||
// Radian should in range [0, 2PI]
|
||||
if (radian < 0) {
|
||||
radian = Math.PI * 2 + radian;
|
||||
}
|
||||
|
||||
// Get the round value
|
||||
value = Math.round(radian / unit);
|
||||
|
||||
// Get the round radian
|
||||
radian = value * unit;
|
||||
|
||||
// Correct the hours or minutes
|
||||
if (options.twelvehour) {
|
||||
if (isHours) {
|
||||
if (value === 0) {
|
||||
value = 12;
|
||||
}
|
||||
} else {
|
||||
if (roundBy5) {
|
||||
value *= 5;
|
||||
}
|
||||
if (value === 60) {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isHours) {
|
||||
if (value === 12) {
|
||||
value = 0;
|
||||
}
|
||||
value = inner ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
|
||||
} else {
|
||||
if (roundBy5) {
|
||||
value *= 5;
|
||||
}
|
||||
if (value === 60) {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Once hours or minutes changed, vibrate the device
|
||||
if (this[this.currentView] !== value) {
|
||||
if (vibrate && this.options.vibrate) {
|
||||
// Do not vibrate too frequently
|
||||
if (! this.vibrateTimer) {
|
||||
navigator[vibrate](10);
|
||||
this.vibrateTimer = setTimeout($.proxy(function(){
|
||||
this.vibrateTimer = null;
|
||||
}, this), 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this[this.currentView] = value;
|
||||
this[isHours ? 'spanHours' : 'spanMinutes'].html(leadingZero(value));
|
||||
|
||||
// If svg is not supported, just add an active class to the tick
|
||||
if (! svgSupported) {
|
||||
this[isHours ? 'hoursView' : 'minutesView'].find('.clockpicker-tick').each(function(){
|
||||
var tick = $(this);
|
||||
tick.toggleClass('active', value === + tick.html());
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Place clock hand at the top when dragging
|
||||
if (dragging || (! isHours && value % 5)) {
|
||||
this.g.insertBefore(this.hand, this.bearing);
|
||||
this.g.insertBefore(this.bg, this.fg);
|
||||
this.bg.setAttribute('class', 'clockpicker-canvas-bg clockpicker-canvas-bg-trans');
|
||||
} else {
|
||||
// Or place it at the bottom
|
||||
this.g.insertBefore(this.hand, this.bg);
|
||||
this.g.insertBefore(this.fg, this.bg);
|
||||
this.bg.setAttribute('class', 'clockpicker-canvas-bg');
|
||||
}
|
||||
|
||||
// Set clock hand and others' position
|
||||
var cx = Math.sin(radian) * radius,
|
||||
cy = - Math.cos(radian) * radius;
|
||||
this.hand.setAttribute('x2', cx);
|
||||
this.hand.setAttribute('y2', cy);
|
||||
this.bg.setAttribute('cx', cx);
|
||||
this.bg.setAttribute('cy', cy);
|
||||
this.fg.setAttribute('cx', cx);
|
||||
this.fg.setAttribute('cy', cy);
|
||||
};
|
||||
|
||||
// Hours and minutes are selected
|
||||
ClockPicker.prototype.done = function() {
|
||||
raiseCallback(this.options.beforeDone);
|
||||
this.hide();
|
||||
var last = this.input.prop('value'),
|
||||
value = leadingZero(this.hours) + ':' + leadingZero(this.minutes);
|
||||
if (this.options.twelvehour) {
|
||||
value = value + this.amOrPm;
|
||||
}
|
||||
|
||||
this.input.prop('value', value);
|
||||
if (value !== last) {
|
||||
this.input.triggerHandler('change');
|
||||
if (! this.isInput) {
|
||||
this.element.trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.autoclose) {
|
||||
this.input.trigger('blur');
|
||||
}
|
||||
|
||||
raiseCallback(this.options.afterDone);
|
||||
};
|
||||
|
||||
// Remove clockpicker from input
|
||||
ClockPicker.prototype.remove = function() {
|
||||
this.element.removeData('clockpicker');
|
||||
this.input.off('focus.clockpicker click.clockpicker');
|
||||
this.addon.off('click.clockpicker');
|
||||
if (this.isShown) {
|
||||
this.hide();
|
||||
}
|
||||
if (this.isAppended) {
|
||||
$win.off('resize.clockpicker' + this.id);
|
||||
this.popover.remove();
|
||||
}
|
||||
};
|
||||
|
||||
// Extends $.fn.clockpicker
|
||||
$.fn.clockpicker = function(option){
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
return this.each(function(){
|
||||
var $this = $(this),
|
||||
data = $this.data('clockpicker');
|
||||
if (! data) {
|
||||
var options = $.extend({}, ClockPicker.DEFAULTS, $this.data(), typeof option == 'object' && option);
|
||||
$this.data('clockpicker', new ClockPicker($this, options));
|
||||
} else {
|
||||
// Manual operatsions. show, hide, remove, e.g.
|
||||
if (typeof data[option] === 'function') {
|
||||
data[option].apply(data, args);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}());
|
||||
|
|
@ -1,19 +1,82 @@
|
|||
<?php if ($this->previewMode): ?>
|
||||
<div class="form-control"><?= $value ?></div>
|
||||
<?php else: ?>
|
||||
<div
|
||||
id="<?= $this->getId() ?>"
|
||||
class="field-datepicker"
|
||||
data-control="datepicker"
|
||||
data-show-time="<?= $showTime ? 'true' : 'false' ?>"
|
||||
data-min-date="<?= $minDate ?>"
|
||||
data-max-date="<?= $maxDate ?>">
|
||||
<input
|
||||
type="text"
|
||||
id="<?= $this->getId('input') ?>"
|
||||
name="<?= $name ?>"
|
||||
value="<?= $value ?>"
|
||||
class="form-control align-right"
|
||||
autocomplete="off">
|
||||
</div>
|
||||
|
||||
<?php if ($mode == 'date'): ?>
|
||||
|
||||
<div
|
||||
id="<?= $this->getId() ?>"
|
||||
class="field-datepicker"
|
||||
data-control="datepicker"
|
||||
data-min-date="<?= $minDate ?>"
|
||||
data-max-date="<?= $maxDate ?>">
|
||||
<input
|
||||
type="text"
|
||||
id="<?= $this->getId('input') ?>"
|
||||
name="<?= $name ?>"
|
||||
value="<?= $value ?>"
|
||||
class="form-control align-right"
|
||||
autocomplete="off">
|
||||
</div>
|
||||
|
||||
<?php elseif ($mode == 'datetime'): ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<!-- Date -->
|
||||
<div
|
||||
id="<?= $this->getId() ?>"
|
||||
class="field-datepicker"
|
||||
data-control="datepicker"
|
||||
data-min-date="<?= $minDate ?>"
|
||||
data-max-date="<?= $maxDate ?>">
|
||||
<input
|
||||
type="text"
|
||||
id="<?= $this->getId('input') ?>"
|
||||
name="<?= $name ?>"
|
||||
value="<?= $value ?>"
|
||||
class="form-control align-right"
|
||||
autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<!-- Time -->
|
||||
<div
|
||||
id="<?= $this->getId() ?>-time"
|
||||
data-control="timepicker"
|
||||
class="field-timepicker clockpicker">
|
||||
<input
|
||||
type="text"
|
||||
id="<?= $this->getId('input') ?>-time"
|
||||
name="<?= $timeName ?>"
|
||||
value="<?= $timeValue ?>"
|
||||
class="form-control align-right"
|
||||
autocomplete="off"
|
||||
data-autoclose="true"
|
||||
data-placement="bottom"
|
||||
data-align="right">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php elseif ($mode == 'time'): ?>
|
||||
|
||||
<div
|
||||
id="<?= $this->getId() ?>"
|
||||
data-control="timepicker"
|
||||
class="field-timepicker clockpicker">
|
||||
<input
|
||||
type="text"
|
||||
id="<?= $this->getId('input') ?>"
|
||||
name="<?= $name ?>"
|
||||
value="<?= $value ?>"
|
||||
class="form-control align-right"
|
||||
autocomplete="off"
|
||||
data-autoclose="true"
|
||||
data-placement="bottom"
|
||||
data-align="right">
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<?php endif ?>
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
<?php endif ?>
|
||||
<a
|
||||
href="{{path}}"
|
||||
class="uploader-file-link oc-icon-paper-clip"
|
||||
class="uploader-file-link oc-icon-paperclip"
|
||||
target="_blank"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -77,4 +77,4 @@
|
|||
|
||||
<!-- Existing images -->
|
||||
<script> $('#<?= $this->getId() ?>').data('populatedData', <?= $fileList ?>); </script>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -97,6 +97,10 @@ return [
|
|||
'group' => [
|
||||
'name' => 'Group',
|
||||
'name_field' => 'Name',
|
||||
'description_field' => 'Description',
|
||||
'is_new_user_default_field' => 'Add new administrators to this group by default',
|
||||
'code_field' => 'Code',
|
||||
'code_comment' => 'Enter a unique code if you want to access it with the API.',
|
||||
'menu_label' => 'Groups',
|
||||
'list_title' => 'Manage Groups',
|
||||
'new' => 'New Administrator Group',
|
||||
|
|
@ -155,6 +159,9 @@ return [
|
|||
'delete' => 'Delete',
|
||||
'deleting' => 'Deleting...',
|
||||
'deleting_name' => 'Deleting :name...',
|
||||
'reset_default' => 'Reset to default',
|
||||
'resetting' => 'Resetting',
|
||||
'resetting_name' => 'Resetting :name',
|
||||
'undefined_tab' => 'Misc',
|
||||
'field_off' => 'Off',
|
||||
'field_on' => 'On',
|
||||
|
|
@ -189,11 +196,14 @@ return [
|
|||
'add_selected' => "Add selected",
|
||||
'add_a_new' => "Add a new :name",
|
||||
'cancel' => "Cancel",
|
||||
'close' => "Close",
|
||||
'add_name' => "Add :name",
|
||||
'create' => "Create",
|
||||
'create_name' => "Create :name",
|
||||
'update' => "Update",
|
||||
'update_name' => "Update :name",
|
||||
'preview' => "Preview",
|
||||
'preview_name' => "Preview :name",
|
||||
'remove' => "Remove",
|
||||
'remove_name' => "Remove :name",
|
||||
'delete' => "Delete",
|
||||
|
|
|
|||
|
|
@ -155,6 +155,9 @@ return [
|
|||
'delete' => 'Törlés',
|
||||
'deleting' => 'Törlés...',
|
||||
'deleting_name' => 'A(z) :name törlése...',
|
||||
'reset_default' => 'Alaphelyzet',
|
||||
'resetting' => 'Visszaállítás',
|
||||
'resetting_name' => 'A(z) :name visszaállítása',
|
||||
'undefined_tab' => 'Egyebek',
|
||||
'field_off' => 'Ki',
|
||||
'field_on' => 'Be',
|
||||
|
|
@ -210,8 +213,8 @@ return [
|
|||
],
|
||||
'warnings' => [
|
||||
'tips' => 'Rendszerkonfigurációs tippek',
|
||||
'tips_description' => 'Vannak olyan problémák, melyekre a rendszer megfelelő konfigurálása érdekében oda kell figyelnie.',
|
||||
'permissions' => 'A(z) :name nevű könyvtár vagy alkönyvtárai a PHP számára nem írhatóak. Adjon megfelelő engedélyeket a webkiszolgálónak erre a könyvtárra.',
|
||||
'tips_description' => 'Olyan problémák vannak, melyekre figyeljen oda a rendszer megfelelő konfigurálása érdekében.',
|
||||
'permissions' => 'A(z) :name könyvtár vagy alkönyvtárai a PHP számára nem írhatóak. Adjon megfelelő engedélyeket a webkiszolgálónak erre a könyvtárra.',
|
||||
'extension' => 'A(z) :name PHP-kiterjesztés nincs telepítve. Telepítse ezt a függvénytárat, és aktiválja a kiterjesztést.'
|
||||
],
|
||||
'editor' => [
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ return [
|
|||
'status' => [
|
||||
'widget_title_default' => 'Статус системы',
|
||||
'online' => 'Онлайн',
|
||||
'maintenance' => 'в разработке',
|
||||
'update_available' => '{0} нет новый обновлений!|{1} доступно новое обновление!|[2,Inf] доступны новые обновления!',
|
||||
]
|
||||
],
|
||||
|
|
@ -154,6 +155,9 @@ return [
|
|||
'delete' => 'Удалить',
|
||||
'deleting' => 'Удаление...',
|
||||
'deleting_name' => 'Удаление :name...',
|
||||
'reset_default' => 'Сбросить настройки',
|
||||
'resetting' => 'Сброс',
|
||||
'resetting_name' => 'Сброс :name',
|
||||
'undefined_tab' => 'Разное',
|
||||
'field_off' => 'Выкл',
|
||||
'field_on' => 'Вкл',
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<link href="<?= Backend::skinAsset('assets/vendor/select2/select2.css') ?>" rel="stylesheet">
|
||||
<link href="<?= Backend::skinAsset('assets/css/october.css') ?>?v<?= System\Models\Parameters::get('system::core.build', 1) ?>" rel="stylesheet">
|
||||
|
||||
<script src="<?= Backend::skinAsset('assets/js/vendor/jquery-2.0.3.min.js') ?>"></script>
|
||||
<script src="<?= Backend::skinAsset('assets/js/vendor/jquery.min.js') ?>"></script>
|
||||
<script src="<?= URL::asset('modules/system/assets/js/framework.js') ?>"></script>
|
||||
<script src="<?= Backend::skinAsset('assets/js/vendor/modernizr.min.js') ?>"></script>
|
||||
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
<script src="<?= Backend::skinAsset('assets/vendor/select2/select2.js') ?>"></script>
|
||||
<script src="<?= Backend::skinAsset('assets/vendor/mustache/mustache.min.js') ?>"></script>
|
||||
<script src="<?= Backend::skinAsset('assets/vendor/dropzone/dropzone.js') ?>"></script>
|
||||
<script src="<?= Backend::skinAsset('assets/vendor/sweet-alert/sweet-alert.js') ?>"></script>
|
||||
|
||||
<script src="<?= URL::asset('modules/system/assets/vendor/bootstrap/js/tooltip.js') ?>"></script>
|
||||
<script src="<?= URL::asset('modules/system/assets/vendor/bootstrap/js/modal.js') ?>"></script>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<title><?= e(trans('backend::lang.auth.title')) ?></title>
|
||||
<link href="<?= URL::to('modules/backend/assets/css/october.css') ?>" rel="stylesheet">
|
||||
|
||||
<script src="<?= URL::to('modules/backend/assets/js/vendor/jquery-2.0.3.min.js') ?>"></script>
|
||||
<script src="<?= URL::to('modules/backend/assets/js/vendor/jquery.min.js') ?>"></script>
|
||||
<script src="<?= URL::to('modules/system/assets/js/framework.js') ?>"></script>
|
||||
<script src="<?= URL::to('modules/backend/assets/js/october.utils.js') ?>"></script>
|
||||
<script src="<?= URL::to('modules/backend/assets/js/vendor/modernizr.min.js') ?>"></script>
|
||||
|
|
|
|||
|
|
@ -111,4 +111,13 @@ class User extends UserBase
|
|||
$message->to($this->email, $this->full_name);
|
||||
});
|
||||
}
|
||||
|
||||
public function getGroupsOptions()
|
||||
{
|
||||
$result = [];
|
||||
foreach (UserGroup::all() as $group) {
|
||||
$result[$group->id] = [$group->name, $group->description];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class UserGroup extends GroupBase
|
|||
* @var array Validation rules
|
||||
*/
|
||||
public $rules = [
|
||||
'name' => 'required|between:4,16|unique:backend_user_groups',
|
||||
'name' => 'required|between:2,128|unique:backend_user_groups',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -28,4 +28,16 @@ class UserGroup extends GroupBase
|
|||
public $belongsToMany = [
|
||||
'users' => ['Backend\Models\User', 'table' => 'backend_users_groups']
|
||||
];
|
||||
|
||||
public function afterCreate()
|
||||
{
|
||||
if ($this->is_new_user_default) {
|
||||
$this->addAllUsersToGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public function addAllUsersToGroup()
|
||||
{
|
||||
$this->users()->sync(User::lists('id'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
# ===================================
|
||||
# Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
name:
|
||||
label: backend::lang.user.group.name_field
|
||||
|
||||
tabs:
|
||||
stretch: true
|
||||
|
|
@ -41,7 +41,6 @@ tabs:
|
|||
permissions[superuser]:
|
||||
context: [create, update]
|
||||
tab: Permissions
|
||||
span: left
|
||||
label: backend::lang.user.superuser
|
||||
type: checkbox
|
||||
comment: backend::lang.user.superuser_comment
|
||||
|
|
@ -49,10 +48,9 @@ tabs:
|
|||
groups:
|
||||
context: [create, update]
|
||||
tab: Permissions
|
||||
span: right
|
||||
label: backend::lang.user.groups
|
||||
commentAbove: backend::lang.user.groups_comment
|
||||
type: relation
|
||||
type: checkboxlist
|
||||
|
||||
secondaryTabs:
|
||||
fields:
|
||||
|
|
|
|||
|
|
@ -5,4 +5,8 @@
|
|||
columns:
|
||||
name:
|
||||
label: backend::lang.user.group.name_field
|
||||
searchable: yes
|
||||
|
||||
description:
|
||||
label: backend::lang.user.group.description_field
|
||||
searchable: yes
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# ===================================
|
||||
# Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
|
||||
name:
|
||||
label: backend::lang.user.group.name_field
|
||||
span: auto
|
||||
|
||||
code:
|
||||
label: backend::lang.user.group.code_field
|
||||
comment: backend::lang.user.group.code_comment
|
||||
span: auto
|
||||
|
||||
description:
|
||||
label: backend::lang.user.group.description_field
|
||||
type: textarea
|
||||
size: tiny
|
||||
|
||||
is_new_user_default:
|
||||
label: backend::lang.user.group.is_new_user_default_field
|
||||
type: checkbox
|
||||
|
||||
tabs:
|
||||
stretch: true
|
||||
|
|
@ -710,7 +710,7 @@ class Form extends WidgetBase
|
|||
}
|
||||
|
||||
$fieldName = $field->fieldName;
|
||||
$defaultValue = (!$this->model->exists) && strlen($field->defaults) ? $field->defaults : null;
|
||||
$defaultValue = (!$this->model->exists) && !empty($field->defaults) ? $field->defaults : null;
|
||||
|
||||
/*
|
||||
* Array field name, eg: field[key][key2][key3]
|
||||
|
|
@ -803,14 +803,11 @@ class Form extends WidgetBase
|
|||
* Give widgets an opportunity to process the data.
|
||||
*/
|
||||
foreach ($this->formWidgets as $field => $widget) {
|
||||
$widgetValue = array_key_exists($field, $data)
|
||||
? $data[$field]
|
||||
: null;
|
||||
$parts = Str::evalHtmlArray($field);
|
||||
$dotted = implode('.', $parts);
|
||||
|
||||
$data[$field] = $widget->getSaveData($widgetValue);
|
||||
if ($data[$field] === FormWidgetBase::NO_SAVE_DATA) {
|
||||
unset($data[$field]);
|
||||
}
|
||||
$widgetValue = $widget->getSaveData(array_get($data, $dotted));
|
||||
array_set($data, $dotted, $widgetValue);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -273,7 +273,7 @@ class Lists extends WidgetBase
|
|||
/**
|
||||
* Applies any filters to the model.
|
||||
*/
|
||||
protected function prepareModel()
|
||||
public function prepareModel()
|
||||
{
|
||||
$query = $this->model->newQuery();
|
||||
$primaryTable = $this->model->getTable();
|
||||
|
|
@ -416,8 +416,8 @@ class Lists extends WidgetBase
|
|||
* Apply sorting
|
||||
*/
|
||||
if ($sortColumn = $this->getSortColumn()) {
|
||||
if (($column = array_get($this->columns, $sortColumn)) && $column->sqlSelect) {
|
||||
$sortColumn = $column->sqlSelect;
|
||||
if (($column = array_get($this->columns, $sortColumn)) && $column->valueFrom) {
|
||||
$sortColumn = $column->valueFrom;
|
||||
}
|
||||
|
||||
$query->orderBy($sortColumn, $this->sortDirection);
|
||||
|
|
@ -681,13 +681,14 @@ class Lists extends WidgetBase
|
|||
*/
|
||||
public function getColumnValue($record, $column)
|
||||
{
|
||||
|
||||
$columnName = $column->columnName;
|
||||
|
||||
/*
|
||||
* Handle taking name from model attribute.
|
||||
* Handle taking value from model relation.
|
||||
*/
|
||||
if ($column->valueFrom) {
|
||||
if ($column->valueFrom && $column->relation) {
|
||||
$columnName = $column->relation;
|
||||
|
||||
if (!array_key_exists($columnName, $record->getRelations())) {
|
||||
$value = null;
|
||||
}
|
||||
|
|
@ -698,14 +699,20 @@ class Lists extends WidgetBase
|
|||
$value = $record->{$columnName}->{$column->valueFrom};
|
||||
}
|
||||
else {
|
||||
$value = $record->{$column->valueFrom};
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Handle taking value from model attribute.
|
||||
*/
|
||||
elseif ($column->valueFrom) {
|
||||
$value = $record->{$column->valueFrom};
|
||||
}
|
||||
/*
|
||||
* Otherwise, if the column is a relation, it will be a custom select,
|
||||
* so prevent the Model from attempting to load the relation
|
||||
* if the value is NULL.
|
||||
*/
|
||||
}
|
||||
else {
|
||||
if ($record->hasRelation($columnName) && array_key_exists($columnName, $record->attributes)) {
|
||||
$value = $record->attributes[$columnName];
|
||||
|
|
|
|||
|
|
@ -5,48 +5,86 @@
|
|||
<!-- Checkbox List -->
|
||||
<?php if (count($fieldOptions)): ?>
|
||||
|
||||
<?php if (count($fieldOptions) > 10): ?>
|
||||
<!-- Quick selection -->
|
||||
<small>
|
||||
<?= e(trans('backend::lang.form.select')) ?>:
|
||||
<a href="javascript:;" onclick="jQuery('#<?= $field->getId('scrollable') ?> input[type=checkbox]').prop('checked', true)"><?= e(trans('backend::lang.form.select_all')) ?></a>,
|
||||
<a href="javascript:;" onclick="jQuery('#<?= $field->getId('scrollable') ?> input[type=checkbox]').prop('checked', false)"><?= e(trans('backend::lang.form.select_none')) ?></a>
|
||||
</small>
|
||||
<?php if ($this->previewMode): ?>
|
||||
<!-- Read-only -->
|
||||
|
||||
<!-- Scrollable Checkbox list -->
|
||||
<div class="field-checkboxlist-scrollable" id="<?= $field->getId('scrollable') ?>">
|
||||
<div class="control-scrollbar" data-control="scrollbar">
|
||||
<?php endif ?>
|
||||
<?php $index = 0; foreach ($fieldOptions as $value => $option): ?>
|
||||
<?php
|
||||
$index++;
|
||||
$checkboxId = 'checkbox_'.$field->getId().'_'.$index;
|
||||
if (!in_array($value, $checkedValues)) continue;
|
||||
if (is_string($option)) $option = [$option];
|
||||
?>
|
||||
<div class="checkbox custom-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="<?= $checkboxId ?>"
|
||||
name="<?= $field->getName() ?>[]"
|
||||
value="<?= $value ?>"
|
||||
disabled="disabled"
|
||||
checked="checked">
|
||||
|
||||
<?php $index = 0; foreach ($fieldOptions as $value => $option): ?>
|
||||
<?php
|
||||
$index++;
|
||||
$checkboxId = 'checkbox_'.$field->getId().'_'.$index;
|
||||
if (is_string($option)) $option = [$option];
|
||||
?>
|
||||
<div class="checkbox custom-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="<?= $checkboxId ?>"
|
||||
name="<?= $field->getName() ?>[]"
|
||||
value="<?= $value ?>"
|
||||
<?= $this->previewMode ? 'readonly="readonly"' : '' ?>
|
||||
<?= in_array($value, $checkedValues) ? 'checked="checked"' : '' ?>>
|
||||
|
||||
<label for="<?= $checkboxId ?>">
|
||||
<?= e(trans($option[0])) ?>
|
||||
</label>
|
||||
<?php if (isset($option[1])): ?>
|
||||
<p class="help-block"><?= e(trans($option[1])) ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php if (count($fieldOptions) > 10): ?>
|
||||
<label for="<?= $checkboxId ?>">
|
||||
<?= e(trans($option[0])) ?>
|
||||
</label>
|
||||
<?php if (isset($option[1])): ?>
|
||||
<p class="help-block"><?= e(trans($option[1])) ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php else: ?>
|
||||
<!-- Editable -->
|
||||
|
||||
<?php if (count($fieldOptions) > 10): ?>
|
||||
<!-- Quick selection -->
|
||||
<small>
|
||||
<?= e(trans('backend::lang.form.select')) ?>:
|
||||
<a href="javascript:;" onclick="jQuery('#<?= $field->getId('scrollable') ?> input[type=checkbox]').prop('checked', true)"><?= e(trans('backend::lang.form.select_all')) ?></a>,
|
||||
<a href="javascript:;" onclick="jQuery('#<?= $field->getId('scrollable') ?> input[type=checkbox]').prop('checked', false)"><?= e(trans('backend::lang.form.select_none')) ?></a>
|
||||
</small>
|
||||
|
||||
<!-- Scrollable Checkbox list -->
|
||||
<div class="field-checkboxlist-scrollable" id="<?= $field->getId('scrollable') ?>">
|
||||
<div class="control-scrollbar" data-control="scrollbar">
|
||||
<?php endif ?>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="<?= $field->getName() ?>"
|
||||
value="0" />
|
||||
|
||||
<?php $index = 0; foreach ($fieldOptions as $value => $option): ?>
|
||||
<?php
|
||||
$index++;
|
||||
$checkboxId = 'checkbox_'.$field->getId().'_'.$index;
|
||||
if (is_string($option)) $option = [$option];
|
||||
?>
|
||||
<div class="checkbox custom-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="<?= $checkboxId ?>"
|
||||
name="<?= $field->getName() ?>[]"
|
||||
value="<?= $value ?>"
|
||||
<?= in_array($value, $checkedValues) ? 'checked="checked"' : '' ?>>
|
||||
|
||||
<label for="<?= $checkboxId ?>">
|
||||
<?= e(trans($option[0])) ?>
|
||||
</label>
|
||||
<?php if (isset($option[1])): ?>
|
||||
<p class="help-block"><?= e(trans($option[1])) ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php if (count($fieldOptions) > 10): ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<!-- No options specified -->
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
|
@ -484,13 +484,13 @@
|
|||
|
||||
var popup = $form.data('oc.popup')
|
||||
|
||||
$(popup.$target).on('click', 'button[data-action=reload]', function(){
|
||||
$(popup.$content).on('click', 'button[data-action=reload]', function(){
|
||||
popup.hide()
|
||||
|
||||
reloadForm(form)
|
||||
})
|
||||
|
||||
$(popup.$target).on('click', 'button[data-action=save]', function(){
|
||||
$(popup.$content).on('click', 'button[data-action=save]', function(){
|
||||
popup.hide()
|
||||
|
||||
$('input[name=templateForceSave]', $form).val(1)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ use Lang;
|
|||
use Cache;
|
||||
use Config;
|
||||
use Validator;
|
||||
use System\Classes\SystemException;
|
||||
use System\Classes\ApplicationException;
|
||||
use October\Rain\Support\ValidationException;
|
||||
use RecursiveDirectoryIterator;
|
||||
|
|
@ -81,7 +80,7 @@ class CmsObject implements ArrayAccess
|
|||
public static function loadCached($theme, $fileName)
|
||||
{
|
||||
if (!FileHelper::validatePath($fileName, static::getMaxAllowedPathNesting())) {
|
||||
throw new SystemException(Lang::get('cms::lang.cms_object.invalid_file', ['name'=>$fileName]));
|
||||
throw new ApplicationException(Lang::get('cms::lang.cms_object.invalid_file', ['name'=>$fileName]));
|
||||
}
|
||||
|
||||
if (!strlen(File::extension($fileName))) {
|
||||
|
|
@ -157,7 +156,7 @@ class CmsObject implements ArrayAccess
|
|||
public static function load($theme, $fileName)
|
||||
{
|
||||
if (!FileHelper::validatePath($fileName, static::getMaxAllowedPathNesting())) {
|
||||
throw new SystemException(Lang::get('cms::lang.cms_object.invalid_file', ['name'=>$fileName]));
|
||||
throw new ApplicationException(Lang::get('cms::lang.cms_object.invalid_file', ['name'=>$fileName]));
|
||||
}
|
||||
|
||||
if (!strlen(File::extension($fileName))) {
|
||||
|
|
@ -391,7 +390,7 @@ class CmsObject implements ArrayAccess
|
|||
{
|
||||
$fullPath = static::getFilePath($this->theme, $this->fileName);
|
||||
if (File::isFile($fullPath) && !is_dir($fullPath) && !@unlink($fullPath)) {
|
||||
throw new SystemException(Lang::get('cms::lang.cms_object.error_deleting', ['name'=>$this->fileName]));
|
||||
throw new ApplicationException(Lang::get('cms::lang.cms_object.error_deleting', ['name'=>$this->fileName]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,9 +73,9 @@ abstract class ComponentBase extends Extendable
|
|||
protected $page;
|
||||
|
||||
/**
|
||||
* @var array Cache of linked Component objects, used for page links.
|
||||
* @var array A collection of external property names used by this component.
|
||||
*/
|
||||
// protected $pageLinkCache = [];
|
||||
protected $externalPropertyNames = [];
|
||||
|
||||
/**
|
||||
* Component constructor. Takes in the page or layout code section object
|
||||
|
|
@ -167,6 +167,80 @@ abstract class ComponentBase extends Extendable
|
|||
return $this->alias;
|
||||
}
|
||||
|
||||
//
|
||||
// External properties
|
||||
//
|
||||
|
||||
/*
|
||||
* Description on how to access external property names.
|
||||
*
|
||||
* # When
|
||||
* pageNumber = "7"
|
||||
* $this->propertyName('pageNumber'); // Returns NULL
|
||||
* $this->paramName('pageNumber'); // Returns NULL
|
||||
*
|
||||
* # When
|
||||
* pageNumber = "{{ :page }}"
|
||||
*
|
||||
* $this->propertyName('pageNumber'); // Returns ":page"
|
||||
* $this->paramName('pageNumber'); // Returns "page"
|
||||
*
|
||||
* # When
|
||||
* pageNumber = "{{ page }}"
|
||||
*
|
||||
* $this->propertyName('pageNumber'); // Returns "page"
|
||||
* $this->paramName('pageNumber'); // Returns NULL
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets names used by external properties.
|
||||
* @param array $names The key should be the property name,
|
||||
* the value should be the external property name.
|
||||
* @return void
|
||||
*/
|
||||
public function setExternalPropertyNames(array $names)
|
||||
{
|
||||
$this->externalPropertyNames = $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an external property name.
|
||||
* @param string $name Property name
|
||||
* @param string $extName External property name
|
||||
*/
|
||||
public function setExternalPropertyName($name, $extName)
|
||||
{
|
||||
return $this->externalPropertyNames[$name] = $extName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the external property name when the property value is an external property reference.
|
||||
* Otherwise the default value specified is returned.
|
||||
* @param string $name The property name
|
||||
* @param mixed $default
|
||||
* @return string
|
||||
*/
|
||||
public function propertyName($name, $default = null)
|
||||
{
|
||||
return array_get($this->externalPropertyNames, $name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the external property name when the property value is a routing parameter reference.
|
||||
* Otherwise the default value specified is returned.
|
||||
* @param string $name The property name
|
||||
* @param mixed $default
|
||||
* @return string
|
||||
*/
|
||||
public function paramName($name, $default = null)
|
||||
{
|
||||
if (($extName = $this->propertyName($name)) && substr($extName, 0, 1) == ':') {
|
||||
return substr($extName, 1);
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Returns a defined property or parameter value.
|
||||
* @todo Remove this method if year >= 2015
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class ComponentHelpers
|
|||
'type' => 'string',
|
||||
'validationPattern' => '^[a-zA-Z]+[0-9a-z\_]*$',
|
||||
'validationMessage' => Lang::get('cms::lang.component.validation_message'),
|
||||
'showExternalParameter' => false
|
||||
'showExternalParam' => false
|
||||
];
|
||||
$result[] = $property;
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ class ComponentHelpers
|
|||
'property' => $name,
|
||||
'title' => array_get($params, 'title', $name),
|
||||
'type' => array_get($params, 'type', 'string'),
|
||||
'showExternalParameter' => array_get($params, 'showExternalParameter', true)
|
||||
'showExternalParam' => array_get($params, 'showExternalParam', true)
|
||||
];
|
||||
|
||||
foreach ($params as $name => $value) {
|
||||
|
|
|
|||
|
|
@ -164,9 +164,9 @@ class Controller extends BaseController
|
|||
* Maintenance mode
|
||||
*/
|
||||
if (
|
||||
MaintenanceSettings::isConfigured()
|
||||
&& MaintenanceSettings::get('is_enabled', false)
|
||||
&& !BackendAuth::getUser()
|
||||
MaintenanceSettings::isConfigured() &&
|
||||
MaintenanceSettings::get('is_enabled', false) &&
|
||||
!BackendAuth::getUser()
|
||||
) {
|
||||
$page = Page::loadCached($this->theme, MaintenanceSettings::get('cms_page'));
|
||||
}
|
||||
|
|
@ -174,12 +174,16 @@ class Controller extends BaseController
|
|||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
if ($event = $this->fireEvent('page.beforeDisplay', [$url, $page], true)) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
if ($event = Event::fire('cms.page.beforeDisplay', [$this, $url, $page], true)) {
|
||||
return $event;
|
||||
if (
|
||||
($event = $this->fireEvent('page.beforeDisplay', [$url, $page], true)) ||
|
||||
($event = Event::fire('cms.page.beforeDisplay', [$this, $url, $page], true))
|
||||
) {
|
||||
if ($event instanceof Page) {
|
||||
$page = $event;
|
||||
}
|
||||
else {
|
||||
return $event;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -217,10 +221,11 @@ class Controller extends BaseController
|
|||
* The 'this' variable is reserved for default variables.
|
||||
*/
|
||||
$this->vars['this'] = [
|
||||
'controller' => $this,
|
||||
'layout' => $this->layout,
|
||||
'page' => $this->page,
|
||||
'layout' => $this->layout,
|
||||
'theme' => $this->theme,
|
||||
'param' => $this->router->getParameters(),
|
||||
'controller' => $this,
|
||||
'environment' => App::environment(),
|
||||
];
|
||||
|
||||
|
|
@ -248,11 +253,10 @@ class Controller extends BaseController
|
|||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
if ($event = $this->fireEvent('page.init', [$url, $page], true)) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
if ($event = Event::fire('cms.page.init', [$this, $url, $page], true)) {
|
||||
if (
|
||||
($event = $this->fireEvent('page.init', [$url, $page], true)) ||
|
||||
($event = Event::fire('cms.page.init', [$this, $url, $page], true))
|
||||
) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
|
|
@ -310,11 +314,10 @@ class Controller extends BaseController
|
|||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
if ($event = $this->fireEvent('page.display', [$url, $page], true)) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
if ($event = Event::fire('cms.page.display', [$this, $url, $page], true)) {
|
||||
if (
|
||||
($event = $this->fireEvent('page.display', [$url, $page], true)) ||
|
||||
($event = Event::fire('cms.page.display', [$this, $url, $page], true))
|
||||
) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
|
|
@ -428,7 +431,7 @@ class Controller extends BaseController
|
|||
$this->vars[$alias] = $this->page->components[$alias] = $componentObj;
|
||||
}
|
||||
|
||||
$this->setComponentPropertiesFromParameters($componentObj);
|
||||
$this->setComponentPropertiesFromParams($componentObj);
|
||||
$componentObj->init();
|
||||
$componentObj->onInit(); // Deprecated: Remove ithis line if year >= 2015
|
||||
return $componentObj;
|
||||
|
|
@ -500,7 +503,7 @@ class Controller extends BaseController
|
|||
$responseContents['X_OCTOBER_REDIRECT'] = $result->getTargetUrl();
|
||||
}
|
||||
|
||||
return Response::make()->setContent($responseContents);
|
||||
return Response::make($responseContents, $this->statusCode);
|
||||
}
|
||||
catch (ValidationException $ex) {
|
||||
/*
|
||||
|
|
@ -510,24 +513,8 @@ class Controller extends BaseController
|
|||
$responseContents['X_OCTOBER_ERROR_MESSAGE'] = $ex->getMessage();
|
||||
return Response::make($responseContents, 406);
|
||||
}
|
||||
catch (ApplicationException $ex) {
|
||||
return Response::make($ex->getMessage(), 500);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
/*
|
||||
* Display a "dumbed down" error if custom page is activated
|
||||
* otherwise display a more detailed error.
|
||||
*/
|
||||
if (Config::get('cms.customErrorPage', false)) {
|
||||
return Response::make($ex->getMessage(), 500);
|
||||
}
|
||||
|
||||
return Response::make(sprintf(
|
||||
'"%s" on line %s of %s',
|
||||
$ex->getMessage(),
|
||||
$ex->getLine(),
|
||||
$ex->getFile()
|
||||
), 500);
|
||||
return Response::make(ApplicationException::getDetailedMessage($ex), 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -601,11 +588,10 @@ class Controller extends BaseController
|
|||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
if ($event = $this->fireEvent('page.start', [], true)) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
if ($event = Event::fire('cms.page.start', [$this], true)) {
|
||||
if (
|
||||
($event = $this->fireEvent('page.start', [], true)) ||
|
||||
($event = Event::fire('cms.page.start', [$this], true))
|
||||
) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
|
|
@ -614,9 +600,9 @@ class Controller extends BaseController
|
|||
*/
|
||||
if ($this->layoutObj) {
|
||||
CmsException::mask($this->layout, 300);
|
||||
$response = (($result = $this->layoutObj->onStart())
|
||||
|| ($result = $this->layout->runComponents())
|
||||
|| ($result = $this->layoutObj->onBeforePageStart())) ? $result: null;
|
||||
$response = (($result = $this->layoutObj->onStart()) ||
|
||||
($result = $this->layout->runComponents()) ||
|
||||
($result = $this->layoutObj->onBeforePageStart())) ? $result: null;
|
||||
CmsException::unmask();
|
||||
|
||||
if ($response) {
|
||||
|
|
@ -628,9 +614,9 @@ class Controller extends BaseController
|
|||
* Run page functions
|
||||
*/
|
||||
CmsException::mask($this->page, 300);
|
||||
$response = (($result = $this->pageObj->onStart())
|
||||
|| ($result = $this->page->runComponents())
|
||||
|| ($result = $this->pageObj->onEnd())) ? $result : null;
|
||||
$response = (($result = $this->pageObj->onStart()) ||
|
||||
($result = $this->page->runComponents()) ||
|
||||
($result = $this->pageObj->onEnd())) ? $result : null;
|
||||
CmsException::unmask();
|
||||
|
||||
if ($response) {
|
||||
|
|
@ -649,11 +635,10 @@ class Controller extends BaseController
|
|||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
if ($event = $this->fireEvent('page.end', [], true)) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
if ($event = Event::fire('cms.page.end', [$this], true)) {
|
||||
if (
|
||||
($event = $this->fireEvent('page.end', [], true)) ||
|
||||
($event = Event::fire('cms.page.end', [$this], true))
|
||||
) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
|
|
@ -671,11 +656,10 @@ class Controller extends BaseController
|
|||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
if ($event = $this->fireEvent('page.render', [$contents], true)) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
if ($event = Event::fire('cms.page.render', [$this, $contents], true)) {
|
||||
if (
|
||||
($event = $this->fireEvent('page.render', [$contents], true)) ||
|
||||
($event = Event::fire('cms.page.render', [$this, $contents], true))
|
||||
) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
|
|
@ -809,7 +793,7 @@ class Controller extends BaseController
|
|||
'obj' => $componentObj
|
||||
]);
|
||||
|
||||
$this->setComponentPropertiesFromParameters($componentObj, $parameters);
|
||||
$this->setComponentPropertiesFromParams($componentObj, $parameters);
|
||||
$componentObj->init();
|
||||
$componentObj->onInit(); // Deprecated: Remove ithis line if year >= 2015
|
||||
}
|
||||
|
|
@ -855,10 +839,10 @@ class Controller extends BaseController
|
|||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
if ($event = $this->fireEvent('page.beforeRenderContent', [$name], true)) {
|
||||
$content = $event;
|
||||
}
|
||||
elseif ($event = Event::fire('cms.page.beforeRenderContent', [$this, $name], true)) {
|
||||
if (
|
||||
($event = $this->fireEvent('page.beforeRenderContent', [$name], true)) ||
|
||||
($event = Event::fire('cms.page.beforeRenderContent', [$this, $name], true))
|
||||
) {
|
||||
$content = $event;
|
||||
}
|
||||
/*
|
||||
|
|
@ -873,11 +857,10 @@ class Controller extends BaseController
|
|||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
if ($event = $this->fireEvent('page.renderContent', [$name, $fileContent], true)) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
if ($event = Event::fire('cms.page.renderContent', [$this, $name, $fileContent], true)) {
|
||||
if (
|
||||
($event = $this->fireEvent('page.renderContent', [$name, $fileContent], true)) ||
|
||||
($event = Event::fire('cms.page.renderContent', [$this, $name, $fileContent], true))
|
||||
) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
|
|
@ -1119,7 +1102,7 @@ class Controller extends BaseController
|
|||
* @param array $parameters Specifies the partial parameters.
|
||||
* @return Returns updated properties.
|
||||
*/
|
||||
protected function setComponentPropertiesFromParameters($component, $parameters = [])
|
||||
protected function setComponentPropertiesFromParams($component, $parameters = [])
|
||||
{
|
||||
$properties = $component->getProperties();
|
||||
$routerParameters = $this->router->getParameters();
|
||||
|
|
@ -1143,6 +1126,7 @@ class Controller extends BaseController
|
|||
}
|
||||
|
||||
$component->setProperty($propertyName, $newPropertyValue);
|
||||
$component->setExternalPropertyName($propertyName, $paramName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class Page extends CmsCompoundObject
|
|||
|
||||
protected $settingsValidationRules = [
|
||||
'title' => 'required',
|
||||
'url' => ['required', 'regex:/^\/[a-z0-9\/\:_\-\*\[\]\+\?\|\.]*$/i']
|
||||
'url' => ['required', 'regex:/^\/[a-z0-9\/\:_\-\*\[\]\+\?\|\.\^\$]*$/i']
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -104,7 +104,7 @@ class Page extends CmsCompoundObject
|
|||
public static function url($page, $params = [], $absolute = true)
|
||||
{
|
||||
/*
|
||||
* Reuse existing controller or create a new one,
|
||||
* Reuse existing controller or create a new one,
|
||||
* assuming that the method is called not during the front-end
|
||||
* request processing.
|
||||
*/
|
||||
|
|
@ -181,11 +181,13 @@ class Page extends CmsCompoundObject
|
|||
return;
|
||||
}
|
||||
|
||||
$page = self::loadCached($theme, $item->reference);
|
||||
$pageUrl = self::url($item->reference);
|
||||
|
||||
$result = [];
|
||||
$result['url'] = $pageUrl;
|
||||
$result['isActive'] = $pageUrl == $url;
|
||||
$result['mtime'] = $page ? $page->mtime : null;
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use DbDongle;
|
|||
use October\Rain\Support\Yaml;
|
||||
use System\Models\Parameters;
|
||||
use System\Classes\SystemException;
|
||||
use Cms\Models\ThemeData;
|
||||
use DirectoryIterator;
|
||||
|
||||
/**
|
||||
|
|
@ -47,10 +48,13 @@ class Theme
|
|||
|
||||
/**
|
||||
* Loads the theme.
|
||||
* @return self
|
||||
*/
|
||||
public function load($dirName)
|
||||
public static function load($dirName)
|
||||
{
|
||||
$this->dirName = $dirName;
|
||||
$theme = new static;
|
||||
$theme->setDirName($dirName);
|
||||
return $theme;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -67,6 +71,15 @@ class Theme
|
|||
return base_path().Config::get('cms.themesDir').'/'.$dirName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the theme directory name.
|
||||
* @return void
|
||||
*/
|
||||
public function setDirName($dirName)
|
||||
{
|
||||
$this->dirName = $dirName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the theme directory name.
|
||||
* @return string
|
||||
|
|
@ -83,8 +96,8 @@ class Theme
|
|||
*/
|
||||
public static function exists($dirName)
|
||||
{
|
||||
$theme = new static;
|
||||
$path = $theme->getPath($dirName);
|
||||
$theme = static::load($dirName);
|
||||
$path = $theme->getPath();
|
||||
|
||||
return File::isDirectory($path);
|
||||
}
|
||||
|
|
@ -121,7 +134,7 @@ class Theme
|
|||
->pluck('value')
|
||||
;
|
||||
|
||||
if ($dbResult !== null) {
|
||||
if ($dbResult !== null && static::exists($dbResult)) {
|
||||
$activeTheme = $dbResult;
|
||||
}
|
||||
}
|
||||
|
|
@ -135,8 +148,8 @@ class Theme
|
|||
throw new SystemException(Lang::get('cms::lang.theme.active.not_set'));
|
||||
}
|
||||
|
||||
$theme = new static;
|
||||
$theme->load($activeTheme);
|
||||
$theme = static::load($activeTheme);
|
||||
|
||||
if (!File::isDirectory($theme->getPath())) {
|
||||
return self::$activeThemeCache = null;
|
||||
}
|
||||
|
|
@ -184,8 +197,8 @@ class Theme
|
|||
throw new SystemException(Lang::get('cms::lang.theme.edit.not_set'));
|
||||
}
|
||||
|
||||
$theme = new static;
|
||||
$theme->load($editTheme);
|
||||
$theme = static::load($editTheme);
|
||||
|
||||
if (!File::isDirectory($theme->getPath())) {
|
||||
return self::$editThemeCache = null;
|
||||
}
|
||||
|
|
@ -210,8 +223,8 @@ class Theme
|
|||
continue;
|
||||
}
|
||||
|
||||
$theme = new static;
|
||||
$theme->load($fileinfo->getFilename());
|
||||
$theme = static::load($fileinfo->getFilename());
|
||||
|
||||
$result[] = $theme;
|
||||
}
|
||||
|
||||
|
|
@ -245,12 +258,7 @@ class Theme
|
|||
*/
|
||||
public function getConfigValue($name, $default = null)
|
||||
{
|
||||
$config = $this->getConfig();
|
||||
if (isset($config[$name])) {
|
||||
return $config[$name];
|
||||
}
|
||||
|
||||
return $default;
|
||||
return array_get($this->getConfig(), $name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -280,4 +288,44 @@ class Theme
|
|||
Cache::forget(self::ACTIVE_KEY);
|
||||
Cache::forget(self::EDIT_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this theme has form fields that supply customization data.
|
||||
* @return bool
|
||||
*/
|
||||
public function hasCustomData()
|
||||
{
|
||||
return $this->getConfigValue('form', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the getter functionality.
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if ($this->hasCustomData()) {
|
||||
$theme = ThemeData::forTheme($this);
|
||||
return $theme->{$name};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an attribute exists on the object.
|
||||
* @param string $key
|
||||
* @return void
|
||||
*/
|
||||
public function __isset($key)
|
||||
{
|
||||
if ($this->hasCustomData()) {
|
||||
$theme = ThemeData::forTheme($this);
|
||||
return isset($theme->{$key});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
<?php namespace Cms\Controllers;
|
||||
|
||||
use Config;
|
||||
use BackendMenu;
|
||||
use Lang;
|
||||
use Input;
|
||||
use Config;
|
||||
use Backend;
|
||||
use Redirect;
|
||||
use BackendMenu;
|
||||
use Backend\Classes\Controller;
|
||||
use System\Classes\SettingsManager;
|
||||
use Cms\Models\ThemeData;
|
||||
use Cms\Classes\Theme as CmsTheme;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Theme selector controller
|
||||
|
|
@ -16,9 +21,13 @@ use Cms\Classes\Theme as CmsTheme;
|
|||
*/
|
||||
class Themes extends Controller
|
||||
{
|
||||
public $requiredPermissions = ['cms.manage_themes'];
|
||||
public $implement = [
|
||||
'Backend.Behaviors.FormController'
|
||||
];
|
||||
|
||||
public $bodyClass = 'compact-container';
|
||||
public $formConfig = 'config_form.yaml';
|
||||
|
||||
public $requiredPermissions = ['cms.manage_themes'];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
|
@ -36,6 +45,7 @@ class Themes extends Controller
|
|||
|
||||
public function index()
|
||||
{
|
||||
$this->bodyClass = 'compact-container';
|
||||
}
|
||||
|
||||
public function index_onSetActiveTheme()
|
||||
|
|
@ -46,4 +56,67 @@ class Themes extends Controller
|
|||
'#theme-list' => $this->makePartial('theme_list')
|
||||
];
|
||||
}
|
||||
|
||||
//
|
||||
// Theme customization
|
||||
//
|
||||
|
||||
public function update($dirName)
|
||||
{
|
||||
try {
|
||||
$model = $this->getThemeData($dirName);
|
||||
$this->asExtension('FormController')->update($model->id);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
}
|
||||
|
||||
public function update_onSave($dirName)
|
||||
{
|
||||
$model = $this->getThemeData($dirName);
|
||||
$this->asExtension('FormController')->update_onSave($model->id);
|
||||
}
|
||||
|
||||
public function update_onResetDefault($dirName)
|
||||
{
|
||||
$model = $this->getThemeData($dirName);
|
||||
$model->delete();
|
||||
|
||||
$redirectUrl = Backend::url('cms/themes/update/'.$dirName);
|
||||
return Redirect::to($redirectUrl);
|
||||
}
|
||||
|
||||
protected function getThemeData($dirName)
|
||||
{
|
||||
if (!$theme = CmsTheme::load($dirName))
|
||||
throw new Exception(Lang::get('Unable to find theme with name :name', $dirName));
|
||||
|
||||
$model = ThemeData::forTheme($theme);
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add form fields defined in theme.yaml
|
||||
*/
|
||||
protected function formExtendFields($form)
|
||||
{
|
||||
$model = $form->model;
|
||||
|
||||
if (!$theme = CmsTheme::load($model->theme))
|
||||
throw new Exception(Lang::get('Unable to find theme with name :name', $this->theme));
|
||||
|
||||
if ($fields = $theme->getConfigValue('form.fields')) {
|
||||
$form->addFields($fields);
|
||||
}
|
||||
|
||||
if ($fields = $theme->getConfigValue('form.tabs.fields')) {
|
||||
$form->addTabFields($fields);
|
||||
}
|
||||
|
||||
if ($fields = $theme->getConfigValue('form.secondaryTabs.fields')) {
|
||||
$form->addSecondaryTabFields($fields);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<?php foreach ($themes as $index => $theme): ?>
|
||||
<?php
|
||||
$isLast = $index == $cnt-1;
|
||||
$isActive = $activeTheme->getDirName() == $theme->getDirName();
|
||||
$isActive = $activeTheme && $activeTheme->getDirName() == $theme->getDirName();
|
||||
$author = $theme->getConfigValue('author');
|
||||
?>
|
||||
<div class="layout-row <?= $isLast ? 'last' : null ?> min-size <?= $isActive ? 'active' : null ?>">
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<div class="layout-cell min-height theme-description">
|
||||
<h3><?= e($theme->getConfigValue('name', $theme->getDirName())) ?></h3>
|
||||
<?php if (strlen($author)): ?>
|
||||
<p class="author">by <a href="<?= e($theme->getConfigValue('authorUrl', '#')) ?>"><?= e($author) ?></a></p>
|
||||
<p class="author">by <a href="<?= e($theme->getConfigValue('homepage', '#')) ?>"><?= e($author) ?></a></p>
|
||||
<?php endif ?>
|
||||
<p class="description"><?= e($theme->getConfigValue('description', 'The theme description is not provided.')) ?></p>
|
||||
<div class="controls">
|
||||
|
|
@ -35,9 +35,18 @@
|
|||
data-request-data="theme: '<?= e($theme->getDirName()) ?>'"
|
||||
data-stripe-load-indicator
|
||||
class="btn btn-primary">
|
||||
<i class="icon-check"></i>
|
||||
<?= e(trans('cms::lang.theme.activate_button')) ?>
|
||||
</button>
|
||||
<?php endif ?>
|
||||
<?php if ($theme->hasCustomData()): ?>
|
||||
<a
|
||||
href="<?= Backend::url('cms/themes/update/'.$theme->getDirName()) ?>"
|
||||
class="btn btn-default">
|
||||
<i class="icon-pencil"></i>
|
||||
<?= e(trans('cms::lang.theme.customize_button')) ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# ===================================
|
||||
# Form Behavior Config
|
||||
# ===================================
|
||||
|
||||
# Record name
|
||||
name: Theme
|
||||
|
||||
# Fields are defined by extension
|
||||
form: []
|
||||
|
||||
# Model Class name
|
||||
modelClass: Cms\Models\ThemeData
|
||||
|
||||
# Default redirect location
|
||||
defaultRedirect: cms/themes
|
||||
|
||||
# Update page
|
||||
update:
|
||||
title: Customize Theme
|
||||
redirect: cms/themes
|
||||
redirectClose: cms/themes
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('cms/themes') ?>">Themes</a></li>
|
||||
<li><?= e(trans($this->pageTitle)) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<?= Form::open(['class'=>'layout']) ?>
|
||||
|
||||
<div class="layout-row">
|
||||
<?= $this->formRender() ?>
|
||||
</div>
|
||||
|
||||
<div class="form-buttons">
|
||||
<div class="loading-indicator-container">
|
||||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="Saving theme..."
|
||||
class="btn btn-primary">
|
||||
<u>S</u>ave
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="Saving theme..."
|
||||
class="btn btn-default">
|
||||
Save and Close
|
||||
</button>
|
||||
|
||||
<span class="btn-text">
|
||||
or <a href="<?= Backend::url('cms/themes') ?>">Cancel</a>
|
||||
</span>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-danger pull-right"
|
||||
data-request="onResetDefault"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.resetting')) ?>"
|
||||
data-request-confirm="<?= e(trans('backend::lang.form.action_confirm')) ?>">
|
||||
<?= e(trans('backend::lang.form.reset_default')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= Form::close() ?>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||
<p><a href="<?= Backend::url('cms/themes') ?>" class="btn btn-default">Return to themes list</a></p>
|
||||
|
||||
<?php endif ?>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class DbCmsThemeData extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('cms_theme_data', function (Blueprint $table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id');
|
||||
$table->string('theme')->nullable()->index();
|
||||
$table->mediumtext('data')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('cms_theme_data');
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ return [
|
|||
'menu_label' => 'Seiten',
|
||||
'no_list_records' => 'Keine Seiten gefunden',
|
||||
'new' => 'Neue Seite',
|
||||
'invalid_url' => 'Ungültiges URL-Format. Die URL muss mit einem Slash beginnen und darf nur Ziffern, lateinische Zeichen und die folgenden Symbole beinhalten: ._-[]:?|/+*',
|
||||
'invalid_url' => 'Ungültiges URL-Format. Die URL muss mit einem Slash beginnen und darf nur Ziffern, lateinische Zeichen und die folgenden Symbole beinhalten: ._-[]:?|/+*^$',
|
||||
'delete_confirm_multiple' => 'Wollen Sie die ausgewählten Seiten wirklich löschen?',
|
||||
'delete_confirm_single' => 'Wollen Sie diese Seite wirklich löschen?',
|
||||
'no_layout' => '-- Kein Layout --'
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ return [
|
|||
'find_more_themes' => 'Find more themes on OctoberCMS Theme Marketplace.',
|
||||
'activate_button' => 'Activate',
|
||||
'active_button' => 'Activate',
|
||||
'customize_button' => 'Customize',
|
||||
],
|
||||
'maintenance' => [
|
||||
'settings_menu' => 'Maintenance mode',
|
||||
|
|
@ -47,7 +48,7 @@ return [
|
|||
'unsaved_label' => 'Unsaved page(s)',
|
||||
'no_list_records' => 'No pages found',
|
||||
'new' => 'New page',
|
||||
'invalid_url' => 'Invalid URL format. The URL should start with the forward slash symbol and can contain digits, Latin letters and the following symbols: ._-[]:?|/+*',
|
||||
'invalid_url' => 'Invalid URL format. The URL should start with the forward slash symbol and can contain digits, Latin letters and the following symbols: ._-[]:?|/+*^$',
|
||||
'delete_confirm_multiple' => 'Do you really want to delete selected pages?',
|
||||
'delete_confirm_single' => 'Do you really want delete this page?',
|
||||
'no_layout' => '-- no layout --'
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ return [
|
|||
'menu_label' => 'Páginas',
|
||||
'no_list_records' => 'No se encontraron páginas',
|
||||
'new' => 'Nueva página',
|
||||
'invalid_url' => 'Formato de URL inválido. El URL debe comenzar con el símbolo de barra diagonal y puede contener dígitos, letras latinas y los siguientes símbolos: _-[]:?|/+*',
|
||||
'invalid_url' => 'Formato de URL inválido. El URL debe comenzar con el símbolo de barra diagonal y puede contener dígitos, letras latinas y los siguientes símbolos: _-[]:?|/+*^$',
|
||||
'delete_confirm_multiple' => '¿Realmente quiere eliminar las páginas seleccionadas?',
|
||||
'delete_confirm_single' => '¿Realmente quieres eliminar esta página?',
|
||||
'no_layout' => '-- ninguna disposición --'
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
'menu_label' => 'Páginas',
|
||||
'no_list_records' => 'No se encontraron páginas',
|
||||
'new' => 'Nueva página',
|
||||
'invalid_url' => 'Formato de URL inválido. El URL debe comenzar con el símbolo de barra diagonal y puede contener dígitos, letras latinas y los siguientes símbolos: _-[]:?|/+*',
|
||||
'invalid_url' => 'Formato de URL inválido. El URL debe comenzar con el símbolo de barra diagonal y puede contener dígitos, letras latinas y los siguientes símbolos: _-[]:?|/+*^$',
|
||||
'delete_confirm_multiple' => '¿Realmente quiere eliminar las páginas seleccionadas?',
|
||||
'delete_confirm_single' => '¿Realmente quieres eliminar esta página?',
|
||||
'no_layout' => '-- ninguna disposición --'
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ return [
|
|||
'menu_label' => 'صفخات',
|
||||
'no_list_records' => 'صفحه ای یافت نشد',
|
||||
'new' => 'New page',
|
||||
'invalid_url' => 'قالب آدرس صحیح نمی باشد. آدرس باید با اسلش شروع شده و می تواند شامل اعداد، خروف لاتین و این کاراکتر ها باشد: ._-[]:?|/+*',
|
||||
'invalid_url' => 'قالب آدرس صحیح نمی باشد. آدرس باید با اسلش شروع شده و می تواند شامل اعداد، خروف لاتین و این کاراکتر ها باشد: ._-[]:?|/+*^$',
|
||||
'delete_confirm_multiple' => 'آیا از حذف صفحات انتخاب شده اطمینان دارید؟',
|
||||
'delete_confirm_single' => 'آیا از حذف این صفحه اطمینان دارید؟',
|
||||
'no_layout' => '-- بدون طرح بندی --'
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ return [
|
|||
'menu_label' => 'Pages',
|
||||
'no_list_records' => 'Aucune page trouvée',
|
||||
'new' => 'Nouvelle page',
|
||||
'invalid_url' => 'Format d\'URL invalide. L\'URL doit commencer par un / et peut contenit des chiffres, des lettres et les symboles suivants: ._-[]:?|/+*',
|
||||
'invalid_url' => 'Format d\'URL invalide. L\'URL doit commencer par un / et peut contenit des chiffres, des lettres et les symboles suivants: ._-[]:?|/+*^$',
|
||||
'delete_confirm_multiple' => 'Voulez-vous vraiment supprimer les pages sélectionnées ?',
|
||||
'delete_confirm_single' => 'Voulez-vous vraiment supprimer cette page ?',
|
||||
'no_layout' => '-- aucun layout --'
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ return [
|
|||
'find_more_themes' => 'További témák az OctoberCMS témák piacterén.',
|
||||
'activate_button' => 'Aktiválás',
|
||||
'active_button' => 'Aktiválás',
|
||||
'customize_button' => 'Testreszabás',
|
||||
],
|
||||
'maintenance' => [
|
||||
'settings_menu' => 'Karbantartás mód',
|
||||
|
|
@ -47,7 +48,7 @@ return [
|
|||
'unsaved_label' => 'Nem mentett lap(ok)',
|
||||
'no_list_records' => 'Nem találhatók lapok',
|
||||
'new' => 'Új lap',
|
||||
'invalid_url' => 'Érvénytelen URL-címformátum. Az URL-címnek perjellel kell kezdődnie, és számokat, latin betűket, valamint a következő számokat tartalmazhatja: ._-[]:?|/+*',
|
||||
'invalid_url' => 'Érvénytelen URL-címformátum. Az URL-címnek perjellel kell kezdődnie, és számokat, latin betűket, valamint a következő számokat tartalmazhatja: ._-[]:?|/+*^$',
|
||||
'delete_confirm_multiple' => 'Valóban törölni akarja a kijelölt lapokat?',
|
||||
'delete_confirm_single' => 'Valóban törölni akarja ezt a lapot?',
|
||||
'no_layout' => '-- nincs elrendezés --'
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ return [
|
|||
'menu_label' => 'Pagine',
|
||||
'no_list_records' => 'Pagine non trovate',
|
||||
'new' => 'Nuova pagina',
|
||||
'invalid_url' => 'Formato URL non valido. L\'URL deve iniziare con una barra e può contenere numeri, lettere e i seguenti simboli: ._-[]:?|/+*',
|
||||
'invalid_url' => 'Formato URL non valido. L\'URL deve iniziare con una barra e può contenere numeri, lettere e i seguenti simboli: ._-[]:?|/+*^$',
|
||||
'delete_confirm_multiple' => 'Sei sicuro di voler eliminare le pagine selezionate?',
|
||||
'delete_confirm_single' => 'Sei sicuro di voler eliminare questa pagina?',
|
||||
'no_layout' => '-- nessun layout --'
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ return [
|
|||
'menu_label' => 'ページ',
|
||||
'no_list_records' => 'ページが見つかりません',
|
||||
'new' => '新ページ',
|
||||
'invalid_url' => '正しくないURL形式。URLはスラッシュ(/)で始まり、数字、ラテン文字、._-[]:?|/+*で構成します。',
|
||||
'invalid_url' => '正しくないURL形式。URLはスラッシュ(/)で始まり、数字、ラテン文字、._-[]:?|/+*^$で構成します。',
|
||||
'delete_confirm_multiple' => '指定した全ページを本当に削除しますか?',
|
||||
'delete_confirm_single' => '本当にこのページを削除しますか?',
|
||||
'no_layout' => '-- レイアウト無し --'
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ return [
|
|||
'menu_label' => 'Pagina\'s',
|
||||
'no_list_records' => 'Geen pagina\'s gevonden',
|
||||
'new' => 'Nieuwe pagina',
|
||||
'invalid_url' => 'Ongeldig URL formaat. De URL moet beginnen met een schuine streep en mag enkel bestaan uit letters, cijfers en de volgende tekens: ._-[]:?|/+*',
|
||||
'invalid_url' => 'Ongeldig URL formaat. De URL moet beginnen met een schuine streep en mag enkel bestaan uit letters, cijfers en de volgende tekens: ._-[]:?|/+*^$',
|
||||
'delete_confirm_multiple' => 'Weet je zeker dat je de geselecteerde pagina\'s wilt verwijderen?',
|
||||
'delete_confirm_single' => 'Weet je zeker dat je deze pagina wilt verwijderen?',
|
||||
'no_layout' => '-- geen layout --'
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ return [
|
|||
'menu_label' => 'Páginas',
|
||||
'no_list_records' => 'Nenhuma página foi encontradas',
|
||||
'new' => 'Nova página',
|
||||
'invalid_url' => 'Formato de URL inválido. O URL deve começar com o símbolo de barra e pode conter dígitos, letras latinas e os seguintes símbolos: _-[]:?|/+*',
|
||||
'invalid_url' => 'Formato de URL inválido. O URL deve começar com o símbolo de barra e pode conter dígitos, letras latinas e os seguintes símbolos: _-[]:?|/+*^$',
|
||||
'delete_confirm_multiple' => 'Você realmente quer excluir as páginas selecionadas?',
|
||||
'delete_confirm_single' => 'Você realmente quer excluir esta página?',
|
||||
'no_layout' => '-- sem layout --'
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ return [
|
|||
'menu_label' => 'Pagini',
|
||||
'no_list_records' => 'Nu au fost gasite pagini',
|
||||
'new' => 'Pagina noua',
|
||||
'invalid_url' => 'Format URL invalid. URL-ul ar trebui sa inceapa cu un slash ( / ) si poate sa contina cifre, caractere latine si urmatoarele simboluri: ._-[]:?|/+*',
|
||||
'invalid_url' => 'Format URL invalid. URL-ul ar trebui sa inceapa cu un slash ( / ) si poate sa contina cifre, caractere latine si urmatoarele simboluri: ._-[]:?|/+*^$',
|
||||
'delete_confirm_multiple' => 'Vreti sa stergeti paginile selectate?',
|
||||
'delete_confirm_single' => 'Vreti sa stergeti aceasta pagina?',
|
||||
'no_layout' => '-- fara macheta --'
|
||||
|
|
|
|||