aboutsummaryrefslogtreecommitdiffstats
path: root/settings
diff options
context:
space:
mode:
authorLukas Reschke <lukas@owncloud.com>2015-04-09 10:07:32 +0200
committerLukas Reschke <lukas@owncloud.com>2015-04-09 10:07:32 +0200
commitba52f6f8fc0d88000332e9e64c48a56c526823d9 (patch)
tree3aa8f934e0d0183e1a842c4163f83dac887bed96 /settings
parent56f1ffe820383ac82c208552213848c6a8deec6d (diff)
parent98698e05e86a85e3f9aa813c314c3ed6739fbb43 (diff)
downloadnextcloud-server-ba52f6f8fc0d88000332e9e64c48a56c526823d9.tar.gz
nextcloud-server-ba52f6f8fc0d88000332e9e64c48a56c526823d9.zip
Merge pull request #15314 from owncloud/app-categories-15274
Add different trust levels to AppStore interface
Diffstat (limited to 'settings')
-rw-r--r--settings/application.php14
-rw-r--r--settings/apps.php42
-rw-r--r--settings/controller/appsettingscontroller.php75
-rw-r--r--settings/css/settings.css67
-rw-r--r--settings/js/apps.js41
-rw-r--r--settings/routes.php40
-rw-r--r--settings/templates/apps.php62
-rw-r--r--settings/tests/js/appsSpec.js54
8 files changed, 294 insertions, 101 deletions
diff --git a/settings/application.php b/settings/application.php
index eb8f0f7a999..be127da31ac 100644
--- a/settings/application.php
+++ b/settings/application.php
@@ -71,7 +71,10 @@ class Application extends App {
$c->query('Request'),
$c->query('L10N'),
$c->query('Config'),
- $c->query('ICacheFactory')
+ $c->query('ICacheFactory'),
+ $c->query('INavigationManager'),
+ $c->query('IAppManager'),
+ $c->query('OcsClient')
);
});
$container->registerService('SecuritySettingsController', function(IContainer $c) {
@@ -192,6 +195,15 @@ class Application extends App {
$container->registerService('ClientService', function(IContainer $c) {
return $c->query('ServerContainer')->getHTTPClientService();
});
+ $container->registerService('INavigationManager', function(IContainer $c) {
+ return $c->query('ServerContainer')->getNavigationManager();
+ });
+ $container->registerService('IAppManager', function(IContainer $c) {
+ return $c->query('ServerContainer')->getAppManager();
+ });
+ $container->registerService('OcsClient', function(IContainer $c) {
+ return $c->query('ServerContainer')->getOcsClient();
+ });
$container->registerService('Util', function(IContainer $c) {
return new \OC_Util();
});
diff --git a/settings/apps.php b/settings/apps.php
deleted file mode 100644
index 7245b6610e0..00000000000
--- a/settings/apps.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-/**
- * @author Bart Visscher <bartv@thisnet.nl>
- * @author Frank Karlitschek <frank@owncloud.org>
- * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
- * @author Lukas Reschke <lukas@owncloud.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <icewind@owncloud.com>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @copyright Copyright (c) 2015, ownCloud, Inc.
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-OC_Util::checkAdminUser();
-\OC::$server->getSession()->close();
-
-// Load the files we need
-\OC_Util::addVendorScript('handlebars/handlebars');
-\OCP\Util::addScript("settings", "settings");
-\OCP\Util::addStyle("settings", "settings");
-\OC_Util::addVendorScript('select2/select2');
-\OC_Util::addVendorStyle('select2/select2');
-\OCP\Util::addScript("settings", "apps");
-\OC_App::setActiveNavigationEntry( "core_apps" );
-
-$tmpl = new OC_Template( "settings", "apps", "user" );
-$tmpl->printPage();
-
diff --git a/settings/controller/appsettingscontroller.php b/settings/controller/appsettingscontroller.php
index 9a85f6d3b97..f1b62bb1d38 100644
--- a/settings/controller/appsettingscontroller.php
+++ b/settings/controller/appsettingscontroller.php
@@ -27,8 +27,13 @@ namespace OC\Settings\Controller;
use OC\App\DependencyAnalyzer;
use OC\App\Platform;
use OC\OCSClient;
+use OCP\App\IAppManager;
use \OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\ContentSecurityPolicy;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\TemplateResponse;
use OCP\ICacheFactory;
+use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IL10N;
use OCP\IConfig;
@@ -44,6 +49,12 @@ class AppSettingsController extends Controller {
private $config;
/** @var \OCP\ICache */
private $cache;
+ /** @var INavigationManager */
+ private $navigationManager;
+ /** @var IAppManager */
+ private $appManager;
+ /** @var OCSClient */
+ private $ocsClient;
/**
* @param string $appName
@@ -51,16 +62,53 @@ class AppSettingsController extends Controller {
* @param IL10N $l10n
* @param IConfig $config
* @param ICacheFactory $cache
+ * @param INavigationManager $navigationManager
+ * @param IAppManager $appManager
+ * @param OCSClient $ocsClient
*/
public function __construct($appName,
IRequest $request,
IL10N $l10n,
IConfig $config,
- ICacheFactory $cache) {
+ ICacheFactory $cache,
+ INavigationManager $navigationManager,
+ IAppManager $appManager,
+ OCSClient $ocsClient) {
parent::__construct($appName, $request);
$this->l10n = $l10n;
$this->config = $config;
$this->cache = $cache->create($appName);
+ $this->navigationManager = $navigationManager;
+ $this->appManager = $appManager;
+ $this->ocsClient = $ocsClient;
+ }
+
+ /**
+ * Enables or disables the display of experimental apps
+ * @param bool $state
+ * @return DataResponse
+ */
+ public function changeExperimentalConfigState($state) {
+ $this->config->setSystemValue('appstore.experimental.enabled', $state);
+ $this->appManager->clearAppsCache();
+ return new DataResponse();
+ }
+
+ /**
+ * @NoCSRFRequired
+ * @return TemplateResponse
+ */
+ public function viewApps() {
+ $params = [];
+ $params['experimentalEnabled'] = $this->config->getSystemValue('appstore.experimental.enabled', false);
+ $this->navigationManager->setActiveEntry('core_apps');
+
+ $templateResponse = new TemplateResponse($this->appName, 'apps', $params, 'user');
+ $policy = new ContentSecurityPolicy();
+ $policy->addAllowedImageDomain('https://apps.owncloud.com');
+ $templateResponse->setContentSecurityPolicy($policy);
+
+ return $templateResponse;
}
/**
@@ -77,16 +125,15 @@ class AppSettingsController extends Controller {
['id' => 1, 'displayName' => (string)$this->l10n->t('Not enabled')],
];
- if(OCSClient::isAppStoreEnabled()) {
- $categories[] = ['id' => 2, 'displayName' => (string)$this->l10n->t('Recommended')];
+ if($this->ocsClient->isAppStoreEnabled()) {
// apps from external repo via OCS
- $ocs = OCSClient::getCategories();
+ $ocs = $this->ocsClient->getCategories();
if ($ocs) {
foreach($ocs as $k => $v) {
- $categories[] = array(
+ $categories[] = [
'id' => $k,
'displayName' => str_replace('ownCloud ', '', $v)
- );
+ ];
}
}
}
@@ -97,7 +144,8 @@ class AppSettingsController extends Controller {
}
/**
- * Get all available categories
+ * Get all available apps in a category
+ *
* @param int $category
* @return array
*/
@@ -134,16 +182,9 @@ class AppSettingsController extends Controller {
});
break;
default:
- if ($category === 2) {
- $apps = \OC_App::getAppstoreApps('approved');
- if ($apps) {
- $apps = array_filter($apps, function ($app) {
- return isset($app['internalclass']) && $app['internalclass'] === 'recommendedapp';
- });
- }
- } else {
- $apps = \OC_App::getAppstoreApps('approved', $category);
- }
+ $filter = $this->config->getSystemValue('appstore.experimental.enabled', false) ? 'all' : 'approved';
+
+ $apps = \OC_App::getAppstoreApps($filter, $category);
if (!$apps) {
$apps = array();
} else {
diff --git a/settings/css/settings.css b/settings/css/settings.css
index 3bc0a442f06..426fdbb8b94 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -203,22 +203,52 @@ input.userFilter {width: 200px;}
background: #fbb;
}
-.recommendedapp {
- font-size: 11px;
- background-position: left center;
- padding-left: 18px;
- vertical-align: top;
+span.version {
+ margin-left: 1em;
+ margin-right: 1em;
+ color: #555;
}
-span.version { margin-left:1em; margin-right:1em; color:#555; }
#app-navigation .app-external,
-.app-version,
-.recommendedapp {
+.app-version {
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
filter: alpha(opacity=50);
opacity: .5;
}
+.app-level {
+ margin-top: 8px;
+}
+.app-level span {
+ color: #555;
+ background-color: transparent;
+ border: 1px solid #555;
+ border-radius: 3px;
+ padding: 3px 6px;
+}
+.app-level .official {
+ border-color: #37ce02;
+ background-position: left center;
+ background-position: 5px center;
+ padding-left: 25px;
+}
+.app-level .approved {
+ border-color: #e8c805;
+}
+.app-level .experimental {
+ background-color: #ce3702;
+ border-color: #ce3702;
+ color: #fff;
+}
+.apps-experimental {
+ color: #ce3702;
+}
+
+.app-score {
+ position: relative;
+ top: 4px;
+}
+
#apps-list {
position: relative;
height: 100%;
@@ -226,6 +256,9 @@ span.version { margin-left:1em; margin-right:1em; color:#555; }
.section {
position: relative;
}
+.section h2.app-name {
+ margin-bottom: 8px;
+}
.app-image {
float: left;
padding-right: 10px;
@@ -245,7 +278,7 @@ span.version { margin-left:1em; margin-right:1em; color:#555; }
.app-name,
.app-version,
.app-score,
-.recommendedapp {
+.app-level {
display: inline-block;
}
@@ -270,13 +303,17 @@ span.version { margin-left:1em; margin-right:1em; color:#555; }
white-space: pre-line;
}
-#app-category-2 {
+#app-category-1 {
border-bottom: 1px solid #e8e8e8;
}
+/* capitalize "Other" category */
+#app-category-925 {
+ text-transform: capitalize;
+}
.app-dependencies {
margin-top: 10px;
- color: #c33;
+ color: #ce3702;
}
.missing-dependencies {
@@ -311,7 +348,7 @@ table.grid td.date{
#security-warning li {
list-style: initial;
margin: 10px 0;
- color: #c33;
+ color: #ce3702;
}
#shareAPI p { padding-bottom: 0.8em; }
#shareAPI input#shareapiExpireAfterNDays {width: 25px;}
@@ -362,12 +399,12 @@ table.grid td.date{
}
span.success {
- background: #37ce02;
- border-radius: 3px;
+ background: #37ce02;
+ border-radius: 3px;
}
span.error {
- background: #ce3702;
+ background: #ce3702;
}
diff --git a/settings/js/apps.js b/settings/js/apps.js
index 3db84e8acd5..1bd7ffdf790 100644
--- a/settings/js/apps.js
+++ b/settings/js/apps.js
@@ -9,6 +9,17 @@ Handlebars.registerHelper('score', function() {
}
return new Handlebars.SafeString('');
});
+Handlebars.registerHelper('level', function() {
+ if(typeof this.level !== 'undefined') {
+ if(this.level === 200) {
+ return new Handlebars.SafeString('<span class="official icon-checkmark">Official</span>');
+ } else if(this.level === 100) {
+ return new Handlebars.SafeString('<span class="approved">Approved</span>');
+ } else {
+ return new Handlebars.SafeString('<span class="experimental">Experimental</span>');
+ }
+ }
+});
OC.Settings = OC.Settings || {};
OC.Settings.Apps = OC.Settings.Apps || {
@@ -73,7 +84,6 @@ OC.Settings.Apps = OC.Settings.Apps || {
this._loadCategoryCall = $.ajax(OC.generateUrl('settings/apps/list?category={categoryId}', {
categoryId: categoryId
}), {
- data:{},
type:'GET',
success: function (apps) {
OC.Settings.Apps.State.apps = _.indexBy(apps.apps, 'id');
@@ -81,13 +91,27 @@ OC.Settings.Apps = OC.Settings.Apps || {
var template = Handlebars.compile(source);
if (apps.apps.length) {
+ apps.apps.sort(function(a,b) {
+ return b.level - a.level;
+ });
+
+ var firstExperimental = false;
_.each(apps.apps, function(app) {
- OC.Settings.Apps.renderApp(app, template, null);
+ if(app.level === 0 && firstExperimental === false) {
+ firstExperimental = true;
+ OC.Settings.Apps.renderApp(app, template, null, true);
+ } else {
+ OC.Settings.Apps.renderApp(app, template, null, false);
+ }
});
} else {
$('#apps-list').addClass('hidden');
$('#apps-list-empty').removeClass('hidden');
}
+
+ $('.app-level .official').tipsy({fallback: t('core', 'Official apps are developed by and within the ownCloud community. They offer functionality central to ownCloud and are ready for production use.')});
+ $('.app-level .approved').tipsy({fallback: t('core', 'Approved apps are developed by trusted developers and have passed a cursory security check. They are actively maintained in an open code repository and their maintainers deem them to be stable for casual to normal use.')});
+ $('.app-level .experimental').tipsy({fallback: t('core', 'This app is not checked for security issues and is new or known to be unstable. Install on your own risk.')});
},
complete: function() {
$('#apps-list').removeClass('icon-loading');
@@ -95,7 +119,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
});
},
- renderApp: function(app, template, selector) {
+ renderApp: function(app, template, selector, firstExperimental) {
if (!template) {
var source = $("#app-template").html();
template = Handlebars.compile(source);
@@ -103,6 +127,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
if (typeof app === 'string') {
app = OC.Settings.Apps.State.apps[app];
}
+ app.firstExperimental = firstExperimental;
var html = template(app);
if (selector) {
@@ -438,6 +463,16 @@ OC.Settings.Apps = OC.Settings.Apps || {
$select.change();
});
+ $(document).on('click', '#enable-experimental-apps', function () {
+ var state = $(this).prop('checked')
+ $.ajax(OC.generateUrl('settings/apps/experimental'), {
+ data: {state: state},
+ type: 'POST',
+ success:function () {
+ location.reload();
+ }
+ });
+ });
}
};
diff --git a/settings/routes.php b/settings/routes.php
index af9ac1d8eea..1bb14812145 100644
--- a/settings/routes.php
+++ b/settings/routes.php
@@ -33,25 +33,27 @@
namespace OC\Settings;
$application = new Application();
-$application->registerRoutes($this, array(
- 'resources' => array(
- 'groups' => array('url' => '/settings/users/groups'),
- 'users' => array('url' => '/settings/users/users')
- ),
- 'routes' => array(
- array('name' => 'MailSettings#setMailSettings', 'url' => '/settings/admin/mailsettings', 'verb' => 'POST'),
- array('name' => 'MailSettings#storeCredentials', 'url' => '/settings/admin/mailsettings/credentials', 'verb' => 'POST'),
- array('name' => 'MailSettings#sendTestMail', 'url' => '/settings/admin/mailtest', 'verb' => 'POST'),
- array('name' => 'AppSettings#listCategories', 'url' => '/settings/apps/categories', 'verb' => 'GET'),
- array('name' => 'AppSettings#listApps', 'url' => '/settings/apps/list', 'verb' => 'GET'),
- array('name' => 'SecuritySettings#trustedDomains', 'url' => '/settings/admin/security/trustedDomains', 'verb' => 'POST'),
- array('name' => 'Users#setMailAddress', 'url' => '/settings/users/{id}/mailAddress', 'verb' => 'PUT'),
- array('name' => 'LogSettings#setLogLevel', 'url' => '/settings/admin/log/level', 'verb' => 'POST'),
- array('name' => 'LogSettings#getEntries', 'url' => '/settings/admin/log/entries', 'verb' => 'GET'),
- array('name' => 'LogSettings#download', 'url' => '/settings/admin/log/download', 'verb' => 'GET'),
+$application->registerRoutes($this, [
+ 'resources' => [
+ 'groups' => ['url' => '/settings/users/groups'],
+ 'users' => ['url' => '/settings/users/users']
+ ],
+ 'routes' => [
+ ['name' => 'MailSettings#setMailSettings', 'url' => '/settings/admin/mailsettings', 'verb' => 'POST'],
+ ['name' => 'MailSettings#storeCredentials', 'url' => '/settings/admin/mailsettings/credentials', 'verb' => 'POST'],
+ ['name' => 'MailSettings#sendTestMail', 'url' => '/settings/admin/mailtest', 'verb' => 'POST'],
+ ['name' => 'AppSettings#listCategories', 'url' => '/settings/apps/categories', 'verb' => 'GET'],
+ ['name' => 'AppSettings#viewApps', 'url' => '/settings/apps', 'verb' => 'GET'],
+ ['name' => 'AppSettings#listApps', 'url' => '/settings/apps/list', 'verb' => 'GET'],
+ ['name' => 'AppSettings#changeExperimentalConfigState', 'url' => '/settings/apps/experimental', 'verb' => 'POST'],
+ ['name' => 'SecuritySettings#trustedDomains', 'url' => '/settings/admin/security/trustedDomains', 'verb' => 'POST'],
+ ['name' => 'Users#setMailAddress', 'url' => '/settings/users/{id}/mailAddress', 'verb' => 'PUT'],
+ ['name' => 'LogSettings#setLogLevel', 'url' => '/settings/admin/log/level', 'verb' => 'POST'],
+ ['name' => 'LogSettings#getEntries', 'url' => '/settings/admin/log/entries', 'verb' => 'GET'],
+ ['name' => 'LogSettings#download', 'url' => '/settings/admin/log/download', 'verb' => 'GET'],
['name' => 'CheckSetup#check', 'url' => '/settings/ajax/checksetup', 'verb' => 'GET'],
- )
-));
+ ]
+]);
/** @var $this \OCP\Route\IRouter */
@@ -62,8 +64,6 @@ $this->create('settings_personal', '/settings/personal')
->actionInclude('settings/personal.php');
$this->create('settings_users', '/settings/users')
->actionInclude('settings/users.php');
-$this->create('settings_apps', '/settings/apps')
- ->actionInclude('settings/apps.php');
$this->create('settings_admin', '/settings/admin')
->actionInclude('settings/admin.php');
// Settings ajax actions
diff --git a/settings/templates/apps.php b/settings/templates/apps.php
index a2fe5d9b63a..31de7fa2318 100644
--- a/settings/templates/apps.php
+++ b/settings/templates/apps.php
@@ -1,3 +1,27 @@
+<?php
+style('settings', 'settings');
+vendor_style(
+ 'core',
+ [
+ 'select2/select2',
+ ]
+);
+vendor_script(
+ 'core',
+ [
+ 'handlebars/handlebars',
+ 'select2/select2'
+ ]
+);
+script(
+ 'settings',
+ [
+ 'settings',
+ 'apps',
+ ]
+);
+/** @var array $_ */
+?>
<script id="categories-template" type="text/x-handlebars-template">
{{#each this}}
<li id="app-category-{{id}}" data-category-id="{{id}}" tabindex="0">
@@ -16,6 +40,18 @@
</script>
<script id="app-template" type="text/x-handlebars">
+ {{#if firstExperimental}}
+ <div class="section apps-experimental">
+ <h2><?php p($l->t('Experimental applications ahead')) ?></h2>
+ <p>
+ <?php p($l->t('Experimental apps are not checked for security ' .
+ 'issues, new or known to be unstable and under heavy ' .
+ 'development. Installing them can cause data loss or security ' .
+ 'breaches.')) ?>
+ </p>
+ </div>
+ {{/if}}
+
<div class="section" id="app-{{id}}">
{{#if preview}}
<div class="app-image{{#if previewAsIcon}} app-image-icon{{/if}} hidden">
@@ -23,17 +59,19 @@
{{/if}}
<h2 class="app-name"><a href="{{detailpage}}" target="_blank">{{name}}</a></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')); ?> {{author}}
{{#if licence}}
({{licence}}-<?php p($l->t('licensed')); ?>)
{{/if}}
</div>
+ {{#if profilepage}}</a>{{/if}}
+ <div class="app-level">
+ {{{level}}}
+ </div>
{{#if score}}
<div class="app-score">{{{score}}}</div>
{{/if}}
- {{#if internalclass}}
- <div class="{{internalclass}} icon-checkmark">{{internallabel}}</div>
- {{/if}}
<div class="app-detailpage"></div>
<div class="app-description-container hidden">
@@ -95,6 +133,24 @@
<ul id="apps-categories">
</ul>
+ <div id="app-settings">
+ <div id="app-settings-header">
+ <button class="settings-button" data-apps-slide-toggle="#app-settings-content"></button>
+ </div>
+
+ <div id="app-settings-content" class="apps-experimental">
+ <input type="checkbox" id="enable-experimental-apps" <?php if($_['experimentalEnabled']) { print_unescaped('checked="checked"'); }?>>
+ <label for="enable-experimental-apps"><?php p($l->t('Enable experimental apps')) ?></label>
+ <p>
+ <small>
+ <?php p($l->t('Experimental apps are not checked for security ' .
+ 'issues, new or known to be unstable and under heavy ' .
+ 'development. Installing them can cause data loss or security ' .
+ 'breaches.')) ?>
+ </small>
+ </p>
+ </div>
+ </div>
</div>
<div id="app-content">
<div id="apps-list" class="icon-loading"></div>
diff --git a/settings/tests/js/appsSpec.js b/settings/tests/js/appsSpec.js
index 39329c19246..311fade2c66 100644
--- a/settings/tests/js/appsSpec.js
+++ b/settings/tests/js/appsSpec.js
@@ -98,4 +98,58 @@ describe('OC.Settings.Apps tests', function() {
expect(results[0]).toEqual('somestuff');
});
});
+
+ describe('loading categories', function() {
+ var suite = this;
+
+ beforeEach( function(){
+ suite.server = sinon.fakeServer.create();
+ });
+
+ afterEach( function(){
+ suite.server.restore();
+ });
+
+ function getResultsFromDom() {
+ var results = [];
+ $('#apps-list .section:not(.hidden)').each(function() {
+ results.push($(this).attr('data-id'));
+ });
+ return results;
+ }
+
+ it('sorts all applications using the level', function() {
+ Apps.loadCategory('TestId');
+
+ suite.server.requests[0].respond(
+ 200,
+ {
+ 'Content-Type': 'application/json'
+ },
+ JSON.stringify({
+ apps: [
+ {
+ id: 'foo',
+ level: 0
+ },
+ {
+ id: 'alpha',
+ level: 300
+ },
+ {
+ id: 'delta',
+ level: 200
+ }
+ ]
+ })
+ );
+
+ var results = getResultsFromDom();
+ expect(results.length).toEqual(3);
+ expect(results[0]).toEqual('alpha');
+ expect(results[1]).toEqual('delta');
+ expect(results[2]).toEqual('foo');
+ });
+ });
+
});