mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
refactor: split appstore from settings
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
bae205fa58
commit
5b756ad8bc
100 changed files with 3506 additions and 281 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -17,16 +17,17 @@ node_modules/
|
|||
|
||||
# ignore all apps except core ones
|
||||
/apps*/*
|
||||
!/apps/admin_audit
|
||||
!/apps/appstore
|
||||
!/apps/cloud_federation_api
|
||||
!/apps/comments
|
||||
!/apps/contactsinteraction
|
||||
!/apps/dashboard
|
||||
!/apps/dav
|
||||
!/apps/files
|
||||
!/apps/encryption
|
||||
!/apps/federation
|
||||
!/apps/federatedfilesharing
|
||||
!/apps/sharebymail
|
||||
!/apps/encryption
|
||||
!/apps/files
|
||||
!/apps/files_external
|
||||
!/apps/files_reminders
|
||||
!/apps/files_sharing
|
||||
|
|
@ -38,9 +39,9 @@ node_modules/
|
|||
!/apps/profile
|
||||
!/apps/provisioning_api
|
||||
!/apps/settings
|
||||
!/apps/sharebymail
|
||||
!/apps/systemtags
|
||||
!/apps/testing
|
||||
!/apps/admin_audit
|
||||
!/apps/updatenotification
|
||||
!/apps/theming
|
||||
!/apps/twofactor_backupcodes
|
||||
|
|
|
|||
|
|
@ -8,6 +8,12 @@ source_file = translationfiles/templates/admin_audit.pot
|
|||
source_lang = en
|
||||
type = PO
|
||||
|
||||
[o:nextcloud:p:nextcloud:r:appstore]
|
||||
file_filter = translationfiles/<lang>/appstore.po
|
||||
source_file = translationfiles/templates/appstore.pot
|
||||
source_lang = en
|
||||
type = PO
|
||||
|
||||
[o:nextcloud:p:nextcloud:r:cloud_federation_api]
|
||||
file_filter = translationfiles/<lang>/cloud_federation_api.po
|
||||
source_file = translationfiles/templates/cloud_federation_api.pot
|
||||
|
|
|
|||
22
apps/appstore/appinfo/info.xml
Normal file
22
apps/appstore/appinfo/info.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../../resources/app-info-shipped.xsd">
|
||||
<id>appstore</id>
|
||||
<name>Nextcloud Appstore</name>
|
||||
<summary>Nextcloud Appstore</summary>
|
||||
<description>Nextcloud Appstore</description>
|
||||
<version>1.0.0</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Nextcloud</author>
|
||||
<namespace>Appstore</namespace>
|
||||
|
||||
<category>customization</category>
|
||||
<bugs>https://github.com/nextcloud/server/issues</bugs>
|
||||
<dependencies>
|
||||
<nextcloud min-version="34" max-version="34"/>
|
||||
</dependencies>
|
||||
</info>
|
||||
27
apps/appstore/appinfo/routes.php
Normal file
27
apps/appstore/appinfo/routes.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
return [
|
||||
'routes' => [
|
||||
['name' => 'AppSettings#getAppDiscoverJSON', 'url' => '/settings/api/apps/discover', 'verb' => 'GET', 'root' => ''],
|
||||
['name' => 'AppSettings#getAppDiscoverMedia', 'url' => '/settings/api/apps/media', 'verb' => 'GET', 'root' => ''],
|
||||
['name' => 'AppSettings#listCategories', 'url' => '/settings/apps/categories', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#viewApps', 'url' => '/settings/apps', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#listApps', 'url' => '/settings/apps/list', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#enableApp', 'url' => '/settings/apps/enable/{appId}', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#enableApp', 'url' => '/settings/apps/enable/{appId}', 'verb' => 'POST' , 'root' => ''],
|
||||
['name' => 'AppSettings#enableApps', 'url' => '/settings/apps/enable', 'verb' => 'POST' , 'root' => ''],
|
||||
['name' => 'AppSettings#disableApp', 'url' => '/settings/apps/disable/{appId}', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#disableApps', 'url' => '/settings/apps/disable', 'verb' => 'POST' , 'root' => ''],
|
||||
['name' => 'AppSettings#updateApp', 'url' => '/settings/apps/update/{appId}', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#uninstallApp', 'url' => '/settings/apps/uninstall/{appId}', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#viewApps', 'url' => '/settings/apps/{category}', 'verb' => 'GET', 'defaults' => ['category' => ''] , 'root' => ''],
|
||||
['name' => 'AppSettings#viewApps', 'url' => '/settings/apps/{category}/{id}', 'verb' => 'GET', 'defaults' => ['category' => '', 'id' => ''] , 'root' => ''],
|
||||
['name' => 'AppSettings#force', 'url' => '/settings/apps/force', 'verb' => 'POST' , 'root' => ''],
|
||||
],
|
||||
];
|
||||
22
apps/appstore/composer/autoload.php
Normal file
22
apps/appstore/composer/autoload.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException($err);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInitAppstore::getLoader();
|
||||
13
apps/appstore/composer/composer.json
Normal file
13
apps/appstore/composer/composer.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"config" : {
|
||||
"vendor-dir": ".",
|
||||
"optimize-autoloader": true,
|
||||
"classmap-authoritative": true,
|
||||
"autoloader-suffix": "Appstore"
|
||||
},
|
||||
"autoload" : {
|
||||
"psr-4": {
|
||||
"OCA\\Appstore\\": "../lib/"
|
||||
}
|
||||
}
|
||||
}
|
||||
18
apps/appstore/composer/composer.lock
generated
Normal file
18
apps/appstore/composer/composer.lock
generated
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d751713988987e9331980363e24189ce",
|
||||
"packages": [],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {},
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
579
apps/appstore/composer/composer/ClassLoader.php
Normal file
579
apps/appstore/composer/composer/ClassLoader.php
Normal file
|
|
@ -0,0 +1,579 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var \Closure(string):void */
|
||||
private static $includeFile;
|
||||
|
||||
/** @var string|null */
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
private $prefixLengthsPsr4 = array();
|
||||
/**
|
||||
* @var array<string, list<string>>
|
||||
*/
|
||||
private $prefixDirsPsr4 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
/**
|
||||
* List of PSR-0 prefixes
|
||||
*
|
||||
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||
*
|
||||
* @var array<string, array<string, list<string>>>
|
||||
*/
|
||||
private $prefixesPsr0 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
/** @var bool */
|
||||
private $useIncludePath = false;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $classMap = array();
|
||||
|
||||
/** @var bool */
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $missingClasses = array();
|
||||
|
||||
/** @var string|null */
|
||||
private $apcuPrefix;
|
||||
|
||||
/**
|
||||
* @var array<string, self>
|
||||
*/
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
/**
|
||||
* @param string|null $vendorDir
|
||||
*/
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
self::initializeIncludeClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string> Array of classname => path
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $classMap Class to filename map
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 base directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return true|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
$includeFile = self::$includeFile;
|
||||
$includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||
*
|
||||
* @return array<string, self>
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $ext
|
||||
* @return string|false
|
||||
*/
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private static function initializeIncludeClosure()
|
||||
{
|
||||
if (self::$includeFile !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
self::$includeFile = \Closure::bind(static function($file) {
|
||||
include $file;
|
||||
}, null, null);
|
||||
}
|
||||
}
|
||||
396
apps/appstore/composer/composer/InstalledVersions.php
Normal file
396
apps/appstore/composer/composer/InstalledVersions.php
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||
*
|
||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||
* @internal
|
||||
*/
|
||||
private static $selfDir = null;
|
||||
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private static $installedIsLocalDir;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all package names with a specific type e.g. 'library'
|
||||
*
|
||||
* @param string $type
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackagesByType($type)
|
||||
{
|
||||
$packagesByType = array();
|
||||
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
foreach ($installed['versions'] as $name => $package) {
|
||||
if (isset($package['type']) && $package['type'] === $type) {
|
||||
$packagesByType[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $packagesByType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @param bool $includeDevRequirements
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints((string) $constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||
*/
|
||||
public static function getInstallPath($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = include __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
return self::getInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
|
||||
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
|
||||
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
|
||||
// so we have to assume it does not, and that may result in duplicate data being returned when listing
|
||||
// all installed packages for example
|
||||
self::$installedIsLocalDir = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private static function getSelfDir()
|
||||
{
|
||||
if (self::$selfDir === null) {
|
||||
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||
}
|
||||
|
||||
return self::$selfDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
$copiedLocalDir = false;
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
$selfDir = self::getSelfDir();
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
$vendorDir = strtr($vendorDir, '\\', '/');
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require $vendorDir.'/composer/installed.php';
|
||||
self::$installedByVendor[$vendorDir] = $required;
|
||||
$installed[] = $required;
|
||||
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
|
||||
self::$installed = $required;
|
||||
self::$installedIsLocalDir = true;
|
||||
}
|
||||
}
|
||||
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
|
||||
$copiedLocalDir = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require __DIR__ . '/installed.php';
|
||||
self::$installed = $required;
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||
$installed[] = self::$installed;
|
||||
}
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
21
apps/appstore/composer/composer/LICENSE
Normal file
21
apps/appstore/composer/composer/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
14
apps/appstore/composer/composer/autoload_classmap.php
Normal file
14
apps/appstore/composer/composer/autoload_classmap.php
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = $vendorDir;
|
||||
|
||||
return array(
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'OCA\\Appstore\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
|
||||
'OCA\\Appstore\\Controller\\AppSettingsController' => $baseDir . '/../lib/Controller/AppSettingsController.php',
|
||||
'OCA\\Appstore\\Search\\AppSearch' => $baseDir . '/../lib/Search/AppSearch.php',
|
||||
'OCA\\Settings\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
|
||||
);
|
||||
9
apps/appstore/composer/composer/autoload_namespaces.php
Normal file
9
apps/appstore/composer/composer/autoload_namespaces.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = $vendorDir;
|
||||
|
||||
return array(
|
||||
);
|
||||
10
apps/appstore/composer/composer/autoload_psr4.php
Normal file
10
apps/appstore/composer/composer/autoload_psr4.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = $vendorDir;
|
||||
|
||||
return array(
|
||||
'OCA\\Appstore\\' => array($baseDir . '/../lib'),
|
||||
);
|
||||
37
apps/appstore/composer/composer/autoload_real.php
Normal file
37
apps/appstore/composer/composer/autoload_real.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInitAppstore
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInitAppstore', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitAppstore', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitAppstore::getInitializer($loader));
|
||||
|
||||
$loader->setClassMapAuthoritative(true);
|
||||
$loader->register(true);
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
40
apps/appstore/composer/composer/autoload_static.php
Normal file
40
apps/appstore/composer/composer/autoload_static.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInitAppstore
|
||||
{
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'O' =>
|
||||
array (
|
||||
'OCA\\Appstore\\' => 13,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'OCA\\Appstore\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/../lib',
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'OCA\\Appstore\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
|
||||
'OCA\\Appstore\\Controller\\AppSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AppSettingsController.php',
|
||||
'OCA\\Appstore\\Search\\AppSearch' => __DIR__ . '/..' . '/../lib/Search/AppSearch.php',
|
||||
'OCA\\Settings\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitAppstore::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitAppstore::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInitAppstore::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
5
apps/appstore/composer/composer/installed.json
Normal file
5
apps/appstore/composer/composer/installed.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"packages": [],
|
||||
"dev": false,
|
||||
"dev-package-names": []
|
||||
}
|
||||
23
apps/appstore/composer/composer/installed.php
Normal file
23
apps/appstore/composer/composer/installed.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => '__root__',
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '3efb1d80e9851e0c33311a7722f523e020654691',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../',
|
||||
'aliases' => array(),
|
||||
'dev' => false,
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '3efb1d80e9851e0c33311a7722f523e020654691',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
0
apps/appstore/l10n/.gitkeep
Normal file
0
apps/appstore/l10n/.gitkeep
Normal file
32
apps/appstore/lib/AppInfo/Application.php
Normal file
32
apps/appstore/lib/AppInfo/Application.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Appstore\AppInfo;
|
||||
|
||||
use OCA\Appstore\Search\AppSearch;
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||
|
||||
class Application extends App implements IBootstrap {
|
||||
public const APP_ID = 'appstore';
|
||||
|
||||
/**
|
||||
* @param array $urlParams
|
||||
*/
|
||||
public function __construct(array $urlParams = []) {
|
||||
parent::__construct(self::APP_ID, $urlParams);
|
||||
}
|
||||
|
||||
public function register(IRegistrationContext $context): void {
|
||||
$context->registerSearchProvider(AppSearch::class);
|
||||
}
|
||||
|
||||
public function boot(IBootContext $context): void {
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
namespace OCA\Settings\Controller;
|
||||
namespace OCA\Appstore\Controller;
|
||||
|
||||
use OC\App\AppManager;
|
||||
use OC\App\AppStore\Bundles\BundleFetcher;
|
||||
|
|
@ -116,8 +116,7 @@ class AppSettingsController extends Controller {
|
|||
$templateResponse = new TemplateResponse('settings', 'settings/empty', ['pageTitle' => $this->l10n->t('Settings')]);
|
||||
$templateResponse->setContentSecurityPolicy($policy);
|
||||
|
||||
Util::addStyle('settings', 'settings');
|
||||
Util::addScript('settings', 'vue-settings-apps-users-management');
|
||||
Util::addScript('appstore', 'main');
|
||||
|
||||
return $templateResponse;
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ declare(strict_types=1);
|
|||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Settings\Search;
|
||||
namespace OCA\Appstore\Search;
|
||||
|
||||
use OCP\IL10N;
|
||||
use OCP\INavigationManager;
|
||||
104
apps/appstore/openapi-administration.json
Normal file
104
apps/appstore/openapi-administration.json
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "settings-administration",
|
||||
"version": "0.0.1",
|
||||
"description": "Nextcloud settings",
|
||||
"license": {
|
||||
"name": "agpl"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"basic_auth": {
|
||||
"type": "http",
|
||||
"scheme": "basic"
|
||||
},
|
||||
"bearer_auth": {
|
||||
"type": "http",
|
||||
"scheme": "bearer"
|
||||
}
|
||||
},
|
||||
"schemas": {}
|
||||
},
|
||||
"paths": {
|
||||
"/index.php/settings/admin/log/download": {
|
||||
"get": {
|
||||
"operationId": "log_settings-download",
|
||||
"summary": "download logfile",
|
||||
"description": "This endpoint requires admin access",
|
||||
"tags": [
|
||||
"log_settings"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Logfile returned",
|
||||
"headers": {
|
||||
"Content-Disposition": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"attachment; filename=\"nextcloud.log\""
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"application/octet-stream": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Logged in account must be an admin",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
2
apps/appstore/openapi-administration.json.license
Normal file
2
apps/appstore/openapi-administration.json.license
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
709
apps/appstore/openapi-full.json
Normal file
709
apps/appstore/openapi-full.json
Normal file
|
|
@ -0,0 +1,709 @@
|
|||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "settings-full",
|
||||
"version": "0.0.1",
|
||||
"description": "Nextcloud settings",
|
||||
"license": {
|
||||
"name": "agpl"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"basic_auth": {
|
||||
"type": "http",
|
||||
"scheme": "basic"
|
||||
},
|
||||
"bearer_auth": {
|
||||
"type": "http",
|
||||
"scheme": "bearer"
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"DeclarativeForm": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"priority",
|
||||
"section_type",
|
||||
"section_id",
|
||||
"storage_type",
|
||||
"title",
|
||||
"app",
|
||||
"fields"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"section_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"personal"
|
||||
]
|
||||
},
|
||||
"section_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"storage_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"internal",
|
||||
"external"
|
||||
]
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"doc_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"app": {
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/DeclarativeFormField"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"DeclarativeFormField": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"title",
|
||||
"type",
|
||||
"default",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"text",
|
||||
"password",
|
||||
"email",
|
||||
"tel",
|
||||
"url",
|
||||
"number",
|
||||
"checkbox",
|
||||
"multi-checkbox",
|
||||
"radio",
|
||||
"select",
|
||||
"multi-select"
|
||||
]
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": {
|
||||
"type": "object"
|
||||
},
|
||||
"options": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sensitive": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OCSMeta": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode"
|
||||
],
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"statuscode": {
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"totalitems": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemsperpage": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/index.php/settings/admin/log/download": {
|
||||
"get": {
|
||||
"operationId": "log_settings-download",
|
||||
"summary": "download logfile",
|
||||
"description": "This endpoint requires admin access",
|
||||
"tags": [
|
||||
"log_settings"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Logfile returned",
|
||||
"headers": {
|
||||
"Content-Disposition": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"attachment; filename=\"nextcloud.log\""
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"application/octet-stream": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Logged in account must be an admin",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/settings/api/declarative/value": {
|
||||
"post": {
|
||||
"operationId": "declarative_settings-set-value",
|
||||
"summary": "Sets a declarative settings value",
|
||||
"tags": [
|
||||
"declarative_settings"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"app",
|
||||
"formId",
|
||||
"fieldId",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"app": {
|
||||
"type": "string",
|
||||
"description": "ID of the app"
|
||||
},
|
||||
"formId": {
|
||||
"type": "string",
|
||||
"description": "ID of the form"
|
||||
},
|
||||
"fieldId": {
|
||||
"type": "string",
|
||||
"description": "ID of the field"
|
||||
},
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "Value to be saved"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Value set successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Not logged in or not an admin user",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid arguments to save value",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/settings/api/declarative/value-sensitive": {
|
||||
"post": {
|
||||
"operationId": "declarative_settings-set-sensitive-value",
|
||||
"summary": "Sets a declarative settings value. Password confirmation is required for sensitive values.",
|
||||
"description": "This endpoint requires password confirmation",
|
||||
"tags": [
|
||||
"declarative_settings"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"app",
|
||||
"formId",
|
||||
"fieldId",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"app": {
|
||||
"type": "string",
|
||||
"description": "ID of the app"
|
||||
},
|
||||
"formId": {
|
||||
"type": "string",
|
||||
"description": "ID of the form"
|
||||
},
|
||||
"fieldId": {
|
||||
"type": "string",
|
||||
"description": "ID of the field"
|
||||
},
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "Value to be saved"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Value set successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Not logged in or not an admin user",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid arguments to save value",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/settings/api/declarative/forms": {
|
||||
"get": {
|
||||
"operationId": "declarative_settings-get-forms",
|
||||
"summary": "Gets all declarative forms with the values prefilled.",
|
||||
"tags": [
|
||||
"declarative_settings"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Forms returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/DeclarativeForm"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
2
apps/appstore/openapi-full.json.license
Normal file
2
apps/appstore/openapi-full.json.license
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
632
apps/appstore/openapi.json
Normal file
632
apps/appstore/openapi.json
Normal file
|
|
@ -0,0 +1,632 @@
|
|||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "settings",
|
||||
"version": "0.0.1",
|
||||
"description": "Nextcloud settings",
|
||||
"license": {
|
||||
"name": "agpl"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"basic_auth": {
|
||||
"type": "http",
|
||||
"scheme": "basic"
|
||||
},
|
||||
"bearer_auth": {
|
||||
"type": "http",
|
||||
"scheme": "bearer"
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"DeclarativeForm": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"priority",
|
||||
"section_type",
|
||||
"section_id",
|
||||
"storage_type",
|
||||
"title",
|
||||
"app",
|
||||
"fields"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"section_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"personal"
|
||||
]
|
||||
},
|
||||
"section_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"storage_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"internal",
|
||||
"external"
|
||||
]
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"doc_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"app": {
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/DeclarativeFormField"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"DeclarativeFormField": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"title",
|
||||
"type",
|
||||
"default",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"text",
|
||||
"password",
|
||||
"email",
|
||||
"tel",
|
||||
"url",
|
||||
"number",
|
||||
"checkbox",
|
||||
"multi-checkbox",
|
||||
"radio",
|
||||
"select",
|
||||
"multi-select"
|
||||
]
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": {
|
||||
"type": "object"
|
||||
},
|
||||
"options": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sensitive": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OCSMeta": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode"
|
||||
],
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"statuscode": {
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"totalitems": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemsperpage": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/ocs/v2.php/settings/api/declarative/value": {
|
||||
"post": {
|
||||
"operationId": "declarative_settings-set-value",
|
||||
"summary": "Sets a declarative settings value",
|
||||
"tags": [
|
||||
"declarative_settings"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"app",
|
||||
"formId",
|
||||
"fieldId",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"app": {
|
||||
"type": "string",
|
||||
"description": "ID of the app"
|
||||
},
|
||||
"formId": {
|
||||
"type": "string",
|
||||
"description": "ID of the form"
|
||||
},
|
||||
"fieldId": {
|
||||
"type": "string",
|
||||
"description": "ID of the field"
|
||||
},
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "Value to be saved"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Value set successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Not logged in or not an admin user",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid arguments to save value",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/settings/api/declarative/value-sensitive": {
|
||||
"post": {
|
||||
"operationId": "declarative_settings-set-sensitive-value",
|
||||
"summary": "Sets a declarative settings value. Password confirmation is required for sensitive values.",
|
||||
"description": "This endpoint requires password confirmation",
|
||||
"tags": [
|
||||
"declarative_settings"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"app",
|
||||
"formId",
|
||||
"fieldId",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"app": {
|
||||
"type": "string",
|
||||
"description": "ID of the app"
|
||||
},
|
||||
"formId": {
|
||||
"type": "string",
|
||||
"description": "ID of the form"
|
||||
},
|
||||
"fieldId": {
|
||||
"type": "string",
|
||||
"description": "ID of the field"
|
||||
},
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "Value to be saved"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Value set successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Not logged in or not an admin user",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid arguments to save value",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/settings/api/declarative/forms": {
|
||||
"get": {
|
||||
"operationId": "declarative_settings-get-forms",
|
||||
"summary": "Gets all declarative forms with the values prefilled.",
|
||||
"tags": [
|
||||
"declarative_settings"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Forms returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/DeclarativeForm"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
2
apps/appstore/openapi.json.license
Normal file
2
apps/appstore/openapi.json.license
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
|
@ -32,7 +32,7 @@ export interface IAppstoreApp {
|
|||
name: string
|
||||
summary: string
|
||||
description: string
|
||||
licence: string
|
||||
license: string
|
||||
author: string[] | Record<string, string>
|
||||
level: number
|
||||
version: string
|
||||
|
|
@ -19,36 +19,36 @@
|
|||
}">
|
||||
<template v-if="useListView">
|
||||
<div v-if="showUpdateAll" class="apps-list__toolbar">
|
||||
{{ n('settings', '%n app has an update available', '%n apps have an update available', counter) }}
|
||||
{{ n('appstore', '%n app has an update available', '%n apps have an update available', counter) }}
|
||||
<NcButton
|
||||
v-if="showUpdateAll"
|
||||
id="app-list-update-all"
|
||||
variant="primary"
|
||||
@click="updateAll">
|
||||
{{ n('settings', 'Update', 'Update all', counter) }}
|
||||
{{ n('appstore', 'Update', 'Update all', counter) }}
|
||||
</NcButton>
|
||||
</div>
|
||||
|
||||
<div v-if="!showUpdateAll" class="apps-list__toolbar">
|
||||
{{ t('settings', 'All apps are up-to-date.') }}
|
||||
{{ t('appstore', 'All apps are up-to-date.') }}
|
||||
</div>
|
||||
|
||||
<TransitionGroup name="apps-list" tag="table" class="apps-list__list-container">
|
||||
<tr key="app-list-view-header">
|
||||
<th>
|
||||
<span class="hidden-visually">{{ t('settings', 'Icon') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Icon') }}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="hidden-visually">{{ t('settings', 'Name') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Name') }}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="hidden-visually">{{ t('settings', 'Version') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Version') }}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="hidden-visually">{{ t('settings', 'Level') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Level') }}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="hidden-visually">{{ t('settings', 'Actions') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Actions') }}</span>
|
||||
</th>
|
||||
</tr>
|
||||
<AppItem
|
||||
|
|
@ -64,19 +64,19 @@
|
|||
class="apps-list__list-container">
|
||||
<tr key="app-list-view-header">
|
||||
<th id="app-table-col-icon">
|
||||
<span class="hidden-visually">{{ t('settings', 'Icon') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Icon') }}</span>
|
||||
</th>
|
||||
<th id="app-table-col-name">
|
||||
<span class="hidden-visually">{{ t('settings', 'Name') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Name') }}</span>
|
||||
</th>
|
||||
<th id="app-table-col-version">
|
||||
<span class="hidden-visually">{{ t('settings', 'Version') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Version') }}</span>
|
||||
</th>
|
||||
<th id="app-table-col-level">
|
||||
<span class="hidden-visually">{{ t('settings', 'Level') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Level') }}</span>
|
||||
</th>
|
||||
<th id="app-table-col-actions">
|
||||
<span class="hidden-visually">{{ t('settings', 'Actions') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Actions') }}</span>
|
||||
</th>
|
||||
</tr>
|
||||
<template v-for="bundle in bundles">
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
{{ bundle.name }}
|
||||
</span>
|
||||
<NcButton variant="secondary" @click="toggleBundle(bundle.id)">
|
||||
{{ t('settings', bundleToggleText(bundle.id)) }}
|
||||
{{ t('appstore', bundleToggleText(bundle.id)) }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</th>
|
||||
|
|
@ -115,23 +115,23 @@
|
|||
<div class="apps-list__list-container">
|
||||
<table v-if="search !== '' && searchApps.length > 0" class="apps-list__list-container">
|
||||
<caption class="apps-list__bundle-header">
|
||||
{{ t('settings', 'Results from other categories') }}
|
||||
{{ t('appstore', 'Results from other categories') }}
|
||||
</caption>
|
||||
<tr key="app-list-view-header">
|
||||
<th>
|
||||
<span class="hidden-visually">{{ t('settings', 'Icon') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Icon') }}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="hidden-visually">{{ t('settings', 'Name') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Name') }}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="hidden-visually">{{ t('settings', 'Version') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Version') }}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="hidden-visually">{{ t('settings', 'Level') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Level') }}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="hidden-visually">{{ t('settings', 'Actions') }}</span>
|
||||
<span class="hidden-visually">{{ t('appstore', 'Actions') }}</span>
|
||||
</th>
|
||||
</tr>
|
||||
<AppItem
|
||||
|
|
@ -145,7 +145,7 @@
|
|||
|
||||
<div v-if="search !== '' && !loading && searchApps.length === 0 && apps.length === 0" id="apps-list-empty" class="emptycontent emptycontent-search">
|
||||
<div id="app-list-empty-icon" class="icon-settings-dark" />
|
||||
<h2>{{ t('settings', 'No apps found for your version') }}</h2>
|
||||
<h2>{{ t('appstore', 'No apps found for your version') }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -157,10 +157,10 @@ import NcButton from '@nextcloud/vue/components/NcButton'
|
|||
import AppItem from './AppList/AppItem.vue'
|
||||
import OfficeSuiteSwitcher from './AppList/OfficeSuiteSwitcher.vue'
|
||||
import { getOfficeSuiteById, OFFICE_SUITES } from '../constants/OfficeSuites.js'
|
||||
import logger from '../logger.ts'
|
||||
import AppManagement from '../mixins/AppManagement.js'
|
||||
import { useAppApiStore } from '../store/app-api-store.ts'
|
||||
import { useAppsStore } from '../store/apps-store.ts'
|
||||
import logger from '../utils/logger.ts'
|
||||
|
||||
export default {
|
||||
name: 'AppList',
|
||||
|
|
@ -313,9 +313,9 @@ export default {
|
|||
bundleToggleText() {
|
||||
return (id) => {
|
||||
if (this.allBundlesEnabled(id)) {
|
||||
return t('settings', 'Disable all')
|
||||
return t('appstore', 'Disable all')
|
||||
}
|
||||
return t('settings', 'Download and enable all')
|
||||
return t('appstore', 'Download and enable all')
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -3,6 +3,53 @@
|
|||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<script setup lang="ts">
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { shallowRef } from 'vue'
|
||||
import IconArrowRight from 'vue-material-design-icons/ArrowRight.vue'
|
||||
import IconCheckCircle from 'vue-material-design-icons/CheckCircle.vue'
|
||||
import { OFFICE_SUITES } from '../../constants/OfficeSuites.ts'
|
||||
|
||||
const { installedApps = [] } = defineProps<{
|
||||
installedApps?: { id: string, active: boolean }[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'suite-selected': [suiteId: string | null]
|
||||
}>()
|
||||
|
||||
const isAllInOne = loadState('settings', 'isAllInOne', false)
|
||||
const selectedSuite = shallowRef<string | null>(getInitialSuite())
|
||||
|
||||
function getInitialSuite() {
|
||||
for (const suite of OFFICE_SUITES) {
|
||||
const app = installedApps.find((a) => a.id === suite.appId)
|
||||
if (app && app.active) {
|
||||
return suite.id
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function selectSuite(suiteId: string) {
|
||||
if (selectedSuite.value === suiteId) {
|
||||
// already selected — keep selection; use the disable button to clear
|
||||
return
|
||||
}
|
||||
selectedSuite.value = suiteId
|
||||
emit('suite-selected', suiteId)
|
||||
}
|
||||
|
||||
function disableSuites() {
|
||||
if (!selectedSuite.value) {
|
||||
return
|
||||
}
|
||||
selectedSuite.value = null
|
||||
emit('suite-selected', null)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="office-suite-switcher">
|
||||
<div v-if="isAllInOne" class="office-suite-switcher__aio-message">
|
||||
|
|
@ -13,7 +60,7 @@
|
|||
<p>{{ t('settings', 'Select your preferred office suite. Please note that installing requires manual server setup.') }}</p>
|
||||
<div class="office-suite-cards">
|
||||
<div
|
||||
v-for="suite in officeSuites"
|
||||
v-for="suite in OFFICE_SUITES"
|
||||
:key="suite.id"
|
||||
class="office-suite-card"
|
||||
:class="{
|
||||
|
|
@ -56,71 +103,6 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import IconArrowRight from 'vue-material-design-icons/ArrowRight.vue'
|
||||
import IconCheckCircle from 'vue-material-design-icons/CheckCircle.vue'
|
||||
import { OFFICE_SUITES } from '../../constants/OfficeSuites.js'
|
||||
|
||||
export default {
|
||||
name: 'OfficeSuiteSwitcher',
|
||||
|
||||
components: {
|
||||
IconCheckCircle,
|
||||
IconArrowRight,
|
||||
},
|
||||
|
||||
props: {
|
||||
installedApps: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['suite-selected'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
isAllInOne: loadState('settings', 'isAllInOne', false),
|
||||
selectedSuite: this.getInitialSuite(),
|
||||
officeSuites: OFFICE_SUITES,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
t,
|
||||
getInitialSuite() {
|
||||
for (const suite of OFFICE_SUITES) {
|
||||
const app = this.installedApps.find((a) => a.id === suite.appId)
|
||||
if (app && app.active) {
|
||||
return suite.id
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
|
||||
selectSuite(suiteId) {
|
||||
if (this.selectedSuite === suiteId) {
|
||||
// already selected — keep selection; use the disable button to clear
|
||||
return
|
||||
}
|
||||
this.selectedSuite = suiteId
|
||||
this.$emit('suite-selected', suiteId)
|
||||
},
|
||||
|
||||
disableSuites() {
|
||||
if (this.selectedSuite === null) {
|
||||
return
|
||||
}
|
||||
this.selectedSuite = null
|
||||
this.$emit('suite-selected', null)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.office-suite-switcher {
|
||||
padding: 20px;
|
||||
|
|
@ -134,13 +116,13 @@ export default {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 8px 0;
|
||||
p {
|
||||
margin: 8px 0;
|
||||
|
||||
&:first-child {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
&:first-child {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.office-suite-cards {
|
||||
|
|
@ -42,7 +42,7 @@ import { defineAsyncComponent, defineComponent, onBeforeMount, ref } from 'vue'
|
|||
import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent'
|
||||
import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
|
||||
import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
|
||||
import logger from '../../logger.ts'
|
||||
import logger from '../../utils/logger.ts'
|
||||
import { filterElements, parseApiResponse } from '../../utils/appDiscoverParser.ts'
|
||||
|
||||
const PostType = defineAsyncComponent(() => import('./PostType.vue'))
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
<template>
|
||||
<NcAppSidebarTab
|
||||
id="desc"
|
||||
:name="t('settings', 'Description')"
|
||||
:name="t('appstore', 'Description')"
|
||||
:order="0">
|
||||
<template #icon>
|
||||
<NcIconSvgWrapper :path="mdiTextShort" />
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
<template>
|
||||
<NcAppSidebarTab
|
||||
id="details"
|
||||
:name="t('settings', 'Details')"
|
||||
:name="t('appstore', 'Details')"
|
||||
:order="1">
|
||||
<template #icon>
|
||||
<NcIconSvgWrapper :path="mdiTextBoxOutline" />
|
||||
|
|
@ -21,15 +21,15 @@
|
|||
:value="app.id"
|
||||
class="groups-enable__checkbox checkbox"
|
||||
@change="setGroupLimit">
|
||||
<label :for="`groups_enable_${app.id}`">{{ t('settings', 'Limit to groups') }}</label>
|
||||
<label :for="`groups_enable_${app.id}`">{{ t('appstore', 'Limit to groups') }}</label>
|
||||
<input
|
||||
type="hidden"
|
||||
class="group_select"
|
||||
:title="t('settings', 'All')"
|
||||
:title="t('appstore', 'All')"
|
||||
value="">
|
||||
<br>
|
||||
<label for="limitToGroups">
|
||||
<span>{{ t('settings', 'Limit app usage to groups') }}</span>
|
||||
<span>{{ t('appstore', 'Limit app usage to groups') }}</span>
|
||||
</label>
|
||||
<NcSelect
|
||||
v-if="isLimitedToGroups(app)"
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
@option:selected="addGroupLimitation"
|
||||
@option:deselected="removeGroupLimitation"
|
||||
@search="asyncFindGroup">
|
||||
<span slot="noResult">{{ t('settings', 'No results') }}</span>
|
||||
<span slot="noResult">{{ t('appstore', 'No results') }}</span>
|
||||
</NcSelect>
|
||||
</div>
|
||||
<div class="app-details__actions-manage">
|
||||
|
|
@ -51,14 +51,14 @@
|
|||
v-if="app.update"
|
||||
class="update primary"
|
||||
type="button"
|
||||
:value="t('settings', 'Update to {version}', { version: app.update })"
|
||||
:value="t('appstore', 'Update to {version}', { version: app.update })"
|
||||
:disabled="installing || isLoading || isManualInstall"
|
||||
@click="update(app.id)">
|
||||
<input
|
||||
v-if="app.canUnInstall"
|
||||
class="uninstall"
|
||||
type="button"
|
||||
:value="t('settings', 'Remove')"
|
||||
:value="t('appstore', 'Remove')"
|
||||
:disabled="installing || isLoading"
|
||||
@click="remove(app.id, removeData)">
|
||||
<input
|
||||
|
|
@ -88,36 +88,36 @@
|
|||
@click="forceEnable(app.id)">
|
||||
<NcButton
|
||||
v-if="app?.app_api && (app.canInstall || app.isCompatible)"
|
||||
:aria-label="t('settings', 'Advanced deploy options')"
|
||||
:aria-label="t('appstore', 'Advanced deploy options')"
|
||||
variant="secondary"
|
||||
@click="() => showDeployOptionsModal = true">
|
||||
<template #icon>
|
||||
<NcIconSvgWrapper :path="mdiToyBrickPlusOutline" />
|
||||
</template>
|
||||
{{ t('settings', 'Deploy options') }}
|
||||
{{ t('appstore', 'Deploy options') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
<p v-if="!defaultDeployDaemonAccessible" class="warning">
|
||||
{{ t('settings', 'Default Deploy daemon is not accessible') }}
|
||||
{{ t('appstore', 'Default Deploy daemon is not accessible') }}
|
||||
</p>
|
||||
<NcCheckboxRadioSwitch
|
||||
v-if="app.canUnInstall"
|
||||
:model-value="removeData"
|
||||
:disabled="installing || isLoading || !defaultDeployDaemonAccessible"
|
||||
@update:modelValue="toggleRemoveData">
|
||||
{{ t('settings', 'Delete data on remove') }}
|
||||
{{ t('appstore', 'Delete data on remove') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
</div>
|
||||
|
||||
<ul class="app-details__dependencies">
|
||||
<li v-if="app.missingMinOwnCloudVersion">
|
||||
{{ t('settings', 'This app has no minimum {productName} version assigned. This will be an error in the future.', { productName }) }}
|
||||
{{ t('appstore', 'This app has no minimum {productName} version assigned. This will be an error in the future.', { productName }) }}
|
||||
</li>
|
||||
<li v-if="app.missingMaxOwnCloudVersion">
|
||||
{{ t('settings', 'This app has no maximum {productName} version assigned. This will be an error in the future.', { productName }) }}
|
||||
{{ t('appstore', 'This app has no maximum {productName} version assigned. This will be an error in the future.', { productName }) }}
|
||||
</li>
|
||||
<li v-if="!app.canInstall">
|
||||
{{ t('settings', 'This app cannot be installed because the following dependencies are not fulfilled:') }}
|
||||
{{ t('appstore', 'This app cannot be installed because the following dependencies are not fulfilled:') }}
|
||||
<ul class="missing-dependencies">
|
||||
<li v-for="(dep, index) in app.missingDependencies" :key="index">
|
||||
{{ dep }}
|
||||
|
|
@ -128,14 +128,14 @@
|
|||
|
||||
<div v-if="lastModified && !app.shipped" class="app-details__section">
|
||||
<h4>
|
||||
{{ t('settings', 'Latest updated') }}
|
||||
{{ t('appstore', 'Latest updated') }}
|
||||
</h4>
|
||||
<NcDateTime :timestamp="lastModified" />
|
||||
</div>
|
||||
|
||||
<div class="app-details__section">
|
||||
<h4>
|
||||
{{ t('settings', 'Author') }}
|
||||
{{ t('appstore', 'Author') }}
|
||||
</h4>
|
||||
<p class="app-details__authors">
|
||||
{{ appAuthors }}
|
||||
|
|
@ -144,7 +144,7 @@
|
|||
|
||||
<div class="app-details__section">
|
||||
<h4>
|
||||
{{ t('settings', 'Categories') }}
|
||||
{{ t('appstore', 'Categories') }}
|
||||
</h4>
|
||||
<p>
|
||||
{{ appCategories }}
|
||||
|
|
@ -152,8 +152,8 @@
|
|||
</div>
|
||||
|
||||
<div v-if="externalResources.length > 0" class="app-details__section">
|
||||
<h4>{{ t('settings', 'Resources') }}</h4>
|
||||
<ul class="app-details__documentation" :aria-label="t('settings', 'Documentation')">
|
||||
<h4>{{ t('appstore', 'Resources') }}</h4>
|
||||
<ul class="app-details__documentation" :aria-label="t('appstore', 'Documentation')">
|
||||
<li v-for="resource of externalResources" :key="resource.id">
|
||||
<a
|
||||
class="appslink"
|
||||
|
|
@ -167,13 +167,13 @@
|
|||
</div>
|
||||
|
||||
<div class="app-details__section">
|
||||
<h4>{{ t('settings', 'Interact') }}</h4>
|
||||
<h4>{{ t('appstore', 'Interact') }}</h4>
|
||||
<div class="app-details__interact">
|
||||
<NcButton
|
||||
:disabled="!app.bugs"
|
||||
:href="app.bugs ?? '#'"
|
||||
:aria-label="t('settings', 'Report a bug')"
|
||||
:title="t('settings', 'Report a bug')">
|
||||
:aria-label="t('appstore', 'Report a bug')"
|
||||
:title="t('appstore', 'Report a bug')">
|
||||
<template #icon>
|
||||
<NcIconSvgWrapper :path="mdiBugOutline" />
|
||||
</template>
|
||||
|
|
@ -181,8 +181,8 @@
|
|||
<NcButton
|
||||
:disabled="!app.bugs"
|
||||
:href="app.bugs ?? '#'"
|
||||
:aria-label="t('settings', 'Request feature')"
|
||||
:title="t('settings', 'Request feature')">
|
||||
:aria-label="t('appstore', 'Request feature')"
|
||||
:title="t('appstore', 'Request feature')">
|
||||
<template #icon>
|
||||
<NcIconSvgWrapper :path="mdiFeatureSearchOutline" />
|
||||
</template>
|
||||
|
|
@ -190,8 +190,8 @@
|
|||
<NcButton
|
||||
v-if="app.appstoreData?.discussion"
|
||||
:href="app.appstoreData.discussion"
|
||||
:aria-label="t('settings', 'Ask questions or discuss')"
|
||||
:title="t('settings', 'Ask questions or discuss')">
|
||||
:aria-label="t('appstore', 'Ask questions or discuss')"
|
||||
:title="t('appstore', 'Ask questions or discuss')">
|
||||
<template #icon>
|
||||
<NcIconSvgWrapper :path="mdiTooltipQuestionOutline" />
|
||||
</template>
|
||||
|
|
@ -199,8 +199,8 @@
|
|||
<NcButton
|
||||
v-if="!app.internal"
|
||||
:href="rateAppUrl"
|
||||
:aria-label="t('settings', 'Rate the app')"
|
||||
:title="t('settings', 'Rate')">
|
||||
:aria-label="t('appstore', 'Rate the app')"
|
||||
:title="t('appstore', 'Rate')">
|
||||
<template #icon>
|
||||
<NcIconSvgWrapper :path="mdiStar" />
|
||||
</template>
|
||||
|
|
@ -335,14 +335,14 @@ export default {
|
|||
resources.push({
|
||||
id: 'appstore',
|
||||
href: this.appstoreUrl,
|
||||
label: t('settings', 'View in store'),
|
||||
label: t('appstore', 'View in store'),
|
||||
})
|
||||
}
|
||||
if (this.app.website) {
|
||||
resources.push({
|
||||
id: 'website',
|
||||
href: this.app.website,
|
||||
label: t('settings', 'Visit website'),
|
||||
label: t('appstore', 'Visit website'),
|
||||
})
|
||||
}
|
||||
if (this.app.documentation) {
|
||||
|
|
@ -350,21 +350,21 @@ export default {
|
|||
resources.push({
|
||||
id: 'doc-user',
|
||||
href: this.app.documentation.user,
|
||||
label: t('settings', 'Usage documentation'),
|
||||
label: t('appstore', 'Usage documentation'),
|
||||
})
|
||||
}
|
||||
if (this.app.documentation.admin) {
|
||||
resources.push({
|
||||
id: 'doc-admin',
|
||||
href: this.app.documentation.admin,
|
||||
label: t('settings', 'Admin documentation'),
|
||||
label: t('appstore', 'Admin documentation'),
|
||||
})
|
||||
}
|
||||
if (this.app.documentation.developer) {
|
||||
resources.push({
|
||||
id: 'doc-developer',
|
||||
href: this.app.documentation.developer,
|
||||
label: t('settings', 'Developer documentation'),
|
||||
label: t('appstore', 'Developer documentation'),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
<NcAppSidebarTab
|
||||
v-if="hasChangelog"
|
||||
id="changelog"
|
||||
:name="t('settings', 'Changelog')"
|
||||
:name="t('appstore', 'Changelog')"
|
||||
:order="2">
|
||||
<template #icon>
|
||||
<NcIconSvgWrapper :path="mdiClockFast" :size="24" />
|
||||
|
|
@ -8,7 +8,7 @@ import type { IAppstoreApp } from '../app-types.ts'
|
|||
import { mdiCog, mdiCogOutline } from '@mdi/js'
|
||||
import { computed, ref, watchEffect } from 'vue'
|
||||
import AppstoreCategoryIcons from '../constants/AppstoreCategoryIcons.ts'
|
||||
import logger from '../logger.ts'
|
||||
import logger from '../utils/logger.ts'
|
||||
|
||||
/**
|
||||
* Get the app icon raw SVG for use with `NcIconSvgWrapper` (do never use without sanitizing)
|
||||
117
apps/appstore/src/constants/AppDiscoverTypes.ts
Normal file
117
apps/appstore/src/constants/AppDiscoverTypes.ts
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Currently known types of app discover section elements
|
||||
*/
|
||||
export const APP_DISCOVER_KNOWN_TYPES = ['post', 'showcase', 'carousel'] as const
|
||||
|
||||
/**
|
||||
* Helper for localized values
|
||||
*/
|
||||
export type ILocalizedValue<T> = Record<string, T | undefined> & { en: T }
|
||||
|
||||
export interface IAppDiscoverElement {
|
||||
/**
|
||||
* Type of the element
|
||||
*/
|
||||
type: typeof APP_DISCOVER_KNOWN_TYPES[number]
|
||||
|
||||
/**
|
||||
* Identifier for this element
|
||||
*/
|
||||
id: string
|
||||
|
||||
/**
|
||||
* Order of this element to pin elements (smaller = shown on top)
|
||||
*/
|
||||
order?: number
|
||||
|
||||
/**
|
||||
* Optional, localized, headline for the element
|
||||
*/
|
||||
headline?: ILocalizedValue<string>
|
||||
|
||||
/**
|
||||
* Optional link target for the element
|
||||
*/
|
||||
link?: string
|
||||
|
||||
/**
|
||||
* Optional date when this element will get valid (only show since then)
|
||||
*/
|
||||
date?: number
|
||||
|
||||
/**
|
||||
* Optional date when this element will be invalid (only show until then)
|
||||
*/
|
||||
expiryDate?: number
|
||||
}
|
||||
|
||||
/** Wrapper for media source and MIME type */
|
||||
type MediaSource = { src: string, mime: string }
|
||||
|
||||
/**
|
||||
* Media content type for posts
|
||||
*/
|
||||
interface IAppDiscoverMediaContent {
|
||||
/**
|
||||
* The media source to show - either one or a list of sources with their MIME type for fallback options
|
||||
*/
|
||||
src: MediaSource | MediaSource[]
|
||||
|
||||
/**
|
||||
* Alternative text for the media
|
||||
*/
|
||||
alt: string
|
||||
|
||||
/**
|
||||
* Optional link target for the media (e.g. to the full video)
|
||||
*/
|
||||
link?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for post media
|
||||
*/
|
||||
interface IAppDiscoverMedia {
|
||||
/**
|
||||
* The alignment of the media element
|
||||
*/
|
||||
alignment?: 'start' | 'end' | 'center'
|
||||
|
||||
/**
|
||||
* The (localized) content
|
||||
*/
|
||||
content: ILocalizedValue<IAppDiscoverMediaContent>
|
||||
}
|
||||
|
||||
/**
|
||||
* An app element only used for the showcase type
|
||||
*/
|
||||
export interface IAppDiscoverApp {
|
||||
/** The App ID */
|
||||
type: 'app'
|
||||
appId: string
|
||||
}
|
||||
|
||||
export interface IAppDiscoverPost extends IAppDiscoverElement {
|
||||
type: 'post'
|
||||
text?: ILocalizedValue<string>
|
||||
media?: IAppDiscoverMedia
|
||||
}
|
||||
|
||||
export interface IAppDiscoverShowcase extends IAppDiscoverElement {
|
||||
type: 'showcase'
|
||||
content: (IAppDiscoverPost | IAppDiscoverApp)[]
|
||||
}
|
||||
|
||||
export interface IAppDiscoverCarousel extends IAppDiscoverElement {
|
||||
type: 'carousel'
|
||||
text?: ILocalizedValue<string>
|
||||
content: IAppDiscoverPost[]
|
||||
}
|
||||
|
||||
export type IAppDiscoverElements = IAppDiscoverPost | IAppDiscoverCarousel | IAppDiscoverShowcase
|
||||
18
apps/appstore/src/constants/AppsConstants.js
Normal file
18
apps/appstore/src/constants/AppsConstants.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
|
||||
/** Enum of verification constants, according to Apps */
|
||||
export const APPS_SECTION_ENUM = Object.freeze({
|
||||
discover: t('settings', 'Discover'),
|
||||
installed: t('settings', 'Your apps'),
|
||||
enabled: t('settings', 'Active apps'),
|
||||
disabled: t('settings', 'Disabled apps'),
|
||||
updates: t('settings', 'Updates'),
|
||||
'app-bundles': t('settings', 'App bundles'),
|
||||
featured: t('settings', 'Featured apps'),
|
||||
supported: t('settings', 'Supported apps'), // From subscription
|
||||
})
|
||||
63
apps/appstore/src/constants/AppstoreCategoryIcons.ts
Normal file
63
apps/appstore/src/constants/AppstoreCategoryIcons.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import {
|
||||
mdiAccountMultipleOutline,
|
||||
mdiAccountOutline,
|
||||
mdiArchiveOutline,
|
||||
mdiCheck,
|
||||
mdiClipboardFlowOutline,
|
||||
mdiClose,
|
||||
mdiCogOutline,
|
||||
mdiControllerClassicOutline,
|
||||
mdiCreationOutline,
|
||||
mdiDownload,
|
||||
mdiFileDocumentEdit,
|
||||
mdiFolder,
|
||||
mdiKeyOutline,
|
||||
mdiMagnify,
|
||||
mdiMonitorEye,
|
||||
mdiMultimedia,
|
||||
mdiOfficeBuildingOutline,
|
||||
mdiOpenInApp,
|
||||
mdiSecurity,
|
||||
mdiStar,
|
||||
mdiStarCircleOutline,
|
||||
mdiStarShootingOutline,
|
||||
mdiTools,
|
||||
mdiViewColumnOutline,
|
||||
} from '@mdi/js'
|
||||
|
||||
/**
|
||||
* SVG paths used for appstore category icons
|
||||
*/
|
||||
export default Object.freeze({
|
||||
// system special categories
|
||||
discover: mdiStarCircleOutline,
|
||||
installed: mdiAccountOutline,
|
||||
enabled: mdiCheck,
|
||||
disabled: mdiClose,
|
||||
bundles: mdiArchiveOutline,
|
||||
supported: mdiStarShootingOutline,
|
||||
featured: mdiStar,
|
||||
updates: mdiDownload,
|
||||
|
||||
// generic category
|
||||
ai: mdiCreationOutline,
|
||||
auth: mdiKeyOutline,
|
||||
customization: mdiCogOutline,
|
||||
dashboard: mdiViewColumnOutline,
|
||||
files: mdiFolder,
|
||||
games: mdiControllerClassicOutline,
|
||||
integration: mdiOpenInApp,
|
||||
monitoring: mdiMonitorEye,
|
||||
multimedia: mdiMultimedia,
|
||||
office: mdiFileDocumentEdit,
|
||||
organization: mdiOfficeBuildingOutline,
|
||||
search: mdiMagnify,
|
||||
security: mdiSecurity,
|
||||
social: mdiAccountMultipleOutline,
|
||||
tools: mdiTools,
|
||||
workflow: mdiClipboardFlowOutline,
|
||||
})
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
/**
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export const OFFICE_SUITES = [
|
||||
import { t } from '@nextcloud/l10n'
|
||||
|
||||
export const OFFICE_SUITES = Object.freeze([
|
||||
{
|
||||
id: 'nextcloud-office',
|
||||
appId: 'richdocuments',
|
||||
|
|
@ -33,24 +35,24 @@ export const OFFICE_SUITES = [
|
|||
learnMoreUrl: 'https://nextcloud.com/onlyoffice/',
|
||||
isPrimary: false,
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
/**
|
||||
* Get office suite configuration by ID
|
||||
*
|
||||
* @param {string} id - The suite ID
|
||||
* @return {object|undefined} The suite configuration or undefined if not found
|
||||
* @param id - The suite ID
|
||||
* @return The suite configuration or undefined if not found
|
||||
*/
|
||||
export function getOfficeSuiteById(id) {
|
||||
export function getOfficeSuiteById(id: string) {
|
||||
return OFFICE_SUITES.find((suite) => suite.id === id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get office suite configuration by app ID
|
||||
*
|
||||
* @param {string} appId - The app ID (richdocuments, onlyoffice, etc.)
|
||||
* @return {object|undefined} The suite configuration or undefined if not found
|
||||
* @param appId - The app ID (richdocuments, onlyoffice, etc.)
|
||||
* @return The suite configuration or undefined if not found
|
||||
*/
|
||||
export function getOfficeSuiteByAppId(appId) {
|
||||
export function getOfficeSuiteByAppId(appId: string) {
|
||||
return OFFICE_SUITES.find((suite) => suite.appId === appId)
|
||||
}
|
||||
39
apps/appstore/src/main.ts
Normal file
39
apps/appstore/src/main.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { getCSPNonce } from '@nextcloud/auth'
|
||||
import { n, t } from '@nextcloud/l10n'
|
||||
import { createPinia, PiniaVuePlugin } from 'pinia'
|
||||
import VTooltipPlugin from 'v-tooltip'
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import { sync } from 'vuex-router-sync'
|
||||
import App from './views/App.vue'
|
||||
import router from './router/index.ts'
|
||||
import { useStore } from './store/index.js'
|
||||
|
||||
// CSP config for webpack dynamic chunk loading
|
||||
|
||||
__webpack_nonce__ = getCSPNonce()
|
||||
|
||||
// bind to window
|
||||
Vue.prototype.t = t
|
||||
Vue.prototype.n = n
|
||||
Vue.use(PiniaVuePlugin)
|
||||
Vue.use(VTooltipPlugin, { defaultHtml: false })
|
||||
Vue.use(Vuex)
|
||||
|
||||
const store = useStore()
|
||||
sync(store, router)
|
||||
|
||||
const pinia = createPinia()
|
||||
|
||||
export default new Vue({
|
||||
router,
|
||||
store,
|
||||
pinia,
|
||||
render: (h) => h(App),
|
||||
el: '#content',
|
||||
})
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import rebuildNavigation from '../service/rebuild-navigation.js'
|
||||
import { rebuildNavigation } from '../service/rebuild-navigation.ts'
|
||||
|
||||
const productName = window.OC.theme.productName
|
||||
|
||||
22
apps/appstore/src/router/index.ts
Normal file
22
apps/appstore/src/router/index.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import routes from './routes.ts'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
const router = new Router({
|
||||
mode: 'history',
|
||||
// if index.php is in the url AND we got this far, then it's working:
|
||||
// let's keep using index.php in the url
|
||||
base: generateUrl(''),
|
||||
linkActiveClass: 'active',
|
||||
routes,
|
||||
})
|
||||
|
||||
export default router
|
||||
46
apps/appstore/src/router/routes.ts
Normal file
46
apps/appstore/src/router/routes.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import type { RouteConfig } from 'vue-router'
|
||||
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
|
||||
const appstoreEnabled = loadState<boolean>('settings', 'appstoreEnabled', true)
|
||||
|
||||
// Dynamic loading
|
||||
const AppStore = () => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStore.vue')
|
||||
const AppStoreNavigation = () => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStoreNavigation.vue')
|
||||
const AppStoreSidebar = () => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStoreSidebar.vue')
|
||||
|
||||
const routes: RouteConfig[] = [
|
||||
{
|
||||
path: '/:index(index.php/)?settings/apps',
|
||||
name: 'apps',
|
||||
redirect: {
|
||||
name: 'apps-category',
|
||||
params: {
|
||||
category: appstoreEnabled ? 'discover' : 'installed',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
default: AppStore,
|
||||
navigation: AppStoreNavigation,
|
||||
sidebar: AppStoreSidebar,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: ':category',
|
||||
name: 'apps-category',
|
||||
children: [
|
||||
{
|
||||
path: ':id',
|
||||
name: 'apps-details',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export default routes
|
||||
23
apps/appstore/src/service/rebuild-navigation.ts
Normal file
23
apps/appstore/src/service/rebuild-navigation.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*!
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { OCSResponse } from '@nextcloud/typings/ocs'
|
||||
|
||||
import axios from '@nextcloud/axios'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
|
||||
/**
|
||||
* Rebuilds the app navigation menu
|
||||
*/
|
||||
export async function rebuildNavigation() {
|
||||
const { data } = await axios.get<OCSResponse>(generateOcsUrl('core/navigation/apps?format=json'))
|
||||
if (data.ocs.meta.statuscode !== 200) {
|
||||
return
|
||||
}
|
||||
|
||||
emit('nextcloud:app-menu.refresh', { apps: data.ocs.data })
|
||||
window.dispatchEvent(new Event('resize'))
|
||||
}
|
||||
11
apps/appstore/src/settings.ts
Normal file
11
apps/appstore/src/settings.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { rebuildNavigation } from './service/rebuild-navigation.ts'
|
||||
|
||||
window.OC.Settings ??= {}
|
||||
window.OC.Settings.Apps ??= {
|
||||
rebuildNavigation,
|
||||
}
|
||||
67
apps/appstore/src/store/api.js
Normal file
67
apps/appstore/src/store/api.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import axios from '@nextcloud/axios'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
|
||||
/**
|
||||
* @param {string} url - The url to sanitize
|
||||
*/
|
||||
function sanitize(url) {
|
||||
return url.replace(/\/$/, '') // Remove last url slash
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
/**
|
||||
* This Promise is used to chain a request that require an admin password confirmation
|
||||
* Since chaining Promise have a very precise behavior concerning catch and then,
|
||||
* you'll need to be careful when using it.
|
||||
* e.g
|
||||
* // store
|
||||
* action(context) {
|
||||
* return api.requireAdmin().then((response) => {
|
||||
* return api.get('url')
|
||||
* .then((response) => {API success})
|
||||
* .catch((error) => {API failure});
|
||||
* }).catch((error) => {requireAdmin failure});
|
||||
* }
|
||||
* // vue
|
||||
* this.$store.dispatch('action').then(() => {always executed})
|
||||
*
|
||||
* Since Promise.then().catch().then() will always execute the last then
|
||||
* this.$store.dispatch('action').then will always be executed
|
||||
*
|
||||
* If you want requireAdmin failure to also catch the API request failure
|
||||
* you will need to throw a new error in the api.get.catch()
|
||||
*
|
||||
* e.g
|
||||
* api.requireAdmin().then((response) => {
|
||||
* api.get('url')
|
||||
* .then((response) => {API success})
|
||||
* .catch((error) => {throw error;});
|
||||
* }).catch((error) => {requireAdmin OR API failure});
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
requireAdmin() {
|
||||
return confirmPassword()
|
||||
},
|
||||
get(url, options) {
|
||||
return axios.get(sanitize(url), options)
|
||||
},
|
||||
post(url, data) {
|
||||
return axios.post(sanitize(url), data)
|
||||
},
|
||||
patch(url, data) {
|
||||
return axios.patch(sanitize(url), data)
|
||||
},
|
||||
put(url, data) {
|
||||
return axios.put(sanitize(url), data)
|
||||
},
|
||||
delete(url, data) {
|
||||
return axios.delete(sanitize(url), { params: data })
|
||||
},
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ import { confirmPassword } from '@nextcloud/password-confirmation'
|
|||
import { generateUrl } from '@nextcloud/router'
|
||||
import { defineStore } from 'pinia'
|
||||
import Vue from 'vue'
|
||||
import logger from '../logger.ts'
|
||||
import logger from '../utils/logger.ts'
|
||||
import api from './api.js'
|
||||
|
||||
interface AppApiState {
|
||||
|
|
@ -30,12 +30,12 @@ interface AppApiState {
|
|||
export const useAppApiStore = defineStore('app-api-apps', {
|
||||
state: (): AppApiState => ({
|
||||
apps: [],
|
||||
updateCount: loadState('settings', 'appstoreExAppUpdateCount', 0),
|
||||
updateCount: loadState('appstore', 'appstoreExAppUpdateCount', 0),
|
||||
loading: {},
|
||||
loadingList: false,
|
||||
statusUpdater: null,
|
||||
daemonAccessible: loadState('settings', 'defaultDaemonConfigAccessible', false),
|
||||
defaultDaemon: loadState('settings', 'defaultDaemonConfig', null),
|
||||
daemonAccessible: loadState('appstore', 'defaultDaemonConfigAccessible', false),
|
||||
defaultDaemon: loadState('appstore', 'defaultDaemonConfig', null),
|
||||
dockerDaemons: [],
|
||||
}),
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ export const useAppApiStore = defineStore('app-api-apps', {
|
|||
actions: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
appsApiFailure(error: any) {
|
||||
showError(t('settings', 'An error occurred during the request. Unable to proceed.') + '<br>' + error.error.response.data.data.message, { isHTML: true })
|
||||
showError(t('appstore', 'An error occurred during the request. Unable to proceed.') + '<br>' + error.error.response.data.data.message, { isHTML: true })
|
||||
logger.error(error)
|
||||
},
|
||||
|
||||
|
|
@ -107,7 +107,7 @@ export const useAppApiStore = defineStore('app-api-apps', {
|
|||
.then(() => {
|
||||
if (response.data.update_required) {
|
||||
showInfo(
|
||||
t('settings', 'The app has been enabled but needs to be updated.'),
|
||||
t('appstore', 'The app has been enabled but needs to be updated.'),
|
||||
{
|
||||
onClick: () => window.location.reload(),
|
||||
close: false,
|
||||
|
|
@ -119,7 +119,7 @@ export const useAppApiStore = defineStore('app-api-apps', {
|
|||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.setError(appId, t('settings', 'Error: This app cannot be enabled because it makes the server unstable'))
|
||||
this.setError(appId, t('appstore', 'Error: This app cannot be enabled because it makes the server unstable'))
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
@ -252,7 +252,7 @@ export const useAppApiStore = defineStore('app-api-apps', {
|
|||
return true
|
||||
} catch (error) {
|
||||
logger.error(error as string)
|
||||
showError(t('settings', 'An error occurred during the request. Unable to proceed.'))
|
||||
showError(t('appstore', 'An error occurred during the request. Unable to proceed.'))
|
||||
this.loadingList = false
|
||||
}
|
||||
},
|
||||
|
|
@ -12,15 +12,15 @@ import { translate as t } from '@nextcloud/l10n'
|
|||
import { generateUrl } from '@nextcloud/router'
|
||||
import { defineStore } from 'pinia'
|
||||
import APPSTORE_CATEGORY_ICONS from '../constants/AppstoreCategoryIcons.ts'
|
||||
import logger from '../logger.ts'
|
||||
import logger from '../utils/logger.ts'
|
||||
|
||||
const showApiError = () => showError(t('settings', 'An error occurred during the request. Unable to proceed.'))
|
||||
const showApiError = () => showError(t('appstore', 'An error occurred during the request. Unable to proceed.'))
|
||||
|
||||
export const useAppsStore = defineStore('settings-apps', {
|
||||
export const useAppsStore = defineStore('appstore-apps', {
|
||||
state: () => ({
|
||||
apps: [] as IAppstoreApp[],
|
||||
categories: [] as IAppstoreCategory[],
|
||||
updateCount: loadState<number>('settings', 'appstoreUpdateCount', 0),
|
||||
updateCount: loadState<number>('appstore', 'appstoreUpdateCount', 0),
|
||||
loading: {
|
||||
apps: false,
|
||||
categories: false,
|
||||
|
|
@ -9,23 +9,23 @@ import { loadState } from '@nextcloud/initial-state'
|
|||
import { PwdConfirmationMode } from '@nextcloud/password-confirmation'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import Vue from 'vue'
|
||||
import logger from '../logger.ts'
|
||||
import logger from '../utils/logger.ts'
|
||||
import api from './api.js'
|
||||
|
||||
const state = {
|
||||
apps: [],
|
||||
bundles: loadState('settings', 'appstoreBundles', []),
|
||||
bundles: loadState('appstore', 'appstoreBundles', []),
|
||||
categories: [],
|
||||
updateCount: loadState('settings', 'appstoreUpdateCount', 0),
|
||||
updateCount: loadState('appstore', 'appstoreUpdateCount', 0),
|
||||
loading: {},
|
||||
gettingCategoriesPromise: null,
|
||||
appApiEnabled: loadState('settings', 'appApiEnabled', false),
|
||||
appApiEnabled: loadState('appstore', 'appApiEnabled', false),
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
|
||||
APPS_API_FAILURE(state, error) {
|
||||
showError(t('settings', 'An error occurred during the request. Unable to proceed.') + '<br>' + error.error.response.data.data.message, { isHTML: true })
|
||||
showError(t('appstore', 'An error occurred during the request. Unable to proceed.') + '<br>' + error.error.response.data.data.message, { isHTML: true })
|
||||
logger.error('An error occurred during the request. Unable to proceed.', { state, error })
|
||||
},
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ const actions = {
|
|||
if (response.data.update_required) {
|
||||
showInfo(
|
||||
t(
|
||||
'settings',
|
||||
'appstore',
|
||||
'The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds.',
|
||||
),
|
||||
{
|
||||
|
|
@ -223,10 +223,10 @@ const actions = {
|
|||
})
|
||||
.catch(() => {
|
||||
if (!Array.isArray(appId)) {
|
||||
showError(t('settings', 'Error: This app cannot be enabled because it makes the server unstable'))
|
||||
showError(t('appstore', 'Error: This app cannot be enabled because it makes the server unstable'))
|
||||
context.commit('setError', {
|
||||
appId: apps,
|
||||
error: t('settings', 'Error: This app cannot be enabled because it makes the server unstable'),
|
||||
error: t('appstore', 'Error: This app cannot be enabled because it makes the server unstable'),
|
||||
})
|
||||
context.dispatch('disableApp', { appId })
|
||||
}
|
||||
39
apps/appstore/src/store/index.js
Normal file
39
apps/appstore/src/store/index.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { Store } from 'vuex'
|
||||
import logger from '../utils/logger.js'
|
||||
import apps from './apps.js'
|
||||
|
||||
const mutations = {
|
||||
API_FAILURE(state, error) {
|
||||
try {
|
||||
const message = error.error.response.data.ocs.meta.message
|
||||
showError(t('appstore', 'An error occurred during the request. Unable to proceed.') + '<br>' + message, { isHTML: true })
|
||||
} catch {
|
||||
showError(t('appstore', 'An error occurred during the request. Unable to proceed.'))
|
||||
}
|
||||
logger.error('An error occurred during the request.', { state, error })
|
||||
},
|
||||
}
|
||||
|
||||
let store = null
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function useStore() {
|
||||
if (store === null) {
|
||||
store = new Store({
|
||||
modules: {
|
||||
apps,
|
||||
},
|
||||
strict: !PRODUCTION,
|
||||
mutations,
|
||||
})
|
||||
}
|
||||
return store
|
||||
}
|
||||
33
apps/appstore/src/utils/handlers.ts
Normal file
33
apps/appstore/src/utils/handlers.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { AxiosError } from '@nextcloud/axios'
|
||||
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import logger from '../utils/logger.ts'
|
||||
|
||||
/**
|
||||
* @param error the error
|
||||
* @param message the message to display
|
||||
*/
|
||||
export function handleError(error: AxiosError, message: string) {
|
||||
let fullMessage = ''
|
||||
|
||||
if (message) {
|
||||
fullMessage += message
|
||||
}
|
||||
|
||||
if (error.response?.status === 429) {
|
||||
if (fullMessage) {
|
||||
fullMessage += '\n'
|
||||
}
|
||||
fullMessage += t('appstore', 'There were too many requests from your network. Retry later or contact your administrator if this is an error.')
|
||||
}
|
||||
|
||||
fullMessage = fullMessage || t('appstore', 'Error')
|
||||
showError(fullMessage)
|
||||
logger.error(fullMessage, { error })
|
||||
}
|
||||
11
apps/appstore/src/utils/logger.ts
Normal file
11
apps/appstore/src/utils/logger.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { getLoggerBuilder } from '@nextcloud/logger'
|
||||
|
||||
export default getLoggerBuilder()
|
||||
.setApp('appstore')
|
||||
.detectUser()
|
||||
.build()
|
||||
16
apps/appstore/src/views/App.vue
Normal file
16
apps/appstore/src/views/App.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<template>
|
||||
<NcContent app-name="settings">
|
||||
<router-view name="navigation" />
|
||||
<router-view />
|
||||
<router-view name="sidebar" />
|
||||
</NcContent>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import NcContent from '@nextcloud/vue/components/NcContent'
|
||||
</script>
|
||||
|
|
@ -92,7 +92,7 @@ const licenseText = computed(() => {
|
|||
return ''
|
||||
}
|
||||
if (app.value.license !== '') {
|
||||
return t('settings', 'Version {version}, {license}-licensed', { version: app.value.version, license: app.value.licence.toString().toUpperCase() })
|
||||
return t('settings', 'Version {version}, {license}-licensed', { version: app.value.version, license: app.value.license.toString().toUpperCase() })
|
||||
}
|
||||
return t('settings', 'Version {version}', { version: app.value.version })
|
||||
})
|
||||
6
apps/appstore/templates/empty.php
Normal file
6
apps/appstore/templates/empty.php
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
?>
|
||||
63
apps/appstore/tests/AppInfo/ApplicationTest.php
Normal file
63
apps/appstore/tests/AppInfo/ApplicationTest.php
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Settings\Tests\AppInfo;
|
||||
|
||||
use OCA\Settings\AppInfo\Application;
|
||||
use OCA\Settings\Controller\AdminSettingsController;
|
||||
use OCA\Settings\Controller\AppSettingsController;
|
||||
use OCA\Settings\Controller\AuthSettingsController;
|
||||
use OCA\Settings\Controller\CheckSetupController;
|
||||
use OCA\Settings\Controller\LogSettingsController;
|
||||
use OCA\Settings\Controller\MailSettingsController;
|
||||
use OCA\Settings\Controller\UsersController;
|
||||
use OCA\Settings\Middleware\SubadminMiddleware;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\IAppContainer;
|
||||
use OCP\AppFramework\Middleware;
|
||||
use Test\TestCase;
|
||||
|
||||
/**
|
||||
* Class ApplicationTest
|
||||
*
|
||||
* @package Tests\Settings
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\Group('DB')]
|
||||
class ApplicationTest extends TestCase {
|
||||
protected Application $app;
|
||||
protected IAppContainer $container;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->app = new Application();
|
||||
$this->container = $this->app->getContainer();
|
||||
}
|
||||
|
||||
public function testContainerAppName(): void {
|
||||
$this->app = new Application();
|
||||
$this->assertEquals('settings', $this->container->get('appName'));
|
||||
}
|
||||
|
||||
public static function dataContainerQuery(): array {
|
||||
return [
|
||||
[AdminSettingsController::class, Controller::class],
|
||||
[AppSettingsController::class, Controller::class],
|
||||
[AuthSettingsController::class, Controller::class],
|
||||
[CheckSetupController::class, Controller::class],
|
||||
[LogSettingsController::class, Controller::class],
|
||||
[MailSettingsController::class, Controller::class],
|
||||
[UsersController::class, Controller::class],
|
||||
|
||||
[SubadminMiddleware::class, Middleware::class],
|
||||
];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('dataContainerQuery')]
|
||||
public function testContainerQuery(string $service, string $expected): void {
|
||||
$this->assertTrue($this->container->query($service) instanceof $expected);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,8 +21,6 @@ use OCP\AppFramework\Services\IInitialState;
|
|||
use OCP\Files\AppData\IAppDataFactory;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IConfig;
|
||||
use OCP\IGroup;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\INavigationManager;
|
||||
use OCP\IRequest;
|
||||
|
|
@ -37,7 +35,7 @@ use Test\TestCase;
|
|||
*
|
||||
* @package Tests\Settings\Controller
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\Group(name: 'DB')]
|
||||
#[\PHPUnit\Framework\Attributes\Group('DB')]
|
||||
class AppSettingsControllerTest extends TestCase {
|
||||
private IRequest&MockObject $request;
|
||||
private IL10N&MockObject $l10n;
|
||||
|
|
@ -47,7 +45,6 @@ class AppSettingsControllerTest extends TestCase {
|
|||
private CategoryFetcher&MockObject $categoryFetcher;
|
||||
private AppFetcher&MockObject $appFetcher;
|
||||
private IFactory&MockObject $l10nFactory;
|
||||
private IGroupManager&MockObject $groupManager;
|
||||
private BundleFetcher&MockObject $bundleFetcher;
|
||||
private Installer&MockObject $installer;
|
||||
private IURLGenerator&MockObject $urlGenerator;
|
||||
|
|
@ -73,7 +70,6 @@ class AppSettingsControllerTest extends TestCase {
|
|||
$this->categoryFetcher = $this->createMock(CategoryFetcher::class);
|
||||
$this->appFetcher = $this->createMock(AppFetcher::class);
|
||||
$this->l10nFactory = $this->createMock(IFactory::class);
|
||||
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||
$this->bundleFetcher = $this->createMock(BundleFetcher::class);
|
||||
$this->installer = $this->createMock(Installer::class);
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
|
|
@ -93,7 +89,6 @@ class AppSettingsControllerTest extends TestCase {
|
|||
$this->categoryFetcher,
|
||||
$this->appFetcher,
|
||||
$this->l10nFactory,
|
||||
$this->groupManager,
|
||||
$this->bundleFetcher,
|
||||
$this->installer,
|
||||
$this->urlGenerator,
|
||||
|
|
@ -173,16 +168,9 @@ class AppSettingsControllerTest extends TestCase {
|
|||
->expects($this->once())
|
||||
->method('setActiveEntry')
|
||||
->with('core_apps');
|
||||
$this->groupManager->expects($this->once())
|
||||
->method('search')
|
||||
->with($this->equalTo(''), $this->equalTo(5))
|
||||
->willReturn([
|
||||
$this->createMock(IGroup::class),
|
||||
$this->createMock(IGroup::class),
|
||||
]);
|
||||
|
||||
$this->initialState
|
||||
->expects($this->exactly(5))
|
||||
->expects($this->exactly(4))
|
||||
->method('provideInitialState');
|
||||
|
||||
$policy = new ContentSecurityPolicy();
|
||||
|
|
@ -213,16 +201,9 @@ class AppSettingsControllerTest extends TestCase {
|
|||
->expects($this->once())
|
||||
->method('setActiveEntry')
|
||||
->with('core_apps');
|
||||
$this->groupManager->expects($this->once())
|
||||
->method('search')
|
||||
->with($this->equalTo(''), $this->equalTo(5))
|
||||
->willReturn([
|
||||
$this->createMock(IGroup::class),
|
||||
$this->createMock(IGroup::class),
|
||||
]);
|
||||
|
||||
$this->initialState
|
||||
->expects($this->exactly(5))
|
||||
->expects($this->exactly(4))
|
||||
->method('provideInitialState');
|
||||
|
||||
$policy = new ContentSecurityPolicy();
|
||||
|
|
@ -20,22 +20,6 @@ return [
|
|||
['name' => 'MailSettings#storeCredentials', 'url' => '/settings/admin/mailsettings/credentials', 'verb' => 'POST' , 'root' => ''],
|
||||
['name' => 'MailSettings#sendTestMail', 'url' => '/settings/admin/mailtest', 'verb' => 'POST' , 'root' => ''],
|
||||
|
||||
['name' => 'AppSettings#getAppDiscoverJSON', 'url' => '/settings/api/apps/discover', 'verb' => 'GET', 'root' => ''],
|
||||
['name' => 'AppSettings#getAppDiscoverMedia', 'url' => '/settings/api/apps/media', 'verb' => 'GET', 'root' => ''],
|
||||
['name' => 'AppSettings#listCategories', 'url' => '/settings/apps/categories', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#viewApps', 'url' => '/settings/apps', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#listApps', 'url' => '/settings/apps/list', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#enableApp', 'url' => '/settings/apps/enable/{appId}', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#enableApp', 'url' => '/settings/apps/enable/{appId}', 'verb' => 'POST' , 'root' => ''],
|
||||
['name' => 'AppSettings#enableApps', 'url' => '/settings/apps/enable', 'verb' => 'POST' , 'root' => ''],
|
||||
['name' => 'AppSettings#disableApp', 'url' => '/settings/apps/disable/{appId}', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#disableApps', 'url' => '/settings/apps/disable', 'verb' => 'POST' , 'root' => ''],
|
||||
['name' => 'AppSettings#updateApp', 'url' => '/settings/apps/update/{appId}', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#uninstallApp', 'url' => '/settings/apps/uninstall/{appId}', 'verb' => 'GET' , 'root' => ''],
|
||||
['name' => 'AppSettings#viewApps', 'url' => '/settings/apps/{category}', 'verb' => 'GET', 'defaults' => ['category' => ''] , 'root' => ''],
|
||||
['name' => 'AppSettings#viewApps', 'url' => '/settings/apps/{category}/{id}', 'verb' => 'GET', 'defaults' => ['category' => '', 'id' => ''] , 'root' => ''],
|
||||
['name' => 'AppSettings#force', 'url' => '/settings/apps/force', 'verb' => 'POST' , 'root' => ''],
|
||||
|
||||
['name' => 'Users#setDisplayName', 'url' => '/settings/users/{username}/displayName', 'verb' => 'POST' , 'root' => ''],
|
||||
['name' => 'Users#setEMailAddress', 'url' => '/settings/users/{id}/mailAddress', 'verb' => 'PUT' , 'root' => ''],
|
||||
['name' => 'Users#setUserSettings', 'url' => '/settings/users/{username}/settings', 'verb' => 'PUT' , 'root' => ''],
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ use OCA\Settings\Listener\UserAddedToGroupActivityListener;
|
|||
use OCA\Settings\Listener\UserRemovedFromGroupActivityListener;
|
||||
use OCA\Settings\Mailer\NewUserMailHelper;
|
||||
use OCA\Settings\Middleware\SubadminMiddleware;
|
||||
use OCA\Settings\Search\AppSearch;
|
||||
use OCA\Settings\Search\SectionSearch;
|
||||
use OCA\Settings\Search\UserSearch;
|
||||
use OCA\Settings\Settings\Admin\MailProvider;
|
||||
|
|
@ -119,7 +118,6 @@ class Application extends App implements IBootstrap {
|
|||
$context->registerServiceAlias('SubadminMiddleware', SubadminMiddleware::class);
|
||||
$context->registerMiddleware(SubadminMiddleware::class);
|
||||
$context->registerSearchProvider(SectionSearch::class);
|
||||
$context->registerSearchProvider(AppSearch::class);
|
||||
$context->registerSearchProvider(UserSearch::class);
|
||||
|
||||
$context->registerConfigLexicon(ConfigLexicon::class);
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ class UsersController extends Controller {
|
|||
$this->initialState->provideInitialState('usersSettings', $serverData);
|
||||
|
||||
Util::addStyle('settings', 'settings');
|
||||
Util::addScript('settings', 'vue-settings-apps-users-management');
|
||||
Util::addScript('settings', 'vue-settings-users-management');
|
||||
|
||||
return new TemplateResponse('settings', 'settings/empty', ['pageTitle' => $this->l10n->t('Settings')]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,9 @@
|
|||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { RouteConfig } from 'vue-router'
|
||||
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
|
||||
const appstoreEnabled = loadState<boolean>('settings', 'appstoreEnabled', true)
|
||||
|
||||
// Dynamic loading
|
||||
const AppStore = () => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStore.vue')
|
||||
const AppStoreNavigation = () => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStoreNavigation.vue')
|
||||
const AppStoreSidebar = () => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStoreSidebar.vue')
|
||||
|
||||
const UserManagement = () => import(/* webpackChunkName: 'settings-users' */'../views/UserManagement.vue')
|
||||
const UserManagementNavigation = () => import(/* webpackChunkName: 'settings-users' */'../views/UserManagementNavigation.vue')
|
||||
|
||||
|
|
@ -32,33 +24,6 @@ const routes: RouteConfig[] = [
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/:index(index.php/)?settings/apps',
|
||||
name: 'apps',
|
||||
redirect: {
|
||||
name: 'apps-category',
|
||||
params: {
|
||||
category: appstoreEnabled ? 'discover' : 'installed',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
default: AppStore,
|
||||
navigation: AppStoreNavigation,
|
||||
sidebar: AppStoreSidebar,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: ':category',
|
||||
name: 'apps-category',
|
||||
children: [
|
||||
{
|
||||
path: ':id',
|
||||
name: 'apps-details',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export default routes
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import axios from '@nextcloud/axios'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
|
||||
export default () => {
|
||||
return axios.get(generateOcsUrl('core/navigation', 2) + '/apps?format=json')
|
||||
.then(({ data }) => {
|
||||
if (data.ocs.meta.statuscode !== 200) {
|
||||
return
|
||||
}
|
||||
|
||||
emit('nextcloud:app-menu.refresh', { apps: data.ocs.data })
|
||||
window.dispatchEvent(new Event('resize'))
|
||||
})
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@
|
|||
import { showError } from '@nextcloud/dialogs'
|
||||
import { Store } from 'vuex'
|
||||
import logger from '../logger.js'
|
||||
import apps from './apps.js'
|
||||
import oc from './oc.js'
|
||||
import settings from './users-settings.js'
|
||||
import users from './users.js'
|
||||
|
|
@ -33,7 +32,6 @@ export function useStore() {
|
|||
store = new Store({
|
||||
modules: {
|
||||
users,
|
||||
apps,
|
||||
settings,
|
||||
oc,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ namespace OCA\Settings\Tests\AppInfo;
|
|||
|
||||
use OCA\Settings\AppInfo\Application;
|
||||
use OCA\Settings\Controller\AdminSettingsController;
|
||||
use OCA\Settings\Controller\AppSettingsController;
|
||||
use OCA\Settings\Controller\AuthSettingsController;
|
||||
use OCA\Settings\Controller\CheckSetupController;
|
||||
use OCA\Settings\Controller\LogSettingsController;
|
||||
|
|
@ -45,7 +44,6 @@ class ApplicationTest extends TestCase {
|
|||
public static function dataContainerQuery(): array {
|
||||
return [
|
||||
[AdminSettingsController::class, Controller::class],
|
||||
[AppSettingsController::class, Controller::class],
|
||||
[AuthSettingsController::class, Controller::class],
|
||||
[CheckSetupController::class, Controller::class],
|
||||
[LogSettingsController::class, Controller::class],
|
||||
|
|
|
|||
1
build/frontend-legacy/apps/appstore
Symbolic link
1
build/frontend-legacy/apps/appstore
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../../apps/appstore
|
||||
|
|
@ -5,6 +5,9 @@
|
|||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
appstore: {
|
||||
main: path.join(__dirname, 'apps/appstore/src', 'main.ts'),
|
||||
},
|
||||
core: {
|
||||
'ajax-cron': path.join(__dirname, 'core/src', 'ajax-cron.ts'),
|
||||
install: path.join(__dirname, 'core/src', 'install.ts'),
|
||||
|
|
@ -56,12 +59,12 @@ module.exports = {
|
|||
'vue-settings-admin-security': path.join(__dirname, 'apps/settings/src', 'main-admin-security.js'),
|
||||
'vue-settings-admin-settings-presets': path.join(__dirname, 'apps/settings/src', 'main-admin-settings-presets.js'),
|
||||
'vue-settings-admin-sharing': path.join(__dirname, 'apps/settings/src', 'admin-settings-sharing.ts'),
|
||||
'vue-settings-apps-users-management': path.join(__dirname, 'apps/settings/src', 'main-apps-users-management.ts'),
|
||||
'vue-settings-nextcloud-pdf': path.join(__dirname, 'apps/settings/src', 'main-nextcloud-pdf.js'),
|
||||
'vue-settings-personal-info': path.join(__dirname, 'apps/settings/src', 'main-personal-info.js'),
|
||||
'vue-settings-personal-password': path.join(__dirname, 'apps/settings/src', 'main-personal-password.js'),
|
||||
'vue-settings-personal-security': path.join(__dirname, 'apps/settings/src', 'main-personal-security.js'),
|
||||
'vue-settings-personal-webauthn': path.join(__dirname, 'apps/settings/src', 'main-personal-webauth.js'),
|
||||
'vue-settings-users-management': path.join(__dirname, 'apps/settings/src', 'main-users-management.ts'),
|
||||
'declarative-settings-forms': path.join(__dirname, 'apps/settings/src', 'main-declarative-settings-forms.ts'),
|
||||
},
|
||||
updatenotification: {
|
||||
|
|
|
|||
|
|
@ -632,6 +632,7 @@ Feature: provisioning
|
|||
Then the OCS status code should be "100"
|
||||
And the HTTP status code should be "200"
|
||||
And apps returned are
|
||||
| appstore |
|
||||
| cloud_federation_api |
|
||||
| comments |
|
||||
| contactsinteraction |
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
"activity",
|
||||
"admin_audit",
|
||||
"app_api",
|
||||
"appstore",
|
||||
"bruteforcesettings",
|
||||
"circles",
|
||||
"cloud_federation_api",
|
||||
|
|
@ -57,6 +58,7 @@
|
|||
"defaultEnabled": [
|
||||
"activity",
|
||||
"app_api",
|
||||
"appstore",
|
||||
"bruteforcesettings",
|
||||
"circles",
|
||||
"comments",
|
||||
|
|
@ -100,6 +102,7 @@
|
|||
"webhook_listeners"
|
||||
],
|
||||
"alwaysEnabled": [
|
||||
"appstore",
|
||||
"cloud_federation_api",
|
||||
"dav",
|
||||
"federatedfilesharing",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class RouteParser {
|
|||
private $controllerNameCache = [];
|
||||
|
||||
private const rootUrlApps = [
|
||||
'appstore',
|
||||
'cloud_federation_api',
|
||||
'core',
|
||||
'files_sharing_raw',
|
||||
|
|
|
|||
|
|
@ -245,8 +245,8 @@ class NavigationManager implements INavigationManager {
|
|||
'type' => 'settings',
|
||||
'id' => 'core_apps',
|
||||
'order' => 5,
|
||||
'href' => $this->urlGenerator->linkToRoute('settings.AppSettings.viewApps'),
|
||||
'icon' => $this->urlGenerator->imagePath('settings', 'apps.svg'),
|
||||
'href' => $this->urlGenerator->linkToRoute('appstore.AppSettings.viewApps'),
|
||||
'icon' => $this->urlGenerator->imagePath('appstore', 'apps.svg'),
|
||||
'name' => $l->t('Apps'),
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -266,6 +266,8 @@ class Router implements IRouter {
|
|||
$app = $this->appManager->cleanAppId($app);
|
||||
\OC::$REQUESTEDAPP = $app;
|
||||
$this->loadRoutes($app);
|
||||
} elseif (str_starts_with($url, '/settings/apps')) {
|
||||
$this->loadRoutes('appstore');
|
||||
} elseif (str_starts_with($url, '/settings/')) {
|
||||
$this->loadRoutes('settings');
|
||||
} elseif (str_starts_with($url, '/core/')) {
|
||||
|
|
|
|||
20
package-lock.json
generated
20
package-lock.json
generated
|
|
@ -31,9 +31,11 @@
|
|||
"@vueuse/integrations": "^14.1.0",
|
||||
"color": "^5.0.3",
|
||||
"debounce": "^3.0.0",
|
||||
"marked": "^17.0.1",
|
||||
"pinia": "^3.0.4",
|
||||
"sortablejs": "^1.15.7",
|
||||
"vue": "^3.5.33",
|
||||
"vue-router": "^5.0.6",
|
||||
"vuex": "^4.1.0",
|
||||
"webdav": "^5.9.0"
|
||||
},
|
||||
|
|
@ -11448,6 +11450,18 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "17.0.6",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-17.0.6.tgz",
|
||||
"integrity": "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/material-colors": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||
|
|
@ -17771,9 +17785,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.3.tgz",
|
||||
"integrity": "sha512-nG1c7aAFac7NYj8Hluo68WyWfc41xkEjaR0ViLHCa3oDvTQ/nIuLJlXJX1NUPw/DXzx/8+OKMng045HHQKQKWw==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.6.tgz",
|
||||
"integrity": "sha512-9+kmUTGbKMyW9Asoy98IXXYIzrTMT7JDAdpDDeEkorHvybpUvBI2wsrSM5jFOXrFydpzRFJ9vAh+80DN2PGu9w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/generator": "^7.28.6",
|
||||
|
|
|
|||
|
|
@ -60,9 +60,11 @@
|
|||
"@vueuse/integrations": "^14.1.0",
|
||||
"color": "^5.0.3",
|
||||
"debounce": "^3.0.0",
|
||||
"marked": "^17.0.1",
|
||||
"pinia": "^3.0.4",
|
||||
"sortablejs": "^1.15.7",
|
||||
"vue": "^3.5.33",
|
||||
"vue-router": "^5.0.6",
|
||||
"vuex": "^4.1.0",
|
||||
"webdav": "^5.9.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -549,6 +549,7 @@ class AppManagerTest extends TestCase {
|
|||
$this->appConfig->setValue('test2', 'enabled', 'no');
|
||||
$this->appConfig->setValue('test3', 'enabled', '["foo"]');
|
||||
$apps = [
|
||||
'appstore',
|
||||
'cloud_federation_api',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
|
|
@ -580,6 +581,7 @@ class AppManagerTest extends TestCase {
|
|||
$this->appConfig->setValue('test3', 'enabled', '["foo"]');
|
||||
$this->appConfig->setValue('test4', 'enabled', '["asd"]');
|
||||
$enabled = [
|
||||
'appstore',
|
||||
'cloud_federation_api',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
|
|
@ -617,6 +619,7 @@ class AppManagerTest extends TestCase {
|
|||
->getMock();
|
||||
|
||||
$appInfos = [
|
||||
'appstore' => ['id' => 'appstore'],
|
||||
'cloud_federation_api' => ['id' => 'cloud_federation_api'],
|
||||
'dav' => ['id' => 'dav'],
|
||||
'files' => ['id' => 'files'],
|
||||
|
|
@ -679,6 +682,7 @@ class AppManagerTest extends TestCase {
|
|||
->getMock();
|
||||
|
||||
$appInfos = [
|
||||
'appstore' => ['id' => 'appstore'],
|
||||
'cloud_federation_api' => ['id' => 'cloud_federation_api'],
|
||||
'dav' => ['id' => 'dav'],
|
||||
'files' => ['id' => 'files'],
|
||||
|
|
@ -728,6 +732,7 @@ class AppManagerTest extends TestCase {
|
|||
$this->appConfig->setValue('test3', 'enabled', '["foo"]');
|
||||
$this->appConfig->setValue('test4', 'enabled', '["asd"]');
|
||||
$enabled = [
|
||||
'appstore',
|
||||
'cloud_federation_api',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ class AppTest extends \Test\TestCase {
|
|||
'app3',
|
||||
'appforgroup1',
|
||||
'appforgroup12',
|
||||
'appstore',
|
||||
'cloud_federation_api',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
|
|
@ -88,6 +89,7 @@ class AppTest extends \Test\TestCase {
|
|||
'app3',
|
||||
'appforgroup12',
|
||||
'appforgroup2',
|
||||
'appstore',
|
||||
'cloud_federation_api',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
|
|
@ -113,6 +115,7 @@ class AppTest extends \Test\TestCase {
|
|||
'appforgroup1',
|
||||
'appforgroup12',
|
||||
'appforgroup2',
|
||||
'appstore',
|
||||
'cloud_federation_api',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
|
|
@ -138,6 +141,7 @@ class AppTest extends \Test\TestCase {
|
|||
'appforgroup1',
|
||||
'appforgroup12',
|
||||
'appforgroup2',
|
||||
'appstore',
|
||||
'cloud_federation_api',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
|
|
@ -163,6 +167,7 @@ class AppTest extends \Test\TestCase {
|
|||
'appforgroup1',
|
||||
'appforgroup12',
|
||||
'appforgroup2',
|
||||
'appstore',
|
||||
'cloud_federation_api',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
|
|
@ -249,11 +254,11 @@ class AppTest extends \Test\TestCase {
|
|||
);
|
||||
|
||||
$apps = \OC_App::getEnabledApps();
|
||||
$this->assertEquals(['files', 'app3', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'profile', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
|
||||
$this->assertEquals(['files', 'app3', 'appstore', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'profile', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
|
||||
|
||||
// mock should not be called again here
|
||||
$apps = \OC_App::getEnabledApps();
|
||||
$this->assertEquals(['files', 'app3', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'profile', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
|
||||
$this->assertEquals(['files', 'app3', 'appstore', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'profile', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
|
||||
|
||||
$this->restoreAppConfig();
|
||||
\OC_User::setUserId(null);
|
||||
|
|
|
|||
Loading…
Reference in a new issue