diff --git a/config/config.sample.php b/config/config.sample.php
index 60932ab7d9..f17473b7d8 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -576,6 +576,15 @@ $CONFIG = array(
*/
'appstoreurl' => 'https://api.owncloud.com/v1',
+/**
+ * Whether to experimental apps in the appstore interface
+ *
+ * Experimental apps are not checked for security issues and are new or known
+ * to be unstable and under heavy development. Installing these can cause data
+ * loss or security breaches.
+ */
+'appstore.experimental.enabled' => false,
+
/**
* Use the ``apps_paths`` parameter to set the location of the Apps directory,
* which should be scanned for available apps, and where user-specific apps
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 880a276c72..87a6a9216d 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -123,7 +123,7 @@
if(OC_User::isAdminUser(OC_User::getUser())):
?>
- class="active">
diff --git a/lib/private/app.php b/lib/private/app.php
index 84bc23608f..ee92f8f5ad 100644
--- a/lib/private/app.php
+++ b/lib/private/app.php
@@ -61,6 +61,7 @@ class OC_App {
static private $loadedApps = array();
static private $altLogin = array();
private static $shippedApps = null;
+ const officialApp = 200;
/**
* clean the appId
@@ -306,8 +307,13 @@ class OC_App {
* @return int
*/
public static function downloadApp($app) {
- $appData= OCSClient::getApplication($app);
- $download= OCSClient::getApplicationDownload($app, 1);
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger()
+ );
+ $appData = $ocsClient->getApplication($app);
+ $download= $ocsClient->getApplicationDownload($app);
if(isset($download['downloadlink']) and $download['downloadlink']!='') {
// Replace spaces in download link without encoding entire URL
$download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
@@ -780,8 +786,9 @@ class OC_App {
}
/**
- * Lists all apps, this is used in apps.php
+ * List all apps, this is used in apps.php
*
+ * @param bool $onlyLocal
* @return array
*/
public static function listAllApps($onlyLocal = false) {
@@ -819,8 +826,7 @@ class OC_App {
if (isset($info['shipped']) and ($info['shipped'] == 'true')) {
$info['internal'] = true;
- $info['internallabel'] = (string)$l->t('Recommended');
- $info['internalclass'] = 'recommendedapp';
+ $info['level'] = self::officialApp;
$info['removable'] = false;
} else {
$info['internal'] = false;
@@ -845,7 +851,7 @@ class OC_App {
}
}
if ($onlyLocal) {
- $remoteApps = array();
+ $remoteApps = [];
} else {
$remoteApps = OC_App::getAppstoreApps();
}
@@ -865,34 +871,6 @@ class OC_App {
} else {
$combinedApps = $appList;
}
- // bring the apps into the right order with a custom sort function
- usort($combinedApps, function ($a, $b) {
-
- // priority 1: active
- if ($a['active'] != $b['active']) {
- return $b['active'] - $a['active'];
- }
-
- // priority 2: shipped
- $aShipped = (array_key_exists('shipped', $a) && $a['shipped'] === 'true') ? 1 : 0;
- $bShipped = (array_key_exists('shipped', $b) && $b['shipped'] === 'true') ? 1 : 0;
- if ($aShipped !== $bShipped) {
- return ($bShipped - $aShipped);
- }
-
- // priority 3: recommended
- $internalClassA = isset($a['internalclass']) ? $a['internalclass'] : '';
- $internalClassB = isset($b['internalclass']) ? $b['internalclass'] : '';
- if ($internalClassA != $internalClassB) {
- $aTemp = ($internalClassA == 'recommendedapp' ? 1 : 0);
- $bTemp = ($internalClassB == 'recommendedapp' ? 1 : 0);
- return ($bTemp - $aTemp);
- }
-
- // priority 4: alphabetical
- return strcasecmp($a['name'], $b['name']);
-
- });
return $combinedApps;
}
@@ -913,15 +891,24 @@ class OC_App {
}
/**
- * get a list of all apps on apps.owncloud.com
- *
- * @return array|false multi-dimensional array of apps.
- * Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description
+ * Get a list of all apps on the appstore
+ * @param string $filter
+ * @param string $category
+ * @return array|bool multi-dimensional array of apps.
+ * Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description
*/
public static function getAppstoreApps($filter = 'approved', $category = null) {
- $categories = array($category);
+ $categories = [$category];
+
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger()
+ );
+
+
if (is_null($category)) {
- $categoryNames = OCSClient::getCategories();
+ $categoryNames = $ocsClient->getCategories();
if (is_array($categoryNames)) {
// Check that categories of apps were retrieved correctly
if (!$categories = array_keys($categoryNames)) {
@@ -933,34 +920,36 @@ class OC_App {
}
$page = 0;
- $remoteApps = OCSClient::getApplications($categories, $page, $filter);
- $app1 = array();
+ $remoteApps = $ocsClient->getApplications($categories, $page, $filter);
+ $apps = [];
$i = 0;
$l = \OC::$server->getL10N('core');
foreach ($remoteApps as $app) {
$potentialCleanId = self::getInternalAppIdByOcs($app['id']);
// enhance app info (for example the description)
- $app1[$i] = OC_App::parseAppInfo($app);
- $app1[$i]['author'] = $app['personid'];
- $app1[$i]['ocs_id'] = $app['id'];
- $app1[$i]['internal'] = 0;
- $app1[$i]['active'] = ($potentialCleanId !== false) ? self::isEnabled($potentialCleanId) : false;
- $app1[$i]['update'] = false;
- $app1[$i]['groups'] = false;
- $app1[$i]['score'] = $app['score'];
- $app1[$i]['removable'] = false;
+ $apps[$i] = OC_App::parseAppInfo($app);
+ $apps[$i]['author'] = $app['personid'];
+ $apps[$i]['ocs_id'] = $app['id'];
+ $apps[$i]['internal'] = 0;
+ $apps[$i]['active'] = ($potentialCleanId !== false) ? self::isEnabled($potentialCleanId) : false;
+ $apps[$i]['update'] = false;
+ $apps[$i]['groups'] = false;
+ $apps[$i]['score'] = $app['score'];
+ $apps[$i]['removable'] = false;
if ($app['label'] == 'recommended') {
- $app1[$i]['internallabel'] = (string)$l->t('Recommended');
- $app1[$i]['internalclass'] = 'recommendedapp';
+ $apps[$i]['internallabel'] = (string)$l->t('Recommended');
+ $apps[$i]['internalclass'] = 'recommendedapp';
}
$i++;
}
- if (empty($app1)) {
+
+
+ if (empty($apps)) {
return false;
} else {
- return $app1;
+ return $apps;
}
}
@@ -1084,7 +1073,12 @@ class OC_App {
public static function installApp($app) {
$l = \OC::$server->getL10N('core');
$config = \OC::$server->getConfig();
- $appData=OCSClient::getApplication($app);
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ $config,
+ \OC::$server->getLogger()
+ );
+ $appData = $ocsClient->getApplication($app);
// check if app is a shipped app or not. OCS apps have an integer as id, shipped apps use a string
if (!is_numeric($app)) {
diff --git a/lib/private/app/appmanager.php b/lib/private/app/appmanager.php
index 2a147d4de6..c9d4a777c4 100644
--- a/lib/private/app/appmanager.php
+++ b/lib/private/app/appmanager.php
@@ -203,7 +203,7 @@ class AppManager implements IAppManager {
/**
* Clear the cached list of apps when enabling/disabling an app
*/
- protected function clearAppsCache() {
+ public function clearAppsCache() {
$settingsMemCache = $this->memCacheFactory->create('settings');
$settingsMemCache->clear('listApps');
}
diff --git a/lib/private/installer.php b/lib/private/installer.php
index e30344b1b1..41f13f0f5f 100644
--- a/lib/private/installer.php
+++ b/lib/private/installer.php
@@ -222,8 +222,13 @@ class OC_Installer{
* @throws Exception
*/
public static function updateAppByOCSId($ocsId) {
- $appData = OCSClient::getApplication($ocsId);
- $download = OCSClient::getApplicationDownload($ocsId, 1);
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger()
+ );
+ $appData = $ocsClient->getApplication($ocsId);
+ $download = $ocsClient->getApplicationDownload($ocsId);
if (isset($download['downloadlink']) && trim($download['downloadlink']) !== '') {
$download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
@@ -385,8 +390,12 @@ class OC_Installer{
$ocsid=OC_Appconfig::getValue( $app, 'ocsid', '');
if($ocsid<>'') {
-
- $ocsdata=OCSClient::getApplication($ocsid);
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger()
+ );
+ $ocsdata = $ocsClient->getApplication($ocsid);
$ocsversion= (string) $ocsdata['version'];
$currentversion=OC_App::getAppVersion($app);
if (version_compare($ocsversion, $currentversion, '>')) {
diff --git a/lib/private/ocsclient.php b/lib/private/ocsclient.php
index f69426ddaf..30747c0d5f 100644
--- a/lib/private/ocsclient.php
+++ b/lib/private/ocsclient.php
@@ -32,36 +32,52 @@
namespace OC;
-/**
- * This class provides an easy way for apps to store config values in the
- * database.
- */
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\ILogger;
+/**
+ * Class OCSClient is a class for communication with the ownCloud appstore
+ *
+ * @package OC
+ */
class OCSClient {
+ /** @var IClientService */
+ private $httpClientService;
+ /** @var IConfig */
+ private $config;
+ /** @var ILogger */
+ private $logger;
+
+ /**
+ * @param IClientService $httpClientService
+ * @param IConfig $config
+ * @param ILogger $logger
+ */
+ public function __construct(IClientService $httpClientService,
+ IConfig $config,
+ ILogger $logger) {
+ $this->httpClientService = $httpClientService;
+ $this->config = $config;
+ $this->logger = $logger;
+ }
/**
* Returns whether the AppStore is enabled (i.e. because the AppStore is disabled for EE)
*
* @return bool
*/
- public static function isAppStoreEnabled() {
- if (\OC::$server->getConfig()->getSystemValue('appstoreenabled', true) === false ) {
- return false;
- }
-
- return true;
+ public function isAppStoreEnabled() {
+ return $this->config->getSystemValue('appstoreenabled', true) === true;
}
/**
* Get the url of the OCS AppStore server.
*
* @return string of the AppStore server
- *
- * This function returns the url of the OCS AppStore server. It´s possible
- * to set it in the config file or it will fallback to the default
*/
- private static function getAppStoreURL() {
- return \OC::$server->getConfig()->getSystemValue('appstoreurl', 'https://api.owncloud.com/v1');
+ private function getAppStoreUrl() {
+ return $this->config->getSystemValue('appstoreurl', 'https://api.owncloud.com/v1');
}
/**
@@ -71,36 +87,50 @@ class OCSClient {
* @note returns NULL if config value appstoreenabled is set to false
* This function returns a list of all the application categories on the OCS server
*/
- public static function getCategories() {
- if (!self::isAppStoreEnabled()) {
+ public function getCategories() {
+ if (!$this->isAppStoreEnabled()) {
return null;
}
- $url = self::getAppStoreURL() . '/content/categories';
- $client = \OC::$server->getHTTPClientService()->newClient();
+ $client = $this->httpClientService->newClient();
try {
- $response = $client->get($url, ['timeout' => 5]);
+ $response = $client->get(
+ $this->getAppStoreUrl() . '/content/categories',
+ [
+ 'timeout' => 5,
+ ]
+ );
} catch(\Exception $e) {
- return null;
- }
-
- if($response->getStatusCode() !== 200) {
+ $this->logger->error(
+ sprintf('Could not get categories: %s', $e->getMessage()),
+ [
+ 'app' => 'core',
+ ]
+ );
return null;
}
$loadEntities = libxml_disable_entity_loader(true);
- $data = simplexml_load_string($response->getBody());
+ $data = @simplexml_load_string($response->getBody());
libxml_disable_entity_loader($loadEntities);
+ if($data === false) {
+ $this->logger->error(
+ 'Could not get categories, content was no valid XML',
+ [
+ 'app' => 'core',
+ ]
+ );
+ return null;
+ }
+
$tmp = $data->data;
$cats = [];
foreach ($tmp->category as $value) {
-
$id = (int)$value->id;
$name = (string)$value->name;
$cats[$id] = $name;
-
}
return $cats;
@@ -108,50 +138,63 @@ class OCSClient {
/**
* Get all the applications from the OCS server
- *
- * @return array|null an array of application data or null
- *
- * This function returns a list of all the applications on the OCS server
- * @param array|string $categories
+ * @param array $categories
* @param int $page
* @param string $filter
+ * @return array An array of application data
*/
- public static function getApplications($categories, $page, $filter) {
- if (!self::isAppStoreEnabled()) {
- return (array());
+ public function getApplications(array $categories, $page, $filter) {
+ if (!$this->isAppStoreEnabled()) {
+ return [];
}
- if (is_array($categories)) {
- $categoriesString = implode('x', $categories);
- } else {
- $categoriesString = $categories;
- }
-
- $version = '&version=' . implode('x', \OC_Util::getVersion());
- $filterUrl = '&filter=' . urlencode($filter);
- $url = self::getAppStoreURL() . '/content/data?categories=' . urlencode($categoriesString)
- . '&sortmode=new&page=' . urlencode($page) . '&pagesize=100' . $filterUrl . $version;
- $apps = [];
-
- $client = \OC::$server->getHTTPClientService()->newClient();
+ $client = $this->httpClientService->newClient();
try {
- $response = $client->get($url, ['timeout' => 5]);
+ $response = $client->get(
+ $this->getAppStoreUrl() . '/content/data',
+ [
+ 'timeout' => 5,
+ 'query' => [
+ 'version' => implode('x', \OC_Util::getVersion()),
+ 'filter' => $filter,
+ 'categories' => implode('x', $categories),
+ 'sortmode' => 'new',
+ 'page' => $page,
+ 'pagesize' => 100,
+ 'approved' => $filter
+ ],
+ ]
+ );
} catch(\Exception $e) {
- return null;
- }
-
- if($response->getStatusCode() !== 200) {
- return null;
+ $this->logger->error(
+ sprintf('Could not get applications: %s', $e->getMessage()),
+ [
+ 'app' => 'core',
+ ]
+ );
+ return [];
}
$loadEntities = libxml_disable_entity_loader(true);
- $data = simplexml_load_string($response->getBody());
+ $data = @simplexml_load_string($response->getBody());
libxml_disable_entity_loader($loadEntities);
+ if($data === false) {
+ $this->logger->error(
+ 'Could not get applications, content was no valid XML',
+ [
+ 'app' => 'core',
+ ]
+ );
+ return [];
+ }
+
$tmp = $data->data->content;
$tmpCount = count($tmp);
+
+ $apps = [];
for ($i = 0; $i < $tmpCount; $i++) {
- $app = array();
+ $app = [];
$app['id'] = (string)$tmp[$i]->id;
$app['name'] = (string)$tmp[$i]->name;
$app['label'] = (string)$tmp[$i]->label;
@@ -167,9 +210,11 @@ class OCSClient {
$app['description'] = (string)$tmp[$i]->description;
$app['score'] = (string)$tmp[$i]->score;
$app['downloads'] = (int)$tmp[$i]->downloads;
+ $app['level'] = (int)$tmp[$i]->approved;
$apps[] = $app;
}
+
return $apps;
}
@@ -182,84 +227,111 @@ class OCSClient {
*
* This function returns an applications from the OCS server
*/
- public static function getApplication($id) {
- if (!self::isAppStoreEnabled()) {
- return null;
- }
- $url = self::getAppStoreURL() . '/content/data/' . urlencode($id);
- $client = \OC::$server->getHTTPClientService()->newClient();
- try {
- $response = $client->get($url, ['timeout' => 5]);
- } catch(\Exception $e) {
+ public function getApplication($id) {
+ if (!$this->isAppStoreEnabled()) {
return null;
}
- if($response->getStatusCode() !== 200) {
+ $client = $this->httpClientService->newClient();
+ try {
+ $response = $client->get(
+ $this->getAppStoreUrl() . '/content/data/' . urlencode($id),
+ [
+ 'timeout' => 5,
+ ]
+ );
+ } catch(\Exception $e) {
+ $this->logger->error(
+ sprintf('Could not get application: %s', $e->getMessage()),
+ [
+ 'app' => 'core',
+ ]
+ );
return null;
}
$loadEntities = libxml_disable_entity_loader(true);
- $data = simplexml_load_string($response->getBody());
+ $data = @simplexml_load_string($response->getBody());
libxml_disable_entity_loader($loadEntities);
- $tmp = $data->data->content;
- if (is_null($tmp)) {
- \OC_Log::write('core', 'Invalid OCS content returned for app ' . $id, \OC_Log::FATAL);
+ if($data === false) {
+ $this->logger->error(
+ 'Could not get application, content was no valid XML',
+ [
+ 'app' => 'core',
+ ]
+ );
return null;
}
+
+ $tmp = $data->data->content;
+
$app = [];
- $app['id'] = $tmp->id;
- $app['name'] = $tmp->name;
- $app['version'] = $tmp->version;
- $app['type'] = $tmp->typeid;
- $app['label'] = $tmp->label;
- $app['typename'] = $tmp->typename;
- $app['personid'] = $tmp->personid;
- $app['detailpage'] = $tmp->detailpage;
- $app['preview1'] = $tmp->smallpreviewpic1;
- $app['preview2'] = $tmp->smallpreviewpic2;
- $app['preview3'] = $tmp->smallpreviewpic3;
+ $app['id'] = (int)$tmp->id;
+ $app['name'] = (string)$tmp->name;
+ $app['version'] = (string)$tmp->version;
+ $app['type'] = (string)$tmp->typeid;
+ $app['label'] = (string)$tmp->label;
+ $app['typename'] = (string)$tmp->typename;
+ $app['personid'] = (string)$tmp->personid;
+ $app['detailpage'] = (string)$tmp->detailpage;
+ $app['preview1'] = (string)$tmp->smallpreviewpic1;
+ $app['preview2'] = (string)$tmp->smallpreviewpic2;
+ $app['preview3'] = (string)$tmp->smallpreviewpic3;
$app['changed'] = strtotime($tmp->changed);
- $app['description'] = $tmp->description;
- $app['detailpage'] = $tmp->detailpage;
- $app['score'] = $tmp->score;
+ $app['description'] = (string)$tmp->description;
+ $app['detailpage'] = (string)$tmp->detailpage;
+ $app['score'] = (int)$tmp->score;
return $app;
}
/**
* Get the download url for an application from the OCS server
- *
+ * @param $id
* @return array|null an array of application data or null
- *
- * This function returns an download url for an applications from the OCS server
- * @param string $id
- * @param integer $item
*/
- public static function getApplicationDownload($id, $item) {
- if (!self::isAppStoreEnabled()) {
+ public function getApplicationDownload($id) {
+ if (!$this->isAppStoreEnabled()) {
return null;
}
- $url = self::getAppStoreURL() . '/content/download/' . urlencode($id) . '/' . urlencode($item);
- $client = \OC::$server->getHTTPClientService()->newClient();
+ $url = $this->getAppStoreUrl() . '/content/download/' . urlencode($id) . '/1';
+ $client = $this->httpClientService->newClient();
try {
- $response = $client->get($url, ['timeout' => 5]);
+ $response = $client->get(
+ $url,
+ [
+ 'timeout' => 5,
+ ]
+ );
} catch(\Exception $e) {
- return null;
- }
-
- if($response->getStatusCode() !== 200) {
+ $this->logger->error(
+ sprintf('Could not get application download URL: %s', $e->getMessage()),
+ [
+ 'app' => 'core',
+ ]
+ );
return null;
}
$loadEntities = libxml_disable_entity_loader(true);
- $data = simplexml_load_string($response->getBody());
+ $data = @simplexml_load_string($response->getBody());
libxml_disable_entity_loader($loadEntities);
+ if($data === false) {
+ $this->logger->error(
+ 'Could not get application download URL, content was no valid XML',
+ [
+ 'app' => 'core',
+ ]
+ );
+ return null;
+ }
+
$tmp = $data->data->content;
- $app = array();
+ $app = [];
if (isset($tmp->downloadlink)) {
- $app['downloadlink'] = $tmp->downloadlink;
+ $app['downloadlink'] = (string)$tmp->downloadlink;
} else {
$app['downloadlink'] = '';
}
diff --git a/lib/private/server.php b/lib/private/server.php
index 8c5169f229..cfdbd800a7 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -391,6 +391,13 @@ class Server extends SimpleContainer implements IServerContainer {
new \OC_Defaults()
);
});
+ $this->registerService('OcsClient', function(Server $c) {
+ return new OCSClient(
+ $this->getHTTPClientService(),
+ $this->getConfig(),
+ $this->getLogger()
+ );
+ });
}
/**
@@ -836,6 +843,13 @@ class Server extends SimpleContainer implements IServerContainer {
return $this->webRoot;
}
+ /**
+ * @return \OC\OCSClient
+ */
+ public function getOcsClient() {
+ return $this->query('OcsClient');
+ }
+
/**
* @return \OCP\IDateTimeZone
*/
diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php
index ee1412fba7..448276ca7f 100644
--- a/lib/private/templatelayout.php
+++ b/lib/private/templatelayout.php
@@ -107,7 +107,7 @@ class OC_TemplateLayout extends OC_Template {
$userDisplayName = OC_User::getDisplayName();
$this->assign('user_displayname', $userDisplayName);
$this->assign('user_uid', OC_User::getUser());
- $this->assign('appsmanagement_active', strpos(\OC::$server->getRequest()->getRequestUri(), OC_Helper::linkToRoute('settings_apps')) === 0 );
+ $this->assign('appsmanagement_active', strpos(\OC::$server->getRequest()->getRequestUri(), \OC::$server->getURLGenerator()->linkToRoute('settings.AppSettings.viewApps')) === 0 );
$this->assign('enableAvatars', $this->config->getSystemValue('enable_avatars', true));
$this->assign('userAvatarSet', \OC_Helper::userAvatarSet(OC_User::getUser()));
} else if ($renderAs == 'error') {
diff --git a/lib/public/app/iappmanager.php b/lib/public/app/iappmanager.php
index f50a7f6417..69b8c335d6 100644
--- a/lib/public/app/iappmanager.php
+++ b/lib/public/app/iappmanager.php
@@ -78,4 +78,9 @@ interface IAppManager {
* @return string[]
*/
public function getInstalledApps();
+
+ /**
+ * Clear the cached list of apps when enabling/disabling an app
+ */
+ public function clearAppsCache();
}
diff --git a/settings/application.php b/settings/application.php
index b459603796..07a458d249 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) {
@@ -191,6 +194,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 7245b6610e..0000000000
--- a/settings/apps.php
+++ /dev/null
@@ -1,42 +0,0 @@
-
- * @author Frank Karlitschek
- * @author Jan-Christoph Borchardt
- * @author Lukas Reschke
- * @author Morris Jobke
- * @author Robin Appelman
- * @author Thomas Müller
- *
- * @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
- *
- */
-
-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 9a85f6d3b9..f1b62bb1d3 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 c619bd7b9b..eb6b0f5405 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -210,6 +210,24 @@ span.version { margin-left:1em; margin-right:1em; color:#555; }
opacity: .5;
}
+.app-level {
+ color: white;
+}
+
+.app-level .official, .app-level .approved {
+ background-color: #E8C805;
+ border-radius: 2px;
+ margin-left: 5px;
+ padding: 3px;
+}
+
+.app-level .experimental {
+ background-color: #F02405;
+ border-radius: 2px;
+ margin-left: 5px;
+ padding: 3px;
+}
+
#apps-list {
position: relative;
height: 100%;
@@ -236,6 +254,7 @@ span.version { margin-left:1em; margin-right:1em; color:#555; }
.app-name,
.app-version,
.app-score,
+.app-level,
.recommendedapp {
display: inline-block;
}
@@ -261,7 +280,7 @@ 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;
}
diff --git a/settings/js/apps.js b/settings/js/apps.js
index 3db84e8acd..f54611369b 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('Official');
+ } else if(this.level === 100) {
+ return new Handlebars.SafeString('Approved');
+ } else {
+ return new Handlebars.SafeString('Experimental');
+ }
+ }
+});
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 and its GitHub repository and offer functionality central to ownCloud. They are ready for serious 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 = $('#enable-experimental-apps').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 5a069e5a1c..86b7fa2375 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 a2fe5d9b63..f930ce6d44 100644
--- a/settings/templates/apps.php
+++ b/settings/templates/apps.php
@@ -1,3 +1,27 @@
+