Merge pull request #6739 from nextcloud/apps-management-updates
Add Updates section to app management
This commit is contained in:
commit
0def38b50e
|
@ -493,16 +493,23 @@ img, object, video, button, textarea, input, select {
|
|||
.icon-category-installed {
|
||||
background-image: url('../img/actions/user.svg?v=1');
|
||||
}
|
||||
|
||||
.icon-category-enabled {
|
||||
background-image: url('../img/actions/checkmark.svg?v=1');
|
||||
}
|
||||
|
||||
.icon-category-disabled {
|
||||
background-image: url('../img/actions/close.svg?v=1');
|
||||
}
|
||||
|
||||
.icon-category-app-bundles {
|
||||
background-image: url('../img/categories/bundles.svg?v=1');
|
||||
}
|
||||
|
||||
.icon-category-updates {
|
||||
background-image: url('../img/actions/download.svg?v=1');
|
||||
}
|
||||
|
||||
.icon-category-files {
|
||||
background-image: url('../img/categories/files.svg?v=1');
|
||||
}
|
||||
|
|
|
@ -768,6 +768,8 @@ class OC_App {
|
|||
}
|
||||
}
|
||||
|
||||
$apps = array_unique($apps);
|
||||
|
||||
return $apps;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ class AppSettingsController extends Controller {
|
|||
const CAT_DISABLED = 1;
|
||||
const CAT_ALL_INSTALLED = 2;
|
||||
const CAT_APP_BUNDLES = 3;
|
||||
const CAT_UPDATES = 4;
|
||||
|
||||
/** @var \OCP\IL10N */
|
||||
private $l10n;
|
||||
|
@ -130,8 +131,10 @@ class AppSettingsController extends Controller {
|
|||
private function getAllCategories() {
|
||||
$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
|
||||
|
||||
$updateCount = count($this->getAppsWithUpdates());
|
||||
$formattedCategories = [
|
||||
['id' => self::CAT_ALL_INSTALLED, 'ident' => 'installed', 'displayName' => (string)$this->l10n->t('Your apps')],
|
||||
['id' => self::CAT_UPDATES, 'ident' => 'updates', 'displayName' => (string)$this->l10n->t('Updates'), 'counter' => $updateCount],
|
||||
['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')],
|
||||
['id' => self::CAT_APP_BUNDLES, 'ident' => 'app-bundles', 'displayName' => (string)$this->l10n->t('App bundles')],
|
||||
|
@ -273,6 +276,28 @@ class AppSettingsController extends Controller {
|
|||
return $formattedApps;
|
||||
}
|
||||
|
||||
private function getAppsWithUpdates() {
|
||||
$appClass = new \OC_App();
|
||||
$apps = $appClass->listAllApps();
|
||||
foreach($apps as $key => $app) {
|
||||
$newVersion = \OC\Installer::isUpdateAvailable($app['id'], $this->appFetcher);
|
||||
if($newVersion !== false) {
|
||||
$apps[$key]['update'] = $newVersion;
|
||||
} else {
|
||||
unset($apps[$key]);
|
||||
}
|
||||
}
|
||||
usort($apps, function ($a, $b) {
|
||||
$a = (string)$a['name'];
|
||||
$b = (string)$b['name'];
|
||||
if ($a === $b) {
|
||||
return 0;
|
||||
}
|
||||
return ($a < $b) ? -1 : 1;
|
||||
});
|
||||
return $apps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available apps in a category
|
||||
*
|
||||
|
@ -301,6 +326,10 @@ class AppSettingsController extends Controller {
|
|||
return ($a < $b) ? -1 : 1;
|
||||
});
|
||||
break;
|
||||
// updates
|
||||
case 'updates':
|
||||
$apps = $this->getAppsWithUpdates();
|
||||
break;
|
||||
// enabled apps
|
||||
case 'enabled':
|
||||
$apps = $appClass->listAllApps();
|
||||
|
|
|
@ -58,8 +58,15 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
type:'GET',
|
||||
success:function (jsondata) {
|
||||
var html = template(jsondata);
|
||||
var updateCategory = $.grep(jsondata, function(element, index) {
|
||||
return element.ident === 'updates'
|
||||
});
|
||||
$('#apps-categories').html(html);
|
||||
$('#app-category-' + OC.Settings.Apps.State.currentCategory).addClass('active');
|
||||
if (updateCategory.length === 1) {
|
||||
OC.Settings.Apps.State.availableUpdates = updateCategory[0].counter;
|
||||
OC.Settings.Apps.refreshUpdateCounter();
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
$('#app-navigation').removeClass('icon-loading');
|
||||
|
@ -84,7 +91,6 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
$('#app-category-' + OC.Settings.Apps.State.currentCategory).removeClass('active');
|
||||
$('#app-category-' + categoryId).addClass('active');
|
||||
OC.Settings.Apps.State.currentCategory = categoryId;
|
||||
OC.Settings.Apps.State.availableUpdates = 0;
|
||||
|
||||
this._loadCategoryCall = $.ajax(OC.generateUrl('settings/apps/list?category={categoryId}', {
|
||||
categoryId: categoryId
|
||||
|
@ -99,7 +105,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
return _.extend({level: 0}, app);
|
||||
});
|
||||
var source;
|
||||
if (categoryId === 'enabled' || categoryId === 'disabled' || categoryId === 'installed' || categoryId === 'app-bundles') {
|
||||
if (categoryId === 'enabled' || categoryId === 'updates' || categoryId === 'disabled' || categoryId === 'installed' || categoryId === 'app-bundles') {
|
||||
source = $("#app-template-installed").html();
|
||||
$('#apps-list').addClass('installed');
|
||||
} else {
|
||||
|
@ -122,6 +128,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
}
|
||||
|
||||
var firstExperimental = false;
|
||||
var hasNewUpdates = false;
|
||||
_.each(appList, function(app) {
|
||||
if(app.level === 0 && firstExperimental === false) {
|
||||
firstExperimental = true;
|
||||
|
@ -131,19 +138,28 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
}
|
||||
|
||||
if (app.update) {
|
||||
hasNewUpdates = true;
|
||||
var $update = $('#app-' + app.id + ' .update');
|
||||
$update.removeClass('hidden');
|
||||
$update.val(t('settings', 'Update to %s').replace(/%s/g, app.update));
|
||||
OC.Settings.Apps.State.availableUpdates++;
|
||||
}
|
||||
});
|
||||
|
||||
if (OC.Settings.Apps.State.availableUpdates > 0) {
|
||||
OC.Settings.Apps.State.$updateNotification = OC.Notification.show(n('settings', 'You have %n app update pending', 'You have %n app updates pending', OC.Settings.Apps.State.availableUpdates));
|
||||
// reload updates if a list with new updates is loaded
|
||||
if (hasNewUpdates) {
|
||||
OC.Settings.Apps.reloadUpdates();
|
||||
} else {
|
||||
// hide update category after all updates are installed
|
||||
// and the user is switching away from the empty updates view
|
||||
OC.Settings.Apps.refreshUpdateCounter();
|
||||
}
|
||||
} else {
|
||||
$('#apps-list').addClass('hidden');
|
||||
$('#apps-list-empty').removeClass('hidden').find('h2').text(t('settings', 'No apps found for your version'));
|
||||
if (categoryId === 'updates') {
|
||||
OC.Settings.Apps.showEmptyUpdates();
|
||||
} else {
|
||||
$('#apps-list').addClass('hidden');
|
||||
$('#apps-list-empty').removeClass('hidden').find('h2').text(t('settings', 'No apps found for your version'));
|
||||
$('#app-list-empty-icon').addClass('icon-search').removeClass('icon-download');
|
||||
}
|
||||
}
|
||||
|
||||
$('.enable.needs-download').tooltip({
|
||||
|
@ -517,6 +533,12 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
}
|
||||
},
|
||||
|
||||
showEmptyUpdates: function() {
|
||||
$('#apps-list').addClass('hidden');
|
||||
$('#apps-list-empty').removeClass('hidden').find('h2').text(t('settings', 'No app updates available'));
|
||||
$('#app-list-empty-icon').removeClass('icon-search').addClass('icon-download');
|
||||
},
|
||||
|
||||
updateApp:function(appId, element) {
|
||||
var oldButtonText = element.val();
|
||||
element.val(t('settings','Updating....'));
|
||||
|
@ -539,13 +561,14 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
var $version = $('#app-' + appId + ' .app-version');
|
||||
$version.text(OC.Settings.Apps.State.apps[appId]['update']);
|
||||
|
||||
if (OC.Settings.Apps.State.$updateNotification) {
|
||||
OC.Notification.hide(OC.Settings.Apps.State.$updateNotification);
|
||||
}
|
||||
|
||||
OC.Settings.Apps.State.availableUpdates--;
|
||||
if (OC.Settings.Apps.State.availableUpdates > 0) {
|
||||
OC.Settings.Apps.State.$updateNotification = OC.Notification.show(n('settings', 'You have %n app update pending', 'You have %n app updates pending', OC.Settings.Apps.State.availableUpdates));
|
||||
OC.Settings.Apps.refreshUpdateCounter();
|
||||
|
||||
if (OC.Settings.Apps.State.currentCategory === 'updates') {
|
||||
$('#app-' + appId).remove();
|
||||
if (OC.Settings.Apps.State.availableUpdates === 0) {
|
||||
OC.Settings.Apps.showEmptyUpdates();
|
||||
}
|
||||
}
|
||||
}
|
||||
},'json');
|
||||
|
@ -656,6 +679,33 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
});
|
||||
},
|
||||
|
||||
reloadUpdates: function() {
|
||||
if (this._loadUpdatesCall) {
|
||||
this._loadUpdatesCall.abort();
|
||||
}
|
||||
this._loadUpdatesCall = $.ajax(OC.generateUrl('settings/apps/list?category=updates'), {
|
||||
type:'GET',
|
||||
success: function (apps) {
|
||||
OC.Settings.Apps.State.availableUpdates = apps.apps.length;
|
||||
OC.Settings.Apps.refreshUpdateCounter();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
refreshUpdateCounter: function() {
|
||||
var $appCategoryUpdates = $('#app-category-updates');
|
||||
var $updateCount = $appCategoryUpdates.find('.app-navigation-entry-utils-counter');
|
||||
if (OC.Settings.Apps.State.availableUpdates > 0) {
|
||||
$updateCount.html(OC.Settings.Apps.State.availableUpdates);
|
||||
$appCategoryUpdates.show();
|
||||
} else {
|
||||
$updateCount.empty();
|
||||
if (OC.Settings.Apps.State.currentCategory !== 'updates') {
|
||||
$appCategoryUpdates.hide();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
showErrorMessage: function(appId, message) {
|
||||
$('div#app-'+appId+' .warning')
|
||||
.show()
|
||||
|
@ -703,6 +753,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
filter: function(query) {
|
||||
var $appList = $('#apps-list'),
|
||||
$emptyList = $('#apps-list-empty');
|
||||
$('#app-list-empty-icon').addClass('icon-search').removeClass('icon-download');
|
||||
$appList.removeClass('hidden');
|
||||
$appList.find('.section').removeClass('hidden');
|
||||
$emptyList.addClass('hidden');
|
||||
|
|
|
@ -19,6 +19,11 @@ script(
|
|||
{{#each this}}
|
||||
<li id="app-category-{{ident}}" data-category-id="{{ident}}" tabindex="0">
|
||||
<a href="#" class="icon-category-{{ident}}">{{displayName}}</a>
|
||||
<div class="app-navigation-entry-utils">
|
||||
<ul>
|
||||
<li class="app-navigation-entry-utils-counter">{{ counter }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
|
||||
|
@ -65,9 +70,6 @@ script(
|
|||
</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}}
|
||||
|
@ -206,7 +208,7 @@ script(
|
|||
</svg>
|
||||
<div id="apps-list"></div>
|
||||
<div id="apps-list-empty" class="hidden emptycontent emptycontent-search">
|
||||
<div class="icon-search"></div>
|
||||
<div id="app-list-empty-icon" class="icon-search"></div>
|
||||
<h2><?php p($l->t('No apps found for your version')) ?></h2>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -101,6 +101,12 @@ class AppSettingsControllerTest extends TestCase {
|
|||
'ident' => 'installed',
|
||||
'displayName' => 'Your apps',
|
||||
],
|
||||
[
|
||||
'id' => 4,
|
||||
'ident' => 'updates',
|
||||
'displayName' => 'Updates',
|
||||
'counter' => 0,
|
||||
],
|
||||
[
|
||||
'id' => 0,
|
||||
'ident' => 'enabled',
|
||||
|
|
Loading…
Reference in New Issue