Merge branch 'develop' into builder-ui

Conflicts:
	modules/backend/assets/css/october.css
This commit is contained in:
alekseybobkov 2015-07-10 18:05:27 -07:00
commit 3851ae68cd
42 changed files with 4431 additions and 5626 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

File diff suppressed because one or more lines are too long

View File

@ -33,7 +33,7 @@
}
.control-tabs, &.control-tabs {
&.master {
&.master-tabs {
overflow: hidden;
&:before, &:after {
@ -178,7 +178,7 @@
}
}
&.secondary {
&.secondary-tabs {
> div > ul.nav-tabs {
background: @color-fancy-secondary-tabs-bg;
> li {
@ -215,7 +215,7 @@
}
}
&.content-tabs {
&.secondary-content-tabs {
> div > ul.nav-tabs {
background: @body-bg;
@ -308,7 +308,7 @@
}
}
&.primary {
&.primary-tabs {
> div > ul.nav-tabs {
background: @color-fancy-primary-tabs-bg;
margin-left: 0!important;
@ -559,7 +559,7 @@
}
}
.content-tabs .field-richeditor {
.secondary-content-tabs .field-richeditor {
.redactor-box .redactor-toolbar {
margin: 20px 20px 0 20px!important;
.border-radius(3px)!important;

View File

@ -42,6 +42,25 @@ body {
height: 100%;
}
//
// Tabs override for Layout
// Primary tabs should use inset by default, unless otherwise specified
// --------------------------------------------------
.control-tabs.primary-tabs {
> ul.nav-tabs, > div > ul.nav-tabs, > div > div > ul.nav-tabs {
margin-left: -20px;
margin-right: -20px;
}
&.tabs-no-inset {
> ul.nav-tabs, > div > ul.nav-tabs, > div > div > ul.nav-tabs {
margin-left: 0;
margin-right: 0;
}
}
}
//
// Flexible layout system
// --------------------------------------------------

View File

@ -3,21 +3,16 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="backend-base-path" content="<?= Backend::baseUrl() ?>">
<meta name="csrf-token" content="<?= csrf_token() ?>">
<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.min.js') ?>"></script>
<script src="<?= URL::to('modules/system/assets/js/framework.js') ?>"></script>
<script src="<?= URL::to('modules/system/assets/ui/js/flashmessage.js') ?>"></script>
<script src="<?= URL::to('modules/system/assets/ui/vendor/modernizr/modernizr.js') ?>"></script>
<script src="<?= URL::to('modules/backend/assets/js/october.utils.js') ?>"></script>
<script src="<?= URL::to('modules/backend/assets/js/october-min.js') ?>"></script>
<script src="<?= URL::to('modules/backend/assets/js/auth/auth.js') ?>"></script>
<script>
<!--
backendBasePath = '<?= Backend::baseUrl() ?>';
// -->
</script>
<?= $this->makeAssets() ?>
<?= Block::placeholder('head') ?>
<?= $this->makeLayoutPartial('custom_styles') ?>

View File

@ -172,10 +172,10 @@ class BrandSettings extends Model
* Compile CSS
*/
$css = '';
$css .= '.fancy-layout .control-tabs.master > div > div.tabs-container > ul.nav-tabs > li a > span.title:before,';
$css .= '.fancy-layout.control-tabs.master > div > div.tabs-container > ul.nav-tabs > li a > span.title:before,';
$css .= '.fancy-layout .control-tabs.master > div > div.tabs-container > ul.nav-tabs > li a > span.title:after,';
$css .= '.fancy-layout.control-tabs.master > div > div.tabs-container > ul.nav-tabs > li a > span.title:after {';
$css .= '.fancy-layout .control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li a > span.title:before,';
$css .= '.fancy-layout.control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li a > span.title:before,';
$css .= '.fancy-layout .control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li a > span.title:after,';
$css .= '.fancy-layout.control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li a > span.title:after {';
$css .= "background-image: url('".$svg."')}";
return $css;

View File

@ -116,13 +116,13 @@ table.table.data {
.fancy-layout {
.control-tabs, &.control-tabs {
&.master {
&.master-tabs {
> div > div.tabs-container {
background: @color-fancy-master-tabs-bg;
}
}
&.secondary {
&.secondary-tabs {
&.content-tabs {
&.primary-collapsed {
> div > ul.nav-tabs {
@ -132,7 +132,7 @@ table.table.data {
}
}
&.primary {
&.primary-tabs {
> div > ul.nav-tabs {
&.master-area {
background: @color-fancy-form-tabless-fields-bg;
@ -140,7 +140,7 @@ table.table.data {
}
}
&.master {
&.master-tabs {
> div > div.tabs-container > ul.nav-tabs > li {
a > span.title {
background-color: @color-fancy-form-inactive-tab;
@ -184,7 +184,7 @@ div.control-componentlist {
.fancy-layout {
.control-tabs, &.control-tabs {
&.primary {
&.primary-tabs {
> div > ul.nav-tabs {
&.component-area {
background: @color-component-list-bg;

View File

@ -21,7 +21,7 @@
<div
id="<?= $this->getId($type.'Tabs') ?>"
class="control-tabs <?= $type ?> layout <?= $tabs->cssClass ?>"
class="control-tabs <?= $type ?>-tabs layout <?= $tabs->cssClass ?>"
data-control="tab"
data-slidable>
<?= $this->makePartial('form_tabs', ['tabs' => $tabs]) ?>

View File

@ -106,9 +106,21 @@
return this
}
// LIST WIDGET HELPERS
// =================
if ($.oc === undefined)
$.oc = {}
$.oc.listToggleChecked = function(el) {
$(el)
.closest('[data-control="listwidget"]')
.listWidget('toggleChecked', el)
}
// LIST WIDGET DATA-API
// ==============
$(document).render(function(){
$('[data-control="listwidget"]').listWidget();
})

View File

@ -326,8 +326,8 @@ div.control-componentlist div.components div.layout-cell.adding > div {
text-shadow: 0 0 1px #475354 !important;
color: #ffffff !important;
}
.fancy-layout .control-tabs.primary > div > ul.nav-tabs.component-area,
.fancy-layout.control-tabs.primary > div > ul.nav-tabs.component-area {
.fancy-layout .control-tabs.primary-tabs > div > ul.nav-tabs.component-area,
.fancy-layout.control-tabs.primary-tabs > div > ul.nav-tabs.component-area {
background: #2c3e50;
}
body.drag div.control-componentlist div.components div.layout-cell > div,

View File

@ -120,7 +120,7 @@
var $el = $(element),
$panel = $el.closest('.form-tabless-fields.collapsed'),
$primaryPanel = $el.closest('.control-tabs.primary.collapsed')
$primaryPanel = $el.closest('.control-tabs.primary-tabs.collapsed')
if ($panel.length > 0)
$panel.removeClass('collapsed')
@ -129,7 +129,7 @@
$primaryPanel.removeClass('collapsed')
var pane = $primaryPanel.closest('.tab-pane'),
$secondaryPanel = $('.control-tabs.secondary', pane)
$secondaryPanel = $('.control-tabs.secondary-tabs', pane)
$secondaryPanel.removeClass('primary-collapsed')
}
@ -208,8 +208,8 @@
})
var $primaryCollapseIcon = $('<a href="javascript:;" class="tab-collapse-icon primary"><i class="icon-chevron-down"></i></a>'),
$primaryPanel = $('.control-tabs.primary', data.pane),
$secondaryPanel = $('.control-tabs.secondary', data.pane),
$primaryPanel = $('.control-tabs.primary-tabs', data.pane),
$secondaryPanel = $('.control-tabs.secondary-tabs', data.pane),
$primaryTabContainer = $('.nav-tabs', $primaryPanel)
$primaryTabContainer.addClass('master-area')
@ -511,7 +511,7 @@
CmsPage.prototype.updateComponentListClass = function(pane) {
var $componentList = $('.control-componentlist', pane),
$primaryPanel = $('.control-tabs.primary', pane),
$primaryPanel = $('.control-tabs.primary-tabs', pane),
$primaryTabContainer = $('.nav-tabs', $primaryPanel),
hasComponents = $('.layout', $componentList).children(':not(.hidden)').length > 0
@ -554,11 +554,11 @@
CmsPage.prototype.updateModifiedCounter = function() {
var counters = {
page: {menu: 'pages', count: 0},
partial: {menu: 'partials', count: 0},
layout: {menu: 'layouts', count: 0},
content: {menu: 'content', count: 0},
asset:{menu: 'assets', count: 0}
page: { menu: 'pages', count: 0 },
partial: { menu: 'partials', count: 0 },
layout: { menu: 'layouts', count: 0 },
content: { menu: 'content', count: 0 },
asset: { menu: 'assets', count: 0}
}
$('> div.tab-content > div.tab-pane[data-modified]', '#cms-master-tabs').each(function(){

View File

@ -354,7 +354,7 @@ div.control-componentlist {
.fancy-layout {
.control-tabs, &.control-tabs {
&.primary {
&.primary-tabs {
> div > ul.nav-tabs {
&.component-area {
background: @color-component-list-bg;

View File

@ -15,7 +15,7 @@
data-pane-classes="layout-cell"
data-max-title-symbols="15"
data-title-as-file-names="true"
class="layout control-tabs master fancy-layout oc-bg-logo"
class="layout control-tabs master-tabs fancy-layout oc-bg-logo"
id="cms-master-tabs">
<div class="layout-row min-size">

View File

@ -0,0 +1,53 @@
.plugin-details-content {
padding: 0 0;
}
.plugin-details-content > *:first-child {
margin-top: 0;
}
.plugin-details-content h1 {
font-size: 28px;
}
.plugin-details-content h2 {
font-size: 24px;
}
.plugin-details-content h3 {
font-size: 20px;
}
.plugin-details-content h4 {
font-size: 17px;
}
.plugin-details-content h1,
.plugin-details-content h2 {
padding-bottom: 10px;
border-bottom: 1px solid #ccc;
}
.plugin-details-content ul,
.plugin-details-content ol {
padding-left: 20px;
}
.plugin-details-content pre {
display: block;
padding: 10px 10px 10px 20px;
font-size: 13px;
word-break: break-all;
word-wrap: break-word;
color: #fff;
background-color: #333;
margin-top: 10px;
margin-bottom: 20px;
margin-left: -20px;
margin-right: -20px;
}
.plugin-details-content pre code {
padding: 0;
font-size: inherit;
color: inherit;
white-space: pre-wrap;
background-color: transparent;
border-radius: 0;
}
.plugin-details-content ul pre,
.plugin-details-content ol pre {
margin-left: -40px;
padding-left: 40px;
}

View File

@ -196,7 +196,7 @@
display: block;
width: 24px;
height: 24px;
background-image: url(../../../../system/assets/ui/images/loader-transparent.svg);
background-image: url('../../../../system/assets/ui/images/loader-transparent.svg');
background-size: 24px 24px;
background-position: 50% 50%;
-webkit-animation: spin 1s linear infinite;

View File

@ -1,3 +1,6 @@
.important-update-label {
margin: 7px 0;
}
.control-updatelist {
border: 1px solid #ccc;
margin-bottom: 20px;
@ -8,11 +11,11 @@
.control-updatelist .update-item .item-header {
background-color: #f5f5f5;
border-bottom: 1px solid #ccc;
padding: 15px 10px;
padding: 0 10px;
}
.control-updatelist .update-item .item-header h5 {
margin: 0;
padding: 0;
padding: 15px 0;
text-transform: uppercase;
font-size: 13px;
}
@ -26,10 +29,13 @@
line-height: 13px;
margin-right: 5px;
}
.control-updatelist .update-item .item-header .important-update {
padding: 7px 0 0 0;
float: right;
}
.control-updatelist .update-item dl {
padding: 10px;
margin-bottom: 0;
font-size: 12px;
}
.control-updatelist .update-item dl:before,
.control-updatelist .update-item dl:after {
@ -43,12 +49,37 @@
.control-updatelist .update-item dl dd {
float: left;
padding: 5px 0;
line-height: 20px;
}
.control-updatelist .update-item dl dt.text-muted,
.control-updatelist .update-item dl dd.text-muted {
color: #999 !important;
}
.control-updatelist .update-item dl dt {
width: 15%;
clear: left;
font-size: 14px;
font-weight: 400;
color: #2b3e50;
}
.control-updatelist .update-item dl dd {
width: 85%;
font-size: 12px;
color: #455152;
}
.control-updatelist .update-item dl .important-update-label {
position: relative;
top: -1px;
background-color: #ab2a1c;
color: #fff;
display: inline-block;
padding: .2em .6em .3em;
font-size: 75%;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25em;
}
.control-updatelist .update-item:last-child {
border-bottom: none;
@ -56,6 +87,27 @@
.control-updatelist .update-item.item-danger .item-header {
background-color: #f2dede;
}
.control-updatelist .update-item.item-danger dl {
.control-updatelist .update-item.item-danger .item-header h5,
.control-updatelist .update-item.item-danger .item-header i,
.control-updatelist .update-item.item-danger .item-header {
color: #a94442;
}
.control-updatelist .update-item.item-success .item-header {
background-color: #dff0d8;
}
.control-updatelist .update-item.item-success .item-header h5,
.control-updatelist .update-item.item-success .item-header i,
.control-updatelist .update-item.item-success .item-header {
color: #3c763d;
}
.control-updatelist .update-item.item-muted {
border-bottom: none;
}
.control-updatelist .update-item.item-muted .item-header h5,
.control-updatelist .update-item.item-muted .item-header i,
.control-updatelist .update-item.item-muted .item-header {
color: rgba(0, 0, 0, 0.35);
}
.control-updatelist .update-item.item-muted dl {
display: none;
}

View File

@ -19,6 +19,55 @@
this.updateSteps = null
}
UpdateProcess.prototype.check = function() {
var $form = $('#updateForm'),
self = this
$form.request('onCheckForUpdates').done(function() {
self.evalConfirmedUpdates()
})
$form.on('change', '[data-important-update-select]', function() {
var $el = $(this),
selectedValue = $el.val(),
$updateItem = $el.closest('.update-item')
$updateItem.removeClass('item-danger item-muted item-success')
if (selectedValue == 'confirm') {
$updateItem.addClass('item-success')
}
else if (selectedValue == 'ignore' || selectedValue == 'skip') {
$updateItem.addClass('item-muted')
}
else {
$updateItem.addClass('item-danger')
}
self.evalConfirmedUpdates()
})
}
UpdateProcess.prototype.evalConfirmedUpdates = function() {
var $form = $('#updateForm'),
hasConfirmed = false
$('[data-important-update-select]', $form).each(function() {
if ($(this).val() == '') {
hasConfirmed = true
}
})
if (hasConfirmed) {
$('#updateListUpdateButton').prop('disabled', true)
$('#updateListImportantLabel').show()
}
else {
$('#updateListUpdateButton').prop('disabled', false)
$('#updateListImportantLabel').hide()
}
}
UpdateProcess.prototype.execute = function(steps) {
this.updateSteps = steps
this.runUpdate()

View File

@ -0,0 +1,51 @@
@import "../../../../backend/assets/less/core/boot.less";
@padding-standard: 20px;
@padding-container: 0;
.plugin-details-content {
padding: 0 @padding-container;
> *:first-child {
margin-top: 0;
}
h1 { font-size: @font-size-h1 - 8; }
h2 { font-size: @font-size-h2 - 6; }
h3 { font-size: @font-size-h3 - 4; }
h4 { font-size: @font-size-h4 - 1; }
h1, h2 { padding-bottom: 10px; border-bottom: 1px solid #ccc; }
ul, ol {
padding-left: @padding-standard;
}
pre {
display: block;
padding: 10px 10px 10px (@padding-standard + @padding-container);
font-size: 13px;
word-break: break-all;
word-wrap: break-word;
color: #fff;
background-color: #333;
margin-top: 10px;
margin-bottom: 20px;
margin-left: -(@padding-standard + @padding-container);
margin-right: -(@padding-standard + @padding-container);
code {
padding: 0;
font-size: inherit;
color: inherit;
white-space: pre-wrap;
background-color: transparent;
border-radius: 0;
}
}
ul pre,
ol pre {
margin-left: -((@padding-standard * 2) + @padding-container);
padding-left: ((@padding-standard * 2) + @padding-container);
}
}

View File

@ -1,5 +1,9 @@
@import "../../../../backend/assets/less/core/boot.less";
.important-update-label {
margin: 7px 0;
}
.control-updatelist {
border: 1px solid #ccc;
@ -12,11 +16,11 @@
.item-header {
background-color: #f5f5f5;
border-bottom: 1px solid #ccc;
padding: 15px 10px;
padding: 0 10px;
h5 {
margin: 0;
padding: 0;
padding: 15px 0;
text-transform: uppercase;
font-size: 13px;
@ -32,23 +36,53 @@
margin-right: 5px;
}
}
.important-update {
padding: 7px 0 0 0;
float: right;
}
}
dl {
padding: 10px;
margin-bottom: 0;
font-size: 12px;
.clearfix;
dt, dd {
float: left;
padding: 5px 0;
line-height: 20px;
&.text-muted {
color: #999 !important;
}
}
dt {
width: 15%;
clear: left;
font-size: 14px;
font-weight: 400;
color: #2b3e50;
}
dd {
width: 85%;
font-size: 12px;
color: #455152;
}
.important-update-label {
position: relative;
top: -1px;
background-color: #ab2a1c;
color: #fff;
display: inline-block;
padding: .2em .6em .3em;
font-size: 75%;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25em;
}
}
@ -59,11 +93,28 @@
&.item-danger {
.item-header {
background-color: @state-danger-bg;
}
dl {
color: @state-danger-text;
h5, i, & { color: @state-danger-text; }
}
}
&.item-success {
.item-header {
background-color: @state-success-bg;
h5, i, & { color: @state-success-text; }
}
}
&.item-muted {
border-bottom: none;
.item-header {
h5, i, & { color: rgba(0,0,0,.35); }
}
dl {
display: none;
}
}
}
}

View File

@ -4,7 +4,6 @@ Scoreboard
# Example
<script src="/assets/js/october.goalmeter.js"></script>
<script src="/assets/js/vendor/raphael-min.js"></script>
<script src="/assets/js/october.chartutils.js"></script>

View File

@ -1,32 +1,159 @@
# Tabs
# Tab control
## Content tabs
This plugin is a wrapper for the Twitter Bootstrap Tab component. It provides the following features:
Content tabs are heavier tabs designed for displaying content.
- Adding tabs
- Optional close icons with 2 states (modified / unmodified). The icon state can be changed by triggering the modified.oc.tab/unmodified.oc.tab events on any element within tab, or on the tab itself.
- Removing tabs with the Close icon, or with triggering an event from inside a tab pane or tab. The removing can be canceled if the confirm.oc.tab event handler returns false.
- Scrolling tabs if they do not fit the screen
- Collapsible tabs
### Supported data attributes:
- data-control="tab" - creates the tab control from an element
- data-closable - enables the Close Tab feature
- data-pane-classes - a list of CSS classes to apply new pane elements
Example with data attributes (data-control="tab"):
<div class="control-tabs master" data-control="tab" data-closable>
<ul class="nav nav-tabs">
<li class="active"><a href="#home">Home</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active">Home</div>
</div>
</div>
### JavaScript API:
- $('#mytabs').ocTab({closable: true, closeConfirmation: 'Do you really want to close this tab? Unsaved data will be lost.'})
- $('#mytabs').ocTab('addTab', 'Tab title', 'Tab content', identifier) - adds tab. The optional identifier parameter allows to associate a identifier with a tab. The identifier can be used with the goTo() method to find and open a tab by it's identifier.
- $('#mytabs').ocTab('closeTab', '.nav-tabs > li.active', true) - closes a tab. The second argument can point to a tab or tab pane. The thrid argument determines whether the tab should be closed without the user confirmation. The default value is false.
- $('.nav-tabs > li.active').trigger('close.oc.tab') - another way to close a tab. The event can be triggered on a tab, tab pane or any element inside a tab or tab pane.
- $('#mytabs').ocTab('modifyTab', '.nav-tabs > li.active') - marks a tab as modified. Use the 'unmodifyTab' to mark a tab as unmodified.
- $('.nav-tabs > li.active').trigger('modified.oc.tab') - another way to mark a tab as modified. The event can be triggered on a tab, tab pane or any element inside a tab or tab pane. Use the 'unmodified.oc.tab' to mark a tab as unmodified.
- $('#mytabs').ocTab('goTo', 'someidentifier') - Finds a tab by it's identifier and opens it.
- $('#mytabs').ocTab('goToPane', '.tab-content .tab-pane:first') - Opens a tab in context of it's content (pane element)
### Supported options:
- closable - adds the "close" icon to the tab and lets users to close tabs. Corresponds the data-closable attribute.
- closeConfirmation - a confirmation to show when a user tries to close a modified tab. Corresponds the data-close-confirmation
attribute. The confirmation is displayed only if the tab was modified.
- slidable - allows the tabs to be switched with the swipe gesture on touch devices. Corresponds the data-slidable attribute.
- paneClasses - a list of CSS classes to apply new pane elements. Corresponds to the data-pane-classes attribute.
- maxTitleSymbols - the maximum number of characters in tab titles.
- titleAsFileNames - treat tab titles as file names. In this mode only the file name part is displayed in the tab, and the directory part
is hidden.
### Supported events:
- beforeClose.oc.tab - triggered on a tab pane element before tab is closed by the user. Call the event's
preventDefault() method to cancel the action.
- afterAllClosed.oc.tab - triggered after all tabs have been closed
# Example
<div class="control-tabs content-tabs" data-control="tab">
<h5>Master</h5>
<div class="control-tabs master-tabs" data-control="tab">
<ul class="nav nav-tabs">
<li class="active"><a href="#tabOne">One</a></li>
<li><a href="#tabTwo">Two</a></li>
<li><a href="#tabThree">Three</a></li>
<li class="active"><a href="#primaryTabOne">One</a></li>
<li><a href="#primaryTabTwo">Two</a></li>
<li><a href="#primaryTabThree">Three</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active">
<div class="padded-container">
Tab one content
</div>
Tab one content
</div>
<div class="tab-pane">
<div class="padded-container">
Tab two content
</div>
Tab two content
</div>
<div class="tab-pane">
<div class="padded-container">
Tab three content
</div>
Tab three content
</div>
</div>
</div>
<hr />
<h5>Primary</h5>
<div class="control-tabs primary-tabs" data-control="tab">
<ul class="nav nav-tabs">
<li class="active"><a href="#primaryTabOne">One</a></li>
<li><a href="#primaryTabTwo">Two</a></li>
<li><a href="#primaryTabThree">Three</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active">
Tab one content
</div>
<div class="tab-pane">
Tab two content
</div>
<div class="tab-pane">
Tab three content
</div>
</div>
</div>
<hr />
<h5>Secondary</h5>
<div class="control-tabs secondary-tabs" data-control="tab">
<ul class="nav nav-tabs">
<li class="active"><a href="#secondaryTabOne">One</a></li>
<li><a href="#secondaryTabTwo">Two</a></li>
<li><a href="#secondaryTabThree">Three</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active">
Tab one content
</div>
<div class="tab-pane">
Tab two content
</div>
<div class="tab-pane">
Tab three content
</div>
</div>
</div>
<hr />
<h5>Content</h5>
<div class="control-tabs content-tabs" data-control="tab">
<ul class="nav nav-tabs">
<li class="active"><a href="#contentTabOne">One</a></li>
<li><a href="#contentTabTwo">Two</a></li>
<li><a href="#contentTabThree">Three</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active">
Tab one content
</div>
<div class="tab-pane">
Tab two content
</div>
<div class="tab-pane">
Tab three content
</div>
</div>
</div>

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,71 +1,13 @@
/*
=require ../vendor/bootstrap/js/transition.js
=require ../vendor/bootstrap/js/tab.js
=require toolbar.js
*/
/*
* Tab control.
* Tab control
*
* This plugin is a wrapper for the Twitter Bootstrap Tab component. It provides the following features:
* - Adding tabs
* - Optional close icons with 2 states (modified / unmodified). The icon state can be changed by
* triggering the modified.oc.tab/unmodified.oc.tab events on any element within tab, or on the tab itself.
* - Removing tabs with the Close icon, or with triggering an event from inside a tab pane or tab.
* The removing can be canceled if the confirm.oc.tab event handler returns false.
* - Scrolling tabs if they do not fit the screen
* - Collapsible tabs
*
* Data attributes:
* - data-control="tab" - creates the tab control from an element
* - data-closable - enables the Close Tab feature
* - data-pane-classes - a list of CSS classes to apply new pane elements
*
* Example with data attributes (data-control="tab"):
*
* <div class="control-tabs master" data-control="tab" data-closable>
* <ul class="nav nav-tabs">
* <li class="active"><a href="#home">Home</a></li>
* </ul>
* <div class="tab-content">
* <div class="tab-pane active">Home</div>
* </div>
* </div>
*
* JavaScript methods:
* - $('#mytabs').ocTab({closable: true, closeConfirmation: 'Do you really want to close this tab? Unsaved data will be lost.'})
* - $('#mytabs').ocTab('addTab', 'Tab title', 'Tab content', identifier) - adds tab. The optional identifier parameter allows to
associate a identifier with a tab. The identifier can be used with the goTo() method to find and open a tab by it's identifier.
* - $('#mytabs').ocTab('closeTab', '.nav-tabs > li.active', true) - closes a tab. The second argument can point to a tab or tab pane.
The thrid argument determines whether the tab should be closed without the user confirmation. The default value is false.
* - $('.nav-tabs > li.active').trigger('close.oc.tab') - another way to close a tab. The event can be triggered on a tab, tab pane
* or any element inside a tab or tab pane.
* - $('#mytabs').ocTab('modifyTab', '.nav-tabs > li.active') - marks a tab as modified. Use the 'unmodifyTab' to mark a tab as unmodified.
* - $('.nav-tabs > li.active').trigger('modified.oc.tab') - another way to mark a tab as modified. The event can be triggered on a tab, tab pane
* or any element inside a tab or tab pane. Use the 'unmodified.oc.tab' to mark a tab as unmodified.
* - $('#mytabs').ocTab('goTo', 'someidentifier') - Finds a tab by it's identifier and opens it.
* - $('#mytabs').ocTab('goToPane', '.tab-content .tab-pane:first') - Opens a tab in context of it's content (pane element)
*
* Supported options:
* - closable - adds the "close" icon to the tab and lets users to close tabs. Corresponds the data-closable attribute.
* - closeConfirmation - a confirmation to show when a user tries to close a modified tab. Corresponds the data-close-confirmation
* attribute. The confirmation is displayed only if the tab was modified.
* - slidable - allows the tabs to be switched with the swipe gesture on touch devices. Corresponds the data-slidable attribute.
* - paneClasses - a list of CSS classes to apply new pane elements. Corresponds to the data-pane-classes attribute.
* - maxTitleSymbols - the maximum number of characters in tab titles.
* - titleAsFileNames - treat tab titles as file names. In this mode only the file name part is displayed in the tab, and the directory part
* is hidden.
*
* Events:
* - beforeClose.oc.tab - triggered on a tab pane element before tab is closed by the user. Call the event's
* preventDefault() method to cancel the action.
* - afterAllClosed.oc.tab - triggered after all tabs have been closed
*
* Dependences:
* - DragScroll (october.dragscroll.js)
* - Toolbar (october.toolbar.js)
* - Touchwipe (jquery.touchwipe.min.js)
* - Documentation: ../docs/tab.md
*/
+function ($) { "use strict";
var Tab = function (element, options) {

View File

@ -150,6 +150,12 @@ table.table.data {
}
}
tr.frozen {
td, th, td a, th a {
color: #337ab7;
}
}
tr.processing {
td, th, td a, th a {
color: #666666;

View File

@ -46,6 +46,7 @@
@nav-pills-active-link-hover-bg: @component-active-bg;
@nav-pills-active-link-hover-color: @component-active-color;
@tab-image-path: @image-path;
.control-tabs {
position: relative;
@ -206,7 +207,12 @@
}
}
&.master {
//
// Master tabs
//
&.master, // Deprecated
&.master-tabs {
> ul.nav-tabs, > div > ul.nav-tabs, > div > div > ul.nav-tabs {
> li {
a {
@ -224,13 +230,18 @@
}
}
&.primary {
//
// Primary tabs
//
&.primary, // Deprecated
&.primary-tabs {
margin-bottom: 5px;
> ul.nav-tabs, > div > ul.nav-tabs, > div > div > ul.nav-tabs {
position: relative;
margin-left: -20px;
margin-right: -20px;
margin-left: 0;
margin-right: 0;
&:before {
position: absolute;
@ -250,9 +261,9 @@
&:first-child {
margin-left: 0;
padding-left: 15px!important;
padding-left: 15px !important;
}
a {
font-size: 12px;
padding-bottom: 3px;
@ -274,7 +285,7 @@
&:before, &:after {
content: ' ';
position: absolute;
background: transparent url(../images/primary-tab-shape.png?v2) no-repeat left -31px;
background: transparent url('@{tab-image-path}/primary-tab-shape.png') no-repeat left -31px;
width: 16px;
height: 26px;
display: block;
@ -337,16 +348,21 @@
> div.tab-content {
}
// Tabs to sit in by the standard offset (0px)
&.tabs-offset {
// Tab divider to sit inset the standard padding (20px)
&.tabs-inset {
> ul.nav-tabs, > div > ul.nav-tabs, > div > div > ul.nav-tabs {
margin-left: 0;
margin-right: 0;
margin-left: -20px;
margin-right: -20px;
}
}
}
&.secondary {
//
// Secondary tabs
//
&.secondary, // Deprecated
&.secondary-tabs {
> ul.nav-tabs, > div > ul.nav-tabs, > div > div > ul.nav-tabs {
> li {
padding-right: 10px;
@ -368,6 +384,10 @@
}
}
//
// Content tabs
//
&.content-tabs {
> ul.nav-tabs {
@ -425,6 +445,17 @@
}
}
// Tab divider to sit inset the standard padding (20px)
&.tabs-inset {
> ul.nav-tabs, > div > ul.nav-tabs, > div > div > ul.nav-tabs {
margin-left: -20px;
margin-right: -20px;
li:first-child {
margin-left: 20px;
}
}
}
// Tabs to sit in by the standard offset (20px)
&.tabs-offset {
> ul.nav-tabs {

View File

@ -1319,7 +1319,7 @@ S2.define('select2/selection/base',[
});
this.$selection.on('blur', function (evt) {
self.trigger('blur', evt);
self._handleBlur(evt);
});
this.$selection.on('keydown', function (evt) {
@ -1366,6 +1366,24 @@ S2.define('select2/selection/base',[
});
};
BaseSelection.prototype._handleBlur = function (evt) {
var self = this;
// This needs to be delayed as the actve element is the body when the tab
// key is pressed, possibly along with others.
window.setTimeout(function () {
// Don't trigger `blur` if the focus is still in the selection
if (
(document.activeElement == self.$selection[0]) ||
($.contains(self.$selection[0], document.activeElement))
) {
return;
}
self.trigger('blur', evt);
}, 1);
};
BaseSelection.prototype._attachCloseHandler = function (container) {
var self = this;
@ -1475,11 +1493,11 @@ S2.define('select2/selection/single',[
this.$selection.find('.select2-selection__rendered').empty();
};
SingleSelection.prototype.display = function (data) {
SingleSelection.prototype.display = function (data, container) {
var template = this.options.get('templateSelection');
var escapeMarkup = this.options.get('escapeMarkup');
return escapeMarkup(template(data));
return escapeMarkup(template(data, container));
};
SingleSelection.prototype.selectionContainer = function () {
@ -1494,9 +1512,9 @@ S2.define('select2/selection/single',[
var selection = data[0];
var formatted = this.display(selection);
var $rendered = this.$selection.find('.select2-selection__rendered');
var formatted = this.display(selection, $rendered);
$rendered.empty().append(formatted);
$rendered.prop('title', selection.title || selection.text);
};
@ -1556,11 +1574,11 @@ S2.define('select2/selection/multiple',[
this.$selection.find('.select2-selection__rendered').empty();
};
MultipleSelection.prototype.display = function (data) {
MultipleSelection.prototype.display = function (data, container) {
var template = this.options.get('templateSelection');
var escapeMarkup = this.options.get('escapeMarkup');
return escapeMarkup(template(data));
return escapeMarkup(template(data, container));
};
MultipleSelection.prototype.selectionContainer = function () {
@ -1587,8 +1605,8 @@ S2.define('select2/selection/multiple',[
for (var d = 0; d < data.length; d++) {
var selection = data[d];
var formatted = this.display(selection);
var $selection = this.selectionContainer();
var formatted = this.display(selection, $selection);
$selection.append(formatted);
$selection.prop('title', selection.title || selection.text);
@ -1777,6 +1795,8 @@ S2.define('select2/selection/search',[
var $rendered = decorated.call(this);
this._transferTabIndex();
return $rendered;
};
@ -1786,32 +1806,34 @@ S2.define('select2/selection/search',[
decorated.call(this, container, $container);
container.on('open', function () {
self.$search.attr('tabindex', 0);
self.$search.focus();
self.$search.trigger('focus');
});
container.on('close', function () {
self.$search.attr('tabindex', -1);
self.$search.val('');
self.$search.focus();
self.$search.trigger('focus');
});
container.on('enable', function () {
self.$search.prop('disabled', false);
self._transferTabIndex();
});
container.on('disable', function () {
self.$search.prop('disabled', true);
});
container.on('focus', function (evt) {
self.$search.trigger('focus');
});
this.$selection.on('focusin', '.select2-search--inline', function (evt) {
self.trigger('focus', evt);
});
this.$selection.on('focusout', '.select2-search--inline', function (evt) {
self.trigger('blur', evt);
self._handleBlur(evt);
});
this.$selection.on('keydown', '.select2-search--inline', function (evt) {
@ -1819,7 +1841,7 @@ S2.define('select2/selection/search',[
self.trigger('keypress', evt);
self._keyUpPrevented = evt.isDefaultPrevented();
self._keyUpPrevented = false; //evt.isDefaultPrevented();
var key = evt.which;
@ -1847,15 +1869,41 @@ S2.define('select2/selection/search',[
this.$selection.on('keyup.search input', '.select2-search--inline',
function (evt) {
var key = evt.which;
// We can freely ignore events from modifier keys
if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) {
return;
}
// Tabbing will be handled during the `keydown` phase
if (key == KEYS.TAB) {
return;
}
self.handleSearch(evt);
});
};
/**
* This method will transfer the tabindex attribute from the rendered
* selection to the search box. This allows for the search box to be used as
* the primary focus instead of the selection container.
*
* @private
*/
Search.prototype._transferTabIndex = function (decorated) {
this.$search.attr('tabindex', this.$selection.attr('tabindex'));
this.$selection.attr('tabindex', '-1');
};
Search.prototype.createPlaceholder = function (decorated, placeholder) {
this.$search.attr('placeholder', placeholder.text);
};
Search.prototype.update = function (decorated, data) {
var searchHadFocus = this.$search[0] == document.activeElement;
this.$search.attr('placeholder', '');
decorated.call(this, data);
@ -1864,6 +1912,9 @@ S2.define('select2/selection/search',[
.append(this.$searchContainer);
this.resizeSearch();
if (searchHadFocus) {
this.$search.focus();
}
};
Search.prototype.handleSearch = function () {
@ -1948,7 +1999,7 @@ S2.define('select2/selection/eventRelay',[
return;
}
params.prevented = evt.isDefaultPrevented();
params.prevented = false; //evt.isDefaultPrevented();
});
};
@ -3259,7 +3310,7 @@ S2.define('select2/data/ajax',[
this.processResults = this.ajaxOptions.processResults;
}
ArrayAdapter.__super__.constructor.call(this, $element, options);
AjaxAdapter.__super__.constructor.call(this, $element, options);
}
Utils.Extend(AjaxAdapter, ArrayAdapter);
@ -3723,7 +3774,7 @@ S2.define('select2/dropdown/search',[
this.$search.on('keydown', function (evt) {
self.trigger('keypress', evt);
self._keyUpPrevented = evt.isDefaultPrevented();
self._keyUpPrevented = false; //evt.isDefaultPrevented();
});
// Workaround for browsers which do not support the `input` event
@ -4868,8 +4919,8 @@ S2.define('select2/core',[
// Hide the original select
$element.addClass('select2-hidden-accessible');
$element.attr('aria-hidden', 'true');
$element.attr('aria-hidden', 'true');
// Synchronize any monitored attributes
this._syncAttributes();
@ -5004,12 +5055,16 @@ S2.define('select2/core',[
Select2.prototype._registerSelectionEvents = function () {
var self = this;
var nonRelayEvents = ['toggle'];
var nonRelayEvents = ['toggle', 'focus'];
this.selection.on('toggle', function () {
self.toggleDropdown();
});
this.selection.on('focus', function (params) {
self.focus(params);
});
this.selection.on('*', function (name, params) {
if ($.inArray(name, nonRelayEvents) !== -1) {
return;
@ -5054,10 +5109,6 @@ S2.define('select2/core',[
self.$container.addClass('select2-container--disabled');
});
this.on('focus', function () {
self.$container.addClass('select2-container--focus');
});
this.on('blur', function () {
self.$container.removeClass('select2-container--focus');
});
@ -5088,7 +5139,12 @@ S2.define('select2/core',[
var key = evt.which;
if (self.isOpen()) {
if (key === KEYS.ENTER) {
if (key === KEYS.ESC || key === KEYS.TAB ||
(key === KEYS.UP && evt.altKey)) {
self.close();
evt.preventDefault();
} else if (key === KEYS.ENTER) {
self.trigger('results:select');
evt.preventDefault();
@ -5103,15 +5159,11 @@ S2.define('select2/core',[
} else if (key === KEYS.DOWN) {
self.trigger('results:next');
evt.preventDefault();
} else if (key === KEYS.ESC || key === KEYS.TAB) {
self.close();
evt.preventDefault();
}
} else {
if (key === KEYS.ENTER || key === KEYS.SPACE ||
((key === KEYS.DOWN || key === KEYS.UP) && evt.altKey)) {
(key === KEYS.DOWN && evt.altKey)) {
self.open();
evt.preventDefault();
@ -5201,6 +5253,20 @@ S2.define('select2/core',[
return this.$container.hasClass('select2-container--open');
};
Select2.prototype.hasFocus = function () {
return this.$container.hasClass('select2-container--focus');
};
Select2.prototype.focus = function (data) {
// No need to re-trigger focus events if we are already focused
if (this.hasFocus()) {
return;
}
this.$container.addClass('select2-container--focus');
this.trigger('focus');
};
Select2.prototype.enable = function (args) {
if (this.options.get('debug') && window.console && console.warn) {
console.warn(
@ -5281,7 +5347,7 @@ S2.define('select2/core',[
this.$element.attr('tabindex', this.$element.data('old-tabindex'));
this.$element.removeClass('select2-hidden-accessible');
this.$element.attr('aria-hidden', 'false');
this.$element.attr('aria-hidden', 'false');
this.$element.removeData('select2');
this.dataAdapter.destroy();
@ -5385,7 +5451,7 @@ S2.define('select2/compat/containerCss',[
containerCssAdapter = containerCssAdapter || _containerAdapter;
if (containerCssClass.indexOf(':all:') !== -1) {
containerCssClass = containerCssClass.replace(':all', '');
containerCssClass = containerCssClass.replace(':all:', '');
var _cssAdapter = containerCssAdapter;
@ -5442,7 +5508,7 @@ S2.define('select2/compat/dropdownCss',[
dropdownCssAdapter = dropdownCssAdapter || _dropdownAdapter;
if (dropdownCssClass.indexOf(':all:') !== -1) {
dropdownCssClass = dropdownCssClass.replace(':all', '');
dropdownCssClass = dropdownCssClass.replace(':all:', '');
var _cssAdapter = dropdownCssAdapter;
@ -6111,4 +6177,4 @@ S2.define('jquery.select2',[
// Return the Select2 instance for anyone who is importing it.
return select2;
}));
}));

File diff suppressed because it is too large Load Diff

View File

@ -209,6 +209,7 @@ class UpdateManager
$versions = $installed->lists('version', 'code');
$names = $installed->lists('name', 'code');
$icons = $installed->lists('icon', 'code');
$frozen = $installed->lists('is_frozen', 'code');
$build = Parameters::get('system::core.build');
$params = [
@ -241,7 +242,17 @@ class UpdateManager
$info['name'] = isset($names[$code]) ? $names[$code] : $code;
$info['old_version'] = isset($versions[$code]) ? $versions[$code] : false;
$info['icon'] = isset($icons[$code]) ? $icons[$code] : false;
$plugins[$code] = $info;
/*
* If plugin has updates frozen, do not add it to the list
* and discount an update unit.
*/
if (isset($frozen[$code]) && $frozen[$code]) {
$updateCount = max(0, --$updateCount);
}
else {
$plugins[$code] = $info;
}
}
$result['plugins'] = $plugins;

View File

@ -2,10 +2,12 @@
use Str;
use Lang;
use Html;
use File;
use Flash;
use Config;
use Backend;
use Markdown;
use Redirect;
use Response;
use BackendMenu;
@ -98,6 +100,71 @@ class Updates extends Controller
}
}
public function details($urlCode = null, $tab = null)
{
try {
$this->pageTitle = 'Plugin details';
$this->addCss('/modules/system/assets/css/updates/details.css', 'core');
$readmeFiles = ['README.md', 'readme.md'];
$upgradeFiles = ['UPGRADE.md', 'upgrade.md'];
$upgrades = $readme = $name = null;
$code = str_replace('-', '.', $urlCode);
/*
* Lookup the plugin
*/
$manager = PluginManager::instance();
$plugin = $manager->findByIdentifier($code);
$code = $manager->getIdentifier($plugin);
$path = $manager->getPluginPath($plugin);
if ($path && $plugin) {
$details = $plugin->pluginDetails();
$readme = $this->getPluginMarkdownFile($path, $readmeFiles);
$upgrades = $this->getPluginMarkdownFile($path, $upgradeFiles);
$pluginVersion = PluginVersion::whereCode($code)->first();
$this->vars['pluginName'] = array_get($details, 'name', 'system::lang.plugin.unnamed');
$this->vars['pluginVersion'] = $pluginVersion->version;
$this->vars['pluginAuthor'] = array_get($details, 'author');
$this->vars['pluginIcon'] = array_get($details, 'icon', 'icon-leaf');
$this->vars['pluginHomepage'] = array_get($details, 'homepage');
}
else {
throw new ApplicationException('Plugin not found');
}
$this->vars['activeTab'] = $tab ?: 'readme';
$this->vars['urlCode'] = $urlCode;
$this->vars['upgrades'] = $upgrades;
$this->vars['readme'] = $readme;
}
catch (Exception $ex) {
$this->handleError($ex);
}
}
protected function getPluginMarkdownFile($path, $filenames)
{
$contents = null;
foreach ($filenames as $file) {
if (!File::exists($path . '/'.$file)) continue;
$contents = File::get($path . '/'.$file);
/*
* Parse markdown, clean HTML, remove first H1 tag
*/
$contents = Markdown::parse($contents);
$contents = Html::clean($contents);
$contents = preg_replace('@<h1[^>]*?>.*?<\/h1>@si', '', $contents, 1);
}
return $contents;
}
/**
* {@inheritDoc}
*/
@ -119,6 +186,10 @@ class Updates extends Controller
return 'negative';
}
if ($record->is_frozen) {
return 'frozen';
}
return 'positive';
}
@ -193,8 +264,11 @@ class Updates extends Controller
$manager = UpdateManager::instance();
$result = $manager->requestUpdateList();
$result = $this->processImportantUpdates($result);
$this->vars['core'] = array_get($result, 'core', false);
$this->vars['hasUpdates'] = array_get($result, 'hasUpdates', false);
$this->vars['hasImportantUpdates'] = array_get($result, 'hasImportantUpdates', false);
$this->vars['pluginList'] = array_get($result, 'plugins', []);
$this->vars['themeList'] = array_get($result, 'themes', []);
}
@ -205,6 +279,31 @@ class Updates extends Controller
return ['#updateContainer' => $this->makePartial('update_list')];
}
/**
* Loops the update list and checks for actionable updates.
*/
protected function processImportantUpdates($result)
{
$hasImportantUpdates = false;
foreach (array_get($result, 'plugins', []) as $code => $plugin) {
$isImportant = false;
foreach (array_get($plugin, 'updates', []) as $version => $description) {
if (strpos($description, '!!!') === false) continue;
$isImportant = $hasImportantUpdates = true;
$detailsUrl = Backend::url('system/updates/details/'.strtolower(str_replace('.', '-', $code)).'/upgrades');
$description = str_replace('!!!', '', $description);
$result['plugins'][$code]['updates'][$version] = [$description, $detailsUrl];
}
$result['plugins'][$code]['isImportant'] = $isImportant ? '1' : '0';
}
$result['hasImportantUpdates'] = $hasImportantUpdates;
return $result;
}
/**
* Contacts the update server for a list of necessary updates.
*/
@ -258,20 +357,69 @@ class Updates extends Controller
public function onApplyUpdates()
{
try {
/*
* Process core
*/
$coreHash = post('hash');
$coreBuild = post('build');
$core = [$coreHash, $coreBuild];
$plugins = post('plugins', []);
if (!is_array($plugins)) {
/*
* Process plugins
*/
$plugins = post('plugins');
if (is_array($plugins)) {
$pluginCodes = [];
foreach ($plugins as $code => $hash) {
$pluginCodes[] = $this->decodeCode($code);
}
$plugins = array_combine($pluginCodes, $plugins);
}
else {
$plugins = [];
}
$themes = post('themes', []);
if (!is_array($themes)) {
/*
* Process themes
*/
$themes = post('themes');
if (is_array($themes)) {
$themeCodes = [];
foreach ($themes as $code => $hash) {
$themeCodes[] = $this->decodeCode($code);
}
$themes = array_combine($themeCodes, $themes);
}
else {
$themes = [];
}
/*
* Process important update actions
*/
$pluginActions = (array) post('plugin_actions');
foreach ($plugins as $code => $hash) {
$_code = $this->encodeCode($code);
if (!array_key_exists($_code, $pluginActions)) continue;
$pluginAction = $pluginActions[$_code];
if (!$pluginAction) {
throw new ApplicationException('Please select an action for plugin '. $code);
}
if ($pluginAction != 'confirm') {
unset($plugins[$code]);
}
if ($pluginAction == 'ignore') {
PluginVersion::whereCode($code)->update([
'is_frozen' => true
]);
}
}
/*
* Update steps
*/
@ -546,6 +694,7 @@ class Updates extends Controller
public function onDisablePlugins()
{
$disable = post('disable', false);
$freeze = post('freeze', false);
if (($checkedIds = post('checked')) && is_array($checkedIds) && count($checkedIds)) {
$manager = PluginManager::instance();
@ -563,6 +712,7 @@ class Updates extends Controller
}
$object->is_disabled = $disable;
$object->is_frozen = $freeze;
$object->save();
}
@ -727,4 +877,23 @@ class Updates extends Controller
return array_values($popular);
}
//
// Helpers
//
/**
* Encode HTML safe product code.
*/
protected function encodeCode($code)
{
return str_replace('.', '_', $code);
}
/**
* Decode HTML safe product code.
*/
protected function decodeCode($code)
{
return str_replace('_', '.', $code);
}
}

View File

@ -12,4 +12,3 @@
</div>
</div>
</div>

View File

@ -0,0 +1,19 @@
<?php
$icon = null;
if ($record->is_disabled) {
$icon = 'eye-slash';
}
elseif ($record->disabledBySystem) {
$icon = 'exclamation';
}
elseif ($record->orphaned) {
$icon = 'question';
}
elseif ($record->is_frozen) {
$icon = 'lock';
}
?>
<span class="<?= $icon ? 'oc-icon-'.$icon : '' ?>">
<?= $value ?>
</span>

View File

@ -28,6 +28,21 @@
</div>
</div>
<div class="form-group">
<!-- Checkbox -->
<div class="checkbox custom-checkbox">
<input
type="checkbox"
name="freeze"
value="1"
id="pluginFreeze">
<label for="pluginFreeze">
<?= e(trans('system::lang.plugins.frozen_label')) ?>
</label>
<p class="help-block"><?= e(trans('system::lang.plugins.frozen_help')) ?></p>
</div>
</div>
</div>
<?php foreach ($checked as $id): ?>

View File

@ -1,4 +1,7 @@
<div data-control="toolbar">
<a href="<?= Backend::url('system/updates') ?>" class="btn btn-default oc-icon-chevron-left">
<?= e(trans('system::lang.updates.return_link')) ?>
</a>
<div class="btn-group">
<button
class="btn btn-default oc-icon-magic"

View File

@ -31,7 +31,7 @@
</div>
<script>
$('#checkUpdatesPopup').on('popupComplete', function(){
$('#updateForm').request('onCheckForUpdates')
$('#checkUpdatesPopup').on('popupComplete', function() {
$.oc.updateProcess.check()
})
</script>

View File

@ -9,21 +9,22 @@
</p>
<div class="control-updatelist">
<div class="control-scrollbar" style="height:300px" data-control="scrollbar">
<div class="control-scrollbar" style="height:400px" data-control="scrollbar">
<?php if ($core): ?>
<div class="update-item">
<div class="item-header">
<h5>
<i class="icon-cube"></i>
<?= e(trans('system::lang.system.name')) ?>
<?php if ($core['old_build']): ?>
<small><?= e(trans('system::lang.updates.core_build_old', ['build'=>$core['old_build']])) ?></small>
<?php endif ?>
</h5>
</div>
<dl>
<dt><?= e(trans('system::lang.updates.core_build_new', ['build'=>$core['build']])) ?></dt>
<dd><?= e(trans('system::lang.updates.core_build_new_help')) ?></dd>
<?php if ($core['old_build']): ?>
<dt class="text-muted"><?= e(trans('system::lang.updates.core_build_new', ['build'=>$core['old_build']])) ?></dt>
<dd class="text-muted"><?= e(trans('system::lang.updates.core_build')) ?></dd>
<?php endif ?>
</dl>
<input type="hidden" name="hash" value="<?= e($core['hash']) ?>" />
<input type="hidden" name="build" value="<?= e($core['build']) ?>" />
@ -36,7 +37,6 @@
<h5>
<i class="icon-picture-o"></i>
<?= e(array_get($theme, 'name', 'Unknown')) ?>
<small><?= e(trans('system::lang.updates.theme_label')) ?></small>
</h5>
</div>
<dl>
@ -44,32 +44,66 @@
<dd><?= e(trans('system::lang.updates.theme_new_install')) ?></dd>
</dl>
<input type="hidden" name="themes[<?= e($code) ?>]" value="<?= e($theme['hash']) ?>" />
<input type="hidden" name="themes[<?= e($this->encodeCode($code)) ?>]" value="<?= e($theme['hash']) ?>" />
</div>
<?php endforeach ?>
<?php foreach ($pluginList as $code => $plugin): ?>
<div class="update-item">
<div class="update-item <?= $plugin['isImportant'] ? 'item-danger' : '' ?>">
<div class="item-header">
<?php if ($plugin['isImportant']): ?>
<div class="important-update form-group form-group-sm">
<select
name="plugin_actions[<?= e($this->encodeCode($code)) ?>]"
class="form-control custom-select select-no-search"
data-important-update-select>
<option value="">-- Select action --</option>
<option value="confirm">Confirm update</option>
<option value="skip">Skip this plugin (once only)</option>
<option value="ignore">Skip this plugin (always)</option>
</select>
</div>
<?php endif ?>
<h5>
<i class="<?= e($plugin['icon'] ?: 'icon-puzzle-piece') ?>"></i>
<?= e($plugin['name']) ?>
<?php if ($plugin['old_version']): ?>
<small><?= e(trans('system::lang.updates.plugin_version_old', ['version'=>$plugin['old_version']])) ?></small>
<?php else: ?>
<small><?= e(trans('system::lang.updates.plugin_version_none')) ?></small>
<?php endif ?>
</h5>
</div>
<dl>
<?php foreach ($plugin['updates'] as $version => $description): ?>
<dt><?= e(trans('system::lang.updates.plugin_version_new', compact('version'))) ?></dt>
<dd><?= e($description) ?></dd>
<?php endforeach ?>
<?php if (!$plugin['old_version']): ?>
<dt>
<?= $plugin['version'] ?>
</dt>
<dd>
<?= e(trans('system::lang.updates.plugin_version_none')) ?>
</dd>
<?php else: ?>
<?php foreach (array_reverse($plugin['updates']) as $version => $description): ?>
<dt><?= e($version) ?></dt>
<?php if (is_array($description)): ?>
<dd>
<span class="important-update-label">Action required</span>
<?= e($description[0]) ?>
<a href="<?= $description[1] ?>" target="_blank">
View upgrade guide <i class="icon-external-link"></i>
</a>
</dd>
<?php else: ?>
<dd><?= e($description) ?></dd>
<?php endif ?>
<?php endforeach ?>
<dt class="text-muted">
<?= e($plugin['old_version']) ?>
</dt>
<dd class="text-muted">
Current version
</dd>
<?php endif ?>
</dl>
<input type="hidden" name="plugins[<?= e($code) ?>]" value="<?= e($plugin['hash']) ?>" />
<input type="hidden" name="plugins[<?= e($this->encodeCode($code)) ?>]" value="<?= e($plugin['hash']) ?>" />
</div>
<?php endforeach ?>
@ -79,8 +113,14 @@
</div>
<div class="modal-footer">
<?php if ($hasImportantUpdates): ?>
<p class="text-danger pull-left oc-icon-exclamation important-update-label" id="updateListImportantLabel">
Some updates need your attention.
</p>
<?php endif ?>
<button
type="button"
id="updateListUpdateButton"
class="btn btn-primary"
data-dismiss="popup"
data-control="popup"

View File

@ -2,11 +2,12 @@
# List Behavior Config
# ===================================
list: ~/modules/system/models/pluginversion/columns.yaml
list: ~/modules/system/models/pluginversion/columns_manage.yaml
modelClass: System\Models\PluginVersion
noRecordsMessage: backend::lang.list.no_records
showSetup: false
showCheckboxes: true
recordOnClick: $.oc.listToggleChecked(this)
toolbar:
buttons: list_manage_toolbar

View File

@ -0,0 +1,76 @@
<?php Block::put('breadcrumb') ?>
<ul>
<li><a href="<?= Backend::url('system/updates') ?>"><?= e(trans('system::lang.updates.menu_label')) ?></a></li>
<li><?= e(trans($this->pageTitle)) ?></li>
</ul>
<?php Block::endPut() ?>
<?php if (!$this->fatalError): ?>
<div class="scoreboard">
<div data-control="toolbar">
<div class="scoreboard-item title-value">
<h4>Plugin</h4>
<p class="oc-<?= $pluginIcon ?>"><?= e(trans($pluginName)) ?></p>
<?php if ($pluginHomepage): ?>
<p class="description">
<a href="<?= e($pluginHomepage) ?>">View homepage</a>
</p>
<?php endif ?>
</div>
<div class="scoreboard-item title-value">
<h4>Current version</h4>
<p><?= e($pluginVersion) ?></p>
</div>
<div class="scoreboard-item title-value">
<h4>Author</h4>
<p><?= e($pluginAuthor) ?></p>
</div>
</div>
</div>
<div class="control-tabs primary-tabs" data-control="tab">
<ul class="nav nav-tabs">
<li class="<?= $activeTab == 'readme' ? 'active' : '' ?>">
<a
href="#readme"
data-tab-url="<?= Backend::url('system/updates/details/'.$urlCode.'/readme') ?>">
Documentation
</a>
</li>
<li class="<?= $activeTab == 'upgrades' ? 'active' : '' ?>">
<a
href="#upgrades"
data-tab-url="<?= Backend::url('system/updates/details/'.$urlCode.'/upgrades') ?>">
Upgrade Guide
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane <?= $activeTab == 'readme' ? 'active' : '' ?>">
<div class="plugin-details-content">
<?php if ($readme): ?>
<?= $readme ?>
<?php else: ?>
<p>Plugin does not provide any documentation.</p>
<?php endif ?>
</div>
</div>
<div class="tab-pane <?= $activeTab == 'upgrades' ? 'active' : '' ?>">
<div class="plugin-details-content">
<?php if ($upgrades): ?>
<?= $upgrades ?>
<?php else: ?>
<p>Plugin does not provide any upgrade instructions.</p>
<?php endif ?>
</div>
</div>
</div>
</div>
<?php else: ?>
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
<p><a href="<?= Backend::url('system/updates') ?>" class="btn btn-default"><?= e(trans('system::lang.settings.return')) ?></a></p>
<?php endif ?>

View File

@ -0,0 +1,21 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class DbSystemAddFrozenFlag extends Migration
{
public function up()
{
Schema::table('system_plugin_versions', function (Blueprint $table) {
$table->boolean('is_frozen')->default(0);
});
}
public function down()
{
Schema::table('system_plugin_versions', function (Blueprint $table) {
$table->dropColumn('is_frozen');
});
}
}

View File

@ -91,6 +91,8 @@ return [
'refresh' => 'Refresh',
'disabled_label' => 'Disabled',
'disabled_help' => 'Plugins that are disabled are ignored by the application.',
'frozen_label' => 'Freeze updates',
'frozen_help' => 'Plugins that are frozen will be ignored by the update process.',
'selected_amount' => 'Plugins selected: :amount',
'remove_confirm' => 'Are you sure you want to remove this plugin?',
'remove_success' => 'Successfully removed those plugins from the system.',
@ -195,6 +197,7 @@ return [
'check_label' => 'Check for updates',
'retry_label' => 'Try again',
'plugin_name' => 'Name',
'plugin_code' => 'Code',
'plugin_description' => 'Description',
'plugin_version' => 'Version',
'plugin_author' => 'Author',
@ -210,8 +213,8 @@ return [
'plugin_downloading' => 'Downloading plugin: :name',
'plugin_extracting' => 'Unpacking plugin: :name',
'plugin_version_none' => 'New plugin',
'plugin_version_old' => 'Current v:version',
'plugin_version_new' => 'v:version',
// 'plugin_version_old' => 'Current v:version',
// 'plugin_version_new' => 'v:version',
'theme_label' => 'Theme',
'theme_new_install' => 'New theme installation.',
'theme_downloading' => 'Downloading theme: :name',
@ -229,7 +232,8 @@ return [
'none' => [
'label' => 'No updates',
'help' => 'No new updates were found.'
]
],
'return_link' => 'Return to system updates'
],
'server' => [
'connect_error' => 'Error connecting to the server.',

View File

@ -49,7 +49,6 @@ class PluginVersion extends Model
$manager = PluginManager::instance();
$pluginObj = $manager->findByIdentifier($this->code);
if ($pluginObj) {
$pluginInfo = $pluginObj->pluginDetails();
foreach ($pluginInfo as $attribute => $info) {

View File

@ -0,0 +1,15 @@
# ===================================
# Column Definitions
# ===================================
columns:
code:
label: system::lang.updates.plugin_code
sortable: false
type: partial
path: column_code
version:
label: system::lang.updates.plugin_version
sortable: false