Merge pull request #19813 from owncloud/stay-on-apps-category-refresh
Improve settings/apps page
This commit is contained in:
commit
0a9150b2da
5 changed files with 207 additions and 35 deletions
|
@ -42,6 +42,8 @@ use OCP\IConfig;
|
|||
* @package OC\Settings\Controller
|
||||
*/
|
||||
class AppSettingsController extends Controller {
|
||||
const CAT_ENABLED = 0;
|
||||
const CAT_DISABLED = 1;
|
||||
|
||||
/** @var \OCP\IL10N */
|
||||
private $l10n;
|
||||
|
@ -94,13 +96,42 @@ class AppSettingsController extends Controller {
|
|||
return new DataResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $category
|
||||
* @return int
|
||||
*/
|
||||
protected function getCategory($category) {
|
||||
if (is_string($category)) {
|
||||
foreach ($this->listCategories() as $cat) {
|
||||
if (isset($cat['ident']) && $cat['ident'] === $category) {
|
||||
$category = (int) $cat['id'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find the category, falling back to enabled
|
||||
if (is_string($category)) {
|
||||
$category = self::CAT_ENABLED;
|
||||
}
|
||||
}
|
||||
return (int) $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoCSRFRequired
|
||||
* @param string $category
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function viewApps() {
|
||||
public function viewApps($category = '') {
|
||||
$categoryId = $this->getCategory($category);
|
||||
if ($categoryId === self::CAT_ENABLED) {
|
||||
// Do not use an arbitrary input string, because we put the category in html
|
||||
$category = 'enabled';
|
||||
}
|
||||
|
||||
$params = [];
|
||||
$params['experimentalEnabled'] = $this->config->getSystemValue('appstore.experimental.enabled', false);
|
||||
$params['category'] = $category;
|
||||
$this->navigationManager->setActiveEntry('core_apps');
|
||||
|
||||
$templateResponse = new TemplateResponse($this->appName, 'apps', $params, 'user');
|
||||
|
@ -121,8 +152,8 @@ class AppSettingsController extends Controller {
|
|||
return $this->cache->get('listCategories');
|
||||
}
|
||||
$categories = [
|
||||
['id' => 0, 'displayName' => (string)$this->l10n->t('Enabled')],
|
||||
['id' => 1, 'displayName' => (string)$this->l10n->t('Not enabled')],
|
||||
['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')],
|
||||
];
|
||||
|
||||
if($this->ocsClient->isAppStoreEnabled()) {
|
||||
|
@ -130,9 +161,12 @@ class AppSettingsController extends Controller {
|
|||
$ocs = $this->ocsClient->getCategories(\OC_Util::getVersion());
|
||||
if ($ocs) {
|
||||
foreach($ocs as $k => $v) {
|
||||
$name = str_replace('ownCloud ', '', $v);
|
||||
$ident = str_replace(' ', '-', urlencode(strtolower($name)));
|
||||
$categories[] = [
|
||||
'id' => $k,
|
||||
'displayName' => str_replace('ownCloud ', '', $v)
|
||||
'ident' => $ident,
|
||||
'displayName' => $name,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -146,12 +180,13 @@ class AppSettingsController extends Controller {
|
|||
/**
|
||||
* Get all available apps in a category
|
||||
*
|
||||
* @param int $category
|
||||
* @param string $category
|
||||
* @param bool $includeUpdateInfo Should we check whether there is an update
|
||||
* in the app store?
|
||||
* @return array
|
||||
*/
|
||||
public function listApps($category = 0, $includeUpdateInfo = true) {
|
||||
public function listApps($category = '', $includeUpdateInfo = true) {
|
||||
$category = $this->getCategory($category);
|
||||
$cacheName = 'listApps-' . $category . '-' . (int) $includeUpdateInfo;
|
||||
|
||||
if(!is_null($this->cache->get($cacheName))) {
|
||||
|
|
|
@ -40,7 +40,8 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
}
|
||||
|
||||
var categories = [
|
||||
{displayName: 'Enabled', id: '0'}
|
||||
{displayName: t('settings', 'Enabled'), ident: 'enabled', id: '0'},
|
||||
{displayName: t('settings', 'Not enabled'), ident: 'disabled', id: '1'}
|
||||
];
|
||||
|
||||
var source = $("#categories-template").html();
|
||||
|
@ -48,7 +49,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
var html = template(categories);
|
||||
$('#apps-categories').html(html);
|
||||
|
||||
OC.Settings.Apps.loadCategory(0);
|
||||
OC.Settings.Apps.loadCategory($('#app-navigation').attr('data-category'));
|
||||
|
||||
this._loadCategoriesCall = $.ajax(OC.generateUrl('settings/apps/categories'), {
|
||||
data:{},
|
||||
|
@ -115,7 +116,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
});
|
||||
} else {
|
||||
$('#apps-list').addClass('hidden');
|
||||
$('#apps-list-empty').removeClass('hidden');
|
||||
$('#apps-list-empty').removeClass('hidden').find('h2').text(t('settings', 'No apps found for your version'));
|
||||
}
|
||||
|
||||
$('.app-level .official').tipsy({fallback: t('settings', 'Official apps are developed by and within the ownCloud community. They offer functionality central to ownCloud and are ready for production use.')});
|
||||
|
@ -398,14 +399,14 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
.text('');
|
||||
},
|
||||
|
||||
showReloadMessage: function(appId) {
|
||||
showReloadMessage: function() {
|
||||
OC.dialogs.info(
|
||||
t(
|
||||
'settings',
|
||||
'The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds.'
|
||||
),
|
||||
t('settings','App update'),
|
||||
function (result) {
|
||||
function () {
|
||||
window.location.reload();
|
||||
},
|
||||
true
|
||||
|
@ -413,24 +414,74 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
},
|
||||
|
||||
filter: function(query) {
|
||||
query = query.toLowerCase();
|
||||
$('#apps-list').find('.section').addClass('hidden');
|
||||
var $appList = $('#apps-list'),
|
||||
$emptyList = $('#apps-list-empty');
|
||||
$appList.removeClass('hidden');
|
||||
$appList.find('.section').removeClass('hidden');
|
||||
$emptyList.addClass('hidden');
|
||||
|
||||
if (query === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
query = query.toLowerCase();
|
||||
$appList.find('.section').addClass('hidden');
|
||||
|
||||
// App Name
|
||||
var apps = _.filter(OC.Settings.Apps.State.apps, function (app) {
|
||||
return app.name.toLowerCase().indexOf(query) !== -1;
|
||||
});
|
||||
|
||||
// App Description
|
||||
apps = apps.concat(_.filter(OC.Settings.Apps.State.apps, function (app) {
|
||||
return app.description.toLowerCase().indexOf(query) !== -1;
|
||||
}));
|
||||
|
||||
// Author Name
|
||||
apps = apps.concat(_.filter(OC.Settings.Apps.State.apps, function (app) {
|
||||
return app.author.toLowerCase().indexOf(query) !== -1;
|
||||
}));
|
||||
|
||||
// App status
|
||||
if (t('settings', 'Official').toLowerCase().indexOf(query) !== -1) {
|
||||
apps = apps.concat(_.filter(OC.Settings.Apps.State.apps, function (app) {
|
||||
return app.level === 200;
|
||||
}));
|
||||
}
|
||||
if (t('settings', 'Approved').toLowerCase().indexOf(query) !== -1) {
|
||||
apps = apps.concat(_.filter(OC.Settings.Apps.State.apps, function (app) {
|
||||
return app.level === 100;
|
||||
}));
|
||||
}
|
||||
if (t('settings', 'Experimental').toLowerCase().indexOf(query) !== -1) {
|
||||
apps = apps.concat(_.filter(OC.Settings.Apps.State.apps, function (app) {
|
||||
return app.level !== 100 && app.level !== 200;
|
||||
}));
|
||||
}
|
||||
|
||||
apps = _.uniq(apps, function(app){return app.id;});
|
||||
|
||||
_.each(apps, function (app) {
|
||||
$('#app-' + app.id).removeClass('hidden');
|
||||
});
|
||||
if (apps.length === 0) {
|
||||
$appList.addClass('hidden');
|
||||
$emptyList.removeClass('hidden');
|
||||
$emptyList.removeClass('hidden').find('h2').text(t('settings', 'No apps found for "{query}"', {
|
||||
query: query
|
||||
}));
|
||||
} else {
|
||||
_.each(apps, function (app) {
|
||||
$('#app-' + app.id).removeClass('hidden');
|
||||
});
|
||||
|
||||
$('#searchresults').hide();
|
||||
$('#searchresults').hide();
|
||||
}
|
||||
},
|
||||
|
||||
_onPopState: function(params) {
|
||||
params = _.extend({
|
||||
category: 'enabled'
|
||||
}, params);
|
||||
|
||||
OC.Settings.Apps.loadCategory(params.category);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -439,10 +490,15 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
initialize: function($el) {
|
||||
OC.Plugins.register('OCA.Search', OC.Settings.Apps.Search);
|
||||
OC.Settings.Apps.loadCategories();
|
||||
OC.Util.History.addOnPopStateHandler(_.bind(this._onPopState, this));
|
||||
|
||||
$(document).on('click', 'ul#apps-categories li', function () {
|
||||
var categoryId = $(this).data('categoryId');
|
||||
OC.Settings.Apps.loadCategory(categoryId);
|
||||
OC.Util.History.pushState({
|
||||
category: categoryId
|
||||
});
|
||||
$('#searchbox').val('');
|
||||
});
|
||||
|
||||
$(document).on('click', '.app-description-toggle-show', function () {
|
||||
|
@ -508,7 +564,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
});
|
||||
|
||||
$(document).on('click', '#enable-experimental-apps', function () {
|
||||
var state = $(this).prop('checked')
|
||||
var state = $(this).prop('checked');
|
||||
$.ajax(OC.generateUrl('settings/apps/experimental'), {
|
||||
data: {state: state},
|
||||
type: 'POST',
|
||||
|
|
|
@ -24,7 +24,7 @@ script(
|
|||
?>
|
||||
<script id="categories-template" type="text/x-handlebars-template">
|
||||
{{#each this}}
|
||||
<li id="app-category-{{id}}" data-category-id="{{id}}" tabindex="0">
|
||||
<li id="app-category-{{ident}}" data-category-id="{{ident}}" tabindex="0">
|
||||
<a>{{displayName}}</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
|
@ -127,7 +127,7 @@ script(
|
|||
</div>
|
||||
</script>
|
||||
|
||||
<div id="app-navigation" class="icon-loading">
|
||||
<div id="app-navigation" class="icon-loading" data-category="<?php p($_['category']);?>">
|
||||
<ul id="apps-categories">
|
||||
|
||||
</ul>
|
||||
|
|
|
@ -24,6 +24,7 @@ describe('OC.Settings.Apps tests', function() {
|
|||
|
||||
beforeEach(function() {
|
||||
var $el = $('<div id="apps-list"></div>' +
|
||||
'<div id="apps-list-empty" class="hidden"></div>' +
|
||||
'<div id="app-template">' +
|
||||
// dummy template for testing
|
||||
'<div id="app-{{id}}" data-id="{{id}}" class="section">{{name}}</div>' +
|
||||
|
@ -59,22 +60,33 @@ describe('OC.Settings.Apps tests', function() {
|
|||
|
||||
beforeEach(function() {
|
||||
loadApps([
|
||||
{id: 'appone', name: 'App One', description: 'The first app'},
|
||||
{id: 'apptwo', name: 'App Two', description: 'The second app'},
|
||||
{id: 'appthree', name: 'App Three', description: 'Third app'},
|
||||
{id: 'somestuff', name: 'Some Stuff', description: 'whatever'}
|
||||
{id: 'appone', name: 'App One', description: 'The first app', author: 'author1', level: 200},
|
||||
{id: 'apptwo', name: 'App Two', description: 'The second app', author: 'author2', level: 100},
|
||||
{id: 'appthree', name: 'App Three', description: 'Third app', author: 'author3', level: 0},
|
||||
{id: 'somestuff', name: 'Some Stuff', description: 'whatever', author: 'author4', level: 0}
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not filter when no query passed', function() {
|
||||
Apps.filter('');
|
||||
expect(getResultsFromDom().length).toEqual(4);
|
||||
});
|
||||
it('returns no results when query does not match anything', function() {
|
||||
expect(getResultsFromDom().length).toEqual(4);
|
||||
expect($('#apps-list:not(.hidden)').length).toEqual(1);
|
||||
expect($('#apps-list-empty:not(.hidden)').length).toEqual(0);
|
||||
|
||||
Apps.filter('absurdity');
|
||||
expect(getResultsFromDom().length).toEqual(0);
|
||||
expect($('#apps-list:not(.hidden)').length).toEqual(0);
|
||||
expect($('#apps-list-empty:not(.hidden)').length).toEqual(1);
|
||||
|
||||
Apps.filter('');
|
||||
expect(getResultsFromDom().length).toEqual(4);
|
||||
expect($('#apps-list:not(.hidden)').length).toEqual(1);
|
||||
expect($('#apps-list-empty:not(.hidden)').length).toEqual(0);
|
||||
expect(getResultsFromDom().length).toEqual(4);
|
||||
});
|
||||
it('returns relevant results when query matches name', function() {
|
||||
expect($('#apps-list:not(.hidden)').length).toEqual(1);
|
||||
expect($('#apps-list-empty:not(.hidden)').length).toEqual(0);
|
||||
|
||||
var results;
|
||||
Apps.filter('app');
|
||||
results = getResultsFromDom();
|
||||
|
@ -82,6 +94,9 @@ describe('OC.Settings.Apps tests', function() {
|
|||
expect(results[0]).toEqual('appone');
|
||||
expect(results[1]).toEqual('apptwo');
|
||||
expect(results[2]).toEqual('appthree');
|
||||
|
||||
expect($('#apps-list:not(.hidden)').length).toEqual(1);
|
||||
expect($('#apps-list-empty:not(.hidden)').length).toEqual(0);
|
||||
});
|
||||
it('returns relevant result when query matches name', function() {
|
||||
var results;
|
||||
|
@ -97,6 +112,45 @@ describe('OC.Settings.Apps tests', function() {
|
|||
expect(results.length).toEqual(1);
|
||||
expect(results[0]).toEqual('somestuff');
|
||||
});
|
||||
it('returns relevant results when query matches author name', function() {
|
||||
var results;
|
||||
Apps.filter('author');
|
||||
results = getResultsFromDom();
|
||||
expect(results.length).toEqual(4);
|
||||
expect(results[0]).toEqual('appone');
|
||||
expect(results[1]).toEqual('apptwo');
|
||||
expect(results[2]).toEqual('appthree');
|
||||
expect(results[3]).toEqual('somestuff');
|
||||
});
|
||||
it('returns relevant result when query matches author name', function() {
|
||||
var results;
|
||||
Apps.filter('thor3');
|
||||
results = getResultsFromDom();
|
||||
expect(results.length).toEqual(1);
|
||||
expect(results[0]).toEqual('appthree');
|
||||
});
|
||||
it('returns relevant result when query matches level name', function() {
|
||||
var results;
|
||||
Apps.filter('Offic');
|
||||
results = getResultsFromDom();
|
||||
expect(results.length).toEqual(1);
|
||||
expect(results[0]).toEqual('appone');
|
||||
});
|
||||
it('returns relevant result when query matches level name', function() {
|
||||
var results;
|
||||
Apps.filter('Appro');
|
||||
results = getResultsFromDom();
|
||||
expect(results.length).toEqual(1);
|
||||
expect(results[0]).toEqual('apptwo');
|
||||
});
|
||||
it('returns relevant result when query matches level name', function() {
|
||||
var results;
|
||||
Apps.filter('Exper');
|
||||
results = getResultsFromDom();
|
||||
expect(results.length).toEqual(2);
|
||||
expect(results[0]).toEqual('appthree');
|
||||
expect(results[1]).toEqual('somestuff');
|
||||
});
|
||||
});
|
||||
|
||||
describe('loading categories', function() {
|
||||
|
|
|
@ -132,10 +132,12 @@ class AppSettingsControllerTest extends TestCase {
|
|||
$expected = [
|
||||
[
|
||||
'id' => 0,
|
||||
'ident' => 'enabled',
|
||||
'displayName' => 'Enabled',
|
||||
],
|
||||
[
|
||||
'id' => 1,
|
||||
'ident' => 'disabled',
|
||||
'displayName' => 'Not enabled',
|
||||
],
|
||||
];
|
||||
|
@ -157,27 +159,33 @@ class AppSettingsControllerTest extends TestCase {
|
|||
$expected = [
|
||||
[
|
||||
'id' => 0,
|
||||
'ident' => 'enabled',
|
||||
'displayName' => 'Enabled',
|
||||
],
|
||||
[
|
||||
'id' => 1,
|
||||
'ident' => 'disabled',
|
||||
'displayName' => 'Not enabled',
|
||||
],
|
||||
[
|
||||
'id' => 0,
|
||||
'ident' => 'tools',
|
||||
'displayName' => 'Tools',
|
||||
],
|
||||
[
|
||||
'id' => 1,
|
||||
'displayName' => 'Awesome Games',
|
||||
'ident' => 'games',
|
||||
'displayName' => 'Games',
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'displayName' => 'PIM',
|
||||
'ident' => 'productivity',
|
||||
'displayName' => 'Productivity',
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'displayName' => 'Papershop',
|
||||
'ident' => 'multimedia',
|
||||
'displayName' => 'Multimedia',
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -201,9 +209,9 @@ class AppSettingsControllerTest extends TestCase {
|
|||
->will($this->returnValue(
|
||||
[
|
||||
'ownCloud Tools',
|
||||
'Awesome Games',
|
||||
'ownCloud PIM',
|
||||
'Papershop',
|
||||
'Games',
|
||||
'ownCloud Productivity',
|
||||
'Multimedia',
|
||||
]
|
||||
));
|
||||
|
||||
|
@ -223,9 +231,28 @@ class AppSettingsControllerTest extends TestCase {
|
|||
$policy = new ContentSecurityPolicy();
|
||||
$policy->addAllowedImageDomain('https://apps.owncloud.com');
|
||||
|
||||
$expected = new TemplateResponse('settings', 'apps', ['experimentalEnabled' => false], 'user');
|
||||
$expected = new TemplateResponse('settings', 'apps', ['experimentalEnabled' => false, 'category' => 'enabled'], 'user');
|
||||
$expected->setContentSecurityPolicy($policy);
|
||||
|
||||
$this->assertEquals($expected, $this->appSettingsController->viewApps());
|
||||
}
|
||||
|
||||
public function testViewAppsNotEnabled() {
|
||||
$this->config
|
||||
->expects($this->once())
|
||||
->method('getSystemValue')
|
||||
->with('appstore.experimental.enabled', false);
|
||||
$this->navigationManager
|
||||
->expects($this->once())
|
||||
->method('setActiveEntry')
|
||||
->with('core_apps');
|
||||
|
||||
$policy = new ContentSecurityPolicy();
|
||||
$policy->addAllowedImageDomain('https://apps.owncloud.com');
|
||||
|
||||
$expected = new TemplateResponse('settings', 'apps', ['experimentalEnabled' => false, 'category' => 'disabled'], 'user');
|
||||
$expected->setContentSecurityPolicy($policy);
|
||||
|
||||
$this->assertEquals($expected, $this->appSettingsController->viewApps('disabled'));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue