summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/img/places/default-app-icon.svg (renamed from core/img/default-app-icon.svg)0
-rw-r--r--settings/Controller/AppSettingsController.php26
-rw-r--r--settings/css/settings.css89
-rw-r--r--settings/js/apps.js63
-rw-r--r--settings/templates/apps.php68
-rw-r--r--tests/Settings/Controller/AppSettingsControllerTest.php13
6 files changed, 205 insertions, 54 deletions
diff --git a/core/img/default-app-icon.svg b/core/img/places/default-app-icon.svg
index 7ef7f086921..7ef7f086921 100644
--- a/core/img/default-app-icon.svg
+++ b/core/img/places/default-app-icon.svg
diff --git a/settings/Controller/AppSettingsController.php b/settings/Controller/AppSettingsController.php
index 6a5b5210c0f..7be6c2bf562 100644
--- a/settings/Controller/AppSettingsController.php
+++ b/settings/Controller/AppSettingsController.php
@@ -49,6 +49,7 @@ use OCP\L10N\IFactory;
class AppSettingsController extends Controller {
const CAT_ENABLED = 0;
const CAT_DISABLED = 1;
+ const CAT_ALL_INSTALLED = 2;
/** @var \OCP\IL10N */
private $l10n;
@@ -103,7 +104,7 @@ class AppSettingsController extends Controller {
*/
public function viewApps($category = '') {
if ($category === '') {
- $category = 'enabled';
+ $category = 'installed';
}
$params = [];
@@ -128,8 +129,9 @@ class AppSettingsController extends Controller {
$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
$formattedCategories = [
- ['id' => self::CAT_ENABLED, 'ident' => 'enabled', 'displayName' => (string)$this->l10n->t('Enabled')],
- ['id' => self::CAT_DISABLED, 'ident' => 'disabled', 'displayName' => (string)$this->l10n->t('Not enabled')],
+ ['id' => self::CAT_ALL_INSTALLED, 'ident' => 'installed', 'displayName' => (string)$this->l10n->t('Your apps')],
+ ['id' => self::CAT_ENABLED, 'ident' => 'enabled', 'displayName' => (string)$this->l10n->t('Enabled apps')],
+ ['id' => self::CAT_DISABLED, 'ident' => 'disabled', 'displayName' => (string)$this->l10n->t('Disabled apps')],
];
$categories = $this->categoryFetcher->get();
foreach($categories as $category) {
@@ -270,6 +272,24 @@ class AppSettingsController extends Controller {
switch ($category) {
// installed apps
+ case 'installed':
+ $apps = $appClass->listAllApps();
+
+ foreach($apps as $key => $app) {
+ $newVersion = \OC\Installer::isUpdateAvailable($app['id'], $this->appFetcher);
+ $apps[$key]['update'] = $newVersion;
+ }
+
+ usort($apps, function ($a, $b) {
+ $a = (string)$a['name'];
+ $b = (string)$b['name'];
+ if ($a === $b) {
+ return 0;
+ }
+ return ($a < $b) ? -1 : 1;
+ });
+ break;
+ // enabled apps
case 'enabled':
$apps = $appClass->listAllApps();
$apps = array_filter($apps, function ($app) {
diff --git a/settings/css/settings.css b/settings/css/settings.css
index 5ca62248c09..a046148dcc4 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -525,6 +525,10 @@ input.userFilter {width: 200px;}
width: 0;
}
+#app-category-disabled {
+ margin-bottom: 20px;
+}
+
.appinfo { margin: 1em 40px; }
#app-navigation .appwarning {
background: #fcc;
@@ -541,9 +545,7 @@ span.version {
#app-navigation .app-external,
.app-version {
- -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
- filter: alpha(opacity=50);
- opacity: .5;
+ color: rgba(85,85,85,.5);
}
.app-level {
@@ -566,6 +568,7 @@ span.version {
.app-score {
position: relative;
top: 4px;
+ opacity: .5;
}
#apps-list {
@@ -638,16 +641,22 @@ span.version {
}
}
-@media (max-width: 600px), (min-width: 771px) and (max-width: 900px) {
- #apps-list .section {
- width: 100%;
- box-sizing: border-box;
+/* hide app version and level on narrower screens */
+@media only screen and (max-width: 768px) {
+ #apps-list.installed .app-version,
+ #apps-list.installed .app-level {
+ display: none !important;
+ }
+}
+@media only screen and (max-width: 700px) {
+ #apps-list.installed .app-groups {
+ display: none !important;
}
}
.section h2.app-name {
- margin-bottom: 8px;
- display: inline;
+ display: block;
+ margin: 8px 0;
}
form.section {
position: relative;
@@ -661,11 +670,10 @@ form.section {
position: relative;
}
.app-image {
- float: left;
- padding-right: 10px;
- width: 80px;
- height: 80px;
- opacity: 0.8;
+ position: relative;
+ height: 150px;
+ opacity: 1;
+ overflow: hidden;
}
.app-name,
.app-version,
@@ -725,6 +733,59 @@ form.section {
margin-bottom: 1em;
}
+#apps-list.installed {
+ display: table;
+ width: 100%;
+ height: auto;
+}
+
+#apps-list.installed .section {
+ display: table-row;
+ padding: 0;
+ margin: 0;
+}
+
+#apps-list.installed .section > *{
+ display: table-cell;
+ height: initial;
+ vertical-align: middle;
+ float: none;
+ border-bottom: 1px solid #eee;
+ padding: 6px;
+ box-sizing: border-box;
+}
+
+#apps-list.installed .app-image {
+ width: 44px;
+ text-align: right;
+}
+
+#apps-list.installed .app-image-icon svg {
+ margin-top: 5px;
+ width: 20px;
+ height: 20px;
+ opacity: .5;
+}
+
+#apps-list:not(.installed) .app-image-icon svg {
+ position: absolute;
+ bottom: 43px; /* position halfway vertically */
+ width: 64px;
+ height: 64px;
+ opacity: .1;
+}
+
+.installed .actions {
+ text-align: right;
+}
+
+#apps-list.installed .groups-enable {
+ margin-top: 0;
+}
+
+#apps-list.installed .groups-enable label {
+ margin-right: 3px;
+}
/* LOG */
#log {
diff --git a/settings/js/apps.js b/settings/js/apps.js
index 9a3ff09337d..6da8c395ecb 100644
--- a/settings/js/apps.js
+++ b/settings/js/apps.js
@@ -40,8 +40,9 @@ OC.Settings.Apps = OC.Settings.Apps || {
}
var categories = [
- {displayName: t('settings', 'Enabled'), ident: 'enabled', id: '0'},
- {displayName: t('settings', 'Not enabled'), ident: 'disabled', id: '1'}
+ {displayName: t('settings', 'Enabled apps'), ident: 'enabled', id: '0'},
+ {displayName: t('settings', 'Disabled apps'), ident: 'disabled', id: '1'},
+ {displayName: t('settings', 'Your apps'), ident: 'installed', id: '2'}
];
var source = $("#categories-template").html();
@@ -73,8 +74,9 @@ OC.Settings.Apps = OC.Settings.Apps || {
if (this._loadCategoryCall) {
this._loadCategoryCall.abort();
}
+
+ $('#app-content').addClass('icon-loading');
$('#apps-list')
- .addClass('icon-loading')
.removeClass('hidden')
.html('');
$('#apps-list-empty').addClass('hidden');
@@ -94,16 +96,27 @@ OC.Settings.Apps = OC.Settings.Apps || {
// default values for missing fields
return _.extend({level: 0}, app);
});
- var source = $("#app-template").html();
+ var source
+ if (categoryId === 'enabled' || categoryId === 'disabled' || categoryId === 'installed') {
+ source = $("#app-template-installed").html();
+ $('#apps-list').addClass('installed');
+ } else {
+ source = $("#app-template").html();
+ $('#apps-list').removeClass('installed');
+ }
var template = Handlebars.compile(source);
if (appList.length) {
appList.sort(function(a,b) {
- var levelDiff = b.level - a.level;
- if (levelDiff === 0) {
- return OC.Util.naturalSortCompare(a.name, b.name);
+ if (a.active !== b.active) {
+ return (a.active ? -1 : 1)
+ } else {
+ var levelDiff = b.level - a.level;
+ if (levelDiff === 0) {
+ return OC.Util.naturalSortCompare(a.name, b.name);
+ }
+ return levelDiff;
}
- return levelDiff;
});
var firstExperimental = false;
@@ -154,7 +167,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
});
},
complete: function() {
- $('#apps-list').removeClass('icon-loading');
+ $('#app-content').removeClass('icon-loading');
}
});
},
@@ -170,7 +183,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
app.firstExperimental = firstExperimental;
if (!app.preview) {
- app.preview = OC.imagePath('core', 'default-app-icon');
+ app.preview = OC.imagePath('core', 'places/default-app-icon');
app.previewAsIcon = true;
}
@@ -222,9 +235,16 @@ OC.Settings.Apps = OC.Settings.Apps || {
currentImage.src = app.preview;
currentImage.onload = function() {
- page.find('.app-image')
- .append(OC.Settings.Apps.imageUrl(app.preview, app.fromAppStore))
- .fadeIn();
+ /* Trigger color inversion for placeholder image too */
+ if(app.previewAsIcon) {
+ page.find('.app-image')
+ .append(OC.Settings.Apps.imageUrl(app.preview, false))
+ .removeClass('icon-loading');
+ } else {
+ page.find('.app-image')
+ .append(OC.Settings.Apps.imageUrl(app.preview, app.fromAppStore))
+ .removeClass('icon-loading');
+ }
};
}
@@ -257,12 +277,13 @@ OC.Settings.Apps = OC.Settings.Apps || {
*/
imageUrl : function (url, appfromstore) {
- var img = '<svg width="72" height="72" viewBox="0 0 72 72">';
-
+ var img;
if (appfromstore) {
+ img = '<svg viewBox="0 0 72 72">';
img += '<image x="0" y="0" width="72" height="72" preserveAspectRatio="xMinYMin meet" xlink:href="' + url + '" class="app-icon" /></svg>';
} else {
- img += '<image x="0" y="0" width="72" height="72" preserveAspectRatio="xMinYMin meet" filter="url(#invertIcon)" xlink:href="' + url + '?v=' + oc_config.version + '" class="app-icon"></image></svg>';
+ img = '<svg width="32" height="32" viewBox="0 0 32 32">';
+ img += '<image x="0" y="0" width="32" height="32" preserveAspectRatio="xMinYMin meet" filter="url(#invertIcon)" xlink:href="' + url + '?v=' + oc_config.version + '" class="app-icon"></image></svg>';
}
return img;
},
@@ -435,15 +456,15 @@ OC.Settings.Apps = OC.Settings.Apps || {
}
OC.Settings.Apps.hideErrorMessage(appId);
- element.val(t('settings','Uninstalling …'));
+ element.val(t('settings','Removing …'));
$.post(OC.filePath('settings','ajax','uninstallapp.php'),{appid:appId},function(result) {
if(!result || result.status !== 'success') {
- OC.Settings.Apps.showErrorMessage(appId, t('settings','Error while uninstalling app'));
- element.val(t('settings','Uninstall'));
+ OC.Settings.Apps.showErrorMessage(appId, t('settings','Error while removing app'));
+ element.val(t('settings','Remove'));
} else {
OC.Settings.Apps.rebuildNavigation();
- element.parent().fadeOut(function() {
- element.remove();
+ element.parents('#apps-list > .section').fadeOut(function() {
+ this.remove();
});
}
},'json');
diff --git a/settings/templates/apps.php b/settings/templates/apps.php
index 0adf5dfcc6f..310513722cf 100644
--- a/settings/templates/apps.php
+++ b/settings/templates/apps.php
@@ -29,10 +29,54 @@ script(
<?php endif; ?>
</script>
+
+<script id="app-template-installed" type="text/x-handlebars">
+<div class="section" id="app-{{id}}">
+ <div class="app-image app-image-icon"></div>
+ <div class="app-name">
+ {{#if detailpage}}
+ <a href="{{detailpage}}" target="_blank" rel="noreferrer">{{name}}</a>
+ {{else}}
+ {{name}}
+ {{/if}}
+ </div>
+ <div class="app-version">{{version}}</div>
+ <div class="app-level">
+ {{{level}}}{{#unless internal}}<a href="https://apps.nextcloud.com/apps/{{id}}"><?php p($l->t('View in store'));?> ↗</a>{{/unless}}
+ </div>
+
+ <div class="app-groups">
+ {{#if active}}
+ <div class="groups-enable">
+ <input type="checkbox" class="groups-enable__checkbox checkbox" id="groups_enable-{{id}}"/>
+ <label for="groups_enable-{{id}}"><?php p($l->t('Limit to groups')); ?></label>
+ <input type="hidden" id="group_select" title="<?php p($l->t('All')); ?>">
+ </div>
+ {{/if}}
+ </div>
+
+ <div class="actions">
+ <div class="app-dependencies update hidden">
+ <p><?php p($l->t('This app has an update available.')); ?></p>
+ </div>
+ <div class="warning hidden"></div>
+ <input class="update hidden" type="submit" value="<?php p($l->t('Update to %s', array('{{update}}'))); ?>" data-appid="{{id}}" />
+ {{#if canUnInstall}}
+ <input class="uninstall" type="submit" value="<?php p($l->t('Remove')); ?>" data-appid="{{id}}" />
+ {{/if}}
+ {{#if active}}
+ <input class="enable" type="submit" data-appid="{{id}}" data-active="true" value="<?php p($l->t("Disable"));?>"/>
+ {{else}}
+ <input class="enable{{#if needsDownload}} needs-download{{/if}}" type="submit" data-appid="{{id}}" data-active="false" {{#unless canInstall}}disabled="disabled"{{/unless}} value="<?php p($l->t("Enable"));?>"/>
+ {{/if}}
+ </div>
+</div>
+</script>
+
<script id="app-template" type="text/x-handlebars">
<div class="section" id="app-{{id}}">
{{#if preview}}
- <div class="app-image{{#if previewAsIcon}} app-image-icon{{/if}} hidden">
+ <div class="app-image{{#if previewAsIcon}} app-image-icon{{/if}} icon-loading">
</div>
{{/if}}
<h2 class="app-name">
@@ -42,14 +86,6 @@ script(
{{name}}
{{/if}}
</h2>
- <div class="app-version"> {{version}}</div>
- {{#if profilepage}}<a href="{{profilepage}}" target="_blank" rel="noreferrer">{{/if}}
- <div class="app-author"><?php p($l->t('by %s', ['{{author}}']));?>
- {{#if licence}}
- (<?php p($l->t('%s-licensed', ['{{licence}}'])); ?>)
- {{/if}}
- </div>
- {{#if profilepage}}</a>{{/if}}
<div class="app-level">
{{{level}}}
</div>
@@ -59,6 +95,14 @@ script(
<div class="app-detailpage"></div>
<div class="app-description-container hidden">
+ <div class="app-version">{{version}}</div>
+ {{#if profilepage}}<a href="{{profilepage}}" target="_blank" rel="noreferrer">{{/if}}
+ <div class="app-author"><?php p($l->t('by %s', ['{{author}}']));?>
+ {{#if licence}}
+ (<?php p($l->t('%s-licensed', ['{{licence}}'])); ?>)
+ {{/if}}
+ </div>
+ {{#if profilepage}}</a>{{/if}}
<div class="app-description">{{{description}}}</div>
<!--<div class="app-changed">{{changed}}</div>-->
{{#if documentation}}
@@ -134,7 +178,7 @@ script(
<input class="enable{{#if needsDownload}} needs-download{{/if}}" type="submit" data-appid="{{id}}" data-active="false" {{#unless canInstall}}disabled="disabled"{{/unless}} value="<?php p($l->t("Enable"));?>"/>
{{/if}}
{{#if canUnInstall}}
- <input class="uninstall" type="submit" value="<?php p($l->t('Uninstall app')); ?>" data-appid="{{id}}" />
+ <input class="uninstall" type="submit" value="<?php p($l->t('Remove')); ?>" data-appid="{{id}}" />
{{/if}}
<div class="warning hidden"></div>
@@ -147,11 +191,11 @@ script(
</ul>
</div>
-<div id="app-content">
+<div id="app-content" class="icon-loading">
<svg class="app-filter">
<defs><filter id="invertIcon"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"></feColorMatrix></filter></defs>
</svg>
- <div id="apps-list" class="icon-loading"></div>
+ <div id="apps-list"></div>
<div id="apps-list-empty" class="hidden emptycontent emptycontent-search">
<div class="icon-search"></div>
<h2><?php p($l->t('No apps found for your version')) ?></h2>
diff --git a/tests/Settings/Controller/AppSettingsControllerTest.php b/tests/Settings/Controller/AppSettingsControllerTest.php
index a3e4a6fd828..14dc33ca191 100644
--- a/tests/Settings/Controller/AppSettingsControllerTest.php
+++ b/tests/Settings/Controller/AppSettingsControllerTest.php
@@ -92,14 +92,19 @@ class AppSettingsControllerTest extends TestCase {
public function testListCategories() {
$expected = new JSONResponse([
[
+ 'id' => 2,
+ 'ident' => 'installed',
+ 'displayName' => 'Your apps',
+ ],
+ [
'id' => 0,
'ident' => 'enabled',
- 'displayName' => 'Enabled',
+ 'displayName' => 'Enabled apps',
],
[
'id' => 1,
'ident' => 'disabled',
- 'displayName' => 'Not enabled',
+ 'displayName' => 'Disabled apps',
],
[
'id' => 'auth',
@@ -175,7 +180,7 @@ class AppSettingsControllerTest extends TestCase {
$policy = new ContentSecurityPolicy();
$policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com');
- $expected = new TemplateResponse('settings', 'apps', ['category' => 'enabled', 'appstoreEnabled' => true], 'user');
+ $expected = new TemplateResponse('settings', 'apps', ['category' => 'installed', 'appstoreEnabled' => true], 'user');
$expected->setContentSecurityPolicy($policy);
$this->assertEquals($expected, $this->appSettingsController->viewApps());
@@ -195,7 +200,7 @@ class AppSettingsControllerTest extends TestCase {
$policy = new ContentSecurityPolicy();
$policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com');
- $expected = new TemplateResponse('settings', 'apps', ['category' => 'enabled', 'appstoreEnabled' => false], 'user');
+ $expected = new TemplateResponse('settings', 'apps', ['category' => 'installed', 'appstoreEnabled' => false], 'user');
$expected->setContentSecurityPolicy($policy);
$this->assertEquals($expected, $this->appSettingsController->viewApps());