Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthieu Napoli <matthieu@mnapoli.fr>2015-04-28 05:59:02 +0300
committerMatthieu Napoli <matthieu@mnapoli.fr>2015-05-19 10:39:17 +0300
commit4b12f96a141b43daeaef52d2f0bbf44d39d9c5f8 (patch)
treecd6dc003107fa09eb284daf5cb998342ea6f2dbc
parent06ebc8b2d528043db4a4c9a30bf8799a9030fba8 (diff)
Ref #7586 Redesign of the marketplace
I cleaned up a lot of CSS rules and HTML, added new CSS components (from Bootstrap mostly) plus merged the "browsePlugins" and "browseThemes" views into one Added the jquery.dotdotdot library
-rw-r--r--bower.json1
-rw-r--r--libs/bower_components/jQuery.dotdotdot/.bower.json38
-rw-r--r--libs/bower_components/jQuery.dotdotdot/.gitignore2
-rw-r--r--libs/bower_components/jQuery.dotdotdot/bower.json29
-rw-r--r--libs/bower_components/jQuery.dotdotdot/src/js/jquery.dotdotdot.js666
-rw-r--r--libs/bower_components/jQuery.dotdotdot/src/js/jquery.dotdotdot.min.js13
-rw-r--r--plugins/CoreAdminHome/stylesheets/generalSettings.less2
-rw-r--r--plugins/CorePluginsAdmin/Controller.php100
-rw-r--r--plugins/CorePluginsAdmin/CorePluginsAdmin.php4
-rwxr-xr-xplugins/CorePluginsAdmin/javascripts/marketplace.js50
-rwxr-xr-xplugins/CorePluginsAdmin/javascripts/pluginDetail.js88
-rw-r--r--plugins/CorePluginsAdmin/javascripts/pluginExtend.js2
-rw-r--r--plugins/CorePluginsAdmin/lang/en.json2
-rw-r--r--plugins/CorePluginsAdmin/stylesheets/marketplace.less444
-rw-r--r--plugins/CorePluginsAdmin/stylesheets/plugin-details.less101
-rw-r--r--plugins/CorePluginsAdmin/stylesheets/plugins_admin.less49
-rw-r--r--plugins/CorePluginsAdmin/templates/browsePlugins.twig62
-rw-r--r--plugins/CorePluginsAdmin/templates/browsePluginsActions.twig27
-rw-r--r--plugins/CorePluginsAdmin/templates/browseThemes.twig49
-rw-r--r--plugins/CorePluginsAdmin/templates/macros.twig4
-rw-r--r--plugins/CorePluginsAdmin/templates/marketplace.twig75
-rw-r--r--plugins/CorePluginsAdmin/templates/marketplace/plugin-list.twig110
-rw-r--r--plugins/CorePluginsAdmin/templates/pluginDetails.twig5
-rw-r--r--plugins/CorePluginsAdmin/templates/pluginMetadata.twig9
-rw-r--r--plugins/CorePluginsAdmin/templates/pluginOverview.twig29
-rw-r--r--plugins/CorePluginsAdmin/templates/themeOverview.twig30
-rw-r--r--plugins/Morpheus/stylesheets/base.less2
-rw-r--r--plugins/Morpheus/stylesheets/general/_forms.less20
-rw-r--r--plugins/Morpheus/stylesheets/ui/_navs.less59
-rw-r--r--plugins/Morpheus/stylesheets/ui/_panels.less73
30 files changed, 1430 insertions, 715 deletions
diff --git a/bower.json b/bower.json
index 01a17439f8..49252d932e 100644
--- a/bower.json
+++ b/bower.json
@@ -27,6 +27,7 @@
"jScrollPane": "~2.0.0",
"jquery-mousewheel": "~3.1.12",
"jquery-placeholder": "~2.0.8",
+ "jQuery.dotdotdot": "~1.7.2",
"jquery.scrollTo": "~1.4.13",
"chroma-js": "~0.6.0",
"visibilityjs": "~1.2.1"
diff --git a/libs/bower_components/jQuery.dotdotdot/.bower.json b/libs/bower_components/jQuery.dotdotdot/.bower.json
new file mode 100644
index 0000000000..7f2ef55e72
--- /dev/null
+++ b/libs/bower_components/jQuery.dotdotdot/.bower.json
@@ -0,0 +1,38 @@
+{
+ "name": "jQuery.dotdotdot",
+ "main": "src/js/jquery.dotdotdot.js",
+ "version": "1.7.2",
+ "homepage": "http://dotdotdot.frebsite.nl/",
+ "authors": [
+ "Fred Heusschen <info@frebsite.nl>"
+ ],
+ "description": "A jQuery plugin for advanced cross-browser ellipsis on multiple line content.",
+ "keywords": [
+ "ellipsis",
+ "dotdotdot",
+ "multiline",
+ "text",
+ "text-overflow",
+ "overflow",
+ "dots"
+ ],
+ "ignore": [
+ ".jshintrc",
+ "Guardfile",
+ "index.html",
+ "*.json",
+ "README.md"
+ ],
+ "dependencies": {
+ "jquery": ">= 1.4.3"
+ },
+ "_release": "1.7.2",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.7.2",
+ "commit": "a8590b57d88d5ef2d3a3e1ccb70b01cdf5ef8f96"
+ },
+ "_source": "git://github.com/BeSite/jQuery.dotdotdot.git",
+ "_target": "~1.7.2",
+ "_originalSource": "jQuery.dotdotdot"
+} \ No newline at end of file
diff --git a/libs/bower_components/jQuery.dotdotdot/.gitignore b/libs/bower_components/jQuery.dotdotdot/.gitignore
new file mode 100644
index 0000000000..e4c80b944f
--- /dev/null
+++ b/libs/bower_components/jQuery.dotdotdot/.gitignore
@@ -0,0 +1,2 @@
+# Ignore Mac system files.
+._* \ No newline at end of file
diff --git a/libs/bower_components/jQuery.dotdotdot/bower.json b/libs/bower_components/jQuery.dotdotdot/bower.json
new file mode 100644
index 0000000000..95c684efde
--- /dev/null
+++ b/libs/bower_components/jQuery.dotdotdot/bower.json
@@ -0,0 +1,29 @@
+{
+ "name": "jQuery.dotdotdot",
+ "main": "src/js/jquery.dotdotdot.js",
+ "version": "1.7.2",
+ "homepage": "http://dotdotdot.frebsite.nl/",
+ "authors": [
+ "Fred Heusschen <info@frebsite.nl>"
+ ],
+ "description": "A jQuery plugin for advanced cross-browser ellipsis on multiple line content.",
+ "keywords": [
+ "ellipsis",
+ "dotdotdot",
+ "multiline",
+ "text",
+ "text-overflow",
+ "overflow",
+ "dots"
+ ],
+ "ignore": [
+ ".jshintrc",
+ "Guardfile",
+ "index.html",
+ "*.json",
+ "README.md"
+ ],
+ "dependencies": {
+ "jquery": ">= 1.4.3"
+ }
+}
diff --git a/libs/bower_components/jQuery.dotdotdot/src/js/jquery.dotdotdot.js b/libs/bower_components/jQuery.dotdotdot/src/js/jquery.dotdotdot.js
new file mode 100644
index 0000000000..50fda104f8
--- /dev/null
+++ b/libs/bower_components/jQuery.dotdotdot/src/js/jquery.dotdotdot.js
@@ -0,0 +1,666 @@
+/*
+ * jQuery dotdotdot 1.7.2
+ *
+ * Copyright (c) Fred Heusschen
+ * www.frebsite.nl
+ *
+ * Plugin website:
+ * dotdotdot.frebsite.nl
+ *
+ * Licensed under the MIT license.
+ * http://en.wikipedia.org/wiki/MIT_License
+ */
+
+(function( $, undef )
+{
+ if ( $.fn.dotdotdot )
+ {
+ return;
+ }
+
+ $.fn.dotdotdot = function( o )
+ {
+ if ( this.length == 0 )
+ {
+ $.fn.dotdotdot.debug( 'No element found for "' + this.selector + '".' );
+ return this;
+ }
+ if ( this.length > 1 )
+ {
+ return this.each(
+ function()
+ {
+ $(this).dotdotdot( o );
+ }
+ );
+ }
+
+
+ var $dot = this;
+
+ if ( $dot.data( 'dotdotdot' ) )
+ {
+ $dot.trigger( 'destroy.dot' );
+ }
+
+ $dot.data( 'dotdotdot-style', $dot.attr( 'style' ) || '' );
+ $dot.css( 'word-wrap', 'break-word' );
+ if ($dot.css( 'white-space' ) === 'nowrap')
+ {
+ $dot.css( 'white-space', 'normal' );
+ }
+
+ $dot.bind_events = function()
+ {
+ $dot.bind(
+ 'update.dot',
+ function( e, c )
+ {
+ e.preventDefault();
+ e.stopPropagation();
+
+ opts.maxHeight = ( typeof opts.height == 'number' )
+ ? opts.height
+ : getTrueInnerHeight( $dot );
+
+ opts.maxHeight += opts.tolerance;
+
+ if ( typeof c != 'undefined' )
+ {
+ if ( typeof c == 'string' || c instanceof HTMLElement )
+ {
+ c = $('<div />').append( c ).contents();
+ }
+ if ( c instanceof $ )
+ {
+ orgContent = c;
+ }
+ }
+
+ $inr = $dot.wrapInner( '<div class="dotdotdot" />' ).children();
+ $inr.contents()
+ .detach()
+ .end()
+ .append( orgContent.clone( true ) )
+ .find( 'br' )
+ .replaceWith( ' <br /> ' )
+ .end()
+ .css({
+ 'height' : 'auto',
+ 'width' : 'auto',
+ 'border' : 'none',
+ 'padding' : 0,
+ 'margin' : 0
+ });
+
+ var after = false,
+ trunc = false;
+
+ if ( conf.afterElement )
+ {
+ after = conf.afterElement.clone( true );
+ after.show();
+ conf.afterElement.detach();
+ }
+
+ if ( test( $inr, opts ) )
+ {
+ if ( opts.wrap == 'children' )
+ {
+ trunc = children( $inr, opts, after );
+ }
+ else
+ {
+ trunc = ellipsis( $inr, $dot, $inr, opts, after );
+ }
+ }
+ $inr.replaceWith( $inr.contents() );
+ $inr = null;
+
+ if ( $.isFunction( opts.callback ) )
+ {
+ opts.callback.call( $dot[ 0 ], trunc, orgContent );
+ }
+
+ conf.isTruncated = trunc;
+ return trunc;
+ }
+
+ ).bind(
+ 'isTruncated.dot',
+ function( e, fn )
+ {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if ( typeof fn == 'function' )
+ {
+ fn.call( $dot[ 0 ], conf.isTruncated );
+ }
+ return conf.isTruncated;
+ }
+
+ ).bind(
+ 'originalContent.dot',
+ function( e, fn )
+ {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if ( typeof fn == 'function' )
+ {
+ fn.call( $dot[ 0 ], orgContent );
+ }
+ return orgContent;
+ }
+
+ ).bind(
+ 'destroy.dot',
+ function( e )
+ {
+ e.preventDefault();
+ e.stopPropagation();
+
+ $dot.unwatch()
+ .unbind_events()
+ .contents()
+ .detach()
+ .end()
+ .append( orgContent )
+ .attr( 'style', $dot.data( 'dotdotdot-style' ) || '' )
+ .data( 'dotdotdot', false );
+ }
+ );
+ return $dot;
+ }; // /bind_events
+
+ $dot.unbind_events = function()
+ {
+ $dot.unbind('.dot');
+ return $dot;
+ }; // /unbind_events
+
+ $dot.watch = function()
+ {
+ $dot.unwatch();
+ if ( opts.watch == 'window' )
+ {
+ var $window = $(window),
+ _wWidth = $window.width(),
+ _wHeight = $window.height();
+
+ $window.bind(
+ 'resize.dot' + conf.dotId,
+ function()
+ {
+ if ( _wWidth != $window.width() || _wHeight != $window.height() || !opts.windowResizeFix )
+ {
+ _wWidth = $window.width();
+ _wHeight = $window.height();
+
+ if ( watchInt )
+ {
+ clearInterval( watchInt );
+ }
+ watchInt = setTimeout(
+ function()
+ {
+ $dot.trigger( 'update.dot' );
+ }, 100
+ );
+ }
+ }
+ );
+ }
+ else
+ {
+ watchOrg = getSizes( $dot );
+ watchInt = setInterval(
+ function()
+ {
+ if ( $dot.is( ':visible' ) )
+ {
+ var watchNew = getSizes( $dot );
+ if ( watchOrg.width != watchNew.width ||
+ watchOrg.height != watchNew.height )
+ {
+ $dot.trigger( 'update.dot' );
+ watchOrg = watchNew;
+ }
+ }
+ }, 500
+ );
+ }
+ return $dot;
+ };
+ $dot.unwatch = function()
+ {
+ $(window).unbind( 'resize.dot' + conf.dotId );
+ if ( watchInt )
+ {
+ clearInterval( watchInt );
+ }
+ return $dot;
+ };
+
+ var orgContent = $dot.contents(),
+ opts = $.extend( true, {}, $.fn.dotdotdot.defaults, o ),
+ conf = {},
+ watchOrg = {},
+ watchInt = null,
+ $inr = null;
+
+
+ if ( !( opts.lastCharacter.remove instanceof Array ) )
+ {
+ opts.lastCharacter.remove = $.fn.dotdotdot.defaultArrays.lastCharacter.remove;
+ }
+ if ( !( opts.lastCharacter.noEllipsis instanceof Array ) )
+ {
+ opts.lastCharacter.noEllipsis = $.fn.dotdotdot.defaultArrays.lastCharacter.noEllipsis;
+ }
+
+
+ conf.afterElement = getElement( opts.after, $dot );
+ conf.isTruncated = false;
+ conf.dotId = dotId++;
+
+
+ $dot.data( 'dotdotdot', true )
+ .bind_events()
+ .trigger( 'update.dot' );
+
+ if ( opts.watch )
+ {
+ $dot.watch();
+ }
+
+ return $dot;
+ };
+
+
+ // public
+ $.fn.dotdotdot.defaults = {
+ 'ellipsis' : '... ',
+ 'wrap' : 'word',
+ 'fallbackToLetter' : true,
+ 'lastCharacter' : {},
+ 'tolerance' : 0,
+ 'callback' : null,
+ 'after' : null,
+ 'height' : null,
+ 'watch' : false,
+ 'windowResizeFix' : true
+ };
+ $.fn.dotdotdot.defaultArrays = {
+ 'lastCharacter' : {
+ 'remove' : [ ' ', '\u3000', ',', ';', '.', '!', '?' ],
+ 'noEllipsis' : []
+ }
+ };
+ $.fn.dotdotdot.debug = function( msg ) {};
+
+
+ // private
+ var dotId = 1;
+
+ function children( $elem, o, after )
+ {
+ var $elements = $elem.children(),
+ isTruncated = false;
+
+ $elem.empty();
+
+ for ( var a = 0, l = $elements.length; a < l; a++ )
+ {
+ var $e = $elements.eq( a );
+ $elem.append( $e );
+ if ( after )
+ {
+ $elem.append( after );
+ }
+ if ( test( $elem, o ) )
+ {
+ $e.remove();
+ isTruncated = true;
+ break;
+ }
+ else
+ {
+ if ( after )
+ {
+ after.detach();
+ }
+ }
+ }
+ return isTruncated;
+ }
+ function ellipsis( $elem, $d, $i, o, after )
+ {
+ var isTruncated = false;
+
+ // Don't put the ellipsis directly inside these elements
+ var notx = 'a table, thead, tbody, tfoot, tr, col, colgroup, object, embed, param, ol, ul, dl, blockquote, select, optgroup, option, textarea, script, style';
+
+ // Don't remove these elements even if they are after the ellipsis
+ var noty = 'script, .dotdotdot-keep';
+
+ $elem
+ .contents()
+ .detach()
+ .each(
+ function()
+ {
+
+ var e = this,
+ $e = $(e);
+
+ if ( typeof e == 'undefined' || ( e.nodeType == 3 && $.trim( e.data ).length == 0 ) )
+ {
+ return true;
+ }
+ else if ( $e.is( noty ) )
+ {
+ $elem.append( $e );
+ }
+ else if ( isTruncated )
+ {
+ return true;
+ }
+ else
+ {
+ $elem.append( $e );
+ if ( after && !$e.is( o.after ) && !$e.find( o.after ).length )
+ {
+ $elem[ $elem.is( notx ) ? 'after' : 'append' ]( after );
+ }
+ if ( test( $i, o ) )
+ {
+ if ( e.nodeType == 3 ) // node is TEXT
+ {
+ isTruncated = ellipsisElement( $e, $d, $i, o, after );
+ }
+ else
+ {
+ isTruncated = ellipsis( $e, $d, $i, o, after );
+ }
+
+ if ( !isTruncated )
+ {
+ $e.detach();
+ isTruncated = true;
+ }
+ }
+
+ if ( !isTruncated )
+ {
+ if ( after )
+ {
+ after.detach();
+ }
+ }
+ }
+ }
+ );
+
+ return isTruncated;
+ }
+ function ellipsisElement( $e, $d, $i, o, after )
+ {
+ var e = $e[ 0 ];
+
+ if ( !e )
+ {
+ return false;
+ }
+
+ var txt = getTextContent( e ),
+ space = ( txt.indexOf(' ') !== -1 ) ? ' ' : '\u3000',
+ separator = ( o.wrap == 'letter' ) ? '' : space,
+ textArr = txt.split( separator ),
+ position = -1,
+ midPos = -1,
+ startPos = 0,
+ endPos = textArr.length - 1;
+
+
+ // Only one word
+ if ( o.fallbackToLetter && startPos == 0 && endPos == 0 )
+ {
+ separator = '';
+ textArr = txt.split( separator );
+ endPos = textArr.length - 1;
+ }
+
+ while ( startPos <= endPos && !( startPos == 0 && endPos == 0 ) )
+ {
+ var m = Math.floor( ( startPos + endPos ) / 2 );
+ if ( m == midPos )
+ {
+ break;
+ }
+ midPos = m;
+
+ setTextContent( e, textArr.slice( 0, midPos + 1 ).join( separator ) + o.ellipsis );
+
+ if ( !test( $i, o ) )
+ {
+ position = midPos;
+ startPos = midPos;
+ }
+ else
+ {
+ endPos = midPos;
+
+ // Fallback to letter
+ if (o.fallbackToLetter && startPos == 0 && endPos == 0 )
+ {
+ separator = '';
+ textArr = textArr[ 0 ].split( separator );
+ position = -1;
+ midPos = -1;
+ startPos = 0;
+ endPos = textArr.length - 1;
+ }
+ }
+ }
+
+ if ( position != -1 && !( textArr.length == 1 && textArr[ 0 ].length == 0 ) )
+ {
+ txt = addEllipsis( textArr.slice( 0, position + 1 ).join( separator ), o );
+ setTextContent( e, txt );
+ }
+ else
+ {
+ var $w = $e.parent();
+ $e.detach();
+
+ var afterLength = ( after && after.closest($w).length ) ? after.length : 0;
+
+ if ( $w.contents().length > afterLength )
+ {
+ e = findLastTextNode( $w.contents().eq( -1 - afterLength ), $d );
+ }
+ else
+ {
+ e = findLastTextNode( $w, $d, true );
+ if ( !afterLength )
+ {
+ $w.detach();
+ }
+ }
+ if ( e )
+ {
+ txt = addEllipsis( getTextContent( e ), o );
+ setTextContent( e, txt );
+ if ( afterLength && after )
+ {
+ $(e).parent().append( after );
+ }
+ }
+ }
+
+ return true;
+ }
+ function test( $i, o )
+ {
+ return $i.innerHeight() > o.maxHeight;
+ }
+ function addEllipsis( txt, o )
+ {
+ while( $.inArray( txt.slice( -1 ), o.lastCharacter.remove ) > -1 )
+ {
+ txt = txt.slice( 0, -1 );
+ }
+ if ( $.inArray( txt.slice( -1 ), o.lastCharacter.noEllipsis ) < 0 )
+ {
+ txt += o.ellipsis;
+ }
+ return txt;
+ }
+ function getSizes( $d )
+ {
+ return {
+ 'width' : $d.innerWidth(),
+ 'height': $d.innerHeight()
+ };
+ }
+ function setTextContent( e, content )
+ {
+ if ( e.innerText )
+ {
+ e.innerText = content;
+ }
+ else if ( e.nodeValue )
+ {
+ e.nodeValue = content;
+ }
+ else if (e.textContent)
+ {
+ e.textContent = content;
+ }
+
+ }
+ function getTextContent( e )
+ {
+ if ( e.innerText )
+ {
+ return e.innerText;
+ }
+ else if ( e.nodeValue )
+ {
+ return e.nodeValue;
+ }
+ else if ( e.textContent )
+ {
+ return e.textContent;
+ }
+ else
+ {
+ return "";
+ }
+ }
+ function getPrevNode( n )
+ {
+ do
+ {
+ n = n.previousSibling;
+ }
+ while ( n && n.nodeType !== 1 && n.nodeType !== 3 );
+
+ return n;
+ }
+ function findLastTextNode( $el, $top, excludeCurrent )
+ {
+ var e = $el && $el[ 0 ], p;
+ if ( e )
+ {
+ if ( !excludeCurrent )
+ {
+ if ( e.nodeType === 3 )
+ {
+ return e;
+ }
+ if ( $.trim( $el.text() ) )
+ {
+ return findLastTextNode( $el.contents().last(), $top );
+ }
+ }
+ p = getPrevNode( e );
+ while ( !p )
+ {
+ $el = $el.parent();
+ if ( $el.is( $top ) || !$el.length )
+ {
+ return false;
+ }
+ p = getPrevNode( $el[0] );
+ }
+ if ( p )
+ {
+ return findLastTextNode( $(p), $top );
+ }
+ }
+ return false;
+ }
+ function getElement( e, $i )
+ {
+ if ( !e )
+ {
+ return false;
+ }
+ if ( typeof e === 'string' )
+ {
+ e = $(e, $i);
+ return ( e.length )
+ ? e
+ : false;
+ }
+ return !e.jquery
+ ? false
+ : e;
+ }
+ function getTrueInnerHeight( $el )
+ {
+ var h = $el.innerHeight(),
+ a = [ 'paddingTop', 'paddingBottom' ];
+
+ for ( var z = 0, l = a.length; z < l; z++ )
+ {
+ var m = parseInt( $el.css( a[ z ] ), 10 );
+ if ( isNaN( m ) )
+ {
+ m = 0;
+ }
+ h -= m;
+ }
+ return h;
+ }
+
+
+ // override jQuery.html
+ var _orgHtml = $.fn.html;
+ $.fn.html = function( str )
+ {
+ if ( str != undef && !$.isFunction( str ) && this.data( 'dotdotdot' ) )
+ {
+ return this.trigger( 'update', [ str ] );
+ }
+ return _orgHtml.apply( this, arguments );
+ };
+
+
+ // override jQuery.text
+ var _orgText = $.fn.text;
+ $.fn.text = function( str )
+ {
+ if ( str != undef && !$.isFunction( str ) && this.data( 'dotdotdot' ) )
+ {
+ str = $( '<div />' ).text( str ).html();
+ return this.trigger( 'update', [ str ] );
+ }
+ return _orgText.apply( this, arguments );
+ };
+
+
+})( jQuery );
diff --git a/libs/bower_components/jQuery.dotdotdot/src/js/jquery.dotdotdot.min.js b/libs/bower_components/jQuery.dotdotdot/src/js/jquery.dotdotdot.min.js
new file mode 100644
index 0000000000..64b4f45145
--- /dev/null
+++ b/libs/bower_components/jQuery.dotdotdot/src/js/jquery.dotdotdot.min.js
@@ -0,0 +1,13 @@
+/*
+ * jQuery dotdotdot 1.7.2
+ *
+ * Copyright (c) Fred Heusschen
+ * www.frebsite.nl
+ *
+ * Plugin website:
+ * dotdotdot.frebsite.nl
+ *
+ * Licensed under the MIT license.
+ * http://en.wikipedia.org/wiki/MIT_License
+ */
+!function(t,e){function n(t,e,n){var r=t.children(),o=!1;t.empty();for(var i=0,d=r.length;d>i;i++){var l=r.eq(i);if(t.append(l),n&&t.append(n),a(t,e)){l.remove(),o=!0;break}n&&n.detach()}return o}function r(e,n,i,d,l){var s=!1,c="a table, thead, tbody, tfoot, tr, col, colgroup, object, embed, param, ol, ul, dl, blockquote, select, optgroup, option, textarea, script, style",u="script, .dotdotdot-keep";return e.contents().detach().each(function(){var f=this,h=t(f);if("undefined"==typeof f||3==f.nodeType&&0==t.trim(f.data).length)return!0;if(h.is(u))e.append(h);else{if(s)return!0;e.append(h),!l||h.is(d.after)||h.find(d.after).length||e[e.is(c)?"after":"append"](l),a(i,d)&&(s=3==f.nodeType?o(h,n,i,d,l):r(h,n,i,d,l),s||(h.detach(),s=!0)),s||l&&l.detach()}}),s}function o(e,n,r,o,d){var c=e[0];if(!c)return!1;var f=s(c),h=-1!==f.indexOf(" ")?" ":" ",p="letter"==o.wrap?"":h,g=f.split(p),v=-1,w=-1,b=0,y=g.length-1;for(o.fallbackToLetter&&0==b&&0==y&&(p="",g=f.split(p),y=g.length-1);y>=b&&(0!=b||0!=y);){var m=Math.floor((b+y)/2);if(m==w)break;w=m,l(c,g.slice(0,w+1).join(p)+o.ellipsis),a(r,o)?(y=w,o.fallbackToLetter&&0==b&&0==y&&(p="",g=g[0].split(p),v=-1,w=-1,b=0,y=g.length-1)):(v=w,b=w)}if(-1==v||1==g.length&&0==g[0].length){var x=e.parent();e.detach();var T=d&&d.closest(x).length?d.length:0;x.contents().length>T?c=u(x.contents().eq(-1-T),n):(c=u(x,n,!0),T||x.detach()),c&&(f=i(s(c),o),l(c,f),T&&d&&t(c).parent().append(d))}else f=i(g.slice(0,v+1).join(p),o),l(c,f);return!0}function a(t,e){return t.innerHeight()>e.maxHeight}function i(e,n){for(;t.inArray(e.slice(-1),n.lastCharacter.remove)>-1;)e=e.slice(0,-1);return t.inArray(e.slice(-1),n.lastCharacter.noEllipsis)<0&&(e+=n.ellipsis),e}function d(t){return{width:t.innerWidth(),height:t.innerHeight()}}function l(t,e){t.innerText?t.innerText=e:t.nodeValue?t.nodeValue=e:t.textContent&&(t.textContent=e)}function s(t){return t.innerText?t.innerText:t.nodeValue?t.nodeValue:t.textContent?t.textContent:""}function c(t){do t=t.previousSibling;while(t&&1!==t.nodeType&&3!==t.nodeType);return t}function u(e,n,r){var o,a=e&&e[0];if(a){if(!r){if(3===a.nodeType)return a;if(t.trim(e.text()))return u(e.contents().last(),n)}for(o=c(a);!o;){if(e=e.parent(),e.is(n)||!e.length)return!1;o=c(e[0])}if(o)return u(t(o),n)}return!1}function f(e,n){return e?"string"==typeof e?(e=t(e,n),e.length?e:!1):e.jquery?e:!1:!1}function h(t){for(var e=t.innerHeight(),n=["paddingTop","paddingBottom"],r=0,o=n.length;o>r;r++){var a=parseInt(t.css(n[r]),10);isNaN(a)&&(a=0),e-=a}return e}if(!t.fn.dotdotdot){t.fn.dotdotdot=function(e){if(0==this.length)return t.fn.dotdotdot.debug('No element found for "'+this.selector+'".'),this;if(this.length>1)return this.each(function(){t(this).dotdotdot(e)});var o=this;o.data("dotdotdot")&&o.trigger("destroy.dot"),o.data("dotdotdot-style",o.attr("style")||""),o.css("word-wrap","break-word"),"nowrap"===o.css("white-space")&&o.css("white-space","normal"),o.bind_events=function(){return o.bind("update.dot",function(e,d){e.preventDefault(),e.stopPropagation(),l.maxHeight="number"==typeof l.height?l.height:h(o),l.maxHeight+=l.tolerance,"undefined"!=typeof d&&(("string"==typeof d||d instanceof HTMLElement)&&(d=t("<div />").append(d).contents()),d instanceof t&&(i=d)),g=o.wrapInner('<div class="dotdotdot" />').children(),g.contents().detach().end().append(i.clone(!0)).find("br").replaceWith(" <br /> ").end().css({height:"auto",width:"auto",border:"none",padding:0,margin:0});var c=!1,u=!1;return s.afterElement&&(c=s.afterElement.clone(!0),c.show(),s.afterElement.detach()),a(g,l)&&(u="children"==l.wrap?n(g,l,c):r(g,o,g,l,c)),g.replaceWith(g.contents()),g=null,t.isFunction(l.callback)&&l.callback.call(o[0],u,i),s.isTruncated=u,u}).bind("isTruncated.dot",function(t,e){return t.preventDefault(),t.stopPropagation(),"function"==typeof e&&e.call(o[0],s.isTruncated),s.isTruncated}).bind("originalContent.dot",function(t,e){return t.preventDefault(),t.stopPropagation(),"function"==typeof e&&e.call(o[0],i),i}).bind("destroy.dot",function(t){t.preventDefault(),t.stopPropagation(),o.unwatch().unbind_events().contents().detach().end().append(i).attr("style",o.data("dotdotdot-style")||"").data("dotdotdot",!1)}),o},o.unbind_events=function(){return o.unbind(".dot"),o},o.watch=function(){if(o.unwatch(),"window"==l.watch){var e=t(window),n=e.width(),r=e.height();e.bind("resize.dot"+s.dotId,function(){n==e.width()&&r==e.height()&&l.windowResizeFix||(n=e.width(),r=e.height(),u&&clearInterval(u),u=setTimeout(function(){o.trigger("update.dot")},100))})}else c=d(o),u=setInterval(function(){if(o.is(":visible")){var t=d(o);(c.width!=t.width||c.height!=t.height)&&(o.trigger("update.dot"),c=t)}},500);return o},o.unwatch=function(){return t(window).unbind("resize.dot"+s.dotId),u&&clearInterval(u),o};var i=o.contents(),l=t.extend(!0,{},t.fn.dotdotdot.defaults,e),s={},c={},u=null,g=null;return l.lastCharacter.remove instanceof Array||(l.lastCharacter.remove=t.fn.dotdotdot.defaultArrays.lastCharacter.remove),l.lastCharacter.noEllipsis instanceof Array||(l.lastCharacter.noEllipsis=t.fn.dotdotdot.defaultArrays.lastCharacter.noEllipsis),s.afterElement=f(l.after,o),s.isTruncated=!1,s.dotId=p++,o.data("dotdotdot",!0).bind_events().trigger("update.dot"),l.watch&&o.watch(),o},t.fn.dotdotdot.defaults={ellipsis:"... ",wrap:"word",fallbackToLetter:!0,lastCharacter:{},tolerance:0,callback:null,after:null,height:null,watch:!1,windowResizeFix:!0},t.fn.dotdotdot.defaultArrays={lastCharacter:{remove:[" "," ",",",";",".","!","?"],noEllipsis:[]}},t.fn.dotdotdot.debug=function(){};var p=1,g=t.fn.html;t.fn.html=function(n){return n!=e&&!t.isFunction(n)&&this.data("dotdotdot")?this.trigger("update",[n]):g.apply(this,arguments)};var v=t.fn.text;t.fn.text=function(n){return n!=e&&!t.isFunction(n)&&this.data("dotdotdot")?(n=t("<div />").text(n).html(),this.trigger("update",[n])):v.apply(this,arguments)}}}(jQuery); \ No newline at end of file
diff --git a/plugins/CoreAdminHome/stylesheets/generalSettings.less b/plugins/CoreAdminHome/stylesheets/generalSettings.less
index a5b6e23878..369f0475cb 100644
--- a/plugins/CoreAdminHome/stylesheets/generalSettings.less
+++ b/plugins/CoreAdminHome/stylesheets/generalSettings.less
@@ -85,7 +85,7 @@ table.admin tbody td:hover, table.admin tbody th:hover {
.admin p, .admin section {
margin-top: 10px;
line-height: 140%;
- padding-bottom: 20px;
+ padding-bottom: 10px;
}
.adminTable {
diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php
index e494923f34..74489fdaad 100644
--- a/plugins/CorePluginsAdmin/Controller.php
+++ b/plugins/CorePluginsAdmin/Controller.php
@@ -47,6 +47,38 @@ class Controller extends Plugin\ControllerAdmin
parent::__construct();
}
+ public function marketplace()
+ {
+ self::dieIfMarketplaceIsDisabled();
+
+ $show = Common::getRequestVar('show', 'plugins', 'string');
+ $query = Common::getRequestVar('query', '', 'string', $_POST);
+ $sort = Common::getRequestVar('sort', $this->defaultSortMethod, 'string');
+ if (!in_array($sort, $this->validSortMethods)) {
+ $sort = $this->defaultSortMethod;
+ }
+ $mode = Common::getRequestVar('mode', 'admin', 'string');
+ if (!in_array($sort, array('user', 'admin'))) {
+ $mode = 'admin';
+ }
+
+ $view = $this->configureView('@CorePluginsAdmin/marketplace');
+
+ $marketplace = new Marketplace();
+
+ $showThemes = ($show === 'themes');
+ $view->plugins = $marketplace->searchPlugins($query, $sort, $showThemes);
+ $view->showThemes = $showThemes;
+ $view->mode = $mode;
+ $view->query = $query;
+ $view->sort = $sort;
+ $view->installNonce = Nonce::getNonce(static::INSTALL_NONCE);
+ $view->updateNonce = Nonce::getNonce(static::UPDATE_NONCE);
+ $view->isSuperUser = Piwik::hasUserSuperUserAccess();
+
+ return $view->render();
+ }
+
private function createUpdateOrInstallView($template, $nonceName)
{
static::dieIfMarketplaceIsDisabled();
@@ -160,6 +192,30 @@ class Controller extends Plugin\ControllerAdmin
return $view->render();
}
+ /**
+ * @deprecated
+ */
+ public function browsePlugins()
+ {
+ $this->redirectToIndex('CorePluginsAdmin', 'marketplace');
+ }
+
+ /**
+ * @deprecated
+ */
+ public function browseThemes()
+ {
+ $this->redirectToIndex('CorePluginsAdmin', 'marketplace', null, null, null, array('show' => 'themes'));
+ }
+
+ /**
+ * @deprecated
+ */
+ public function userBrowsePlugins()
+ {
+ $this->redirectToIndex('CorePluginsAdmin', 'marketplace', null, null, null, array('mode' => 'user'));
+ }
+
private function dieIfMarketplaceIsDisabled()
{
if (!CorePluginsAdmin::isMarketplaceEnabled()) {
@@ -179,50 +235,6 @@ class Controller extends Plugin\ControllerAdmin
}
}
- private function createBrowsePluginsOrThemesView($template, $themesOnly)
- {
- static::dieIfMarketplaceIsDisabled();
-
- $query = Common::getRequestVar('query', '', 'string', $_POST);
- $sort = Common::getRequestVar('sort', $this->defaultSortMethod, 'string');
-
- if (!in_array($sort, $this->validSortMethods)) {
- $sort = $this->defaultSortMethod;
- }
-
- $view = $this->configureView('@CorePluginsAdmin/' . $template);
-
- $marketplace = new Marketplace();
- $view->plugins = $marketplace->searchPlugins($query, $sort, $themesOnly);
-
- $view->query = $query;
- $view->sort = $sort;
- $view->installNonce = Nonce::getNonce(static::INSTALL_NONCE);
- $view->updateNonce = Nonce::getNonce(static::UPDATE_NONCE);
- $view->isSuperUser = Piwik::hasUserSuperUserAccess();
-
- return $view;
- }
-
- public function browsePlugins()
- {
- $view = $this->createBrowsePluginsOrThemesView('browsePlugins', $themesOnly = false);
- return $view->render();
- }
-
- public function browseThemes()
- {
- $view = $this->createBrowsePluginsOrThemesView('browseThemes', $themesOnly = true);
- return $view->render();
- }
-
- public function userBrowsePlugins()
- {
- $view = $this->createBrowsePluginsOrThemesView('browsePlugins', $themesOnly = false);
- $view->mode = 'user';
- return $view->render();
- }
-
private function createPluginsOrThemesView($template, $themesOnly)
{
Piwik::checkUserHasSuperUserAccess();
diff --git a/plugins/CorePluginsAdmin/CorePluginsAdmin.php b/plugins/CorePluginsAdmin/CorePluginsAdmin.php
index f08ed3e572..f51e214785 100644
--- a/plugins/CorePluginsAdmin/CorePluginsAdmin.php
+++ b/plugins/CorePluginsAdmin/CorePluginsAdmin.php
@@ -29,6 +29,7 @@ class CorePluginsAdmin extends \Piwik\Plugin
{
$stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/marketplace.less";
$stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/plugins_admin.less";
+ $stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/plugin-details.less";
}
public static function isMarketplaceEnabled()
@@ -43,8 +44,9 @@ class CorePluginsAdmin extends \Piwik\Plugin
public function getJsFiles(&$jsFiles)
{
+ $jsFiles[] = "libs/bower_components/jQuery.dotdotdot/src/js/jquery.dotdotdot.min.js";
$jsFiles[] = "plugins/CoreHome/javascripts/popover.js";
- $jsFiles[] = "plugins/CorePluginsAdmin/javascripts/pluginDetail.js";
+ $jsFiles[] = "plugins/CorePluginsAdmin/javascripts/marketplace.js";
$jsFiles[] = "plugins/CorePluginsAdmin/javascripts/pluginOverview.js";
$jsFiles[] = "plugins/CorePluginsAdmin/javascripts/pluginExtend.js";
$jsFiles[] = "plugins/CorePluginsAdmin/javascripts/plugins.js";
diff --git a/plugins/CorePluginsAdmin/javascripts/marketplace.js b/plugins/CorePluginsAdmin/javascripts/marketplace.js
new file mode 100755
index 0000000000..64cb5087ab
--- /dev/null
+++ b/plugins/CorePluginsAdmin/javascripts/marketplace.js
@@ -0,0 +1,50 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+$(document).ready(function () {
+
+ // Keeps the plugin descriptions the same height
+ $('.marketplace .plugin .description').dotdotdot({
+ after: 'a.more',
+ watch: 'window'
+ });
+
+ $('.marketplace').on('click', '.plugin-details', function (event) {
+ event.preventDefault();
+
+ var pluginName = $(this).attr('data-pluginName');
+ if (!pluginName) {
+ return;
+ }
+
+ var activeTab = $(this).attr('data-activePluginTab');
+ if (activeTab) {
+ pluginName += '!' + activeTab;
+ }
+
+ broadcast.propagateNewPopoverParameter('browsePluginDetail', pluginName);
+ });
+
+ broadcast.addPopoverHandler('browsePluginDetail', function (value) {
+ var pluginName = value;
+ var activeTab = null;
+
+ if (-1 !== value.indexOf('!')) {
+ activeTab = value.substr(value.indexOf('!') + 1);
+ pluginName = value.substr(0, value.indexOf('!'));
+ }
+
+ var url = 'module=CorePluginsAdmin&action=pluginDetails&pluginName=' + encodeURIComponent(pluginName);
+
+ if (activeTab) {
+ url += '&activeTab=' + encodeURIComponent(activeTab);
+ }
+
+ Piwik_Popover.createPopupAndLoadUrl(url, 'details');
+ });
+
+});
diff --git a/plugins/CorePluginsAdmin/javascripts/pluginDetail.js b/plugins/CorePluginsAdmin/javascripts/pluginDetail.js
deleted file mode 100755
index 989940e417..0000000000
--- a/plugins/CorePluginsAdmin/javascripts/pluginDetail.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-$(document).ready(function () {
-
- function syncMaxHeight (selector) {
-
- if (!selector) {
- return;
- }
-
- var $nodes = $(selector);
-
- if (!$nodes) {
- return;
- }
-
- var max = {};
- $nodes.each(function (index, node) {
- var $node = $(node);
- var top = $node.position().top;
-
- var height = $node.height();
-
- if (!max[top]) {
- max[top] = height;
- } else if (max[top] < height) {
- max[top] = height;
- } else {
- $node.height(max[top] + 'px');
- }
- });
-
- $nodes.each(function (index, node) {
- var $node = $(node);
- var top = $node.position().top;
-
- $node.height(max[top] + 'px');
- });
- }
-
- syncMaxHeight('.pluginslist .plugin');
- syncMaxHeight('.themeslist .plugin');
-
- $('.pluginslist, #plugins, .themeslist').on('click', '[data-pluginName]', function (event) {
- if ($(event.target).hasClass('install') || $(event.target).hasClass('uninstall')) {
- return;
- }
-
- var pluginName = $(this).attr('data-pluginName');
-
- if (!pluginName) {
- return;
- }
-
- var activeTab = $(event.target).attr('data-activePluginTab');
- if (activeTab) {
- pluginName += '!' + activeTab;
- }
-
- broadcast.propagateNewPopoverParameter('browsePluginDetail', pluginName);
- });
-
- var showPopover = function (value) {
- var pluginName = value;
- var activeTab = null;
-
- if (-1 !== value.indexOf('!')) {
- activeTab = value.substr(value.indexOf('!') + 1);
- pluginName = value.substr(0, value.indexOf('!'));
- }
-
- var url = 'module=CorePluginsAdmin&action=pluginDetails&pluginName=' + encodeURIComponent(pluginName);
-
- if (activeTab) {
- url += '&activeTab=' + encodeURIComponent(activeTab);
- }
-
- Piwik_Popover.createPopupAndLoadUrl(url, 'details');
- };
-
- broadcast.addPopoverHandler('browsePluginDetail', showPopover);
-
-}); \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/javascripts/pluginExtend.js b/plugins/CorePluginsAdmin/javascripts/pluginExtend.js
index ecaf872504..63af1f8433 100644
--- a/plugins/CorePluginsAdmin/javascripts/pluginExtend.js
+++ b/plugins/CorePluginsAdmin/javascripts/pluginExtend.js
@@ -7,7 +7,7 @@
$(document).ready(function () {
- $('.pluginslistActionBar .uploadPlugin').click(function (event) {
+ $('.uploadPlugin').click(function (event) {
event.preventDefault();
piwikHelper.modalConfirm('#installPluginByUpload', {
diff --git a/plugins/CorePluginsAdmin/lang/en.json b/plugins/CorePluginsAdmin/lang/en.json
index 762159c2e2..6831209c04 100644
--- a/plugins/CorePluginsAdmin/lang/en.json
+++ b/plugins/CorePluginsAdmin/lang/en.json
@@ -15,6 +15,7 @@
"BeCarefulUsingPlugins": "Plugins that are not authored by Piwik team must be used with care: we did not review them.",
"BeCarefulUsingThemes": "Themes that are not authored by Piwik team must be used with care: we did not review them.",
"ByXDevelopers": "by %s developers",
+ "CannotInstall": "Cannot install (help)",
"Changelog": "Changelog",
"ChangeSettingsPossible": "You can change %ssettings%s for this plugin.",
"CorePluginTooltip": "Core plugins have no version since they are distributed with Piwik.",
@@ -44,6 +45,7 @@
"MenuPlatform": "Platform",
"MissingRequirementsNotice": "Please update %1$s %2$s to a newer version, %1$s %3$s is required.",
"MissingRequirementsPleaseInstallNotice": "Please install %1$s %2$s as it is required by %3$s.",
+ "NewVersion": "new version",
"NoPluginsFound": "No plugins found",
"NotAllowedToBrowseMarketplacePlugins": "You can browse the list of plugins that can be installed to customize or extend your Piwik platform. Please contact your administrator if you need any of these installed.",
"NotAllowedToBrowseMarketplaceThemes": "You can browse the list of themes that can be installed to customize the appearance of the Piwik platform. Please contact your administrator to get any of these installed for you.",
diff --git a/plugins/CorePluginsAdmin/stylesheets/marketplace.less b/plugins/CorePluginsAdmin/stylesheets/marketplace.less
index 0dd91da7e0..fd6442a9d2 100644
--- a/plugins/CorePluginsAdmin/stylesheets/marketplace.less
+++ b/plugins/CorePluginsAdmin/stylesheets/marketplace.less
@@ -1,378 +1,82 @@
-.extendPlatform {
- min-width: 580px;
-
- .introduction { max-width:980px; }
- .byPlugins { width:50%;float:left; }
- .byThemes { width:50%;float:left; }
- .teaserImage { width: 128px; height: 128px; margin: 64px; }
- .header { font-size: 1.6em; }
- .callToAction { font-size: 1.1em;line-height: 2em; }
-}
-
-#plugins {
-
- .desc .missingRequirementsNotice {
- color: red;
- }
-
- .plugin-desc-missingrequirements {
- font-weight:bold;
- font-style: italic;
- a {
- text-decoration: underline !important;
- color: black;
+.marketplace {
+ .plugin-search {
+ float: right;
+
+ input, button {
+ height: 41px;
+ margin-bottom: 0;
+ }
+ button {
+ font-size: inherit !important; // because the default Piwik button style is crazy
+ }
+ }
+
+ .marketplace-max-width {
+ max-width: 980px;
+ }
+
+ .plugin {
+ text-align: center;
+ .description {
+ @line-height: 18px;
+ line-height: @line-height;
+ height: @line-height * 3; // 3 lines of text
+ padding-bottom: 0;
+ margin-bottom: 10px;
+ .more {
+ text-decoration: underline;
+ color: @theme-color-text;
+ }
+ }
+ img.preview {
+ max-width: 250px;
+ width: 100%;
+ }
+ .metadata {
+ color: @color-silver-l50;
+ font-size: 95%;
+ margin: 15px 15px 10px;
+ list-style: none;
+ li {
+ text-overflow: ellipsis;
+ overflow-x: hidden;
+ white-space: nowrap;
+ line-height: 18px;
+ }
+ .update-available {
+ // Code taken from Bootstrap's labels
+ font-weight: bold;
+ background-color: #f0ad4e;
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 76%;
+ line-height: 1;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: 0.25em;
+ text-decoration: none;
+ }
+ }
+ .panel-footer {
+ padding: 12px 40px;
+ }
+ }
+
+ .footer-message {
+ margin-top:30px;
+ font-style: italic;
}
- }
-
- .settingsLink {
- text-align: right;
- width: 100%;
- display: inline-block;
- font-style: italic;
- }
-}
-
-.admin .pluginsFilter {
- color: @theme-color-text-lighter;
- .active {
- font-weight: bold;
- }
-
- a {
- color: @theme-color-link;
- text-decoration: none;
- }
-
- a .counter {
- color: #999999;
- font-weight: normal;
- }
-
- a:hover {
- text-decoration: underline;
- }
-
- .status {
- display: inline-block;
- margin-left: 20px;
- }
-
- .getNewPlugins {
- float: right;
- }
}
#installPluginByUpload {
- .description {
- margin-top: 30px;
- margin-bottom: 20px;
- }
-
- .startUpload {
- margin-top: 20px;
- margin-bottom: 20px;
- }
-}
-
-.pluginslist {
- margin-top: 20px;
- max-width: 980px;
- clear: right;
-
- .plugin {
- border: 1px solid #dadada;
- padding: 15px;
- background-color: #f2f2f2;
- margin-bottom: 15px;
- position: relative;
-
- .missingRequirementsNotice,
- .updateAvailableNotice {
- font-size: 14px;
- padding: 10px;
- color: #9b7a44;
- display: inline-block;
- background-color: #ffffe0;
- border-radius: 3px;
- margin-top: 1px;
- margin-bottom: 16px;
-
- a {
- color: #9b7a44;
- font-weight: bold;
- }
- }
-
- &:hover {
- background-color: #EFEEEC;
- }
-
- li {
- display: inline-block;
- padding-right: 50px;
- font-size: 90%;
-
- &.even {
- padding-right: 0;
- width: 48%;
- overflow: hidden;
- white-space: nowrap;
- }
- &.odd {
- padding-right: 0;
- width: 48%;
- overflow: hidden;
- white-space: nowrap;
- }
- }
-
- ul {
- list-style: none;
- margin-left: 0;
- line-height: 140%;
- }
-
- .header {
- margin-top: 0px;
- margin-bottom: 15px;
- h3 {
- font-size: 16px;
- }
- }
-
.description {
- padding-bottom: 10px;
- }
- .install {
- float: right;
- margin-top: 3px;
+ margin-top: 30px;
+ margin-bottom: 20px;
}
- .update {
- .install
+ .startUpload {
+ margin-top: 20px;
+ margin-bottom: 20px;
}
- h3 .more {
- font-weight: bold;
- text-decoration: none;
- &:hover {
- text-decoration: underline;
- }
- }
- .more {
- text-decoration: underline;
- color: @theme-color-text;
- }
- .content {
- margin-bottom: 46px;
- cursor: pointer;
- }
-
- .featuredIcon {
- margin-right: 3px;
- margin-bottom: 3px;
- height: 24px;
- width: 24px;
- position: absolute;
- right: 1px;
- margin-top: -22px;
- }
-
- .footer {
- position: absolute;
- bottom: 4px;
- left: 0px;
- right: 0px;
- cursor: pointer;
- }
- .metadataSeparator {
- background-color: lightgray;
- color: #333;
- border: 0px;
- height: 1px;
- width: 100%;
- }
- .metadata {
- margin-top: 10px;
- margin-left: 15px;
- margin-right: 15px;
- }
- }
-
- &.themes .plugin {
- .header {
- display: inline;
- }
- .content {
- margin-bottom: 57px;
- }
- .preview {
- width: 250px;
- height: 250px;
- }
- .footer {
- position: absolute;
- bottom: 7px;
- left: 0px;
- right: 0px;
- }
- }
-}
-
-.pluginFooterMessage {
- float:left;
- margin-top:30px;
- line-height: 2em;
- font-style: italic;
-}
-
-.pluginslistNonSuperUserHint {
- margin-top: 30px;
- margin-bottom: 30px;
- width: 500px;
-}
-
-.pluginslistActionBar {
- min-width: 650px;
-
- form {
- display: inline;
- }
-
- .sort {
- .active {
- font-weight: bold;
- }
- }
-
- .infoBox {
- margin: 0px 0px 20px 0px;
- line-height: 1.5em;
- }
}
-
-.pluginDetails {
- font-size: 13px;
- text-align: left;
- line-height: 20px;
-
- h3, h4, h5, h6 {
- margin: 20px 0px 10px 0px;
- color: #000000;
- }
-
- .ui-tabs-panel ul, .ui-tabs-panel ol {
- list-style: initial;
- padding-left: 20px;
- }
-
- .content .missingRequirementsNotice,
- .content .updateAvailableNotice {
- font-size: 14px;
- padding: 10px;
- color: #9b7a44;
- display: inline-block;
- background-color: #ffffe0;
- border-radius: 3px;
-
- a {
- color: #9b7a44;
- font-weight: bold;
- }
- a:hover {
- text-decoration: underline;
- }
- }
-
- p, .ui-tabs-panel ul, .ui-tabs-panel li {
- text-align: left;
- line-height: 20px;
- }
-
- .header .intro {
- margin-bottom: 15px;
- }
-
- .content p {
- margin: 0 0 10px;
- }
-
- .description {
- padding-right: 25px;
- }
-
- .ui-tabs {
- padding: 0em;
- }
-
- .ui-tabs .ui-tabs-nav {
- padding: 0em;
- border-bottom: 1px solid #cccccc;
- margin-right: 25px;
- border-radius: 0px;
- font-size: 15px;
- }
-
- .ui-tabs .ui-tabs-panel {
- padding: 1.4em 3em 0em 0em;
- }
-
- .content a {
- color: @theme-color-link;
- text-decoration: none;
- }
-
- .metadata dl {
- padding-right: 25px;
- }
-
- .metadata a:hover {
- text-decoration: underline;
- }
-
- .ui-state-default {
- border: 0px !important;
- }
-
- .ui-state-active {
- padding-bottom: 0px !important;
- }
-
- .ui-state-active.ui-state-default {
- border: 1px solid #cccccc !important;
- }
-
- .ui-state-default:hover {
- background-color: #eeeeee !important;
- }
-
- .install {
- padding: 11px 19px;
- font-size: 17.5px;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
- color: #ffffff;
- background-color: #5bb75b;
- display: inline-block;
- text-decoration: none;
- }
-
- .install:hover {
- text-decoration: underline;
- }
-
- dt {
- font-weight: bold;
- line-height: 20px;
- }
- dd {
- margin-left: 10px;
- line-height: 20px;
- }
-
- .featuredIcon {
- height: 16px;
- width: 16px;
- margin-right: 5px;
- }
-
-} \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/stylesheets/plugin-details.less b/plugins/CorePluginsAdmin/stylesheets/plugin-details.less
new file mode 100644
index 0000000000..c447e35025
--- /dev/null
+++ b/plugins/CorePluginsAdmin/stylesheets/plugin-details.less
@@ -0,0 +1,101 @@
+.pluginDetails {
+ font-size: 13px;
+ text-align: left;
+ line-height: 20px;
+
+ h3, h4, h5, h6 {
+ margin: 20px 0px 10px 0px;
+ color: #000000;
+ }
+
+ .ui-tabs-panel ul, .ui-tabs-panel ol {
+ list-style: initial;
+ padding-left: 20px;
+ }
+
+ p, .ui-tabs-panel ul, .ui-tabs-panel li {
+ text-align: left;
+ line-height: 20px;
+ }
+
+ .header .intro {
+ margin-bottom: 15px;
+ }
+
+ .content p {
+ margin: 0 0 10px;
+ }
+
+ .description {
+ padding-right: 25px;
+ }
+
+ .ui-tabs {
+ padding: 0em;
+ }
+
+ .ui-tabs .ui-tabs-nav {
+ padding: 0em;
+ border-bottom: 1px solid #cccccc;
+ margin-right: 25px;
+ border-radius: 0px;
+ font-size: 15px;
+ }
+
+ .ui-tabs .ui-tabs-panel {
+ padding: 1.4em 3em 0em 0em;
+ }
+
+ .content a {
+ color: @theme-color-link;
+ text-decoration: none;
+ }
+
+ .ui-state-default {
+ border: 0px !important;
+ }
+
+ .ui-state-active {
+ padding-bottom: 0px !important;
+ }
+
+ .ui-state-active.ui-state-default {
+ border: 1px solid #cccccc !important;
+ }
+
+ .ui-state-default:hover {
+ background-color: #eeeeee !important;
+ }
+
+ .install {
+ padding: 11px 19px;
+ font-size: 17.5px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ color: #ffffff;
+ background-color: #5bb75b;
+ display: inline-block;
+ text-decoration: none;
+ }
+
+ .install:hover {
+ text-decoration: underline;
+ }
+
+ dt {
+ font-weight: bold;
+ line-height: 20px;
+ }
+ dd {
+ margin-left: 10px;
+ line-height: 20px;
+ }
+
+ .featuredIcon {
+ height: 16px;
+ width: 16px;
+ margin-right: 5px;
+ }
+
+}
diff --git a/plugins/CorePluginsAdmin/stylesheets/plugins_admin.less b/plugins/CorePluginsAdmin/stylesheets/plugins_admin.less
index dc3a4965d7..1a913423c7 100644
--- a/plugins/CorePluginsAdmin/stylesheets/plugins_admin.less
+++ b/plugins/CorePluginsAdmin/stylesheets/plugins_admin.less
@@ -52,3 +52,52 @@ table.entityTable tr td a.uninstall {
font-style:italic;
color:#777;
}
+
+#plugins {
+
+ .plugin-desc-missingrequirements {
+ font-weight:bold;
+ font-style: italic;
+ a {
+ text-decoration: underline !important;
+ color: black;
+ }
+ }
+
+ .settingsLink {
+ text-align: right;
+ width: 100%;
+ display: inline-block;
+ font-style: italic;
+ }
+}
+
+.admin .pluginsFilter {
+ color: @theme-color-text-lighter;
+ .active {
+ font-weight: bold;
+ }
+
+ a {
+ color: @theme-color-link;
+ text-decoration: none;
+ }
+
+ a .counter {
+ color: #999999;
+ font-weight: normal;
+ }
+
+ a:hover {
+ text-decoration: underline;
+ }
+
+ .status {
+ display: inline-block;
+ margin-left: 20px;
+ }
+
+ .getNewPlugins {
+ float: right;
+ }
+}
diff --git a/plugins/CorePluginsAdmin/templates/browsePlugins.twig b/plugins/CorePluginsAdmin/templates/browsePlugins.twig
deleted file mode 100644
index 6dad365703..0000000000
--- a/plugins/CorePluginsAdmin/templates/browsePlugins.twig
+++ /dev/null
@@ -1,62 +0,0 @@
-{% extends mode is defined and mode == 'user' ? "user.twig" : "admin.twig" %}
-{% import '@CorePluginsAdmin/macros.twig' as pluginsMacro %}
-
-{% block content %}
-
- <div class="pluginslistActionBar">
-
- <h2 piwik-enriched-headline
- feature-name="{{ 'CorePluginsAdmin_Marketplace'|translate }}"
- >{{ 'CorePluginsAdmin_TeaserExtendPiwikByPlugin'|translate }}</h2>
-
- <div class="infoBox">
- {{ 'CorePluginsAdmin_PluginsExtendPiwik'|translate }}
- {{ 'CorePluginsAdmin_InstallingNewPluginViaMarketplaceOrUpload'|translate('<a href="#" class="uploadPlugin">','</a>')|raw }}
- <br/>
- {{ 'CorePluginsAdmin_BeCarefulUsingPlugins'|translate }}
- </div>
-
- {% include "@CorePluginsAdmin/browsePluginsActions.twig" %}
- </div>
-
- {% if not isSuperUser %}
- <div class="pluginslistNonSuperUserHint">
- {{ 'CorePluginsAdmin_NotAllowedToBrowseMarketplacePlugins'|translate }}
- </div>
- {% endif %}
-
- <div class="row pluginslist">
-
- {% if plugins|length %}
-
- {% for plugin in plugins %}
-
- <div class="col-md-4">
- <div class="plugin">
- <div class="content" data-pluginName="{{ plugin.name }}">
- {% include "@CorePluginsAdmin/pluginOverview.twig" %}
- </div>
-
- <div class="footer" data-pluginName="{{ plugin.name }}">
- {% if plugin.featured %}
- {{ pluginsMacro.featuredIcon('right') }}
- {% endif %}
- {% include "@CorePluginsAdmin/pluginMetadata.twig" %}
- </div>
- </div>
- </div>
-
- {% endfor %}
-
- {% else %}
- {{ 'CorePluginsAdmin_NoPluginsFound'|translate }}
- {% endif %}
- </div>
-
- <div class="pluginFooterMessage">
- {% set marketplaceSellPluginSubject = 'CorePluginsAdmin_MarketplaceSellPluginSubject'|translate %}
- {{ 'CorePluginsAdmin_GetEarlyAccessForPaidPlugins'|translate("<a href='mailto:hello@piwik.org?subject=" ~ marketplaceSellPluginSubject ~ "'>", "</a>")|raw }}
- <br/>
- {{ 'CorePluginsAdmin_DevelopersLearnHowToDevelopPlugins'|translate('<a href="?module=Proxy&action=redirect&url=http://developer.piwik.org/plugins" target="_blank">', '</a>')|raw }}
- </div>
-{% endblock %}
diff --git a/plugins/CorePluginsAdmin/templates/browsePluginsActions.twig b/plugins/CorePluginsAdmin/templates/browsePluginsActions.twig
deleted file mode 100644
index 0940df2b96..0000000000
--- a/plugins/CorePluginsAdmin/templates/browsePluginsActions.twig
+++ /dev/null
@@ -1,27 +0,0 @@
-<div class="ui-confirm" id="installPluginByUpload">
- <h2>{{ 'CorePluginsAdmin_TeaserExtendPiwikByUpload'|translate }}</h2>
-
- <p class="description"> {{ 'CorePluginsAdmin_AllowedUploadFormats'|translate }} </p>
-
- <form enctype="multipart/form-data"
- method="post"
- id="uploadPluginForm"
- action="{{ linkTo({'action':'uploadPlugin', 'nonce': installNonce}) }}">
- <input type="file" name="pluginZip">
- <br />
- <input class="startUpload" type="submit" value="{{ 'CorePluginsAdmin_UploadZipFile'|translate }}">
- </form>
-</div>
-
-<div class="sort">
- <a href="{{ linkTo({'sort': 'popular', 'query': ''}) }}" {% if 'popular' == sort %}class="active"{% endif %}>{{ 'CorePluginsAdmin_SortByPopular'|translate }}</a>
- |
- <a href="{{ linkTo({'sort': 'newest', 'query': ''}) }}" {% if 'newest' == sort %}class="active"{% endif %}>{{ 'CorePluginsAdmin_SortByNewest'|translate }}</a>
- |
- <a href="{{ linkTo({'sort': 'alpha', 'query': ''}) }}" {% if 'alpha' == sort %}class="active"{% endif %}>{{ 'CorePluginsAdmin_SortByAlpha'|translate }}</a>
- |
- <form action="{{ linkTo({'sort': ''}) }}" method="POST">
- <input value="{{ query }}" placeholder="{{ 'General_Search'|translate }} {{ plugins|length }} {{ 'General_Plugins'|translate|lcfirst }}..." type="text" name="query"/>
- <button type="submit">{{ 'General_Search'|translate }}</button>
- </form>
-</div>
diff --git a/plugins/CorePluginsAdmin/templates/browseThemes.twig b/plugins/CorePluginsAdmin/templates/browseThemes.twig
deleted file mode 100644
index 106f507b00..0000000000
--- a/plugins/CorePluginsAdmin/templates/browseThemes.twig
+++ /dev/null
@@ -1,49 +0,0 @@
-{% extends 'admin.twig' %}
-
-{% block content %}
-
- <div class="pluginslistActionBar">
-
- <h2 piwik-enriched-headline
- feature-name="{{ 'CorePluginsAdmin_Marketplace'|translate }}"
- >{{ 'CorePluginsAdmin_TeaserExtendPiwikByTheme'|translate }}</h2>
-
- <div class="infoBox">
- {{ 'CorePluginsAdmin_ThemesDescription'|translate }}
- {{ 'CorePluginsAdmin_InstallingNewPluginViaMarketplaceOrUpload'|translate('<a href="#" class="uploadPlugin">','</a>')|raw }}
- <br/>
- {{ 'CorePluginsAdmin_BeCarefulUsingThemes'|translate }}
- </div>
-
- </div>
-
- {% if not isSuperUser %}
- <div class="pluginslistNonSuperUserHint">
- {{ 'CorePluginsAdmin_NotAllowedToBrowseMarketplaceThemes'|translate }}
- </div>
- {% endif %}
-
- <div class="row pluginslist themes">
-
- {% if plugins|length %}
- {% for plugin in plugins %}
-
- <div class="col-md-4">
- <div class="plugin">
- <div class="content" data-pluginName="{{ plugin.name }}">
- {% include "@CorePluginsAdmin/themeOverview.twig" %}
- </div>
-
- <div class="footer" data-pluginName="{{ plugin.name }}">
- {% include "@CorePluginsAdmin/pluginMetadata.twig" %}
- </div>
- </div>
- </div>
-
- {% endfor %}
- {% else %}
- {{ 'CorePluginsAdmin_NoThemesFound'|translate }}
- {% endif %}
-
- </div>
-{% endblock %}
diff --git a/plugins/CorePluginsAdmin/templates/macros.twig b/plugins/CorePluginsAdmin/templates/macros.twig
index 6eda0a2d31..5e0325d84a 100644
--- a/plugins/CorePluginsAdmin/templates/macros.twig
+++ b/plugins/CorePluginsAdmin/templates/macros.twig
@@ -96,13 +96,13 @@
{% macro missingRequirementsPleaseUpdateNotice(plugin) %}
{% if plugin.missingRequirements and 0 < plugin.missingRequirements|length %}
{% for req in plugin.missingRequirements -%}
- <p class="missingRequirementsNotice">
+ <div class="alert alert-danger">
{% set requirement = req.requirement|capitalize %}
{% if 'Php' == requirement %}
{% set requirement = 'PHP' %}
{% endif %}
{{ 'CorePluginsAdmin_MissingRequirementsNotice'|translate(requirement, req.actualVersion, req.requiredVersion) }}
- </p>
+ </div>
{%- endfor %}
{% endif %}
{% endmacro %}
diff --git a/plugins/CorePluginsAdmin/templates/marketplace.twig b/plugins/CorePluginsAdmin/templates/marketplace.twig
new file mode 100644
index 0000000000..9dd302f5ac
--- /dev/null
+++ b/plugins/CorePluginsAdmin/templates/marketplace.twig
@@ -0,0 +1,75 @@
+{% extends mode is defined and mode == 'user' ? "user.twig" : "admin.twig" %}
+{% import '@CorePluginsAdmin/macros.twig' as pluginsMacro %}
+
+{% block content %}
+
+ <div class="marketplace">
+
+ <h2 piwik-enriched-headline feature-name="{{ 'CorePluginsAdmin_Marketplace'|translate }}">
+ {{ 'CorePluginsAdmin_Marketplace'|translate }}
+ </h2>
+
+ <ul class="nav nav-pills">
+ <li {% if not showThemes %}class="active"{% endif %}>
+ <a href="{{ linkTo({'show': 'plugins'}) }}">{{ 'General_Plugins'|translate }}</a>
+ </li>
+ <li {% if showThemes %}class="active"{% endif %}>
+ <a href="{{ linkTo({'show': 'themes'}) }}">{{ 'CorePluginsAdmin_Themes'|translate }}</a>
+ </li>
+ </ul>
+
+ <div class="marketplace-max-width">
+
+ <p>
+ {% if showThemes %}
+ {{ 'CorePluginsAdmin_ThemesDescription'|translate }}
+ {{ 'CorePluginsAdmin_InstallingNewPluginViaMarketplaceOrUpload'|translate('<a href="#" class="uploadPlugin">','</a>')|raw }}
+ <br/>
+ {{ 'CorePluginsAdmin_BeCarefulUsingThemes'|translate }}
+ {% else %}
+ {{ 'CorePluginsAdmin_PluginsExtendPiwik'|translate }}
+ {{ 'CorePluginsAdmin_InstallingNewPluginViaMarketplaceOrUpload'|translate('<a href="#" class="uploadPlugin">','</a>')|raw }}
+ <br/>
+ {{ 'CorePluginsAdmin_BeCarefulUsingPlugins'|translate }}
+ {% endif %}
+ </p>
+
+ {% if not isSuperUser %}
+ <p>
+ {% if showThemes %}
+ {{ 'CorePluginsAdmin_NotAllowedToBrowseMarketplaceThemes'|translate }}
+ {% else %}
+ {{ 'CorePluginsAdmin_NotAllowedToBrowseMarketplacePlugins'|translate }}
+ {% endif %}
+ </p>
+ {% endif %}
+
+ </div>
+
+ <hr/>
+
+ <div class="ui-confirm" id="installPluginByUpload">
+ <h2>{{ 'CorePluginsAdmin_TeaserExtendPiwikByUpload'|translate }}</h2>
+
+ <p class="description"> {{ 'CorePluginsAdmin_AllowedUploadFormats'|translate }} </p>
+
+ <form enctype="multipart/form-data" method="post" id="uploadPluginForm"
+ action="{{ linkTo({'action':'uploadPlugin', 'nonce': installNonce}) }}">
+ <input type="file" name="pluginZip">
+ <br />
+ <input class="startUpload" type="submit" value="{{ 'CorePluginsAdmin_UploadZipFile'|translate }}">
+ </form>
+ </div>
+
+ {% include '@CorePluginsAdmin/marketplace/plugin-list.twig' %}
+
+ <div class="footer-message">
+ {% set marketplaceSellPluginSubject = 'CorePluginsAdmin_MarketplaceSellPluginSubject'|translate %}
+ {{ 'CorePluginsAdmin_GetEarlyAccessForPaidPlugins'|translate("<a href='mailto:hello@piwik.org?subject=" ~ marketplaceSellPluginSubject ~ "'>", "</a>")|raw }}
+ <br/>
+ {{ 'CorePluginsAdmin_DevelopersLearnHowToDevelopPlugins'|translate('<a href="?module=Proxy&action=redirect&url=http://developer.piwik.org/plugins" target="_blank">', '</a>')|raw }}
+ </div>
+
+ </div>
+
+{% endblock %}
diff --git a/plugins/CorePluginsAdmin/templates/marketplace/plugin-list.twig b/plugins/CorePluginsAdmin/templates/marketplace/plugin-list.twig
new file mode 100644
index 0000000000..eaf166e466
--- /dev/null
+++ b/plugins/CorePluginsAdmin/templates/marketplace/plugin-list.twig
@@ -0,0 +1,110 @@
+<div class="row marketplace-max-width">
+
+ {# Hide filters and search for themes because we don't have many of them #}
+ {% if not showThemes %}
+ <div class="col-sm-12 clearfix">
+
+ <form action="{{ linkTo({'sort': ''}) }}" method="post" class="plugin-search">
+ <input value="{{ query }}" placeholder="{{ 'General_Search'|translate }} {{ plugins|length }} {{ 'General_Plugins'|translate|lcfirst }}..." type="text" name="query"/>
+ <button type="submit">{{ 'General_Search'|translate }}</button>
+ </form>
+
+ <ul class="nav nav-pills">
+ <li {% if 'popular' == sort %}class="active"{% endif %}>
+ <a href="{{ linkTo({'sort': 'popular', 'query': ''}) }}">
+ {{ 'CorePluginsAdmin_SortByPopular'|translate }}
+ </a>
+ </li>
+ <li {% if 'newest' == sort %}class="active"{% endif %}>
+ <a href="{{ linkTo({'sort': 'newest', 'query': ''}) }}">
+ {{ 'CorePluginsAdmin_SortByNewest'|translate }}
+ </a>
+ </li>
+ <li {% if 'alpha' == sort %}class="active"{% endif %}>
+ <a href="{{ linkTo({'sort': 'alpha', 'query': ''}) }}">
+ {{ 'CorePluginsAdmin_SortByAlpha'|translate }}
+ </a>
+ </li>
+ </ul>
+
+ </div>
+ {% endif %}
+
+ {% for plugin in plugins %}
+
+ <div class="col-md-4">
+ <div class="panel plugin">
+
+ <div class="panel-heading">
+ <h3 class="panel-title panel-title-block" title="{{ 'General_MoreDetails'|translate }}">
+ <a class="plugin-details" href="#" data-pluginName="{{ plugin.name }}">{{ plugin.name }}</a>
+ </h3>
+ </div>
+
+ <div class="panel-body">
+ <p class="description">
+ {{ plugin.description }}
+ <a class="more plugin-details" href="#" data-pluginName="{{ plugin.name }}" title="{{ 'General_MoreDetails'|translate }}">
+ &rsaquo; {{ 'General_MoreLowerCase'|translate }}</a>
+ </p>
+
+ {% if showThemes %}
+ {# Screenshot for themes #}
+ <a class="more plugin-details" href="#" data-pluginName="{{ plugin.name }}">
+ <img title="{{ 'General_MoreDetails'|translate }}"
+ class="preview" src="{{ plugin.screenshots|first }}?w=250&h=150"/></a>
+ {% endif %}
+
+ <ul class="metadata">
+ <li>
+ {{ 'CorePluginsAdmin_Version'|translate }}: {{ plugin.latestVersion }}
+ {% if plugin.canBeUpdated %}
+ <a class="plugin-details update-available" href="#" data-pluginName="{{ plugin.name }}" data-activePluginTab="changelog"
+ title="{{ 'CorePluginsAdmin_PluginUpdateAvailable'|translate(plugin.currentVersion, plugin.latestVersion) }}">
+ {{ 'CorePluginsAdmin_NewVersion'|translate }}</a>
+ {% endif %}
+ </li>
+ <li>{{ 'CorePluginsAdmin_Updated'|translate }}: {{ plugin.lastUpdated }}</li>
+ <li>{{ 'General_Downloads'|translate }}: {{ plugin.numDownloads }}</li>
+ <li>{{ 'CorePluginsAdmin_Developer'|translate }}: {{ pluginsMacro.pluginDeveloper(plugin.owner) }}</li>
+ </ul>
+ </div>
+
+ {% if isSuperUser %}
+ <div class="panel-footer" data-pluginName="{{ plugin.name }}">
+ {% if plugin.canBeUpdated and 0 == plugin.missingRequirements|length %}
+ <a class="btn btn-block"
+ href="{{ linkTo({'action':'updatePlugin', 'pluginName': plugin.name, 'nonce': updateNonce}) }}">
+ {{ 'CoreUpdater_UpdateTitle'|translate }}
+ </a>
+ {% elseif plugin.isInstalled %}
+ <button class="btn btn-noop btn-block">
+ {{ 'General_Installed'|translate }}
+ </button>
+ {% elseif plugin.missingRequirements|length > 0 %}
+ <a class="btn btn-link btn-block plugin-details" href="#" data-pluginName="{{ plugin.name }}" title="{{ 'General_MoreDetails'|translate }}">
+ {{ 'CorePluginsAdmin_CannotInstall'|translate }}
+ </a>
+ {% else %}
+ <a href="{{ linkTo({'action': 'installPlugin', 'pluginName': plugin.name, 'nonce': installNonce}) }}"
+ class="btn btn-block">
+ {{ 'CorePluginsAdmin_ActionInstall'|translate }}
+ </a>
+ {% endif %}
+ </div>
+ {% endif %}
+
+ </div>
+ </div>
+
+ {% endfor %}
+
+ {% if plugins|length == 0 %}
+ {% if showThemes %}
+ {{ 'CorePluginsAdmin_NoThemesFound'|translate }}
+ {% else %}
+ {{ 'CorePluginsAdmin_NoPluginsFound'|translate }}
+ {% endif %}
+ {% endif %}
+
+</div>
diff --git a/plugins/CorePluginsAdmin/templates/pluginDetails.twig b/plugins/CorePluginsAdmin/templates/pluginDetails.twig
index 077f3755c5..7359ce3a19 100644
--- a/plugins/CorePluginsAdmin/templates/pluginDetails.twig
+++ b/plugins/CorePluginsAdmin/templates/pluginDetails.twig
@@ -68,9 +68,10 @@
<div id="tabs-changelog">
{{ pluginsMacro.missingRequirementsPleaseUpdateNotice(plugin) }}
{% if plugin.canBeUpdated %}
- <p class="updateAvailableNotice">{{ 'CorePluginsAdmin_PluginUpdateAvailable'|translate(plugin.currentVersion, plugin.latestVersion) }}
+ <div class="alert alert-warning">
+ {{ 'CorePluginsAdmin_PluginUpdateAvailable'|translate(plugin.currentVersion, plugin.latestVersion) }}
{% if plugin.repositoryChangelogUrl %}<a rel="noreferrer" target="_blank" href="{{ plugin.repositoryChangelogUrl }}">{{ 'CorePluginsAdmin_ViewRepositoryChangelog'|translate }}</a>{% endif %}
- </p>
+ </div>
{% endif %}
{% if latestVersion.readmeHtml.changelog %}
diff --git a/plugins/CorePluginsAdmin/templates/pluginMetadata.twig b/plugins/CorePluginsAdmin/templates/pluginMetadata.twig
deleted file mode 100644
index 2c48868786..0000000000
--- a/plugins/CorePluginsAdmin/templates/pluginMetadata.twig
+++ /dev/null
@@ -1,9 +0,0 @@
-{% import '@CorePluginsAdmin/macros.twig' as plugins %}
-
-<hr class="metadataSeparator"/>
-<ul class="metadata">
- <li class="odd">{{ 'CorePluginsAdmin_Version'|translate }}: <strong>{{ plugin.latestVersion }}</strong></li>
- <li class="even">{{ 'CorePluginsAdmin_Updated'|translate }}: <strong>{{ plugin.lastUpdated }}</strong></li>
- <li class="odd">{{ 'General_Downloads'|translate }}: <strong>{{ plugin.numDownloads }}</strong></li>
- <li class="even">{{ 'CorePluginsAdmin_Developer'|translate }}: <strong>{{ plugins.pluginDeveloper(plugin.owner) }}</strong></li>
-</ul> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/templates/pluginOverview.twig b/plugins/CorePluginsAdmin/templates/pluginOverview.twig
deleted file mode 100644
index f49d8878ed..0000000000
--- a/plugins/CorePluginsAdmin/templates/pluginOverview.twig
+++ /dev/null
@@ -1,29 +0,0 @@
-{% import '@CorePluginsAdmin/macros.twig' as plugins %}
-
-{% if isSuperUser %}
- {% if plugin.canBeUpdated and 0 == plugin.missingRequirements|length %}
- <a class="update"
- href="{{ linkTo({'action':'updatePlugin', 'pluginName': plugin.name, 'nonce': updateNonce}) }}"
- >{{ 'CoreUpdater_UpdateTitle'|translate }}</a>
- {% elseif plugin.isInstalled %}
- <span class="install">{{ 'General_Installed'|translate }}</span>
- {% elseif 0 < plugin.missingRequirements|length %}
- {% else %}
- <a href="{{ linkTo({'action': 'installPlugin', 'pluginName': plugin.name, 'nonce': installNonce}) }}"
- class="install">{{ 'CorePluginsAdmin_ActionInstall'|translate }}</a>
- {% endif %}
-{% endif %}
-
-<h3 class="header" title="{{ 'General_MoreDetails'|translate }}">
- <a href="javascript:void(0);" class="more">{{ plugin.name }}</a>
-</h3>
-<p class="description">{{ plugin.description }}
- <br />
- <a href="javascript:void(0);" title="{{ 'General_MoreDetails'|translate }}" class="more">&rsaquo; {{ 'General_MoreLowerCase'|translate }}</a>
-</p>
-
-{% if plugin.canBeUpdated %}
- <p class="updateAvailableNotice" data-activePluginTab="changelog">{{ 'CorePluginsAdmin_PluginUpdateAvailable'|translate(plugin.currentVersion, plugin.latestVersion) }}</p>
-{% endif %}
-
-{{ plugins.missingRequirementsPleaseUpdateNotice(plugin) }} \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/templates/themeOverview.twig b/plugins/CorePluginsAdmin/templates/themeOverview.twig
deleted file mode 100644
index 3458b309c1..0000000000
--- a/plugins/CorePluginsAdmin/templates/themeOverview.twig
+++ /dev/null
@@ -1,30 +0,0 @@
-{% import '@CorePluginsAdmin/macros.twig' as plugins %}
-
-{% if isSuperUser %}
- {% if plugin.canBeUpdated and 0 == plugin.missingRequirements|length %}
- <a href="{{ linkTo({'action':'updatePlugin', 'pluginName': plugin.name, 'nonce': updateNonce}) }}"
- class="update"
- >{{ 'CoreUpdater_UpdateTitle'|translate }}</a>
- {% elseif plugin.isInstalled %}
- <span class="install">{{ 'General_Installed'|translate }}</span>
- {% elseif 0 < plugin.missingRequirements|length %}
- {% else %}
- <a href="{{ linkTo({'action': 'installPlugin', 'pluginName': plugin.name, 'nonce': installNonce}) }}"
- class="install">{{ 'CorePluginsAdmin_ActionInstall'|translate }}</a>
- {% endif %}
-{% endif %}
-
-<h3 class="header" title="{{ 'General_MoreDetails'|translate }}">
- <a href="javascript:void(0);" class="more">{{ plugin.name }}</a>
-</h3>
-
-<p class="description">{% if plugin.featured %}{{ plugins.featuredIcon('right') }}{% endif %}{{ plugin.description }}</p>
-
-{% if plugin.canBeUpdated %}
- <p class="updateAvailableNotice">{{ 'CorePluginsAdmin_PluginUpdateAvailable'|translate(plugin.currentVersion, plugin.latestVersion) }}</p>
-{% endif %}
-
-{{ plugins.missingRequirementsPleaseUpdateNotice(plugin) }}
-
-<a href="javascript:void(0);" class="more"><img title="{{ 'General_MoreDetails'|translate }}"
- class="preview" src="{{ plugin.screenshots|first }}?w=250&h=250"/></a>
diff --git a/plugins/Morpheus/stylesheets/base.less b/plugins/Morpheus/stylesheets/base.less
index 3a3405234a..a0bdba4047 100644
--- a/plugins/Morpheus/stylesheets/base.less
+++ b/plugins/Morpheus/stylesheets/base.less
@@ -39,3 +39,5 @@
@import "ui/_code";
@import "ui/_alerts";
+@import "ui/_navs";
+@import "ui/_panels";
diff --git a/plugins/Morpheus/stylesheets/general/_forms.less b/plugins/Morpheus/stylesheets/general/_forms.less
index 71dd52f9ca..1ae56fcb94 100644
--- a/plugins/Morpheus/stylesheets/general/_forms.less
+++ b/plugins/Morpheus/stylesheets/general/_forms.less
@@ -51,6 +51,26 @@ button[type="button"],
display: block;
width: 100%;
}
+.btn.disabled, .btn[disabled], fieldset[disabled] .btn {
+ pointer-events: none;
+ cursor: not-allowed;
+ filter: alpha(opacity=65);
+ opacity: .65;
+}
+// See http://getbootstrap.com/css/#buttons-options
+.btn.btn-link {
+ // We have to use !important unfortunately because the default button style uses it...
+ background: transparent !important;
+ color: @theme-color-link !important;
+ text-decoration: underline !important;
+}
+.btn.btn-noop {
+ // We have to use !important unfortunately because the default button style uses it...
+ background: transparent !important;
+ color: @theme-color-text !important;
+ pointer-events: none;
+ cursor: not-allowed;
+}
.top_bar_sites_selector {
.sites_autocomplete .custom_select {
diff --git a/plugins/Morpheus/stylesheets/ui/_navs.less b/plugins/Morpheus/stylesheets/ui/_navs.less
new file mode 100644
index 0000000000..e274dde0e1
--- /dev/null
+++ b/plugins/Morpheus/stylesheets/ui/_navs.less
@@ -0,0 +1,59 @@
+// Most of this is Bootstrap code and can be removed when switching to Bootstrap
+
+.nav {
+ margin-bottom: 20px;
+ padding-left: 0; // Override default ul/ol
+ list-style: none;
+
+ > li {
+ position: relative;
+ display: block;
+
+ > a {
+ position: relative;
+ display: block;
+ padding: 11px 70px;
+ text-decoration: none;
+ }
+ }
+}
+// .clearfix
+.nav:before, .nav:after {
+ display: table;
+ content: " ";
+}
+.nav:after {
+ clear: both;
+}
+
+.nav-pills {
+ > li {
+ float: left;
+ border: 1px solid @color-silver-l80;
+ border-left: 0;
+
+ &:first-child {
+ border-radius: 3px 0 0 3px;
+ border: 1px solid @color-silver-l80;
+ }
+ &:last-child {
+ border-radius: 0 3px 3px 0;
+ }
+
+ > a {
+ color: @theme-color-link;
+ background-color: @color-silver-l95;
+
+ &:hover,
+ &:focus {
+ background-color: @color-silver-l85;
+ }
+ }
+
+ &.active > a {
+ color: @theme-color-text;
+ background-color: @theme-color-background-base;
+ cursor: default;
+ }
+ }
+}
diff --git a/plugins/Morpheus/stylesheets/ui/_panels.less b/plugins/Morpheus/stylesheets/ui/_panels.less
new file mode 100644
index 0000000000..722362cfc6
--- /dev/null
+++ b/plugins/Morpheus/stylesheets/ui/_panels.less
@@ -0,0 +1,73 @@
+// Most of this is Bootstrap code and can be removed when switching to Bootstrap
+
+.panel {
+ margin-bottom: 20px;
+ background-color: @color-silver-l95;
+ border: 1px solid @color-silver-l80;
+ .border-radius(3px);
+ box-shadow: 0 1px 1px rgba(0,0,0,.05);
+}
+
+.panel-body {
+ padding: 15px;
+}
+// .clearfix
+.panel-body:before, .panel-body:after {
+ display: table;
+ content: " ";
+}
+.panel-body:after {
+ clear: both;
+}
+
+.panel-heading {
+ padding: 10px 15px;
+ background-color: @theme-color-background-base;
+ border-bottom: 1px solid @color-silver-l80;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+ position: relative;
+
+ // Within heading, strip any `h*` tag of its default margins for spacing.
+ .panel-title {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-size: 16px;
+ line-height: inherit;
+ color: inherit;
+ text-overflow: ellipsis;
+ overflow: hidden;
+
+ &.panel-title-block {
+ height: 20px;
+ > a {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ padding-top: 10px;
+ }
+ }
+
+ > a,
+ > small,
+ > .small,
+ > small > a,
+ > .small > a {
+ color: inherit;
+ text-decoration: none;
+ &:focus, &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+}
+
+.panel-footer {
+ padding: 10px 15px;
+ background-color: @theme-color-background-base;
+ border-top: 1px solid @color-silver-l80;
+ border-bottom-left-radius: 3px;
+ border-bottom-right-radius: 3px;
+}