Added the Media tab, minor update in .htaccess to allow temporary public directory to be accessible; implemented the basic UI components and navigation; implemented grid, list and tiles view modes; implemented drag-select interface; implemented Media Library cache refreshing; implemented thumbnail generating for local and remote media files; fixed memory leak in third-party Flot Resize library; minor update in the AJAX framework - AJAX request cancelling is not considered as an error anymore; added back-end UI components for creating panels.
This commit is contained in:
parent
1c273f28ba
commit
18e058ad59
|
|
@ -21,6 +21,7 @@
|
||||||
RewriteRule ^vendor/.* index.php [L,NC]
|
RewriteRule ^vendor/.* index.php [L,NC]
|
||||||
RewriteRule ^storage/cms/.* index.php [L,NC]
|
RewriteRule ^storage/cms/.* index.php [L,NC]
|
||||||
RewriteRule ^storage/logs/.* index.php [L,NC]
|
RewriteRule ^storage/logs/.* index.php [L,NC]
|
||||||
|
RewriteCond %{REQUEST_URI} !storage/temp/public
|
||||||
RewriteRule ^storage/temp/.* index.php [L,NC]
|
RewriteRule ^storage/temp/.* index.php [L,NC]
|
||||||
RewriteRule ^storage/framework/.* index.php [L,NC]
|
RewriteRule ^storage/framework/.* index.php [L,NC]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,8 @@
|
||||||
"october/system": "~1.0",
|
"october/system": "~1.0",
|
||||||
"october/backend": "~1.0",
|
"october/backend": "~1.0",
|
||||||
"october/cms": "~1.0",
|
"october/cms": "~1.0",
|
||||||
"october/rain": "~1.0"
|
"october/rain": "~1.0",
|
||||||
|
"league/flysystem-aws-s3-v2": "~1.0"
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"classmap": [
|
"classmap": [
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1833,10 +1833,10 @@ if(typeof item.series.data[item.dataIndex][1]==='number'){content=this.adjustVal
|
||||||
if(typeof item.series.xaxis.tickFormatter!=='undefined'){content=content.replace(xPattern,item.series.xaxis.tickFormatter(item.series.data[item.dataIndex][0],item.series.xaxis));}
|
if(typeof item.series.xaxis.tickFormatter!=='undefined'){content=content.replace(xPattern,item.series.xaxis.tickFormatter(item.series.data[item.dataIndex][0],item.series.xaxis));}
|
||||||
if(typeof item.series.yaxis.tickFormatter!=='undefined'){content=content.replace(yPattern,item.series.yaxis.tickFormatter(item.series.data[item.dataIndex][1],item.series.yaxis));}
|
if(typeof item.series.yaxis.tickFormatter!=='undefined'){content=content.replace(yPattern,item.series.yaxis.tickFormatter(item.series.data[item.dataIndex][1],item.series.yaxis));}
|
||||||
return content;};FlotTooltip.prototype.isTimeMode=function(axisName,item){return(typeof item.series[axisName].options.mode!=='undefined'&&item.series[axisName].options.mode==='time');};FlotTooltip.prototype.isXDateFormat=function(item){return(typeof this.tooltipOptions.xDateFormat!=='undefined'&&this.tooltipOptions.xDateFormat!==null);};FlotTooltip.prototype.isYDateFormat=function(item){return(typeof this.tooltipOptions.yDateFormat!=='undefined'&&this.tooltipOptions.yDateFormat!==null);};FlotTooltip.prototype.timestampToDate=function(tmst,dateFormat){var theDate=new Date(tmst);return $.plot.formatDate(theDate,dateFormat);};FlotTooltip.prototype.adjustValPrecision=function(pattern,content,value){var precision;if(content.match(pattern)!==null){if(RegExp.$1!==''){precision=RegExp.$1;value=value.toFixed(precision);content=content.replace(pattern,value);}}
|
return content;};FlotTooltip.prototype.isTimeMode=function(axisName,item){return(typeof item.series[axisName].options.mode!=='undefined'&&item.series[axisName].options.mode==='time');};FlotTooltip.prototype.isXDateFormat=function(item){return(typeof this.tooltipOptions.xDateFormat!=='undefined'&&this.tooltipOptions.xDateFormat!==null);};FlotTooltip.prototype.isYDateFormat=function(item){return(typeof this.tooltipOptions.yDateFormat!=='undefined'&&this.tooltipOptions.yDateFormat!==null);};FlotTooltip.prototype.timestampToDate=function(tmst,dateFormat){var theDate=new Date(tmst);return $.plot.formatDate(theDate,dateFormat);};FlotTooltip.prototype.adjustValPrecision=function(pattern,content,value){var precision;if(content.match(pattern)!==null){if(RegExp.$1!==''){precision=RegExp.$1;value=value.toFixed(precision);content=content.replace(pattern,value);}}
|
||||||
return content;};var init=function(plot){new FlotTooltip(plot);};$.plot.plugins.push({init:init,options:defaultOptions,name:'tooltip',version:'0.6.1'});})(jQuery);(function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this);(function($){var options={};function init(plot){function onResize(){var placeholder=plot.getPlaceholder();if(placeholder.width()==0||placeholder.height()==0)
|
return content;};var init=function(plot){new FlotTooltip(plot);};$.plot.plugins.push({init:init,options:defaultOptions,name:'tooltip',version:'0.6.1'});})(jQuery);(function($){var options={};function init(plot){function onResize(){var placeholder=plot.getPlaceholder();if(placeholder.width()==0||placeholder.height()==0)
|
||||||
return;plot.resize();plot.setupGrid();plot.draw();}
|
return;plot.resize();plot.setupGrid();plot.draw();}
|
||||||
function bindEvents(plot,eventHolder){plot.getPlaceholder().resize(onResize);}
|
function bindEvents(plot,eventHolder){$(window).bind('resize',onResize)}
|
||||||
function shutdown(plot,eventHolder){plot.getPlaceholder().unbind("resize",onResize);}
|
function shutdown(plot,eventHolder){$(window).unbind('resize',onResize)}
|
||||||
plot.hooks.bindEvents.push(bindEvents);plot.hooks.shutdown.push(shutdown);}
|
plot.hooks.bindEvents.push(bindEvents);plot.hooks.shutdown.push(shutdown);}
|
||||||
$.plot.plugins.push({init:init,options:options,name:'resize',version:'1.0'});})(jQuery);(function($){var options={xaxis:{timezone:null,timeformat:null,twelveHourClock:false,monthNames:null}};function floorInBase(n,base){return base*Math.floor(n/base);}
|
$.plot.plugins.push({init:init,options:options,name:'resize',version:'1.0'});})(jQuery);(function($){var options={xaxis:{timezone:null,timeformat:null,twelveHourClock:false,monthNames:null}};function floorInBase(n,base){return base*Math.floor(n/base);}
|
||||||
function formatDate(d,fmt,monthNames,dayNames){if(typeof d.strftime=="function"){return d.strftime(fmt);}
|
function formatDate(d,fmt,monthNames,dayNames){if(typeof d.strftime=="function"){return d.strftime(fmt);}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-default.on {
|
||||||
|
background-color: #95a5a6;
|
||||||
|
color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-group {
|
.btn-group {
|
||||||
.btn{
|
.btn{
|
||||||
border-right: 1px solid rgba(0,0,0,0.09);
|
border-right: 1px solid rgba(0,0,0,0.09);
|
||||||
|
|
@ -62,6 +67,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn, .btn-group {
|
||||||
|
&.offset-right {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.btn-icon {
|
.btn-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
|
|
@ -76,12 +87,32 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover:before {
|
&:hover:before {
|
||||||
|
color: @color-link;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.danger:hover:before {
|
||||||
color: @color-btn-danger;
|
color: @color-btn-danger;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.pull-right:before {
|
&.pull-right:before {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.margin-left {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
font-size: 17px;
|
||||||
|
height: 17px;
|
||||||
|
line-height: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.larger {
|
||||||
|
font-size: 21px;
|
||||||
|
height: 21px;
|
||||||
|
line-height: 17px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-text {
|
.btn-text {
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,17 @@ label {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.field-section {
|
||||||
|
border-bottom: 1px solid @color-form-field-border;
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 7px;
|
||||||
|
|
||||||
|
> p:first-child,
|
||||||
|
> h4:first-child {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.field-textarea {
|
.field-textarea {
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
&.size-tiny { min-height: @size-tiny; }
|
&.size-tiny { min-height: @size-tiny; }
|
||||||
|
|
@ -233,6 +244,12 @@ label {
|
||||||
&.size-giant { min-height: @size-giant; }
|
&.size-giant { min-height: @size-giant; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.field-checkboxlist {
|
||||||
|
.checkbox:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.field-checkboxlist-scrollable {
|
.field-checkboxlist-scrollable {
|
||||||
background: white;
|
background: white;
|
||||||
border: 1px solid @color-list-border;
|
border: 1px solid @color-list-border;
|
||||||
|
|
|
||||||
|
|
@ -169,11 +169,11 @@ table.table.data {
|
||||||
border-left: 3px solid @color-list-stripe-active;
|
border-left: 3px solid @color-list-stripe-active;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tr:not(.no-data):hover td {
|
tr:not(.no-data):hover td, tr:not(.no-data).selected td, {
|
||||||
background: @color-list-hover-bg !important;
|
background: @color-list-hover-bg !important;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
a, span {
|
a, span, i[class^="icon-"] {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -242,6 +242,22 @@ table.table.data {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.icons {
|
||||||
|
td i[class^="icon-"] {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 7px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #95a5a6;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
.user-select(none);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tfoot {
|
tfoot {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
table.name-value-list {
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 4px 0 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:first-child {
|
||||||
|
th, td {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #95a5a6;
|
||||||
|
padding-right: 15px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
color: #2b3e50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
div.panel {
|
||||||
|
@panel-border-color: #ecf0f1;
|
||||||
|
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
&.no-padding {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.no-padding-bottom {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.padding-top {
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.padding-less {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.transparent {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.border-left {
|
||||||
|
border-left: 1px solid @panel-border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.border-right {
|
||||||
|
border-right: 1px solid @panel-border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.border-bottom {
|
||||||
|
border-bottom: 1px solid @panel-border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.triangle-down {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
.triangle(down, 15px, 8px, white);
|
||||||
|
position: absolute;
|
||||||
|
left: 15px;
|
||||||
|
bottom: -8px;
|
||||||
|
z-index: 101;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
.triangle(down, 17px, 9px, #edeeef);
|
||||||
|
position: absolute;
|
||||||
|
left: 14px;
|
||||||
|
bottom: -9px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Panel sections
|
||||||
|
*/
|
||||||
|
|
||||||
|
h3.section, > label {
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #95a5a6;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> label {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
.nav.selector-group {
|
||||||
|
font-size: 13px;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
|
||||||
|
li {
|
||||||
|
a {
|
||||||
|
padding: 7px 20px 7px 23px;
|
||||||
|
color: #95a5a6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-left: 3px solid #e6802b;
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
a {
|
||||||
|
padding-left: 20px;
|
||||||
|
color: #2b3e50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i[class^="icon-"] {
|
||||||
|
font-size: 17px;
|
||||||
|
margin-right: 6px;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.panel {
|
||||||
|
.nav.selector-group {
|
||||||
|
margin: 0 -20px 0 -20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,10 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
.horizontal-scroll-indicators(@color-scroll-indicator);
|
.horizontal-scroll-indicators(@color-scroll-indicator);
|
||||||
|
|
||||||
|
&.standalone-paddings {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
left: -10px;
|
left: -10px;
|
||||||
}
|
}
|
||||||
|
|
@ -22,6 +26,10 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
|
|
||||||
|
&.last {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.horizontal-scroll-indicators(@color-scroll-indicator);
|
.horizontal-scroll-indicators(@color-scroll-indicator);
|
||||||
|
|
||||||
&:before { left: -10px; }
|
&:before { left: -10px; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
ul.tree-path {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 1px;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
.icon(@angle-right);
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-left: 5px;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
color: #95a5a6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
a {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.go-up {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-right: 7px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #95a5a6;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: @color-link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.root a {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #405261;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #95a5a6;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,11 @@ body.loading, body.loading * {
|
||||||
cursor: wait !important;
|
cursor: wait !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.no-select {
|
||||||
|
.user-select(none);
|
||||||
|
cursor: default!important;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Layout canvas
|
// Layout canvas
|
||||||
//
|
//
|
||||||
|
|
@ -114,6 +119,10 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.whiteboard {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Layout styles
|
// Layout styles
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,10 @@
|
||||||
@import "controls/treeview.less";
|
@import "controls/treeview.less";
|
||||||
@import "controls/callout.less";
|
@import "controls/callout.less";
|
||||||
@import "controls/sidenav-tree.less";
|
@import "controls/sidenav-tree.less";
|
||||||
|
@import "controls/panels.less";
|
||||||
|
@import "controls/selector-group.less";
|
||||||
|
@import "controls/tree-path.less";
|
||||||
|
@import "controls/namevaluelist.less";
|
||||||
|
|
||||||
// Vendor
|
// Vendor
|
||||||
@import "../vendor/sweet-alert/sweet-alert.less";
|
@import "../vendor/sweet-alert/sweet-alert.less";
|
||||||
|
|
@ -20,7 +20,14 @@ can just fix the size of their placeholders.
|
||||||
* http://benalman.com/about/license/
|
* http://benalman.com/about/license/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this);
|
/*
|
||||||
|
* The plugin depends on jQuery.Resize plugin https://github.com/cowboy/jquery-resize
|
||||||
|
* which causes the memory leaking. The plugin dependency was replaced with the native
|
||||||
|
* window.resize event as we don't need the jQuery.Resize functionality anyways.
|
||||||
|
* -ab March 1, 2015
|
||||||
|
*/
|
||||||
|
|
||||||
|
// (function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this);
|
||||||
|
|
||||||
(function ($) {
|
(function ($) {
|
||||||
var options = { }; // no options
|
var options = { }; // no options
|
||||||
|
|
@ -40,11 +47,14 @@ can just fix the size of their placeholders.
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindEvents(plot, eventHolder) {
|
function bindEvents(plot, eventHolder) {
|
||||||
plot.getPlaceholder().resize(onResize);
|
//plot.getPlaceholder().resize(onResize);
|
||||||
|
|
||||||
|
$(window).bind('resize', onResize)
|
||||||
}
|
}
|
||||||
|
|
||||||
function shutdown(plot, eventHolder) {
|
function shutdown(plot, eventHolder) {
|
||||||
plot.getPlaceholder().unbind("resize", onResize);
|
//plot.getPlaceholder().unbind("resize", onResize);
|
||||||
|
$(window).unbind('resize', onResize)
|
||||||
}
|
}
|
||||||
|
|
||||||
plot.hooks.bindEvents.push(bindEvents);
|
plot.hooks.bindEvents.push(bindEvents);
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ class FilterScope
|
||||||
protected function evalConfig($config)
|
protected function evalConfig($config)
|
||||||
{
|
{
|
||||||
if (isset($config['options'])) {
|
if (isset($config['options'])) {
|
||||||
$this->options($config['options']);
|
$this->options = $config['options'];
|
||||||
}
|
}
|
||||||
if (isset($config['context'])) {
|
if (isset($config['context'])) {
|
||||||
$this->context = $config['context'];
|
$this->context = $config['context'];
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,10 @@ class Filter extends WidgetBase
|
||||||
*/
|
*/
|
||||||
protected function getAvailableOptions($scope, $searchQuery = null)
|
protected function getAvailableOptions($scope, $searchQuery = null)
|
||||||
{
|
{
|
||||||
|
if (count($scope->options)) {
|
||||||
|
return $scope->options;
|
||||||
|
}
|
||||||
|
|
||||||
$available = [];
|
$available = [];
|
||||||
$nameColumn = $this->getScopeNameColumn($scope);
|
$nameColumn = $this->getScopeNameColumn($scope);
|
||||||
$options = $this->getOptionsFromModel($scope, $searchQuery);
|
$options = $this->getOptionsFromModel($scope, $searchQuery);
|
||||||
|
|
@ -210,7 +214,7 @@ class Filter extends WidgetBase
|
||||||
$query = $model->newQuery();
|
$query = $model->newQuery();
|
||||||
|
|
||||||
$this->fireEvent('filter.extendQuery', [$query, $scope]);
|
$this->fireEvent('filter.extendQuery', [$query, $scope]);
|
||||||
|
|
||||||
if (!$searchQuery) {
|
if (!$searchQuery) {
|
||||||
return $query->get();
|
return $query->get();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
use App;
|
use App;
|
||||||
use Str;
|
use Str;
|
||||||
use Lang;
|
use Lang;
|
||||||
use Form as FormHelper;
|
|
||||||
use Input;
|
use Input;
|
||||||
use Event;
|
use Event;
|
||||||
|
use Form as FormHelper;
|
||||||
use Backend\Classes\FormTabs;
|
use Backend\Classes\FormTabs;
|
||||||
use Backend\Classes\FormField;
|
use Backend\Classes\FormField;
|
||||||
use Backend\Classes\WidgetBase;
|
use Backend\Classes\WidgetBase;
|
||||||
|
|
@ -23,7 +23,6 @@ use October\Rain\Database\Model;
|
||||||
*/
|
*/
|
||||||
class Form extends WidgetBase
|
class Form extends WidgetBase
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?php if (!$field->hidden): ?>
|
<?php if (!$field->hidden): ?>
|
||||||
|
|
||||||
<?php if (in_array($field->type, ['checkbox', 'switch'])): ?>
|
<?php if (in_array($field->type, ['checkbox', 'switch', 'section'])): ?>
|
||||||
|
|
||||||
<?= $this->makePartial('field_'.$field->type, ['field' => $field]) ?>
|
<?= $this->makePartial('field_'.$field->type, ['field' => $field]) ?>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,85 +5,86 @@
|
||||||
<!-- Checkbox List -->
|
<!-- Checkbox List -->
|
||||||
<?php if (count($fieldOptions)): ?>
|
<?php if (count($fieldOptions)): ?>
|
||||||
|
|
||||||
<?php if ($this->previewMode): ?>
|
<div class="field-checkboxlist">
|
||||||
<!-- Read-only -->
|
|
||||||
|
|
||||||
<?php $index = 0; foreach ($fieldOptions as $value => $option): ?>
|
<?php if ($this->previewMode): ?>
|
||||||
<?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">
|
|
||||||
|
|
||||||
<label for="<?= $checkboxId ?>">
|
<?php $index = 0; foreach ($fieldOptions as $value => $option): ?>
|
||||||
<?= e(trans($option[0])) ?>
|
<?php
|
||||||
</label>
|
$index++;
|
||||||
<?php if (isset($option[1])): ?>
|
$checkboxId = 'checkbox_'.$field->getId().'_'.$index;
|
||||||
<p class="help-block"><?= e(trans($option[1])) ?></p>
|
if (!in_array($value, $checkedValues)) continue;
|
||||||
<?php endif ?>
|
if (is_string($option)) $option = [$option];
|
||||||
</div>
|
?>
|
||||||
<?php endforeach ?>
|
<div class="checkbox custom-checkbox">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="<?= $checkboxId ?>"
|
||||||
|
name="<?= $field->getName() ?>[]"
|
||||||
|
value="<?= $value ?>"
|
||||||
|
disabled="disabled"
|
||||||
|
checked="checked">
|
||||||
|
|
||||||
<?php else: ?>
|
<label for="<?= $checkboxId ?>">
|
||||||
<!-- Editable -->
|
<?= e(trans($option[0])) ?>
|
||||||
|
</label>
|
||||||
<?php if (count($fieldOptions) > 10): ?>
|
<?php if (isset($option[1])): ?>
|
||||||
<!-- Quick selection -->
|
<p class="help-block"><?= e(trans($option[1])) ?></p>
|
||||||
<small>
|
<?php endif ?>
|
||||||
<?= 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>
|
||||||
</div>
|
<?php endforeach ?>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<?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 endif ?>
|
||||||
|
|
||||||
<?php endif ?>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<!-- Section -->
|
||||||
|
<div class="field-section">
|
||||||
|
<?php if ($field->label): ?>
|
||||||
|
<h4><?= e(trans($field->label)) ?></h4>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if ($field->comment): ?>
|
||||||
|
<p class="help-block"><?= e(trans($field->comment)) ?></p>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
|
@ -83,7 +83,13 @@ class ServiceProvider extends ModuleServiceProvider
|
||||||
'permissions' => ['cms.manage_pages', 'cms.manage_layouts', 'cms.manage_partials']
|
'permissions' => ['cms.manage_pages', 'cms.manage_layouts', 'cms.manage_partials']
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
],
|
||||||
|
'media' => [
|
||||||
|
'label' => 'cms::lang.media.menu_label',
|
||||||
|
'icon' => 'icon-folder',
|
||||||
|
'url' => Backend::url('cms/media'),
|
||||||
|
'permissions' => ['cms.*'],
|
||||||
|
'order' => 20
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -56,10 +56,8 @@ class MediaLibrary
|
||||||
*/
|
*/
|
||||||
protected function init()
|
protected function init()
|
||||||
{
|
{
|
||||||
$this->storagePath = Config::get('cms.storage.media.path', '/storage/app/media');
|
$this->storagePath = rtrim(Config::get('cms.storage.media.path', '/storage/app/media'), '/');
|
||||||
$this->storageDisk = Storage::disk(
|
$this->storageFolder = self::validatePath(
|
||||||
Config::get('cms.storage.media.disk', 'local'));
|
|
||||||
$this->storageFolder = $this->validatePath(
|
|
||||||
Config::get('cms.storage.media.folder', 'media'), true);
|
Config::get('cms.storage.media.folder', 'media'), true);
|
||||||
$this->ignoreNames = Config::get('cms.storage.media.ignore', $this->defaultIgnoreNames);
|
$this->ignoreNames = Config::get('cms.storage.media.ignore', $this->defaultIgnoreNames);
|
||||||
|
|
||||||
|
|
@ -74,7 +72,7 @@ class MediaLibrary
|
||||||
*/
|
*/
|
||||||
public function listFolderContents($folder = '/', $sortBy = 'title')
|
public function listFolderContents($folder = '/', $sortBy = 'title')
|
||||||
{
|
{
|
||||||
$folder = $this->validatePath($folder);
|
$folder = self::validatePath($folder);
|
||||||
$fullFolderPath = $this->getMediaPath($folder);
|
$fullFolderPath = $this->getMediaPath($folder);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -109,12 +107,28 @@ class MediaLibrary
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns URL of a Library file.
|
* Determines if a file with the specified path exists in the library.
|
||||||
* @param string $path Specifies a file path relative to the Library root.
|
* @param string $path Specifies the file path relative the the Library root.
|
||||||
|
* @return boolean Returns TRUE if the file exists.
|
||||||
*/
|
*/
|
||||||
public function url($path)
|
public function exists($path)
|
||||||
{
|
{
|
||||||
|
$path = self::validatePath($path);
|
||||||
|
$fullPath = $this->getMediaPath($path);
|
||||||
|
|
||||||
|
return $this->getStorageDisk()->exists($fullPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a file contents.
|
||||||
|
* @param string $path Specifies the file path relative the the Library root.
|
||||||
|
* @return string Returns the file contents
|
||||||
|
*/
|
||||||
|
public function get($path)
|
||||||
|
{
|
||||||
|
$path = self::validatePath($path);
|
||||||
|
$fullPath = $this->getMediaPath($path);
|
||||||
|
return $this->getStorageDisk()->get($fullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -137,7 +151,7 @@ class MediaLibrary
|
||||||
* @param boolean $normalizeOnly Specifies if only the normalization, without validation should be performed.
|
* @param boolean $normalizeOnly Specifies if only the normalization, without validation should be performed.
|
||||||
* @return string Returns a normalized path.
|
* @return string Returns a normalized path.
|
||||||
*/
|
*/
|
||||||
protected function validatePath($path, $normalizeOnly = false)
|
public static function validatePath($path, $normalizeOnly = false)
|
||||||
{
|
{
|
||||||
$path = str_replace('\\', '/', $path);
|
$path = str_replace('\\', '/', $path);
|
||||||
$path = '/'.trim($path, '/');
|
$path = '/'.trim($path, '/');
|
||||||
|
|
@ -171,7 +185,7 @@ class MediaLibrary
|
||||||
*/
|
*/
|
||||||
protected function getMediaRelativePath($path)
|
protected function getMediaRelativePath($path)
|
||||||
{
|
{
|
||||||
$path = $this->validatePath($path, true);
|
$path = self::validatePath($path, true);
|
||||||
|
|
||||||
if (substr($path, 0, $this->storageFolderNameLength) == $this->storageFolder)
|
if (substr($path, 0, $this->storageFolderNameLength) == $this->storageFolder)
|
||||||
return substr($path, $this->storageFolderNameLength);
|
return substr($path, $this->storageFolderNameLength);
|
||||||
|
|
@ -202,10 +216,18 @@ class MediaLibrary
|
||||||
if (!$this->isVisible($relativePath))
|
if (!$this->isVisible($relativePath))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
$lastModified = $this->storageDisk->lastModified($path);
|
/*
|
||||||
$size = $itemType == MediaLibraryItem::TYPE_FILE ? $this->storageDisk->size($path) : $this->getFolderItemCount($path);
|
* S3 doesn't allow getting the last modified timestamp for folders,
|
||||||
|
* so this feature is disabled - folders timestamp is always NULL.
|
||||||
|
*/
|
||||||
|
$lastModified = $itemType == MediaLibraryItem::TYPE_FILE ?
|
||||||
|
$this->getStorageDisk()->lastModified($path) : null;
|
||||||
|
|
||||||
return new MediaLibraryItem($relativePath, $size, $lastModified, $itemType);
|
$size = $itemType == MediaLibraryItem::TYPE_FILE ?
|
||||||
|
$this->getStorageDisk()->size($path) : $this->getFolderItemCount($path);
|
||||||
|
|
||||||
|
$publicUrl = $this->storagePath.$relativePath;
|
||||||
|
return new MediaLibraryItem($relativePath, $size, $lastModified, $itemType, $publicUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -216,8 +238,8 @@ class MediaLibrary
|
||||||
protected function getFolderItemCount($path)
|
protected function getFolderItemCount($path)
|
||||||
{
|
{
|
||||||
$folderItems = array_merge(
|
$folderItems = array_merge(
|
||||||
$this->storageDisk->files($path),
|
$this->getStorageDisk()->files($path),
|
||||||
$this->storageDisk->directories($path));
|
$this->getStorageDisk()->directories($path));
|
||||||
|
|
||||||
$size = 0;
|
$size = 0;
|
||||||
foreach ($folderItems as $folderItem) {
|
foreach ($folderItems as $folderItem) {
|
||||||
|
|
@ -240,13 +262,13 @@ class MediaLibrary
|
||||||
'folders' => []
|
'folders' => []
|
||||||
];
|
];
|
||||||
|
|
||||||
$files = $this->storageDisk->files($fullFolderPath);
|
$files = $this->getStorageDisk()->files($fullFolderPath);
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
if ($libraryItem = $this->initLibraryItem($file, MediaLibraryItem::TYPE_FILE))
|
if ($libraryItem = $this->initLibraryItem($file, MediaLibraryItem::TYPE_FILE))
|
||||||
$result['files'][] = $libraryItem;
|
$result['files'][] = $libraryItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
$folders = $this->storageDisk->directories($fullFolderPath);
|
$folders = $this->getStorageDisk()->directories($fullFolderPath);
|
||||||
foreach ($folders as $folder) {
|
foreach ($folders as $folder) {
|
||||||
if ($libraryItem = $this->initLibraryItem($folder, MediaLibraryItem::TYPE_FOLDER))
|
if ($libraryItem = $this->initLibraryItem($folder, MediaLibraryItem::TYPE_FOLDER))
|
||||||
$result['folders'][] = $libraryItem;
|
$result['folders'][] = $libraryItem;
|
||||||
|
|
@ -284,4 +306,20 @@ class MediaLibrary
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes and returns the Media Library disk.
|
||||||
|
* This method should always be used instead of trying to access the
|
||||||
|
* $storageDisk property directly as initializing the disc requires
|
||||||
|
* communicating with the remote storage.
|
||||||
|
* @return mixed Returns the storage disk object.
|
||||||
|
*/
|
||||||
|
protected function getStorageDisk()
|
||||||
|
{
|
||||||
|
if ($this->storageDisk)
|
||||||
|
return $this->storageDisk;
|
||||||
|
|
||||||
|
return $this->storageDisk = Storage::disk(
|
||||||
|
Config::get('cms.storage.media.disk', 'local'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
<?php namespace Cms\Classes;
|
<?php namespace Cms\Classes;
|
||||||
|
|
||||||
|
use Config;
|
||||||
|
use File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a file or folder in the Media Library.
|
* Represents a file or folder in the Media Library.
|
||||||
*
|
*
|
||||||
|
|
@ -9,9 +12,13 @@
|
||||||
class MediaLibraryItem
|
class MediaLibraryItem
|
||||||
{
|
{
|
||||||
const TYPE_FILE = 'file';
|
const TYPE_FILE = 'file';
|
||||||
|
|
||||||
const TYPE_FOLDER = 'folder';
|
const TYPE_FOLDER = 'folder';
|
||||||
|
|
||||||
|
const FILE_TYPE_IMAGE = 'image';
|
||||||
|
const FILE_TYPE_VIDEO = 'video';
|
||||||
|
const FILE_TYPE_AUDIO = 'audio';
|
||||||
|
const FILE_TYPE_DOCUMENT = 'document';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string Specifies the item path relative to the Library root.
|
* @var string Specifies the item path relative to the Library root.
|
||||||
*/
|
*/
|
||||||
|
|
@ -34,16 +41,93 @@ class MediaLibraryItem
|
||||||
*/
|
*/
|
||||||
public $type;
|
public $type;
|
||||||
|
|
||||||
public function __construct($path, $size, $lastModified, $type)
|
/**
|
||||||
|
* @var string Specifies the public URL of the item.
|
||||||
|
*/
|
||||||
|
public $publicUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Contains a default list of files and directories to ignore.
|
||||||
|
* The list can be customized with the following configuration options:
|
||||||
|
* - cms.storage.media.image_extensions
|
||||||
|
* - cms.storage.media.video_extensions
|
||||||
|
* - cms.storage.media.audo_extensions
|
||||||
|
*/
|
||||||
|
protected static $defaultTypeExtensions = [
|
||||||
|
'image' => ['gif', 'png', 'jpg', 'jpeg', 'bmp'],
|
||||||
|
'video' => ['mp4', 'avi', 'mov', 'mpg'],
|
||||||
|
'audio' => ['mp3', 'wav', 'wma', 'm4a']
|
||||||
|
];
|
||||||
|
|
||||||
|
protected static $imageExtensions;
|
||||||
|
protected static $videoExtensions;
|
||||||
|
protected static $audioExtensions;
|
||||||
|
|
||||||
|
public function __construct($path, $size, $lastModified, $type, $publicUrl)
|
||||||
{
|
{
|
||||||
$this->path = $path;
|
$this->path = $path;
|
||||||
$this->size = $size;
|
$this->size = $size;
|
||||||
$this->lastModified = $lastModified;
|
$this->lastModified = $lastModified;
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
|
$this->publicUrl = $publicUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isFile()
|
public function isFile()
|
||||||
{
|
{
|
||||||
return $this->type == self::TYPE_FILE;
|
return $this->type == self::TYPE_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file type by its name.
|
||||||
|
* The known file types are: image, video, audio, document
|
||||||
|
* @return string Returns the file type or NULL if the item is a folder.
|
||||||
|
*/
|
||||||
|
public function getFileType()
|
||||||
|
{
|
||||||
|
if (!$this->isFile())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!self::$imageExtensions) {
|
||||||
|
self::$imageExtensions = Config::get('cms.storage.media.image_extensions', self::$defaultTypeExtensions['image']);
|
||||||
|
self::$videoExtensions = Config::get('cms.storage.media.video_extensions', self::$defaultTypeExtensions['video']);
|
||||||
|
self::$audioExtensions = Config::get('cms.storage.media.audio_extensions', self::$defaultTypeExtensions['audio']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$extension = pathinfo($this->path, PATHINFO_EXTENSION);
|
||||||
|
if (!strlen($extension))
|
||||||
|
return self::FILE_TYPE_DOCUMENT;
|
||||||
|
|
||||||
|
if (in_array($extension, self::$imageExtensions))
|
||||||
|
return self::FILE_TYPE_IMAGE;
|
||||||
|
|
||||||
|
if (in_array($extension, self::$videoExtensions))
|
||||||
|
return self::FILE_TYPE_VIDEO;
|
||||||
|
|
||||||
|
if (in_array($extension, self::$audioExtensions))
|
||||||
|
return self::FILE_TYPE_AUDIO;
|
||||||
|
|
||||||
|
return self::FILE_TYPE_DOCUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item size as string.
|
||||||
|
* For file-type items the size is the number of bytes. For folder-type items
|
||||||
|
* the size is the number of items contained by the item.
|
||||||
|
* @return string Returns the size as string.
|
||||||
|
*/
|
||||||
|
public function sizeToString()
|
||||||
|
{
|
||||||
|
return $this->type == self::TYPE_FILE ?
|
||||||
|
File::sizeToString($this->size) :
|
||||||
|
$this->size.' '.trans('cms::lang.media.folder_size_items');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item last modification date as string.
|
||||||
|
* @return string Returns the item last modification date as string.
|
||||||
|
*/
|
||||||
|
public function lastModifiedAsString()
|
||||||
|
{
|
||||||
|
return $this->lastModified ? date('M d, Y', $this->lastModified) : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php namespace Cms\Controllers;
|
||||||
|
|
||||||
|
use BackendMenu;
|
||||||
|
use Backend\Classes\Controller;
|
||||||
|
use Cms\Widgets\MediaManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CMS Media Manager
|
||||||
|
*
|
||||||
|
* @package october\cms
|
||||||
|
* @author Alexey Bobkov, Samuel Georges
|
||||||
|
*/
|
||||||
|
class Media extends Controller
|
||||||
|
{
|
||||||
|
public $requiredPermissions = ['cms.*'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
BackendMenu::setContext('October.Cms', 'media', true);
|
||||||
|
$this->pageTitle = 'cms::lang.media.menu_label';
|
||||||
|
|
||||||
|
$manager = new MediaManager($this, 'manager');
|
||||||
|
$manager->bindToController();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$this->bodyClass = 'compact-container';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?= Block::put('head') ?><?= Block::endPut() ?>
|
||||||
|
|
||||||
|
<?= Block::put('body') ?>
|
||||||
|
<div class="layout">
|
||||||
|
<?= $this->widget->manager->render() ?>
|
||||||
|
</div>
|
||||||
|
<?= Block::endPut() ?>
|
||||||
|
|
@ -181,6 +181,27 @@ return [
|
||||||
'manage_themes' => 'Manage themes'
|
'manage_themes' => 'Manage themes'
|
||||||
],
|
],
|
||||||
'media' => [
|
'media' => [
|
||||||
'invalid_path' => "Invalid file path specified: ':path'."
|
'invalid_path' => "Invalid file path specified: ':path'.",
|
||||||
|
'menu_label' => 'Media',
|
||||||
|
'upload' => 'Upload',
|
||||||
|
'add_folder' => 'Add folder',
|
||||||
|
'search' => 'Search',
|
||||||
|
'filter_everything' => 'Everything',
|
||||||
|
'filter_images' => 'Images',
|
||||||
|
'filter_video' => 'Video',
|
||||||
|
'filter_audio' => 'Audio',
|
||||||
|
'filter_documents' => 'Documents',
|
||||||
|
'library' => 'Library',
|
||||||
|
'folder_size_items' => 'item(s)',
|
||||||
|
'size' => 'Size',
|
||||||
|
'title' => 'Title',
|
||||||
|
'last_modified' => 'Last modified',
|
||||||
|
'public_url' => 'Public URL',
|
||||||
|
'click_here' => 'Click here',
|
||||||
|
'thumbnail_error' => 'Error generating thumbnail.',
|
||||||
|
'return_to_parent' => 'Return to the parent folder',
|
||||||
|
'return_to_parent_label' => 'Go up ..',
|
||||||
|
'nothing_selected' => 'Nothing is selected.',
|
||||||
|
'multiple_selected' => 'Multiple items selected.'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,444 @@
|
||||||
|
<?php namespace Cms\Widgets;
|
||||||
|
|
||||||
|
use URL;
|
||||||
|
use Str;
|
||||||
|
use Lang;
|
||||||
|
use File;
|
||||||
|
use Input;
|
||||||
|
use Request;
|
||||||
|
use Response;
|
||||||
|
use Exception;
|
||||||
|
use SystemException;
|
||||||
|
use ApplicationException;
|
||||||
|
use Backend\Classes\WidgetBase;
|
||||||
|
use Cms\Classes\MediaLibrary;
|
||||||
|
use Cms\Classes\MediaLibraryItem;
|
||||||
|
use October\Rain\Database\Attach\Resizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Media Manager widget.
|
||||||
|
*
|
||||||
|
* @package october\cms
|
||||||
|
* @author Alexey Bobkov, Samuel Georges
|
||||||
|
*/
|
||||||
|
class MediaManager extends WidgetBase
|
||||||
|
{
|
||||||
|
const FOLDER_ROOT = '/';
|
||||||
|
const VIEW_MODE_GRID = 'grid';
|
||||||
|
const VIEW_MODE_LIST = 'list';
|
||||||
|
const VIEW_MODE_TILES = 'tiles';
|
||||||
|
|
||||||
|
protected $brokenImageHash = null;
|
||||||
|
|
||||||
|
public function __construct($controller, $alias)
|
||||||
|
{
|
||||||
|
$this->alias = $alias;
|
||||||
|
|
||||||
|
parent::__construct($controller, []);
|
||||||
|
$this->bindToController();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the widget.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$this->prepareVars();
|
||||||
|
|
||||||
|
return $this->makePartial('body');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Event handlers
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function onSearch()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onGoToFolder()
|
||||||
|
{
|
||||||
|
$path = Input::get('path');
|
||||||
|
|
||||||
|
if (Input::get('clearCache'))
|
||||||
|
MediaLibrary::instance()->resetCache();
|
||||||
|
|
||||||
|
$this->setCurrentFolder($path);
|
||||||
|
$this->prepareVars();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'#'.$this->getId('item-list') => $this->makePartial('item-list'),
|
||||||
|
'#'.$this->getId('folder-path') => $this->makePartial('folder-path')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onGenerateThumbnails()
|
||||||
|
{
|
||||||
|
$batch = Input::get('batch');
|
||||||
|
if (!is_array($batch))
|
||||||
|
return;
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($batch as $thumbnailInfo)
|
||||||
|
$result[] = $this->generateThumbnail($thumbnailInfo);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'generatedThumbnails'=>$result
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onGetSidebarThumbnail()
|
||||||
|
{
|
||||||
|
$path = Input::get('path');
|
||||||
|
$lastModified = Input::get('lastModified');
|
||||||
|
|
||||||
|
$thumbnailParams = $this->getThumbnailParams();
|
||||||
|
$thumbnailParams['width'] = 300;
|
||||||
|
$thumbnailParams['height'] = 255;
|
||||||
|
$thumbnailParams['mode'] = 'auto';
|
||||||
|
|
||||||
|
$path = MediaLibrary::validatePath($path);
|
||||||
|
|
||||||
|
if (!is_numeric($lastModified))
|
||||||
|
throw new ApplicationException('Invalid input data');
|
||||||
|
|
||||||
|
// If the thumbnail file exists - just return the thumbnail marup,
|
||||||
|
// otherwise generate a new thumbnail.
|
||||||
|
$thumbnailPath = $this->thumbnailExists($thumbnailParams, $path, $lastModified);
|
||||||
|
if ($thumbnailPath) {
|
||||||
|
return [
|
||||||
|
'markup'=>$this->makePartial('thumbnail-image', [
|
||||||
|
'isError' => $this->thumbnailIsError($thumbnailPath),
|
||||||
|
'imageUrl' => $this->getThumbnailImageUrl($thumbnailPath)
|
||||||
|
])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$thumbnailInfo = $thumbnailParams;
|
||||||
|
$thumbnailInfo['path'] = $path;
|
||||||
|
$thumbnailInfo['lastModified'] = $lastModified;
|
||||||
|
$thumbnailInfo['id'] = 'sidebar-thumbnail';
|
||||||
|
|
||||||
|
return $this->generateThumbnail($thumbnailInfo, $thumbnailParams, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onChangeView()
|
||||||
|
{
|
||||||
|
$viewMode = Input::get('view');
|
||||||
|
$path = Input::get('path');
|
||||||
|
|
||||||
|
$this->setViewMode($viewMode);
|
||||||
|
$this->setCurrentFolder($path);
|
||||||
|
|
||||||
|
$this->prepareVars();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'#'.$this->getId('item-list') => $this->makePartial('item-list'),
|
||||||
|
'#'.$this->getId('folder-path') => $this->makePartial('folder-path'),
|
||||||
|
'#'.$this->getId('view-mode-buttons') => $this->makePartial('view-mode-buttons')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Methods for th internal use
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function prepareVars()
|
||||||
|
{
|
||||||
|
clearstatcache();
|
||||||
|
|
||||||
|
$folder = $this->getCurrentFolder();
|
||||||
|
$viewMode = $this->getViewMode();
|
||||||
|
|
||||||
|
$this->vars['items'] = $this->listFolderItems($folder);
|
||||||
|
$this->vars['currentFolder'] = $folder;
|
||||||
|
$this->vars['isRootFolder'] = $folder == self::FOLDER_ROOT;
|
||||||
|
$this->vars['pathSegments'] = $this->splitPathToSegments($folder);
|
||||||
|
$this->vars['viewMode'] = $viewMode;
|
||||||
|
|
||||||
|
$this->vars['thumbnailParams'] = $this->getThumbnailParams($viewMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function listFolderItems($folder)
|
||||||
|
{
|
||||||
|
return MediaLibrary::instance()->listFolderContents($folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCurrentFolder()
|
||||||
|
{
|
||||||
|
$folder = $this->getSession('media_folder', self::FOLDER_ROOT);
|
||||||
|
|
||||||
|
return $folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setCurrentFolder($path)
|
||||||
|
{
|
||||||
|
$path = MediaLibrary::validatePath($path);
|
||||||
|
|
||||||
|
$this->putSession('media_folder', $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function itemTypeToIconClass($item, $itemType)
|
||||||
|
{
|
||||||
|
if ($item->type == MediaLibraryItem::TYPE_FOLDER)
|
||||||
|
return 'icon-folder';
|
||||||
|
|
||||||
|
switch ($itemType) {
|
||||||
|
case MediaLibraryItem::FILE_TYPE_IMAGE : return "icon-picture-o";
|
||||||
|
case MediaLibraryItem::FILE_TYPE_VIDEO : return "icon-video-camera";
|
||||||
|
case MediaLibraryItem::FILE_TYPE_AUDIO : return "icon-volume-up";
|
||||||
|
default : return "icon-file";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function splitPathToSegments($path)
|
||||||
|
{
|
||||||
|
$path = MediaLibrary::validatePath($path, true);
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
do {
|
||||||
|
$result[] = $path;
|
||||||
|
} while (($path = dirname($path)) != '/');
|
||||||
|
|
||||||
|
return array_reverse($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds widget specific asset files. Use $this->addJs() and $this->addCss()
|
||||||
|
* to register new assets to include on the page.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function loadAssets()
|
||||||
|
{
|
||||||
|
$this->addCss('css/mediamanager.css', 'core');
|
||||||
|
$this->addJs('js/mediamanager.js', 'core');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getViewMode()
|
||||||
|
{
|
||||||
|
return $this->getSession('view_mode', self::VIEW_MODE_GRID);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setViewMode($viewMode)
|
||||||
|
{
|
||||||
|
if (!in_array($viewMode, [self::VIEW_MODE_GRID, self::VIEW_MODE_LIST, self::VIEW_MODE_TILES]))
|
||||||
|
throw new SystemException('Invalid input data');
|
||||||
|
|
||||||
|
return $this->putSession('view_mode', $viewMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getThumbnailParams($viewMode = null)
|
||||||
|
{
|
||||||
|
$result = [
|
||||||
|
'mode' => 'crop',
|
||||||
|
'ext' => 'png'
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($viewMode) {
|
||||||
|
if ($viewMode == self::VIEW_MODE_LIST) {
|
||||||
|
$result['width'] = 75;
|
||||||
|
$result['height'] = 75;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$result['width'] = 165;
|
||||||
|
$result['height'] = 165;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getThumbnailImagePath($thumbnailParams, $itemPath, $lastModified)
|
||||||
|
{
|
||||||
|
$itemSignature = md5($itemPath).$lastModified;
|
||||||
|
|
||||||
|
$thumbFile = 'thumb_' .
|
||||||
|
$itemSignature . '_' .
|
||||||
|
$thumbnailParams['width'] . 'x' .
|
||||||
|
$thumbnailParams['height'] . '_' .
|
||||||
|
$thumbnailParams['mode'] . '.' .
|
||||||
|
$thumbnailParams['ext'];
|
||||||
|
|
||||||
|
$partition = implode('/', array_slice(str_split($itemSignature, 3), 0, 3)) . '/';
|
||||||
|
|
||||||
|
$result = $this->getThumbnailDirectory().$partition.$thumbFile;
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getThumbnailImageUrl($imagePath)
|
||||||
|
{
|
||||||
|
return URL::to('/storage/temp'.$imagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function thumbnailExists($thumbnailParams, $itemPath, $lastModified)
|
||||||
|
{
|
||||||
|
$thumbnailPath = $this->getThumbnailImagePath($thumbnailParams, $itemPath, $lastModified);
|
||||||
|
|
||||||
|
$fullPath = temp_path(ltrim($thumbnailPath, '/'));
|
||||||
|
if (File::exists($fullPath))
|
||||||
|
return $thumbnailPath;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function thumbnailIsError($thumbnailPath)
|
||||||
|
{
|
||||||
|
$fullPath = temp_path(ltrim($thumbnailPath, '/'));
|
||||||
|
return hash_file('crc32', $fullPath) == $this->getBrokenImageHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getLocalTempFilePath($fileName)
|
||||||
|
{
|
||||||
|
$fileName = md5($fileName.uniqid().microtime());
|
||||||
|
|
||||||
|
$path = temp_path() . '/media';
|
||||||
|
|
||||||
|
if (!File::isDirectory($path))
|
||||||
|
File::makeDirectory($path, 0777, true, true);
|
||||||
|
|
||||||
|
return $path.'/'.$fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getThumbnailDirectory()
|
||||||
|
{
|
||||||
|
return '/public/';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPlaceholderId($item)
|
||||||
|
{
|
||||||
|
return 'placeholder'.md5($item->path.'-'.$item->lastModified.uniqid(microtime()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateThumbnail($thumbnailInfo, $thumbnailParams = null)
|
||||||
|
{
|
||||||
|
$tempFilePath = null;
|
||||||
|
$fullThumbnailPath = null;
|
||||||
|
$thumbnailPath = null;
|
||||||
|
$markup = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get and validate input data
|
||||||
|
$path = $thumbnailInfo['path'];
|
||||||
|
$width = $thumbnailInfo['width'];
|
||||||
|
$height = $thumbnailInfo['height'];
|
||||||
|
$lastModified = $thumbnailInfo['lastModified'];
|
||||||
|
|
||||||
|
if (!is_numeric($width) || !is_numeric($height) || !is_numeric($lastModified))
|
||||||
|
throw new ApplicationException('Invalid input data');
|
||||||
|
|
||||||
|
if (!$thumbnailParams) {
|
||||||
|
$thumbnailParams = $this->getThumbnailParams();
|
||||||
|
$thumbnailParams['width'] = $width;
|
||||||
|
$thumbnailParams['height'] = $height;
|
||||||
|
}
|
||||||
|
|
||||||
|
$thumbnailPath = $this->getThumbnailImagePath($thumbnailParams, $path, $lastModified);
|
||||||
|
$fullThumbnailPath = temp_path(ltrim($thumbnailPath, '/'));
|
||||||
|
|
||||||
|
// Save the file locally
|
||||||
|
$library = MediaLibrary::instance();
|
||||||
|
$tempFilePath = $this->getLocalTempFilePath($path);
|
||||||
|
|
||||||
|
if (!@File::put($tempFilePath, $library->get($path)))
|
||||||
|
throw new SystemException('Error saving remote file to a temporary location');
|
||||||
|
|
||||||
|
// Resize the thumbnail and save to the thumbnails directory
|
||||||
|
$this->resizeImage($fullThumbnailPath, $thumbnailParams, $tempFilePath);
|
||||||
|
|
||||||
|
// Delete the temporary file
|
||||||
|
File::delete($tempFilePath);
|
||||||
|
$markup = $this->makePartial('thumbnail-image', [
|
||||||
|
'isError' => false,
|
||||||
|
'imageUrl' => $this->getThumbnailImageUrl($thumbnailPath)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
catch (Exception $ex) {
|
||||||
|
if ($tempFilePath)
|
||||||
|
File::delete($tempFilePath);
|
||||||
|
|
||||||
|
if ($fullThumbnailPath)
|
||||||
|
$this->copyBrokenImage($fullThumbnailPath);
|
||||||
|
|
||||||
|
$markup = $this->makePartial('thumbnail-image', ['isError' => true]);
|
||||||
|
|
||||||
|
// TODO: We need to log all types of exceptions here
|
||||||
|
traceLog($ex->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($markup && ($id = $thumbnailInfo['id']))
|
||||||
|
return [
|
||||||
|
'id'=>$id,
|
||||||
|
'markup'=>$markup
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function resizeImage($fullThumbnailPath, $thumbnailParams, $tempFilePath)
|
||||||
|
{
|
||||||
|
$thumbnailDir = dirname($fullThumbnailPath);
|
||||||
|
if (!File::isDirectory($thumbnailDir)) {
|
||||||
|
if (File::makeDirectory($thumbnailDir, 0777, true) === false)
|
||||||
|
throw new SystemException('Error creating thumbnail directory');
|
||||||
|
}
|
||||||
|
|
||||||
|
$targetDimensions = $this->getTargetDimensions($thumbnailParams['width'], $thumbnailParams['height'], $tempFilePath);
|
||||||
|
|
||||||
|
$targetWidth = $targetDimensions[0];
|
||||||
|
$targetHeight = $targetDimensions[1];
|
||||||
|
|
||||||
|
$resizer = Resizer::open($tempFilePath);
|
||||||
|
$resizer->resize($targetWidth, $targetHeight, $thumbnailParams['mode'], [0, 0]);
|
||||||
|
$resizer->save($fullThumbnailPath, 95);
|
||||||
|
|
||||||
|
File::chmod($fullThumbnailPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getBrokenImagePath()
|
||||||
|
{
|
||||||
|
return __DIR__.'/mediamanager/assets/images/broken-thumbnail.gif';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getBrokenImageHash()
|
||||||
|
{
|
||||||
|
if ($this->brokenImageHash)
|
||||||
|
return $this->brokenImageHash;
|
||||||
|
|
||||||
|
$fullPath = $this->getBrokenImagePath();
|
||||||
|
return $this->brokenImageHash = hash_file('crc32', $fullPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function copyBrokenImage($path)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$thumbnailDir = dirname($path);
|
||||||
|
if (!File::isDirectory($thumbnailDir)) {
|
||||||
|
if (File::makeDirectory($thumbnailDir, 0777, true) === false)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File::copy($this->getBrokenImagePath(), $path);
|
||||||
|
}
|
||||||
|
catch (Exception $ex) {
|
||||||
|
traceLog($ex->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTargetDimensions($width, $height, $originalImagePath)
|
||||||
|
{
|
||||||
|
$originalDimensions = [$width, $height];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dimensions = getimagesize($originalImagePath);
|
||||||
|
if (!$dimensions)
|
||||||
|
return $originalDimensions;
|
||||||
|
|
||||||
|
if ($dimensions[0] > $width || $dimensions[1] > $height)
|
||||||
|
return $originalDimensions;
|
||||||
|
|
||||||
|
return $dimensions;
|
||||||
|
}
|
||||||
|
catch (Exception $ex) {
|
||||||
|
return $originalDimensions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,288 @@
|
||||||
|
div[data-control="media-manager"] audio,
|
||||||
|
div[data-control="media-manager"] video {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] video {
|
||||||
|
background: #ecf0f1;
|
||||||
|
max-height: 225px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-player-fallback {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #95a5a6;
|
||||||
|
background: #ecf0f1;
|
||||||
|
line-height: 180%;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-player-fallback.panel-embedded {
|
||||||
|
padding: 20px;
|
||||||
|
margin: -20px -20px 0 -20px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] p.thumbnail-error-message {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 25px;
|
||||||
|
color: #bdc3c7;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list {
|
||||||
|
padding: 0 0 20px 20px;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
margin: 0 20px 20px 0;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li .icon-container {
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li .icon-container i {
|
||||||
|
color: #95a5a6;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li .icon-container div {
|
||||||
|
display: table-cell;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li .icon-container.image > div.icon-wrapper {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li h4 {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #2b3e50;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding-right: 15px;
|
||||||
|
line-height: 150%;
|
||||||
|
margin: 15px 0 5px 0;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li p.size {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #95a5a6;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li .image-placeholder {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li .image-placeholder i {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-left: 2px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li .image-placeholder[data-loading] i {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li .image-placeholder[data-loading]:after {
|
||||||
|
background-image: url(../../../../../../modules/backend/assets/images/loading-indicator-transparent.svg);
|
||||||
|
background-position: 50% 50%;
|
||||||
|
content: ' ';
|
||||||
|
-webkit-animation: spin 1s linear infinite;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
background-size: 28px 28px;
|
||||||
|
position: absolute;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: -14px;
|
||||||
|
margin-left: -14px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li i.icon-chain-broken {
|
||||||
|
padding: 0;
|
||||||
|
color: #bdc3c7;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list li[data-item-type=folder] i {
|
||||||
|
color: #4da7e8;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li {
|
||||||
|
height: 75px;
|
||||||
|
width: 260px;
|
||||||
|
border: 1px solid #ecf0f1;
|
||||||
|
background: #f6f8f9;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li .icon-container {
|
||||||
|
border-right: 1px solid #f6f8f9;
|
||||||
|
width: 75px;
|
||||||
|
height: 75px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li .icon-container i {
|
||||||
|
font-size: 35px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li .icon-container.image {
|
||||||
|
border-right: 1px solid #ecf0f1!important;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li .icon-container p.thumbnail-error-message {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list .icon-wrapper {
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li .info {
|
||||||
|
margin-left: 90px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li .image-placeholder {
|
||||||
|
width: 75px;
|
||||||
|
height: 75px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li[data-root] h4 {
|
||||||
|
margin-top: 27px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li.selected {
|
||||||
|
background: #4da7e8 !important;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li.selected i,
|
||||||
|
div[data-control="media-manager"] .media-list.list li.selected p.size {
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li.selected h4 {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.list li.selected .icon-container {
|
||||||
|
border-right-color: #4da7e8 !important;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles li {
|
||||||
|
width: 167px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles .icon-wrapper {
|
||||||
|
width: 167px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles li .image-placeholder {
|
||||||
|
width: 165px;
|
||||||
|
height: 165px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles li .image-placeholder[data-loading]:after {
|
||||||
|
background-image: url(../../../../../../modules/backend/assets/images/loading-indicator-transparent.svg);
|
||||||
|
background-position: 50% 50%;
|
||||||
|
content: ' ';
|
||||||
|
-webkit-animation: spin 1s linear infinite;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
background-size: 55px 55px;
|
||||||
|
position: absolute;
|
||||||
|
width: 55px;
|
||||||
|
height: 55px;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: -27.5px;
|
||||||
|
margin-left: -27.5px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles li .icon-container {
|
||||||
|
width: 167px;
|
||||||
|
height: 167px;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #ecf0f1;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f6f8f9;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles li .icon-container i {
|
||||||
|
font-size: 55px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles li .icon-container p {
|
||||||
|
font-family: 'Open Sans', Arial, sans-serif;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles li.selected .icon-container {
|
||||||
|
background: #4da7e8 !important;
|
||||||
|
border-color: #2581b8;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles li.selected .icon-container i,
|
||||||
|
div[data-control="media-manager"] .media-list.tiles li.selected .icon-container p {
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles li.selected h4 {
|
||||||
|
color: #2581b8;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles i.icon-chain-broken {
|
||||||
|
margin-top: 47px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .media-list.tiles p.size {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .sidebar-image-placeholder-container {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .sidebar-image-placeholder {
|
||||||
|
display: table-cell;
|
||||||
|
height: 225px;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid #ecf0f1;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .sidebar-image-placeholder[data-loading] {
|
||||||
|
background: #ecf0f1;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .sidebar-image-placeholder[data-loading]:after {
|
||||||
|
background-image: url(../../../../../../modules/backend/assets/images/loading-indicator-transparent.svg);
|
||||||
|
background-position: 50% 50%;
|
||||||
|
content: ' ';
|
||||||
|
-webkit-animation: spin 1s linear infinite;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
background-size: 62px 62px;
|
||||||
|
position: absolute;
|
||||||
|
width: 62px;
|
||||||
|
height: 62px;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: -31px;
|
||||||
|
margin-left: -31px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .sidebar-image-placeholder i.icon-chain-broken,
|
||||||
|
div[data-control="media-manager"] .sidebar-image-placeholder i.icon-crop,
|
||||||
|
div[data-control="media-manager"] .sidebar-image-placeholder i.icon-asterisk {
|
||||||
|
color: #bdc3c7;
|
||||||
|
font-size: 55px;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .sidebar-image-placeholder.no-border {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .sidebar-image-placeholder p {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 25px;
|
||||||
|
color: #bdc3c7;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] .list-container {
|
||||||
|
position: relative;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] [data-control="item-list"] {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
div[data-control="media-manager"] div[data-control="selection-marker"] {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 50;
|
||||||
|
border: 1px dashed #95a5a6;
|
||||||
|
}
|
||||||
|
body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover .icon-container {
|
||||||
|
background: #4da7e8 !important;
|
||||||
|
border-color: #2581b8;
|
||||||
|
}
|
||||||
|
body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover .icon-container i,
|
||||||
|
body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover .icon-container p {
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover h4 {
|
||||||
|
color: #2581b8;
|
||||||
|
}
|
||||||
|
body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover {
|
||||||
|
background: #4da7e8 !important;
|
||||||
|
}
|
||||||
|
body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover i,
|
||||||
|
body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover p.size {
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover h4 {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover .icon-container {
|
||||||
|
border-right-color: #4da7e8 !important;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
|
|
@ -0,0 +1,665 @@
|
||||||
|
/*
|
||||||
|
* Media manager control class
|
||||||
|
*
|
||||||
|
* Dependences:
|
||||||
|
* - Scrollbar (october.scrollbar.js)
|
||||||
|
*/
|
||||||
|
+function ($) { "use strict";
|
||||||
|
|
||||||
|
// MEDIA MANAGER CLASS DEFINITION
|
||||||
|
// ============================
|
||||||
|
|
||||||
|
var MediaManager = function(element, options) {
|
||||||
|
this.$el = $(element)
|
||||||
|
this.$form = this.$el.closest('form')
|
||||||
|
|
||||||
|
this.options = options
|
||||||
|
|
||||||
|
// Event handlers
|
||||||
|
this.navigateHandler = this.onNavigate.bind(this)
|
||||||
|
this.commandClickHandler = this.onCommandClick.bind(this)
|
||||||
|
this.itemClickHandler = this.onItemClick.bind(this)
|
||||||
|
this.listMouseDownHandler = this.onListMouseDown.bind(this)
|
||||||
|
this.listMouseUpHandler = this.onListMouseUp.bind(this)
|
||||||
|
this.listMouseMoveHandler = this.onListMouseMove.bind(this)
|
||||||
|
|
||||||
|
// Instance-bound methods
|
||||||
|
this.updateSidebarPreviewBound = this.updateSidebarPreview.bind(this)
|
||||||
|
this.replacePlaceholderBound = this.replacePlaceholder.bind(this)
|
||||||
|
this.placeholdersUpdatedBound = this.placeholdersUpdated.bind(this)
|
||||||
|
this.afterNavigateBound = this.afterNavigate.bind(this)
|
||||||
|
this.releaseSidebarThumbnailAjaxBound = this.releaseSidebarThumbnailAjax.bind(this)
|
||||||
|
this.replaceSidebarPlaceholderBound = this.replaceSidebarPlaceholder.bind(this)
|
||||||
|
|
||||||
|
// State properties
|
||||||
|
this.selectTimer = null
|
||||||
|
this.sidebarPreviewElement = null
|
||||||
|
this.itemListElement = null
|
||||||
|
this.thumbnailQueue = []
|
||||||
|
this.activeThumbnailQueueLength = 0
|
||||||
|
this.sidebarThumbnailAjax = null
|
||||||
|
this.selectionMarker = null
|
||||||
|
this.dropzone = null
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialization
|
||||||
|
//
|
||||||
|
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.dispose = function() {
|
||||||
|
this.unregisterHandlers()
|
||||||
|
this.clearSelectTimer()
|
||||||
|
|
||||||
|
this.$el = null
|
||||||
|
this.$form = null
|
||||||
|
this.updateSidebarPreviewBound = null
|
||||||
|
this.replacePlaceholderBound = null
|
||||||
|
this.placeholdersUpdatedBound = null
|
||||||
|
this.sidebarPreviewElement = null
|
||||||
|
this.itemListElement = null
|
||||||
|
this.afterNavigateBound = null
|
||||||
|
this.replaceSidebarPlaceholderBound = null
|
||||||
|
this.sidebarThumbnailAjax = null
|
||||||
|
this.selectionMarker = null
|
||||||
|
this.thumbnailQueue = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// MEDIA MANAGER INTERNAL METHODS
|
||||||
|
// ============================
|
||||||
|
|
||||||
|
MediaManager.prototype.init = function() {
|
||||||
|
this.itemListElement = this.$el.find('[data-control="item-list"]').get(0)
|
||||||
|
this.registerHandlers()
|
||||||
|
|
||||||
|
this.updateSidebarPreview()
|
||||||
|
this.generateThumbnails()
|
||||||
|
this.initUploader()
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.registerHandlers = function() {
|
||||||
|
this.$el.on('dblclick', this.navigateHandler)
|
||||||
|
this.$el.on('click.tree-path', 'ul.tree-path', this.navigateHandler)
|
||||||
|
this.$el.on('click.command', '[data-command]', this.commandClickHandler)
|
||||||
|
this.$el.on('click.item', '[data-type="media-item"]', this.itemClickHandler)
|
||||||
|
|
||||||
|
if (this.itemListElement)
|
||||||
|
this.itemListElement.addEventListener('mousedown', this.listMouseDownHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.unregisterHandlers = function() {
|
||||||
|
this.$el.off('dblclick', this.navigateHandler)
|
||||||
|
this.$el.off('click.tree-path', this.navigateHandler)
|
||||||
|
this.$el.off('click.command', this.commandClickHandler)
|
||||||
|
this.$el.off('click.item', this.itemClickHandler)
|
||||||
|
|
||||||
|
if (this.itemListElement) {
|
||||||
|
this.itemListElement.removeEventListener('mousedown', this.listMouseDownHandler)
|
||||||
|
this.itemListElement.removeEventListener('mousemove', this.listMouseMoveHandler)
|
||||||
|
}
|
||||||
|
document.removeEventListener('mouseup', this.listMouseUpHandler)
|
||||||
|
|
||||||
|
this.navigateHandler = null
|
||||||
|
this.commandClickHandler = null
|
||||||
|
this.itemClickHandler = null
|
||||||
|
this.listMouseDownHandler = null
|
||||||
|
this.listMouseUpHandler = null
|
||||||
|
this.listMouseMoveHandler = null
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.changeView = function(view) {
|
||||||
|
$.oc.stripeLoadIndicator.show()
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
view: view,
|
||||||
|
path: this.$el.find('[data-type="current-folder"]').val()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$form.request(this.options.alias+'::onChangeView', {
|
||||||
|
data: data
|
||||||
|
}).always(function() {
|
||||||
|
$.oc.stripeLoadIndicator.hide()
|
||||||
|
}).done(this.afterNavigateBound)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Selecting
|
||||||
|
*/
|
||||||
|
|
||||||
|
MediaManager.prototype.clearSelectTimer = function() {
|
||||||
|
if (this.selectTimer == null)
|
||||||
|
return
|
||||||
|
|
||||||
|
clearTimeout(this.selectTimer)
|
||||||
|
this.selectTimer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.selectItem = function(node, expandSelection) {
|
||||||
|
if (!expandSelection) {
|
||||||
|
var items = this.$el.get(0).querySelectorAll('[data-type="media-item"].selected')
|
||||||
|
|
||||||
|
// The class attribute is used only for selecting, it's safe to clear it
|
||||||
|
for (var i = 0, len = items.length; i < len; i++)
|
||||||
|
items[i].setAttribute('class', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expandSelection)
|
||||||
|
node.setAttribute('class', 'selected')
|
||||||
|
else {
|
||||||
|
if (node.getAttribute('class') == 'selected')
|
||||||
|
node.setAttribute('class', '')
|
||||||
|
else
|
||||||
|
node.setAttribute('class', 'selected')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clearSelectTimer()
|
||||||
|
|
||||||
|
if (this.isPreviewSidebarVisible()) {
|
||||||
|
// Use the timeout to prevent too many AJAX requests
|
||||||
|
// when the selection changes too quickly (with the keyboard arrows)
|
||||||
|
this.selectTimer = setTimeout(this.updateSidebarPreviewBound, 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Navigation
|
||||||
|
*/
|
||||||
|
|
||||||
|
MediaManager.prototype.gotoFolder = function(path, clearCache) {
|
||||||
|
var data = {
|
||||||
|
path: path
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearCache)
|
||||||
|
data.clearCache = true
|
||||||
|
|
||||||
|
$.oc.stripeLoadIndicator.show()
|
||||||
|
this.$form.request(this.options.alias+'::onGoToFolder', {
|
||||||
|
data: data
|
||||||
|
}).always(function() {
|
||||||
|
$.oc.stripeLoadIndicator.hide()
|
||||||
|
}).done(this.afterNavigateBound)
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.afterNavigate = function() {
|
||||||
|
this.generateThumbnails()
|
||||||
|
this.updateSidebarPreview(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sidebar
|
||||||
|
*/
|
||||||
|
|
||||||
|
MediaManager.prototype.isPreviewSidebarVisible = function() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.updateSidebarMediaPreview = function(items) {
|
||||||
|
var previewPanel = this.sidebarPreviewElement,
|
||||||
|
previewContainer = previewPanel.querySelector('[data-control="media-preview-container"]'),
|
||||||
|
template = ''
|
||||||
|
|
||||||
|
for (var i = 0, len = previewContainer.children.length; i < len; i++)
|
||||||
|
previewContainer.removeChild(previewContainer.children[i])
|
||||||
|
|
||||||
|
if (items.length == 1) {
|
||||||
|
var item = items[0],
|
||||||
|
documentType = item.getAttribute('data-document-type')
|
||||||
|
|
||||||
|
switch (documentType) {
|
||||||
|
case 'audio' :
|
||||||
|
template = previewPanel.querySelector('[data-control="audio-template"]').innerHTML
|
||||||
|
break;
|
||||||
|
case 'video' :
|
||||||
|
template = previewPanel.querySelector('[data-control="video-template"]').innerHTML
|
||||||
|
break;
|
||||||
|
case 'image' :
|
||||||
|
template = previewPanel.querySelector('[data-control="image-template"]').innerHTML
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
previewContainer.innerHTML = template
|
||||||
|
.replace('{src}', item.getAttribute('data-public-url'))
|
||||||
|
.replace('{path}', item.getAttribute('data-path'))
|
||||||
|
.replace('{last-modified}', item.getAttribute('data-last-modified-ts'))
|
||||||
|
|
||||||
|
if (documentType == 'image')
|
||||||
|
this.loadSidebarThumbnail()
|
||||||
|
}
|
||||||
|
else if (items.length == 0) {
|
||||||
|
template = previewPanel.querySelector('[data-control="no-selection-template"]').innerHTML
|
||||||
|
previewContainer.innerHTML = template
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
template = previewPanel.querySelector('[data-control="multi-selection-template"]').innerHTML
|
||||||
|
previewContainer.innerHTML = template
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.updateSidebarPreview = function(resetSidebar) {
|
||||||
|
if (!this.sidebarPreviewElement)
|
||||||
|
this.sidebarPreviewElement = this.$el.get(0).querySelector('[data-control="preview-sidebar"]')
|
||||||
|
|
||||||
|
var items = resetSidebar === undefined ? this.$el.get(0).querySelectorAll('[data-type="media-item"].selected') : [],
|
||||||
|
previewPanel = this.sidebarPreviewElement
|
||||||
|
|
||||||
|
if (items.length == 0) {
|
||||||
|
// No items are selected
|
||||||
|
this.sidebarPreviewElement.querySelector('[data-control="sidebar-labels"]').setAttribute('class', 'hide')
|
||||||
|
}
|
||||||
|
else if (items.length == 1) {
|
||||||
|
this.sidebarPreviewElement.querySelector('[data-control="sidebar-labels"]').setAttribute('class', 'panel')
|
||||||
|
|
||||||
|
// One item is selected - display the details
|
||||||
|
var item = items[0]
|
||||||
|
|
||||||
|
previewPanel.querySelector('[data-label="size"]').textContent = item.getAttribute('data-size')
|
||||||
|
previewPanel.querySelector('[data-label="title"]').textContent = item.getAttribute('data-title')
|
||||||
|
previewPanel.querySelector('[data-label="last-modified"]').textContent = item.getAttribute('data-last-modified')
|
||||||
|
previewPanel.querySelector('[data-label="public-url"]').setAttribute('href', item.getAttribute('data-public-url'))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Multiple items are selected
|
||||||
|
this.sidebarPreviewElement.querySelector('[data-control="sidebar-labels"]').setAttribute('class', 'hide')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateSidebarMediaPreview(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.loadSidebarThumbnail = function() {
|
||||||
|
if (this.sidebarThumbnailAjax) {
|
||||||
|
try {
|
||||||
|
this.sidebarThumbnailAjax.abort()
|
||||||
|
}
|
||||||
|
catch (e) {}
|
||||||
|
this.sidebarThumbnailAjax = null
|
||||||
|
}
|
||||||
|
|
||||||
|
var sidebarThumbnail = this.sidebarPreviewElement.querySelector('[data-control="sidebar-thumbnail"]')
|
||||||
|
if (!sidebarThumbnail)
|
||||||
|
return
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
path: sidebarThumbnail.getAttribute('data-path'),
|
||||||
|
lastModified: sidebarThumbnail.getAttribute('data-last-modified')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sidebarThumbnailAjax = this.$form.request(this.options.alias+'::onGetSidebarThumbnail', {
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
.done(this.replaceSidebarPlaceholderBound)
|
||||||
|
.always(this.releaseSidebarThumbnailAjaxBound)
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.replaceSidebarPlaceholder = function(response) {
|
||||||
|
var sidebarThumbnail = this.sidebarPreviewElement.querySelector('[data-control="sidebar-thumbnail"]')
|
||||||
|
if (!sidebarThumbnail)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!response.markup)
|
||||||
|
return
|
||||||
|
|
||||||
|
sidebarThumbnail.innerHTML = response.markup
|
||||||
|
sidebarThumbnail.removeAttribute('data-loading')
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.releaseSidebarThumbnailAjax = function() {
|
||||||
|
this.sidebarThumbnailAjax = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Thumbnails
|
||||||
|
*/
|
||||||
|
|
||||||
|
MediaManager.prototype.generateThumbnails = function() {
|
||||||
|
this.thumbnailQueue = []
|
||||||
|
|
||||||
|
var placeholders = this.itemListElement.querySelectorAll('[data-type="media-item"] div.image-placeholder')
|
||||||
|
for (var i = (placeholders.length-1); i >= 0; i--)
|
||||||
|
this.thumbnailQueue.push({
|
||||||
|
id: placeholders[i].getAttribute('id'),
|
||||||
|
width: placeholders[i].getAttribute('data-width'),
|
||||||
|
height: placeholders[i].getAttribute('data-height'),
|
||||||
|
path: placeholders[i].getAttribute('data-path'),
|
||||||
|
lastModified: placeholders[i].getAttribute('data-last-modified')
|
||||||
|
})
|
||||||
|
|
||||||
|
this.handleThumbnailQueue()
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.handleThumbnailQueue = function() {
|
||||||
|
var maxThumbnailQueueLength = 2,
|
||||||
|
maxThumbnailBatchLength = 3
|
||||||
|
|
||||||
|
if (this.activeThumbnailQueueLength >= maxThumbnailQueueLength)
|
||||||
|
return
|
||||||
|
|
||||||
|
for (var i = this.activeThumbnailQueueLength; i < maxThumbnailQueueLength && this.thumbnailQueue.length > 0; i++) {
|
||||||
|
var batch = []
|
||||||
|
|
||||||
|
for (var j = 0; j < maxThumbnailBatchLength && this.thumbnailQueue.length > 0; j++)
|
||||||
|
batch.push(this.thumbnailQueue.pop())
|
||||||
|
|
||||||
|
this.activeThumbnailQueueLength++
|
||||||
|
|
||||||
|
this.handleThumbnailBatch(batch).always(this.placeholdersUpdatedBound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.handleThumbnailBatch = function(batch) {
|
||||||
|
var data = {
|
||||||
|
batch: batch
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0, len = batch.length; i < len; i++) {
|
||||||
|
var placeholder = document.getElementById(batch[i].id)
|
||||||
|
if (placeholder)
|
||||||
|
placeholder.setAttribute('data-loading', 'true')
|
||||||
|
}
|
||||||
|
|
||||||
|
var promise = this.$form.request(this.options.alias+'::onGenerateThumbnails', {
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
|
||||||
|
promise.done(this.replacePlaceholderBound)
|
||||||
|
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.replacePlaceholder = function(response) {
|
||||||
|
if (!response.generatedThumbnails)
|
||||||
|
return
|
||||||
|
|
||||||
|
for (var i = 0, len = response.generatedThumbnails.length; i < len; i++) {
|
||||||
|
var thumbnailInfo = response.generatedThumbnails[i]
|
||||||
|
|
||||||
|
if (!thumbnailInfo.id || !thumbnailInfo.markup)
|
||||||
|
continue
|
||||||
|
|
||||||
|
var node = document.getElementById(thumbnailInfo.id)
|
||||||
|
if (!node)
|
||||||
|
continue
|
||||||
|
|
||||||
|
var placeholderContainer = node.parentNode
|
||||||
|
if (placeholderContainer)
|
||||||
|
placeholderContainer.innerHTML = thumbnailInfo.markup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.placeholdersUpdated = function() {
|
||||||
|
this.activeThumbnailQueueLength--
|
||||||
|
|
||||||
|
this.handleThumbnailQueue()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Drag-select
|
||||||
|
*/
|
||||||
|
|
||||||
|
MediaManager.prototype.getAbsolutePosition = function(element) {
|
||||||
|
// TODO: refactor to a core library
|
||||||
|
|
||||||
|
var top = document.body.scrollTop,
|
||||||
|
left = 0
|
||||||
|
|
||||||
|
do {
|
||||||
|
top += element.offsetTop || 0;
|
||||||
|
top -= element.scrollTop || 0;
|
||||||
|
left += element.offsetLeft || 0;
|
||||||
|
element = element.offsetParent;
|
||||||
|
} while(element)
|
||||||
|
|
||||||
|
return {
|
||||||
|
top: top,
|
||||||
|
left: left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.getEventPagePosition = function(ev) {
|
||||||
|
// TODO: refactor to a core library
|
||||||
|
|
||||||
|
if (ev.pageX || ev.pageY) {
|
||||||
|
return {
|
||||||
|
x: ev.pageX,
|
||||||
|
y: ev.pageY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ev.clientX || ev.clientY) {
|
||||||
|
return {
|
||||||
|
x: (ev.clientX + document.body.scrollLeft + document.documentElement.scrollLeft),
|
||||||
|
y: (ev.clientY + document.body.scrollTop + document.documentElement.scrollTop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.getRelativePosition = function(element, pageX, pageY) {
|
||||||
|
var absolutePosition = this.getAbsolutePosition(element)
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: (pageX - absolutePosition.left),
|
||||||
|
y: (pageY - absolutePosition.top)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.createSelectionMarker = function() {
|
||||||
|
if (this.selectionMarker)
|
||||||
|
return
|
||||||
|
|
||||||
|
this.selectionMarker = document.createElement('div')
|
||||||
|
this.selectionMarker.setAttribute('data-control', 'selection-marker')
|
||||||
|
|
||||||
|
this.itemListElement.insertBefore(this.selectionMarker, this.itemListElement.firstChild)
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.doObjectsCollide = function(aTop, aLeft, aWidth, aHeight, bTop, bLeft, bWidth, bHeight) {
|
||||||
|
return !(
|
||||||
|
((aTop + aHeight) < (bTop)) ||
|
||||||
|
(aTop > (bTop + bHeight)) ||
|
||||||
|
((aLeft + aWidth) < bLeft) ||
|
||||||
|
(aLeft > (bLeft + bWidth))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Uploading
|
||||||
|
*/
|
||||||
|
|
||||||
|
MediaManager.prototype.initUploader = function() {
|
||||||
|
if (this.itemListElement) {
|
||||||
|
// this.dropzone = new Dropzone(this.itemListElement, uploaderOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// disable
|
||||||
|
// dropzone.on('error', $.proxy(self.onUploadFail, self))
|
||||||
|
// dropzone.on('success', $.proxy(self.onUploadSuccess, self))
|
||||||
|
// dropzone.on('complete', $.proxy(self.onUploadComplete, self))
|
||||||
|
// dropzone.on('sending', function(file, xhr, formData) {
|
||||||
|
// $.each(self.$form.serializeArray(), function (index, field) {
|
||||||
|
// formData.append(field.name, field.value)
|
||||||
|
// })
|
||||||
|
// self.onUploadStart()
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
// EVENT HANDLERS
|
||||||
|
// ============================
|
||||||
|
|
||||||
|
MediaManager.prototype.onNavigate = function(ev) {
|
||||||
|
var $item = $(ev.target).closest('[data-type="media-item"]')
|
||||||
|
|
||||||
|
if (!$item.length || !$item.data('path').length)
|
||||||
|
return
|
||||||
|
|
||||||
|
if ($item.data('item-type') == 'folder')
|
||||||
|
this.gotoFolder($item.data('path'))
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.onCommandClick = function(ev) {
|
||||||
|
var command = $(ev.target).data('command')
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case 'refresh' :
|
||||||
|
this.gotoFolder(
|
||||||
|
this.$el.find('[data-type="current-folder"]').val(),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
break;
|
||||||
|
case 'change-view' :
|
||||||
|
this.changeView($(ev.target).data('view'))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.onItemClick = function(ev) {
|
||||||
|
if (ev.currentTarget.hasAttribute('data-root'))
|
||||||
|
return
|
||||||
|
|
||||||
|
this.selectItem(ev.currentTarget, ev.shiftKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.onListMouseDown = function(ev) {
|
||||||
|
this.itemListElement.addEventListener('mousemove', this.listMouseMoveHandler)
|
||||||
|
document.addEventListener('mouseup', this.listMouseUpHandler)
|
||||||
|
|
||||||
|
var pagePosition = this.getEventPagePosition(ev),
|
||||||
|
relativePosition = this.getRelativePosition(this.itemListElement, pagePosition.x, pagePosition.y)
|
||||||
|
|
||||||
|
this.selectionStartPoint = relativePosition
|
||||||
|
this.selectionStarted = false
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.onListMouseUp = function(ev) {
|
||||||
|
this.itemListElement.removeEventListener('mousemove', this.listMouseMoveHandler)
|
||||||
|
document.removeEventListener('mouseup', this.listMouseUpHandler)
|
||||||
|
$(document.body).removeClass('no-select')
|
||||||
|
|
||||||
|
if (this.selectionStarted) {
|
||||||
|
var items = this.itemListElement.querySelectorAll('[data-type="media-item"]:not([data-root])'),
|
||||||
|
selectionPosition = this.getAbsolutePosition(this.selectionMarker)
|
||||||
|
|
||||||
|
for (var index = 0, len = items.length; index < len; index++) {
|
||||||
|
var item = items[index],
|
||||||
|
itemPosition = this.getAbsolutePosition(item)
|
||||||
|
|
||||||
|
if (this.doObjectsCollide(
|
||||||
|
selectionPosition.top,
|
||||||
|
selectionPosition.left,
|
||||||
|
this.selectionMarker.offsetWidth,
|
||||||
|
this.selectionMarker.offsetHeight,
|
||||||
|
itemPosition.top,
|
||||||
|
itemPosition.left,
|
||||||
|
item.offsetWidth,
|
||||||
|
item.offsetHeight)
|
||||||
|
) {
|
||||||
|
if (!ev.shiftKey)
|
||||||
|
item.setAttribute('class', 'selected')
|
||||||
|
else {
|
||||||
|
if (item.getAttribute('class') == 'selected')
|
||||||
|
item.setAttribute('class', '')
|
||||||
|
else
|
||||||
|
item.setAttribute('class', 'selected')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!ev.shiftKey)
|
||||||
|
item.setAttribute('class', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateSidebarPreview()
|
||||||
|
this.selectionMarker.setAttribute('class', 'hide')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectionStarted = false
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaManager.prototype.onListMouseMove = function(ev) {
|
||||||
|
var pagePosition = this.getEventPagePosition(ev),
|
||||||
|
relativePosition = this.getRelativePosition(this.itemListElement, pagePosition.x, pagePosition.y)
|
||||||
|
|
||||||
|
var deltaX = relativePosition.x - this.selectionStartPoint.x,
|
||||||
|
deltaY = relativePosition.y - this.selectionStartPoint.y
|
||||||
|
|
||||||
|
if (!this.selectionStarted && (Math.abs(deltaX) > 2 || Math.abs(deltaY) > 2)) {
|
||||||
|
// Start processing the selection only if the mouse was moved by
|
||||||
|
// at least 2 pixels.
|
||||||
|
this.createSelectionMarker()
|
||||||
|
|
||||||
|
this.selectionMarker.setAttribute('class', '')
|
||||||
|
this.selectionStarted = true
|
||||||
|
$(document.body).addClass('no-select')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.selectionStarted) {
|
||||||
|
if (deltaX >= 0) {
|
||||||
|
this.selectionMarker.style.left = this.selectionStartPoint.x + 'px'
|
||||||
|
this.selectionMarker.style.width = deltaX + 'px'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.selectionMarker.style.left = relativePosition.x + 'px'
|
||||||
|
this.selectionMarker.style.width = Math.abs(deltaX) + 'px'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deltaY >= 0) {
|
||||||
|
this.selectionMarker.style.height = deltaY + 'px'
|
||||||
|
this.selectionMarker.style.top = this.selectionStartPoint.y + 'px'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.selectionMarker.style.top = relativePosition.y + 'px'
|
||||||
|
this.selectionMarker.style.height = Math.abs(deltaY) + 'px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MEDIA MANAGER PLUGIN DEFINITION
|
||||||
|
// ============================
|
||||||
|
|
||||||
|
MediaManager.DEFAULTS = {
|
||||||
|
alias: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
var old = $.fn.mediaManager
|
||||||
|
|
||||||
|
$.fn.mediaManager = function (option) {
|
||||||
|
var args = Array.prototype.slice.call(arguments, 1),
|
||||||
|
result = undefined
|
||||||
|
|
||||||
|
this.each(function () {
|
||||||
|
var $this = $(this)
|
||||||
|
var data = $this.data('oc.mediaManager')
|
||||||
|
var options = $.extend({}, MediaManager.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||||
|
if (!data) $this.data('oc.mediaManager', (data = new MediaManager(this, options)))
|
||||||
|
if (typeof option == 'string') result = data[option].apply(data, args)
|
||||||
|
if (typeof result != 'undefined') return false
|
||||||
|
})
|
||||||
|
|
||||||
|
return result ? result : this
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.mediaManager.Constructor = MediaManager
|
||||||
|
|
||||||
|
// MEDIA MANAGER NO CONFLICT
|
||||||
|
// =================
|
||||||
|
|
||||||
|
$.fn.mediaManager.noConflict = function () {
|
||||||
|
$.fn.mediaManager = old
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// MEDIA MANAGER DATA-API
|
||||||
|
// ===============
|
||||||
|
|
||||||
|
$(document).on('render', function(){
|
||||||
|
$('div[data-control=media-manager]').mediaManager()
|
||||||
|
})
|
||||||
|
|
||||||
|
}(window.jQuery);
|
||||||
|
|
@ -0,0 +1,324 @@
|
||||||
|
@import "../../../../../backend/assets/less/core/boot.less";
|
||||||
|
|
||||||
|
.media-selected-tiles() {
|
||||||
|
.icon-container {
|
||||||
|
background: @color-list-hover-bg!important;
|
||||||
|
border-color: #2581b8;
|
||||||
|
|
||||||
|
i, p {
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
color: #2581b8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-selected-list() {
|
||||||
|
background: @color-list-hover-bg!important;
|
||||||
|
|
||||||
|
i, p.size {
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-container {
|
||||||
|
border-right-color: @color-list-hover-bg!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div[data-control="media-manager"] {
|
||||||
|
.loading-indicator-pseudo-absolute(@size) {
|
||||||
|
background-image: url(../../../../../../modules/backend/assets/images/loading-indicator-transparent.svg);
|
||||||
|
background-position: 50% 50%;
|
||||||
|
content: ' ';
|
||||||
|
.animation(spin 1s linear infinite);
|
||||||
|
|
||||||
|
background-size: @size @size;
|
||||||
|
position: absolute;
|
||||||
|
width: @size;
|
||||||
|
height: @size;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: -@size/2;
|
||||||
|
margin-left: -@size/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio, video {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
video {
|
||||||
|
background: #ecf0f1;
|
||||||
|
max-height: 225px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-player-fallback {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #95a5a6;
|
||||||
|
background: #ecf0f1;
|
||||||
|
line-height: 180%;
|
||||||
|
|
||||||
|
&.panel-embedded {
|
||||||
|
padding: 20px;
|
||||||
|
margin: -20px -20px 0 -20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-message() {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 25px;
|
||||||
|
color: #bdc3c7;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.thumbnail-error-message {
|
||||||
|
.icon-message();
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-list {
|
||||||
|
padding: 0 0 20px 20px;
|
||||||
|
.user-select(none);
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
margin: 0 20px 20px 0;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
.border-radius(3px);
|
||||||
|
|
||||||
|
.icon-container {
|
||||||
|
display: table;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #95a5a6;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: table-cell;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-container.image {
|
||||||
|
> div.icon-wrapper {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #2b3e50;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding-right: 15px;
|
||||||
|
line-height: 150%;
|
||||||
|
margin: 15px 0 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.size {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #95a5a6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-placeholder {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
i {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-loading] {
|
||||||
|
i {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-loading]:after {
|
||||||
|
.loading-indicator-pseudo-absolute(28px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i.icon-chain-broken {
|
||||||
|
padding: 0;
|
||||||
|
color: #bdc3c7;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-item-type=folder] i {
|
||||||
|
color: @color-list-hover-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.list {
|
||||||
|
li {
|
||||||
|
height: 75px;
|
||||||
|
width: 260px;
|
||||||
|
border: 1px solid #ecf0f1;
|
||||||
|
background: #f6f8f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .icon-container {
|
||||||
|
border-right: 1px solid #f6f8f9;
|
||||||
|
width: 75px;
|
||||||
|
height: 75px;
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.image {
|
||||||
|
border-right: 1px solid #ecf0f1!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.thumbnail-error-message {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wrapper {
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .info {
|
||||||
|
margin-left: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .image-placeholder {
|
||||||
|
width: 75px;
|
||||||
|
height: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li[data-root] h4 {
|
||||||
|
margin-top: 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.selected {
|
||||||
|
.media-selected-list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tiles {
|
||||||
|
li {
|
||||||
|
width: 167px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wrapper {
|
||||||
|
width: 167px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .image-placeholder {
|
||||||
|
width: 165px;
|
||||||
|
height: 165px;
|
||||||
|
|
||||||
|
&[data-loading]:after {
|
||||||
|
.loading-indicator-pseudo-absolute(55px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li .icon-container {
|
||||||
|
width: 167px;
|
||||||
|
height: 167px;
|
||||||
|
.border-radius(3px);
|
||||||
|
border: 1px solid #ecf0f1;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f6f8f9;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-family: @font-family-sans-serif;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li.selected {
|
||||||
|
.media-selected-tiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
i.icon-chain-broken {
|
||||||
|
margin-top: 47px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.size {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-image-placeholder-container {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-image-placeholder {
|
||||||
|
display: table-cell;
|
||||||
|
height: 225px;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid #ecf0f1;
|
||||||
|
|
||||||
|
&[data-loading] {
|
||||||
|
background: #ecf0f1;
|
||||||
|
&:after {
|
||||||
|
.loading-indicator-pseudo-absolute(62px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i.icon-chain-broken, i.icon-crop, i.icon-asterisk {
|
||||||
|
color: #bdc3c7;
|
||||||
|
font-size: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.no-border {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
.icon-message();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-container {
|
||||||
|
position: relative;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-control="item-list"] {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[data-control="selection-marker"] {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 50;
|
||||||
|
border: 1px dashed #95a5a6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.no-select) {
|
||||||
|
div[data-control="media-manager"] .media-list {
|
||||||
|
&.tiles {
|
||||||
|
li:hover {
|
||||||
|
.media-selected-tiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
li:hover {
|
||||||
|
.media-selected-list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<div data-control="media-manager" class="layout" data-alias="<?= $this->alias ?>">
|
||||||
|
<?= $this->makePartial('toolbar') ?>
|
||||||
|
|
||||||
|
<div class="layout-row whiteboard">
|
||||||
|
<div class="layout">
|
||||||
|
<div class="layout-row">
|
||||||
|
<div class="layout-cell width-200 panel border-right">
|
||||||
|
<?= $this->makePartial('left-sidebar') ?>
|
||||||
|
</div>
|
||||||
|
<div class="layout-cell">
|
||||||
|
<div class="layout">
|
||||||
|
|
||||||
|
<div class="layout-row min-size">
|
||||||
|
<?= $this->makePartial('folder-toolbar') ?>
|
||||||
|
</div>
|
||||||
|
<div class="layout-row">
|
||||||
|
<!-- Main area -->
|
||||||
|
<div class="layout">
|
||||||
|
<div class="layout-cell ">
|
||||||
|
<div class="layout">
|
||||||
|
<div class="layout-row">
|
||||||
|
<!-- Main area - list -->
|
||||||
|
<div data-control="item-list">
|
||||||
|
<div id="<?= $this->getId('item-list') ?>" >
|
||||||
|
<?= $this->makePartial('item-list') ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layout-row min-size">
|
||||||
|
<!-- Main area - bottom toolbar -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layout-cell width-300 panel border-left no-padding" data-control="preview-sidebar">
|
||||||
|
<!-- Right sidebar -->
|
||||||
|
<?= $this->makePartial('right-sidebar') ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<ul class="tree-path">
|
||||||
|
<li class="root"><a href="#" data-type="media-item" data-item-type="folder" data-path="/"><?= e(trans('cms::lang.media.library')) ?></a></li>
|
||||||
|
|
||||||
|
<?php foreach ($pathSegments as $segment): ?>
|
||||||
|
<?php if ($segment != '/'): ?>
|
||||||
|
<li><a href="#" data-type="media-item" data-item-type="folder" data-path="<?= e($segment) ?>"><?= basename($segment) ?></a></li>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endforeach?>
|
||||||
|
</ul>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<div class="panel padding-less border-bottom triangle-down">
|
||||||
|
<div class="layout">
|
||||||
|
<div class="layout-cell">
|
||||||
|
<div class="layout-row" id="<?= $this->getId('folder-path') ?>">
|
||||||
|
<?= $this->makePartial('folder-path') ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layout-cell">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="oc-icon-sign-out btn-icon pull-right larger">
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<ul class="media-list <?= $listClass ?>">
|
||||||
|
<?php if (count($items) > 0 || !$isRootFolder): ?>
|
||||||
|
<?php if (!$isRootFolder): ?>
|
||||||
|
<li data-type="media-item" data-item-type="folder" data-root data-path="<?= e(dirname($currentFolder)) ?>">
|
||||||
|
<div class="icon-container folder">
|
||||||
|
<div class="icon-wrapper"><i class="icon-folder"></i></div>
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<h4 title="<?= e(trans('cms::lang.media.return_to_parent')) ?>"><?= e(trans('cms::lang.media.return_to_parent_label')) ?></h4>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php foreach ($items as $item):
|
||||||
|
$itemType = $item->getFileType();
|
||||||
|
?>
|
||||||
|
<li data-type="media-item"
|
||||||
|
data-item-type="<?= $item->type ?>"
|
||||||
|
data-path="<?= e($item->path) ?>"
|
||||||
|
data-title="<?= e(basename($item->path)) ?>"
|
||||||
|
data-size="<?= e($item->sizeToString()) ?>"
|
||||||
|
data-last-modified="<?= e($item->lastModifiedAsString()) ?>"
|
||||||
|
data-last-modified-ts="<?= $item->lastModified ?>"
|
||||||
|
data-public-url="<?= e($item->publicUrl) ?>"
|
||||||
|
data-document-type="<?= e($itemType) ?>"
|
||||||
|
>
|
||||||
|
<?= $this->makePartial('item-icon', ['itemType'=>$itemType, 'item'=>$item]) ?>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<h4 title="<?= e(basename($item->path)) ?>"><?= e(basename($item->path)) ?></h4>
|
||||||
|
<p class="size"><?= e($item->sizeToString()) ?></p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</ul>
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
<div class="icon-container <?= $itemType ?>">
|
||||||
|
<div class="icon-wrapper"><i class="<?= $this->itemTypeToIconClass($item, $itemType) ?>"></i></div>
|
||||||
|
|
||||||
|
<?php if ($itemType == Cms\Classes\MediaLibraryItem::FILE_TYPE_IMAGE):
|
||||||
|
$thumbnailPath = $this->thumbnailExists($thumbnailParams, $item->path, $item->lastModified);
|
||||||
|
?>
|
||||||
|
<div>
|
||||||
|
<?php if (!$thumbnailPath): ?>
|
||||||
|
<div class="image-placeholder"
|
||||||
|
data-width="<?= $thumbnailParams['width'] ?>"
|
||||||
|
data-height="<?= $thumbnailParams['height'] ?>"
|
||||||
|
data-path="<?= e($item->path) ?>"
|
||||||
|
data-last-modified="<?= $item->lastModified ?>"
|
||||||
|
id="<?= $this->getPlaceholderId($item) ?>"
|
||||||
|
>
|
||||||
|
<div class="icon-wrapper"><i class="<?= $this->itemTypeToIconClass($item, $itemType) ?>"></i></div>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<?= $this->makePartial('thumbnail-image', [
|
||||||
|
'isError' => $this->thumbnailIsError($thumbnailPath),
|
||||||
|
'imageUrl' => $this->getThumbnailImageUrl($thumbnailPath)
|
||||||
|
]) ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<div class="panel no-padding padding-top">
|
||||||
|
<input type="hidden" data-type="current-folder" value="<?= e($currentFolder) ?>"/>
|
||||||
|
<div class="list-container">
|
||||||
|
<?php if ($viewMode == Cms\Widgets\MediaManager::VIEW_MODE_GRID): ?>
|
||||||
|
<?= $this->makePartial('list-grid') ?>
|
||||||
|
<?php elseif ($viewMode == Cms\Widgets\MediaManager::VIEW_MODE_LIST): ?>
|
||||||
|
<?= $this->makePartial('list-list') ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<?= $this->makePartial('list-tiles') ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<div data-control="media-preview-container"></div>
|
||||||
|
|
||||||
|
<script type="text/template" data-control="audio-template">
|
||||||
|
<div class="panel no-padding-bottom">
|
||||||
|
<audio src="{src}" controls>
|
||||||
|
<div class="media-player-fallback panel-embedded">Your browser doesn't support HTML5 audio.</div>
|
||||||
|
</audio>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/template" data-control="video-template">
|
||||||
|
<video src="{src}" controls poster="<?= URL::to('modules/cms/Widgets/mediamanager/assets/images/video-poster.png') ?>">
|
||||||
|
<div class="panel media-player-fallback">Your browser doesn't support HTML5 video.</div>
|
||||||
|
</video>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/template" data-control="image-template">
|
||||||
|
<div class="sidebar-image-placeholder-container"><div class="sidebar-image-placeholder" data-path="{path}" data-last-modified="{last-modified}" data-loading="true" data-control="sidebar-thumbnail"></div></div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/template" data-control="no-selection-template">
|
||||||
|
<div class="sidebar-image-placeholder-container">
|
||||||
|
<div class="sidebar-image-placeholder no-border">
|
||||||
|
<i class="icon-crop"></i>
|
||||||
|
<p><?= e(trans('cms::lang.media.nothing_selected')) ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/template" data-control="multi-selection-template">
|
||||||
|
<div class="sidebar-image-placeholder-container">
|
||||||
|
<div class="sidebar-image-placeholder no-border">
|
||||||
|
<i class="icon-asterisk"></i>
|
||||||
|
<p><?= e(trans('cms::lang.media.multiple_selected')) ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<h3 class="section">Display</h3>
|
||||||
|
|
||||||
|
<ul class="nav nav-stacked selector-group">
|
||||||
|
<li role="presentation" class="active">
|
||||||
|
<a href="#">
|
||||||
|
<i class="icon-recycle"></i>
|
||||||
|
|
||||||
|
<?= e(trans('cms::lang.media.filter_everything')) ?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#">
|
||||||
|
<i class="icon-picture-o"></i>
|
||||||
|
|
||||||
|
<?= e(trans('cms::lang.media.filter_images')) ?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#">
|
||||||
|
<i class="icon-video-camera"></i>
|
||||||
|
|
||||||
|
<?= e(trans('cms::lang.media.filter_video')) ?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#">
|
||||||
|
<i class="icon-volume-up"></i>
|
||||||
|
|
||||||
|
<?= e(trans('cms::lang.media.filter_audio')) ?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#">
|
||||||
|
<i class="icon-file"></i>
|
||||||
|
|
||||||
|
<?= e(trans('cms::lang.media.filter_documents')) ?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<table class="table data">
|
||||||
|
<tbody class="icons clickable">
|
||||||
|
<?php if (count($items) > 0 || !$isRootFolder): ?>
|
||||||
|
<?php if (!$isRootFolder): ?>
|
||||||
|
<tr data-type="media-item" data-item-type="folder" data-root data-path="<?= e(dirname($currentFolder)) ?>">
|
||||||
|
<td><i class="icon-folder"></i>..</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php foreach ($items as $item):
|
||||||
|
$itemType = $item->getFileType();
|
||||||
|
?>
|
||||||
|
<tr data-type="media-item"
|
||||||
|
data-item-type="<?= $item->type ?>"
|
||||||
|
data-path="<?= e($item->path) ?>"
|
||||||
|
data-title="<?= e(basename($item->path)) ?>"
|
||||||
|
data-size="<?= e($item->sizeToString()) ?>"
|
||||||
|
data-last-modified="<?= e($item->lastModifiedAsString()) ?>"
|
||||||
|
data-last-modified-ts="<?= $item->lastModified ?>"
|
||||||
|
data-public-url="<?= e($item->publicUrl) ?>"
|
||||||
|
data-document-type="<?= e($itemType) ?>"
|
||||||
|
>
|
||||||
|
<td><i class="<?= $this->itemTypeToIconClass($item, $itemType) ?>"></i> <?= e(basename($item->path)) ?></td>
|
||||||
|
<td><?= e($item->sizeToString()) ?></td>
|
||||||
|
<td><?= e($item->lastModifiedAsString()) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<?= $this->makePartial('generic-list', ['listClass'=>'list']) ?>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<?= $this->makePartial('generic-list', ['listClass'=>'tiles']) ?>
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?= $this->makePartial('item-sidebar-preview') ?>
|
||||||
|
|
||||||
|
<div class="panel hide" data-control="sidebar-labels">
|
||||||
|
<label><?= e(trans('cms::lang.media.title')) ?></label>
|
||||||
|
<p data-label="title"></p>
|
||||||
|
|
||||||
|
<table class="name-value-list">
|
||||||
|
<tr>
|
||||||
|
<th><?= e(trans('cms::lang.media.size')) ?></th>
|
||||||
|
<td data-label="size"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><?= e(trans('cms::lang.media.public_url')) ?></th>
|
||||||
|
<td><a href="#" data-label="public-url" target="_blank"><?= e(trans('cms::lang.media.click_here')) ?></a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><?= e(trans('cms::lang.media.last_modified')) ?></th>
|
||||||
|
<td data-label="last-modified"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php if (!$isError): ?>
|
||||||
|
<img src="<?= $imageUrl ?>"/>
|
||||||
|
<?php else: ?>
|
||||||
|
<i class="icon-chain-broken" title="<?= e(trans('cms::lang.media.thumbnail_error')) ?>"></i>
|
||||||
|
<p class="thumbnail-error-message"><?= e(trans('cms::lang.media.thumbnail_error')) ?></p>
|
||||||
|
<?php endif ?>
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<div class="layout-row min-size">
|
||||||
|
<div class="layout control-toolbar standalone-paddings">
|
||||||
|
<div class="layout-cell">
|
||||||
|
<div class="btn-group offset-right">
|
||||||
|
<button type="button" class="btn btn-primary oc-icon-upload"
|
||||||
|
><?= e(trans('cms::lang.media.upload')) ?></button>
|
||||||
|
<button type="button" class="btn btn-primary oc-icon-folder"
|
||||||
|
><?= e(trans('cms::lang.media.add_folder')) ?></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-default oc-icon-refresh empty offset-right" data-command="refresh"></button>
|
||||||
|
|
||||||
|
<div class="btn-group offset-right" id="<?= $this->getId('view-mode-buttons') ?>">
|
||||||
|
<?= $this->makePartial('view-mode-buttons') ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layout-cell width-fix">
|
||||||
|
<div class="relative toolbar-item loading-indicator-container size-input-text last">
|
||||||
|
<input placeholder="<?= e(trans('cms::lang.media.search')) ?>" type="text" name="search" value=""
|
||||||
|
class="form-control icon search" autocomplete="off"
|
||||||
|
data-track-input
|
||||||
|
data-load-indicator
|
||||||
|
data-load-indicator-opaque
|
||||||
|
data-request="<?= $this->getEventHandler('onSearch') ?>"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-default oc-icon-align-justify empty <?= $viewMode == Cms\Widgets\MediaManager::VIEW_MODE_GRID ? 'on' : '' ?>"
|
||||||
|
data-command="change-view"
|
||||||
|
data-view="<?= Cms\Widgets\MediaManager::VIEW_MODE_GRID ?>">
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-default oc-icon-th empty <?= $viewMode == Cms\Widgets\MediaManager::VIEW_MODE_LIST ? 'on' : '' ?>"
|
||||||
|
data-command="change-view"
|
||||||
|
data-view="<?= Cms\Widgets\MediaManager::VIEW_MODE_LIST ?>">
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-default oc-icon-th-large empty <?= $viewMode == Cms\Widgets\MediaManager::VIEW_MODE_TILES ? 'on' : '' ?>"
|
||||||
|
data-command="change-view"
|
||||||
|
data-view="<?= Cms\Widgets\MediaManager::VIEW_MODE_TILES ?>">
|
||||||
|
</button>
|
||||||
|
|
@ -120,7 +120,7 @@ if (window.jQuery === undefined)
|
||||||
var errorMsg,
|
var errorMsg,
|
||||||
updatePromise = $.Deferred()
|
updatePromise = $.Deferred()
|
||||||
|
|
||||||
if (isUnloading)
|
if (isUnloading || errorThrown == 'abort')
|
||||||
return
|
return
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue