2011-03-03 23:55:32 +03:00
< ? php
2019-12-03 21:57:53 +03:00
2018-02-17 17:37:57 +03:00
declare ( strict_types = 1 );
2019-12-03 21:57:53 +03:00
2011-03-11 17:25:48 +03:00
/**
2016-07-21 18:07:57 +03:00
* @ copyright Copyright ( c ) 2016 , ownCloud , Inc .
2016-10-27 18:41:15 +03:00
* @ copyright Copyright ( c ) 2016 , Lukas Reschke < lukas @ statuscode . ch >
2016-07-21 18:07:57 +03:00
*
2016-05-26 20:56:05 +03:00
* @ author Arthur Schiwon < blizzz @ arthur - schiwon . de >
2015-03-26 13:44:34 +03:00
* @ author Bart Visscher < bartv @ thisnet . nl >
* @ author Bernhard Posselt < dev @ bernhard - posselt . com >
* @ author Borjan Tchakaloff < borjan @ tchakaloff . fr >
* @ author Brice Maron < brice @ bmaron . net >
* @ author Christopher Schäpers < kondou @ ts . unde . re >
2019-12-03 21:57:53 +03:00
* @ author Christoph Wurst < christoph @ winzerhof - wurst . at >
* @ author Daniel Rudolf < github . com @ daniel - rudolf . de >
2016-05-26 20:56:05 +03:00
* @ author Frank Karlitschek < frank @ karlitschek . de >
2017-11-06 22:15:27 +03:00
* @ author Georg Ehrke < oc . list @ georgehrke . com >
2015-03-26 13:44:34 +03:00
* @ author Jakob Sack < mail @ jakobsack . de >
2016-07-21 18:07:57 +03:00
* @ author Joas Schilling < coding @ schilljs . com >
2019-12-03 21:57:53 +03:00
* @ author Jörn Friedrich Dreyer < jfd @ butonic . de >
2017-11-06 17:56:42 +03:00
* @ author Julius Haertl < jus @ bitgrid . net >
* @ author Julius Härtl < jus @ bitgrid . net >
2015-03-26 13:44:34 +03:00
* @ author Kamil Domanski < kdomanski @ kdemail . net >
2016-05-26 20:56:05 +03:00
* @ author Lukas Reschke < lukas @ statuscode . ch >
2015-03-26 13:44:34 +03:00
* @ author Markus Goetz < markus @ woboq . com >
* @ author Morris Jobke < hey @ morrisjobke . de >
2015-10-05 21:54:56 +03:00
* @ author RealRancor < Fisch . 666 @ gmx . de >
2016-07-21 19:13:36 +03:00
* @ author Robin Appelman < robin @ icewind . nl >
2016-01-12 17:02:16 +03:00
* @ author Robin McCorkell < robin @ mccorkell . me . uk >
2016-07-21 18:07:57 +03:00
* @ author Roeland Jago Douma < roeland @ famdouma . nl >
2015-03-26 13:44:34 +03:00
* @ author Sam Tuke < mail @ samtuke . com >
2017-11-06 17:56:42 +03:00
* @ author Sebastian Wessalowski < sebastian @ wessalowski . org >
2015-03-26 13:44:34 +03:00
* @ author Thomas Müller < thomas . mueller @ tmit . eu >
* @ author Thomas Tanghus < thomas @ tanghus . net >
* @ author Vincent Petry < pvince81 @ owncloud . com >
2011-03-11 17:25:48 +03:00
*
2015-03-26 13:44:34 +03:00
* @ license AGPL - 3.0
2014-05-21 14:14:10 +04:00
*
2015-03-26 13:44:34 +03:00
* This code is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License , version 3 ,
* as published by the Free Software Foundation .
2014-05-21 14:14:10 +04:00
*
2015-03-26 13:44:34 +03:00
* This program is distributed in the hope that it will be useful ,
2011-03-11 17:25:48 +03:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2015-03-26 13:44:34 +03:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
2011-03-11 17:25:48 +03:00
*
2015-03-26 13:44:34 +03:00
* You should have received a copy of the GNU Affero General Public License , version 3 ,
2019-12-03 21:57:53 +03:00
* along with this program . If not , see < http :// www . gnu . org / licenses />
2011-03-11 17:25:48 +03:00
*
*/
2014-12-11 19:02:07 +03:00
use OC\App\DependencyAnalyzer ;
use OC\App\Platform ;
2020-07-15 18:06:27 +03:00
use OC\AppFramework\Bootstrap\Coordinator ;
2017-06-01 17:56:34 +03:00
use OC\DB\MigrationService ;
2016-04-28 16:15:34 +03:00
use OC\Installer ;
2016-04-19 16:36:11 +03:00
use OC\Repair ;
2019-11-14 19:20:14 +03:00
use OC\ServerNotAvailableException ;
2016-08-16 01:52:41 +03:00
use OCP\App\ManagerEvent ;
2020-07-15 18:06:27 +03:00
use OCP\AppFramework\QueryException ;
use OCP\Authentication\IAlternativeLogin ;
2018-04-25 16:22:28 +03:00
use OCP\ILogger ;
2011-03-11 17:25:48 +03:00
2011-03-12 12:28:10 +03:00
/**
* This class manages the apps . It allows them to register and integrate in the
2014-07-09 12:20:17 +04:00
* ownCloud ecosystem . Furthermore , this class is responsible for installing ,
2011-03-12 12:28:10 +03:00
* upgrading and removing apps .
*/
2014-04-17 17:30:27 +04:00
class OC_App {
2020-04-10 17:48:31 +03:00
private static $adminForms = [];
private static $personalForms = [];
private static $appTypes = [];
private static $loadedApps = [];
private static $altLogin = [];
private static $alreadyRegistered = [];
2020-04-10 17:54:27 +03:00
public const supportedApp = 300 ;
public const officialApp = 200 ;
2011-03-03 23:55:32 +03:00
2013-02-09 18:03:47 +04:00
/**
2014-07-09 12:20:17 +04:00
* clean the appId
2015-02-02 16:47:38 +03:00
*
2020-11-20 14:19:59 +03:00
* @ psalm - taint - escape file
2020-11-21 03:57:25 +03:00
* @ psalm - taint - escape include
2020-11-20 14:19:59 +03:00
*
2018-02-17 17:37:57 +03:00
* @ param string $app AppId that needs to be cleaned
2013-02-09 18:03:47 +04:00
* @ return string
*/
2018-02-17 17:37:57 +03:00
public static function cleanAppId ( string $app ) : string {
2020-03-26 11:30:18 +03:00
return str_replace ([ '\0' , '/' , '\\' , '..' ], '' , $app );
2013-02-09 18:03:47 +04:00
}
2015-08-18 11:18:36 +03:00
/**
* Check if an app is loaded
*
* @ param string $app
* @ return bool
*/
2018-02-17 17:37:57 +03:00
public static function isAppLoaded ( string $app ) : bool {
2020-11-06 16:26:42 +03:00
return isset ( self :: $loadedApps [ $app ]);
2015-08-18 11:18:36 +03:00
}
2011-03-03 23:55:32 +03:00
/**
2014-05-19 19:50:53 +04:00
* loads all apps
2015-02-02 16:47:38 +03:00
*
2018-02-17 17:22:00 +03:00
* @ param string [] $types
2012-09-23 04:39:11 +04:00
* @ return bool
2011-03-03 23:55:32 +03:00
*
2014-07-09 12:20:17 +04:00
* This function walks through the ownCloud directory and loads all apps
2015-03-17 17:33:55 +03:00
* it can find . A directory contains an app if the file / appinfo / info . xml
2011-03-11 16:59:24 +03:00
* exists .
2012-03-30 16:39:07 +04:00
*
2018-02-17 17:22:00 +03:00
* if $types is set to non - empty array , only apps of those types will be loaded
2011-03-03 23:55:32 +03:00
*/
2018-02-17 17:22:00 +03:00
public static function loadApps ( array $types = []) : bool {
2019-02-06 19:08:41 +03:00
if (( bool ) \OC :: $server -> getSystemConfig () -> getValue ( 'maintenance' , false )) {
2014-07-24 19:51:28 +04:00
return false ;
}
2012-05-31 15:00:58 +04:00
// Load the enabled apps here
2012-03-30 16:00:24 +04:00
$apps = self :: getEnabledApps ();
2015-09-15 12:55:23 +03:00
// Add each apps' folder as allowed class path
2020-04-10 15:19:56 +03:00
foreach ( $apps as $app ) {
2020-11-07 19:24:41 +03:00
// If the app is already loaded then autoloading it makes no sense
if ( ! isset ( self :: $loadedApps [ $app ])) {
$path = self :: getAppPath ( $app );
if ( $path !== false ) {
self :: registerAutoloading ( $app , $path );
}
2015-09-15 13:09:48 +03:00
}
2015-09-15 12:55:23 +03:00
}
2012-05-26 22:40:12 +04:00
// prevent app.php from printing output
ob_start ();
2014-04-17 17:30:27 +04:00
foreach ( $apps as $app ) {
2020-11-06 16:26:42 +03:00
if ( ! isset ( self :: $loadedApps [ $app ]) && ( $types === [] || self :: isType ( $app , $types ))) {
2014-01-31 18:12:21 +04:00
self :: loadApp ( $app );
2011-06-19 17:18:52 +04:00
}
2011-03-03 23:55:32 +03:00
}
2012-05-26 22:40:12 +04:00
ob_end_clean ();
2011-03-03 23:55:32 +03:00
2013-02-09 20:27:57 +04:00
return true ;
}
2011-03-03 23:55:32 +03:00
2012-05-14 02:28:22 +04:00
/**
* load a single app
2014-04-17 17:30:27 +04:00
*
2012-09-23 04:39:11 +04:00
* @ param string $app
2018-02-15 13:20:22 +03:00
* @ throws Exception
2012-05-14 02:28:22 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function loadApp ( string $app ) {
2020-11-06 16:26:42 +03:00
self :: $loadedApps [ $app ] = true ;
2016-02-14 22:57:09 +03:00
$appPath = self :: getAppPath ( $app );
2020-04-10 15:19:56 +03:00
if ( $appPath === false ) {
2016-02-14 22:57:09 +03:00
return ;
}
2016-05-02 15:11:34 +03:00
// in case someone calls loadApp() directly
self :: registerAutoloading ( $app , $appPath );
2020-07-15 18:06:27 +03:00
/** @var Coordinator $coordinator */
$coordinator = \OC :: $server -> query ( Coordinator :: class );
2020-07-13 15:58:52 +03:00
$isBootable = $coordinator -> isBootable ( $app );
$hasAppPhpFile = is_file ( $appPath . '/appinfo/app.php' );
if ( $isBootable && $hasAppPhpFile ) {
\OC :: $server -> getLogger () -> error ( '/appinfo/app.php is not loaded when \OCP\AppFramework\Bootstrap\IBootstrap on the application class is used. Migrate everything from app.php to the Application class.' , [
'app' => $app ,
]);
} elseif ( $hasAppPhpFile ) {
2020-06-30 10:43:40 +03:00
\OC :: $server -> getLogger () -> debug ( '/appinfo/app.php is deprecated, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.' , [
'app' => $app ,
]);
2014-10-04 00:13:55 +04:00
\OC :: $server -> getEventLogger () -> start ( 'load_app_' . $app , 'Load app: ' . $app );
2018-02-15 13:20:22 +03:00
try {
self :: requireAppFile ( $app );
2018-08-24 18:16:54 +03:00
} catch ( Throwable $ex ) {
2020-04-10 15:19:56 +03:00
if ( $ex instanceof ServerNotAvailableException ) {
2019-11-14 19:20:14 +03:00
throw $ex ;
}
2019-12-10 11:01:35 +03:00
if ( ! \OC :: $server -> getAppManager () -> isShipped ( $app ) && ! self :: isType ( $app , [ 'authentication' ])) {
2020-05-19 11:48:57 +03:00
\OC :: $server -> getLogger () -> logException ( $ex , [
'message' => " App $app threw an error during app.php load and will be disabled: " . $ex -> getMessage (),
]);
2019-12-10 11:01:35 +03:00
// Only disable apps which are not shipped and that are not authentication apps
2019-07-23 11:28:47 +03:00
\OC :: $server -> getAppManager () -> disableApp ( $app , true );
2020-05-19 11:48:57 +03:00
} else {
\OC :: $server -> getLogger () -> logException ( $ex , [
'message' => " App $app threw an error during app.php load: " . $ex -> getMessage (),
]);
2018-02-15 13:20:22 +03:00
}
}
2014-10-04 00:13:55 +04:00
\OC :: $server -> getEventLogger () -> end ( 'load_app_' . $app );
2012-05-14 02:28:22 +04:00
}
2020-06-17 15:44:02 +03:00
$coordinator -> bootApp ( $app );
2016-10-20 18:57:44 +03:00
$info = self :: getAppInfo ( $app );
if ( ! empty ( $info [ 'activity' ][ 'filters' ])) {
foreach ( $info [ 'activity' ][ 'filters' ] as $filter ) {
\OC :: $server -> getActivityManager () -> registerFilter ( $filter );
}
}
2016-10-25 19:00:25 +03:00
if ( ! empty ( $info [ 'activity' ][ 'settings' ])) {
foreach ( $info [ 'activity' ][ 'settings' ] as $setting ) {
\OC :: $server -> getActivityManager () -> registerSetting ( $setting );
}
}
2016-11-04 13:33:33 +03:00
if ( ! empty ( $info [ 'activity' ][ 'providers' ])) {
foreach ( $info [ 'activity' ][ 'providers' ] as $provider ) {
\OC :: $server -> getActivityManager () -> registerProvider ( $provider );
}
}
2018-01-29 15:14:56 +03:00
if ( ! empty ( $info [ 'settings' ][ 'admin' ])) {
foreach ( $info [ 'settings' ][ 'admin' ] as $setting ) {
\OC :: $server -> getSettingsManager () -> registerSetting ( 'admin' , $setting );
}
}
if ( ! empty ( $info [ 'settings' ][ 'admin-section' ])) {
foreach ( $info [ 'settings' ][ 'admin-section' ] as $section ) {
\OC :: $server -> getSettingsManager () -> registerSection ( 'admin' , $section );
}
}
if ( ! empty ( $info [ 'settings' ][ 'personal' ])) {
foreach ( $info [ 'settings' ][ 'personal' ] as $setting ) {
\OC :: $server -> getSettingsManager () -> registerSetting ( 'personal' , $setting );
}
}
if ( ! empty ( $info [ 'settings' ][ 'personal-section' ])) {
foreach ( $info [ 'settings' ][ 'personal-section' ] as $section ) {
\OC :: $server -> getSettingsManager () -> registerSection ( 'personal' , $section );
}
}
2017-09-27 02:22:17 +03:00
if ( ! empty ( $info [ 'collaboration' ][ 'plugins' ])) {
// deal with one or many plugin entries
$plugins = isset ( $info [ 'collaboration' ][ 'plugins' ][ 'plugin' ][ '@value' ]) ?
[ $info [ 'collaboration' ][ 'plugins' ][ 'plugin' ]] : $info [ 'collaboration' ][ 'plugins' ][ 'plugin' ];
foreach ( $plugins as $plugin ) {
2020-04-10 15:19:56 +03:00
if ( $plugin [ '@attributes' ][ 'type' ] === 'collaborator-search' ) {
2017-09-27 02:22:17 +03:00
$pluginInfo = [
2017-09-27 14:22:59 +03:00
'shareType' => $plugin [ '@attributes' ][ 'share-type' ],
2017-09-27 02:22:17 +03:00
'class' => $plugin [ '@value' ],
];
\OC :: $server -> getCollaboratorSearch () -> registerPlugin ( $pluginInfo );
2020-04-10 11:35:09 +03:00
} elseif ( $plugin [ '@attributes' ][ 'type' ] === 'autocomplete-sort' ) {
2017-08-30 11:56:02 +03:00
\OC :: $server -> getAutoCompleteManager () -> registerSorter ( $plugin [ '@value' ]);
2017-09-27 02:22:17 +03:00
}
2017-09-06 22:57:00 +03:00
}
}
2012-05-14 02:28:22 +04:00
}
2016-05-02 15:11:34 +03:00
/**
2016-05-10 12:49:55 +03:00
* @ internal
2016-05-02 15:11:34 +03:00
* @ param string $app
* @ param string $path
2020-01-13 17:14:56 +03:00
* @ param bool $force
2016-05-02 15:11:34 +03:00
*/
2020-01-13 17:14:56 +03:00
public static function registerAutoloading ( string $app , string $path , bool $force = false ) {
2016-08-23 00:49:46 +03:00
$key = $app . '-' . $path ;
2020-01-13 17:14:56 +03:00
if ( ! $force && isset ( self :: $alreadyRegistered [ $key ])) {
2016-08-23 00:49:46 +03:00
return ;
}
2017-10-17 12:49:32 +03:00
2016-08-23 00:49:46 +03:00
self :: $alreadyRegistered [ $key ] = true ;
2017-10-17 12:49:32 +03:00
2016-05-02 15:11:34 +03:00
// Register on PSR-4 composer autoloader
$appNamespace = \OC\AppFramework\App :: buildAppNamespace ( $app );
2017-03-22 13:50:58 +03:00
\OC :: $server -> registerNamespace ( $app , $appNamespace );
2017-10-17 12:49:32 +03:00
if ( file_exists ( $path . '/composer/autoload.php' )) {
require_once $path . '/composer/autoload.php' ;
} else {
\OC :: $composerAutoloader -> addPsr4 ( $appNamespace . '\\' , $path . '/lib/' , true );
// Register on legacy autoloader
\OC :: $loader -> addValidRoot ( $path );
}
// Register Test namespace only when testing
2016-07-25 11:58:03 +03:00
if ( defined ( 'PHPUNIT_RUN' ) || defined ( 'CLI_TEST_RUN' )) {
2016-05-02 15:11:34 +03:00
\OC :: $composerAutoloader -> addPsr4 ( $appNamespace . '\\Tests\\' , $path . '/tests/' , true );
}
}
2014-10-17 14:28:27 +04:00
/**
* Load app . php from the given app
*
* @ param string $app app name
2018-02-15 14:23:49 +03:00
* @ throws Error
2014-10-17 14:28:27 +04:00
*/
2018-02-17 17:37:57 +03:00
private static function requireAppFile ( string $app ) {
2018-02-15 13:20:22 +03:00
// encapsulated here to avoid variable scope conflicts
require_once $app . '/appinfo/app.php' ;
2014-10-17 14:28:27 +04:00
}
2012-03-30 16:39:07 +04:00
/**
2012-05-14 19:58:50 +04:00
* check if an app is of a specific type
2014-04-17 17:30:27 +04:00
*
2012-03-30 16:39:07 +04:00
* @ param string $app
2018-02-17 17:22:00 +03:00
* @ param array $types
2012-09-23 04:39:11 +04:00
* @ return bool
2012-03-30 16:39:07 +04:00
*/
2018-02-17 17:22:00 +03:00
public static function isType ( string $app , array $types ) : bool {
2014-04-17 17:30:27 +04:00
$appTypes = self :: getAppTypes ( $app );
foreach ( $types as $type ) {
if ( array_search ( $type , $appTypes ) !== false ) {
2012-03-30 16:39:07 +04:00
return true ;
}
}
return false ;
}
2012-05-02 13:14:11 +04:00
2012-04-14 19:53:02 +04:00
/**
* get the types of an app
2014-04-17 17:30:27 +04:00
*
2012-04-14 19:53:02 +04:00
* @ param string $app
* @ return array
*/
2018-02-17 17:37:57 +03:00
private static function getAppTypes ( string $app ) : array {
2012-04-14 19:53:02 +04:00
//load the cache
2014-04-17 17:30:27 +04:00
if ( count ( self :: $appTypes ) == 0 ) {
2015-07-03 15:16:29 +03:00
self :: $appTypes = \OC :: $server -> getAppConfig () -> getValues ( false , 'types' );
2012-04-14 19:53:02 +04:00
}
2012-05-02 13:14:11 +04:00
2014-04-17 17:30:27 +04:00
if ( isset ( self :: $appTypes [ $app ])) {
2012-09-04 14:32:27 +04:00
return explode ( ',' , self :: $appTypes [ $app ]);
2012-05-20 20:51:45 +04:00
}
2018-02-17 17:18:34 +03:00
return [];
2012-05-15 00:49:20 +04:00
}
2012-05-02 13:14:11 +04:00
2012-05-15 00:49:20 +04:00
/**
* read app types from info . xml and cache them in the database
*/
2018-02-17 17:29:15 +03:00
public static function setAppTypes ( string $app ) {
$appManager = \OC :: $server -> getAppManager ();
$appData = $appManager -> getAppInfo ( $app );
2020-04-10 15:19:56 +03:00
if ( ! is_array ( $appData )) {
2016-03-31 00:29:26 +03:00
return ;
}
2012-06-04 23:30:58 +04:00
2014-04-17 17:30:27 +04:00
if ( isset ( $appData [ 'types' ])) {
$appTypes = implode ( ',' , $appData [ 'types' ]);
} else {
$appTypes = '' ;
2017-01-04 12:40:14 +03:00
$appData [ 'types' ] = [];
2012-04-14 19:53:02 +04:00
}
2012-05-02 13:14:11 +04:00
2018-02-17 17:29:15 +03:00
$config = \OC :: $server -> getConfig ();
$config -> setAppValue ( $app , 'types' , $appTypes );
2017-01-04 12:40:14 +03:00
2018-02-17 17:29:15 +03:00
if ( $appManager -> hasProtectedAppType ( $appData [ 'types' ])) {
$enabled = $config -> getAppValue ( $app , 'enabled' , 'yes' );
2017-01-04 12:40:14 +03:00
if ( $enabled !== 'yes' && $enabled !== 'no' ) {
2018-02-17 17:29:15 +03:00
$config -> setAppValue ( $app , 'enabled' , 'yes' );
2017-01-04 12:40:14 +03:00
}
}
2012-04-14 19:53:02 +04:00
}
2013-01-14 23:30:39 +04:00
2014-09-02 16:30:46 +04:00
/**
* Returns apps enabled for the current user .
*
* @ param bool $forceRefresh whether to refresh the cache
* @ param bool $all whether to return apps for all users , not only the
* currently logged in one
2015-02-02 16:47:38 +03:00
* @ return string []
2014-09-02 16:30:46 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function getEnabledApps ( bool $forceRefresh = false , bool $all = false ) : array {
2015-12-18 13:42:09 +03:00
if ( ! \OC :: $server -> getSystemConfig () -> getValue ( 'installed' , false )) {
2018-02-17 17:18:34 +03:00
return [];
2013-02-10 01:43:35 +04:00
}
2014-09-02 16:30:46 +04:00
// in incognito mode or when logged out, $user will be false,
// which is also the case during an upgrade
2015-02-02 16:47:38 +03:00
$appManager = \OC :: $server -> getAppManager ();
if ( $all ) {
$user = null ;
} else {
$user = \OC :: $server -> getUserSession () -> getUser ();
2013-10-05 21:18:18 +04:00
}
2015-02-02 16:47:38 +03:00
if ( is_null ( $user )) {
$apps = $appManager -> getInstalledApps ();
} else {
2015-02-05 17:11:07 +03:00
$apps = $appManager -> getEnabledAppsForUser ( $user );
2012-03-30 16:00:24 +04:00
}
2015-02-02 16:47:38 +03:00
$apps = array_filter ( $apps , function ( $app ) {
return $app !== 'files' ; //we add this manually
});
2014-04-21 15:38:08 +04:00
sort ( $apps );
array_unshift ( $apps , 'files' );
2012-03-30 16:00:24 +04:00
return $apps ;
}
2011-06-19 17:18:52 +04:00
/**
2014-05-19 19:50:53 +04:00
* checks whether or not an app is enabled
2015-02-02 16:47:38 +03:00
*
2012-09-23 04:39:11 +04:00
* @ param string $app app
* @ return bool
2017-10-24 15:05:46 +03:00
* @ deprecated 13.0 . 0 use \OC :: $server -> getAppManager () -> isEnabledForUser ( $appId )
2011-06-19 17:18:52 +04:00
*
* This function checks whether or not an app is enabled .
*/
2018-02-17 17:37:57 +03:00
public static function isEnabled ( string $app ) : bool {
2015-02-02 16:47:38 +03:00
return \OC :: $server -> getAppManager () -> isEnabledForUser ( $app );
2011-06-19 17:18:52 +04:00
}
/**
2014-05-19 19:50:53 +04:00
* enables an app
2015-02-02 16:47:38 +03:00
*
2016-10-27 18:41:15 +03:00
* @ param string $appId
2014-04-18 16:29:45 +04:00
* @ param array $groups ( optional ) when set , only these groups will have access to the app
2013-08-06 19:19:18 +04:00
* @ throws \Exception
* @ return void
2011-06-19 17:18:52 +04:00
*
* This function set an app as enabled in appconfig .
*/
2018-02-17 17:36:16 +03:00
public function enable ( string $appId ,
array $groups = []) {
2016-10-27 18:41:15 +03:00
// Check if app is already downloaded
2018-02-22 18:00:26 +03:00
/** @var Installer $installer */
2017-11-24 12:27:58 +03:00
$installer = \OC :: $server -> query ( Installer :: class );
2016-10-27 18:41:15 +03:00
$isDownloaded = $installer -> isDownloaded ( $appId );
2020-04-10 15:19:56 +03:00
if ( ! $isDownloaded ) {
2016-10-31 13:07:54 +03:00
$installer -> downloadApp ( $appId );
2016-10-27 18:41:15 +03:00
}
2017-05-15 08:03:35 +03:00
$installer -> installApp ( $appId );
2014-05-21 14:14:10 +04:00
2015-02-16 18:44:51 +03:00
$appManager = \OC :: $server -> getAppManager ();
2018-02-17 17:36:16 +03:00
if ( $groups !== []) {
2015-03-24 14:56:42 +03:00
$groupManager = \OC :: $server -> getGroupManager ();
$groupsList = [];
foreach ( $groups as $group ) {
$groupItem = $groupManager -> get ( $group );
if ( $groupItem instanceof \OCP\IGroup ) {
$groupsList [] = $groupManager -> get ( $group );
}
}
2016-10-27 18:41:15 +03:00
$appManager -> enableAppForGroups ( $appId , $groupsList );
2015-02-02 16:47:38 +03:00
} else {
2016-10-27 18:41:15 +03:00
$appManager -> enableApp ( $appId );
2014-06-06 00:54:27 +04:00
}
2014-05-21 14:14:10 +04:00
}
2012-06-15 01:00:02 +04:00
/**
2012-07-24 02:39:59 +04:00
* Get the path where to install apps
2014-04-17 17:30:27 +04:00
*
2015-01-16 21:31:15 +03:00
* @ return string | false
2012-07-24 02:39:59 +04:00
*/
2012-06-15 01:00:02 +04:00
public static function getInstallPath () {
2015-12-18 13:42:09 +03:00
if ( \OC :: $server -> getSystemConfig () -> getValue ( 'appstoreenabled' , true ) == false ) {
2012-06-15 01:00:02 +04:00
return false ;
}
2014-04-17 17:30:27 +04:00
foreach ( OC :: $APPSROOTS as $dir ) {
if ( isset ( $dir [ 'writable' ]) && $dir [ 'writable' ] === true ) {
2012-06-15 01:00:02 +04:00
return $dir [ 'path' ];
2013-02-10 01:43:35 +04:00
}
2012-06-15 01:00:02 +04:00
}
2018-04-25 16:22:28 +03:00
\OCP\Util :: writeLog ( 'core' , 'No application directories are marked as writable.' , ILogger :: ERROR );
2012-06-15 01:00:02 +04:00
return null ;
}
2014-05-21 14:14:10 +04:00
/**
* search for an app in all app - directories
2015-02-02 16:47:38 +03:00
*
2015-04-07 14:49:16 +03:00
* @ param string $appId
2015-12-08 12:01:09 +03:00
* @ return false | string
2014-05-21 14:14:10 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function findAppInDirectories ( string $appId ) {
2015-12-08 12:01:09 +03:00
$sanitizedAppId = self :: cleanAppId ( $appId );
2020-04-10 15:19:56 +03:00
if ( $sanitizedAppId !== $appId ) {
2015-12-08 12:01:09 +03:00
return false ;
}
2018-02-17 17:18:34 +03:00
static $app_dir = [];
2014-05-21 14:14:10 +04:00
if ( isset ( $app_dir [ $appId ])) {
return $app_dir [ $appId ];
2012-06-28 23:54:33 +04:00
}
2014-05-21 14:14:10 +04:00
2018-02-17 17:18:34 +03:00
$possibleApps = [];
2015-02-02 16:47:38 +03:00
foreach ( OC :: $APPSROOTS as $dir ) {
if ( file_exists ( $dir [ 'path' ] . '/' . $appId )) {
2014-05-21 14:14:10 +04:00
$possibleApps [] = $dir ;
2012-06-15 01:00:02 +04:00
}
}
2014-05-21 14:14:10 +04:00
2014-06-02 23:37:39 +04:00
if ( empty ( $possibleApps )) {
2014-05-21 14:14:10 +04:00
return false ;
2015-02-02 16:47:38 +03:00
} elseif ( count ( $possibleApps ) === 1 ) {
2014-06-02 23:37:39 +04:00
$dir = array_shift ( $possibleApps );
2014-05-21 14:14:10 +04:00
$app_dir [ $appId ] = $dir ;
return $dir ;
} else {
2018-02-17 17:18:34 +03:00
$versionToLoad = [];
2015-02-02 16:47:38 +03:00
foreach ( $possibleApps as $possibleApp ) {
2018-05-03 09:22:03 +03:00
$version = self :: getAppVersionByPath ( $possibleApp [ 'path' ] . '/' . $appId );
2014-05-21 14:14:10 +04:00
if ( empty ( $versionToLoad ) || version_compare ( $version , $versionToLoad [ 'version' ], '>' )) {
2020-03-26 11:30:18 +03:00
$versionToLoad = [
2014-05-21 14:14:10 +04:00
'dir' => $possibleApp ,
'version' => $version ,
2020-03-26 11:30:18 +03:00
];
2014-05-21 14:14:10 +04:00
}
}
$app_dir [ $appId ] = $versionToLoad [ 'dir' ];
return $versionToLoad [ 'dir' ];
//TODO - write test
}
2012-06-15 01:00:02 +04:00
}
2014-05-21 14:14:10 +04:00
2012-06-02 02:05:20 +04:00
/**
2014-04-16 00:55:20 +04:00
* Get the directory for the given app .
* If the app is defined in multiple directories , the first one is taken . ( false if not found )
2014-07-09 12:20:17 +04:00
*
2020-11-21 04:11:37 +03:00
* @ psalm - taint - specialize
*
2014-07-09 12:20:52 +04:00
* @ param string $appId
2014-04-16 00:55:20 +04:00
* @ return string | false
2019-09-05 19:35:40 +03:00
* @ deprecated 11.0 . 0 use \OC :: $server -> getAppManager () -> getAppPath ()
2014-04-16 00:55:20 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function getAppPath ( string $appId ) {
2014-07-09 12:20:17 +04:00
if ( $appId === null || trim ( $appId ) === '' ) {
2014-06-25 18:24:55 +04:00
return false ;
}
2014-07-09 12:20:17 +04:00
if (( $dir = self :: findAppInDirectories ( $appId )) != false ) {
return $dir [ 'path' ] . '/' . $appId ;
2012-06-04 01:13:30 +04:00
}
2012-09-23 04:39:11 +04:00
return false ;
2012-06-04 01:13:30 +04:00
}
/**
2014-04-16 00:55:20 +04:00
* Get the path for the given app on the access
* If the app is defined in multiple directories , the first one is taken . ( false if not found )
2014-07-09 12:20:17 +04:00
*
* @ param string $appId
2014-04-16 00:55:20 +04:00
* @ return string | false
2019-09-05 19:35:40 +03:00
* @ deprecated 18.0 . 0 use \OC :: $server -> getAppManager () -> getAppWebPath ()
2014-04-16 00:55:20 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function getAppWebPath ( string $appId ) {
2014-07-09 12:20:17 +04:00
if (( $dir = self :: findAppInDirectories ( $appId )) != false ) {
return OC :: $WEBROOT . $dir [ 'url' ] . '/' . $appId ;
2012-06-02 02:05:20 +04:00
}
2012-09-23 04:39:11 +04:00
return false ;
2012-06-02 02:05:20 +04:00
}
2012-04-14 18:27:58 +04:00
/**
2016-02-10 13:04:12 +03:00
* get the last version of the app from appinfo / info . xml
2014-07-09 12:20:17 +04:00
*
* @ param string $appId
2016-12-09 19:46:05 +03:00
* @ param bool $useCache
2014-02-06 19:30:58 +04:00
* @ return string
2018-01-29 15:09:32 +03:00
* @ deprecated 14.0 . 0 use \OC :: $server -> getAppManager () -> getAppVersion ()
2012-04-14 18:27:58 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function getAppVersion ( string $appId , bool $useCache = true ) : string {
2018-01-29 15:09:32 +03:00
return \OC :: $server -> getAppManager () -> getAppVersion ( $appId , $useCache );
2014-05-21 14:14:10 +04:00
}
/**
* get app 's version based on it' s path
2015-02-02 16:47:38 +03:00
*
2014-05-21 14:14:10 +04:00
* @ param string $path
* @ return string
*/
2018-02-17 17:37:57 +03:00
public static function getAppVersionByPath ( string $path ) : string {
2014-05-21 14:14:10 +04:00
$infoFile = $path . '/appinfo/info.xml' ;
2018-01-29 15:09:32 +03:00
$appData = \OC :: $server -> getAppManager () -> getAppInfo ( $infoFile , true );
2016-02-10 13:04:12 +03:00
return isset ( $appData [ 'version' ]) ? $appData [ 'version' ] : '' ;
2012-04-14 18:27:58 +04:00
}
2011-10-05 14:31:33 +04:00
2014-05-21 14:14:10 +04:00
2011-03-11 16:59:24 +03:00
/**
2014-05-19 19:50:53 +04:00
* Read all app metadata from the info . xml file
2014-07-09 12:20:17 +04:00
*
* @ param string $appId id of the app or the path of the info . xml file
2016-09-30 12:00:58 +03:00
* @ param bool $path
* @ param string $lang
2014-07-05 15:32:21 +04:00
* @ return array | null
2012-09-18 17:35:27 +04:00
* @ note all data is read from info . xml , not just pre - defined fields
2018-01-29 15:09:32 +03:00
* @ deprecated 14.0 . 0 use \OC :: $server -> getAppManager () -> getAppInfo ()
2014-04-17 17:30:27 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function getAppInfo ( string $appId , bool $path = false , string $lang = null ) {
2018-01-29 15:09:32 +03:00
return \OC :: $server -> getAppManager () -> getAppInfo ( $appId , $path , $lang );
2011-03-11 16:59:24 +03:00
}
2011-10-05 14:31:33 +04:00
2011-08-08 23:42:25 +04:00
/**
2014-05-19 19:50:53 +04:00
* Returns the navigation
2015-02-02 16:47:38 +03:00
*
2014-04-21 17:44:54 +04:00
* @ return array
2018-02-09 10:42:27 +03:00
* @ deprecated 14.0 . 0 use \OC :: $server -> getNavigationManager () -> getAll ()
2011-08-08 23:42:25 +04:00
*
* This function returns an array containing all entries added . The
* entries are sorted by the key 'order' ascending . Additional to the keys
* given for each app the following keys exist :
* - active : boolean , signals if the user is on this navigation entry
*/
2018-02-17 17:37:57 +03:00
public static function getNavigation () : array {
2018-01-29 12:07:12 +03:00
return OC :: $server -> getNavigationManager () -> getAll ();
2011-08-08 23:42:25 +04:00
}
2011-10-05 14:31:33 +04:00
2017-03-26 20:40:41 +03:00
/**
* Returns the Settings Navigation
*
* @ return string []
2018-02-09 10:42:27 +03:00
* @ deprecated 14.0 . 0 use \OC :: $server -> getNavigationManager () -> getAll ( 'settings' )
2017-03-26 20:40:41 +03:00
*
* This function returns an array containing all settings pages added . The
* entries are sorted by the key 'order' ascending .
*/
2018-02-17 17:37:57 +03:00
public static function getSettingsNavigation () : array {
2018-01-29 12:07:12 +03:00
return OC :: $server -> getNavigationManager () -> getAll ( 'settings' );
2017-03-02 01:04:27 +03:00
}
2011-07-25 22:12:35 +04:00
/**
* get the id of loaded app
2014-04-17 17:30:27 +04:00
*
2011-07-25 22:12:35 +04:00
* @ return string
*/
2018-02-17 17:37:32 +03:00
public static function getCurrentApp () : string {
2015-02-10 15:02:48 +03:00
$request = \OC :: $server -> getRequest ();
$script = substr ( $request -> getScriptName (), strlen ( OC :: $WEBROOT ) + 1 );
2018-01-12 17:01:45 +03:00
$topFolder = substr ( $script , 0 , strpos ( $script , '/' ) ? : 0 );
2012-09-29 01:15:19 +04:00
if ( empty ( $topFolder )) {
2015-02-10 15:02:48 +03:00
$path_info = $request -> getPathInfo ();
2012-09-29 01:15:19 +04:00
if ( $path_info ) {
2014-04-17 17:30:27 +04:00
$topFolder = substr ( $path_info , 1 , strpos ( $path_info , '/' , 1 ) - 1 );
2012-09-29 01:15:19 +04:00
}
}
2014-04-17 17:30:27 +04:00
if ( $topFolder == 'apps' ) {
$length = strlen ( $topFolder );
2018-02-17 17:37:32 +03:00
return substr ( $script , $length + 1 , strpos ( $script , '/' , $length + 1 ) - $length - 1 ) ? : '' ;
2014-04-17 17:30:27 +04:00
} else {
2011-07-25 22:12:35 +04:00
return $topFolder ;
}
}
2011-10-05 14:31:33 +04:00
2011-08-09 01:32:54 +04:00
/**
2015-02-12 02:11:38 +03:00
* @ param string $type
* @ return array
2011-08-09 01:32:54 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function getForms ( string $type ) : array {
2018-02-17 17:18:34 +03:00
$forms = [];
2014-04-17 17:30:27 +04:00
switch ( $type ) {
2011-08-09 01:32:54 +04:00
case 'admin' :
2014-04-17 17:30:27 +04:00
$source = self :: $adminForms ;
2013-02-09 20:27:57 +04:00
break ;
2011-08-09 01:32:54 +04:00
case 'personal' :
2014-04-17 17:30:27 +04:00
$source = self :: $personalForms ;
2013-02-09 20:27:57 +04:00
break ;
2012-09-23 04:39:11 +04:00
default :
2018-02-17 17:18:34 +03:00
return [];
2011-08-09 01:32:54 +04:00
}
2014-04-17 17:30:27 +04:00
foreach ( $source as $form ) {
$forms [] = include $form ;
2011-08-09 01:32:54 +04:00
}
return $forms ;
}
2011-10-05 14:31:33 +04:00
2011-08-09 01:32:54 +04:00
/**
* register an admin form to be shown
2014-04-17 17:30:27 +04:00
*
2014-02-19 12:31:54 +04:00
* @ param string $app
* @ param string $page
2011-08-09 01:32:54 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function registerAdmin ( string $app , string $page ) {
2014-04-17 17:30:27 +04:00
self :: $adminForms [] = $app . '/' . $page . '.php' ;
2011-08-09 01:32:54 +04:00
}
2011-10-05 14:31:33 +04:00
2011-08-09 01:32:54 +04:00
/**
* register a personal form to be shown
2015-04-07 14:49:16 +03:00
* @ param string $app
* @ param string $page
2011-08-09 01:32:54 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function registerPersonal ( string $app , string $page ) {
2014-04-17 17:30:27 +04:00
self :: $personalForms [] = $app . '/' . $page . '.php' ;
2011-08-09 01:32:54 +04:00
}
2011-10-05 14:31:33 +04:00
2015-02-12 02:11:38 +03:00
/**
* @ param array $entry
2020-07-15 18:06:27 +03:00
* @ deprecated 20.0 . 0 Please register your alternative login option using the registerAlternativeLogin () on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface
2015-02-12 02:11:38 +03:00
*/
public static function registerLogIn ( array $entry ) {
2020-07-15 18:06:27 +03:00
\OC :: $server -> getLogger () -> debug ( 'OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface' );
2013-01-29 20:28:08 +04:00
self :: $altLogin [] = $entry ;
}
2015-02-12 02:11:38 +03:00
/**
* @ return array
*/
2018-02-17 17:37:57 +03:00
public static function getAlternativeLogIns () : array {
2020-07-15 18:06:27 +03:00
/** @var Coordinator $bootstrapCoordinator */
$bootstrapCoordinator = \OC :: $server -> query ( Coordinator :: class );
foreach ( $bootstrapCoordinator -> getRegistrationContext () -> getAlternativeLogins () as $registration ) {
if ( ! in_array ( IAlternativeLogin :: class , class_implements ( $registration [ 'class' ]), true )) {
\OC :: $server -> getLogger () -> error ( 'Alternative login option {option} does not implement {interface} and is therefore ignored.' , [
'option' => $registration [ 'class' ],
'interface' => IAlternativeLogin :: class ,
'app' => $registration [ 'app' ],
]);
continue ;
}
try {
/** @var IAlternativeLogin $provider */
$provider = \OC :: $server -> query ( $registration [ 'class' ]);
} catch ( QueryException $e ) {
\OC :: $server -> getLogger () -> logException ( $e , [
'message' => 'Alternative login option {option} can not be initialised.' ,
'option' => $registration [ 'class' ],
'app' => $registration [ 'app' ],
]);
}
try {
$provider -> load ();
self :: $altLogin [] = [
'name' => $provider -> getLabel (),
'href' => $provider -> getLink (),
'style' => $provider -> getClass (),
];
} catch ( Throwable $e ) {
\OC :: $server -> getLogger () -> logException ( $e , [
'message' => 'Alternative login option {option} had an error while loading.' ,
'option' => $registration [ 'class' ],
'app' => $registration [ 'app' ],
]);
}
}
2013-01-29 20:28:08 +04:00
return self :: $altLogin ;
}
2011-08-10 14:20:43 +04:00
/**
2014-05-19 19:50:53 +04:00
* get a list of all apps in the apps folder
2015-02-02 16:47:38 +03:00
*
2020-05-28 21:37:24 +03:00
* @ return string [] an array of app names ( string IDs )
2012-09-18 17:35:27 +04:00
* @ todo : change the name of this method to getInstalledApps , which is more accurate
2011-08-10 14:20:43 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function getAllApps () : array {
2018-02-17 17:18:34 +03:00
$apps = [];
2012-10-14 23:04:08 +04:00
2014-04-17 17:30:27 +04:00
foreach ( OC :: $APPSROOTS as $apps_dir ) {
if ( ! is_readable ( $apps_dir [ 'path' ])) {
2018-04-25 16:22:28 +03:00
\OCP\Util :: writeLog ( 'core' , 'unable to read app folder : ' . $apps_dir [ 'path' ], ILogger :: WARN );
2012-09-19 23:26:57 +04:00
continue ;
}
2014-04-17 17:30:27 +04:00
$dh = opendir ( $apps_dir [ 'path' ]);
2012-10-14 23:04:08 +04:00
2014-04-17 17:30:27 +04:00
if ( is_resource ( $dh )) {
2013-09-04 15:06:04 +04:00
while (( $file = readdir ( $dh )) !== false ) {
2015-07-08 11:25:39 +03:00
if ( $file [ 0 ] != '.' and is_dir ( $apps_dir [ 'path' ] . '/' . $file ) and is_file ( $apps_dir [ 'path' ] . '/' . $file . '/appinfo/info.xml' )) {
2013-09-04 15:06:04 +04:00
$apps [] = $file ;
}
}
2011-08-10 14:20:43 +04:00
}
}
2012-10-14 23:04:08 +04:00
2017-10-03 16:03:11 +03:00
$apps = array_unique ( $apps );
2011-08-10 14:20:43 +04:00
return $apps ;
}
2012-10-14 23:04:08 +04:00
2013-01-22 01:18:11 +04:00
/**
2015-03-30 16:58:20 +03:00
* List all apps , this is used in apps . php
2015-02-02 16:47:38 +03:00
*
2013-01-22 01:18:11 +04:00
* @ return array
*/
2018-02-17 17:37:57 +03:00
public function listAllApps () : array {
2013-01-22 01:18:11 +04:00
$installedApps = OC_App :: getAllApps ();
2017-08-01 19:57:00 +03:00
$appManager = \OC :: $server -> getAppManager ();
2015-10-16 17:38:43 +03:00
//we don't want to show configuration for these
2017-08-01 19:57:00 +03:00
$blacklist = $appManager -> getAlwaysEnabledApps ();
2018-02-17 17:18:34 +03:00
$appList = [];
2016-09-30 12:00:58 +03:00
$langCode = \OC :: $server -> getL10N ( 'core' ) -> getLanguageCode ();
2016-09-23 22:47:47 +03:00
$urlGenerator = \OC :: $server -> getURLGenerator ();
2019-05-10 15:50:24 +03:00
/** @var \OCP\Support\Subscription\IRegistry $subscriptionRegistry */
$subscriptionRegistry = \OC :: $server -> query ( \OCP\Support\Subscription\IRegistry :: class );
$supportedApps = $subscriptionRegistry -> delegateGetSupportedApps ();
2013-01-22 01:18:11 +04:00
2014-04-17 17:30:27 +04:00
foreach ( $installedApps as $app ) {
if ( array_search ( $app , $blacklist ) === false ) {
2016-09-30 12:00:58 +03:00
$info = OC_App :: getAppInfo ( $app , false , $langCode );
2016-03-31 00:29:26 +03:00
if ( ! is_array ( $info )) {
2018-04-25 16:22:28 +03:00
\OCP\Util :: writeLog ( 'core' , 'Could not read app info file for app "' . $app . '"' , ILogger :: ERROR );
2016-03-31 00:29:26 +03:00
continue ;
}
2013-01-22 01:18:11 +04:00
if ( ! isset ( $info [ 'name' ])) {
2018-04-25 16:22:28 +03:00
\OCP\Util :: writeLog ( 'core' , 'App id "' . $app . '" has no name in appinfo' , ILogger :: ERROR );
2013-01-22 01:18:11 +04:00
continue ;
}
2018-01-17 23:10:40 +03:00
$enabled = \OC :: $server -> getConfig () -> getAppValue ( $app , 'enabled' , 'no' );
2014-04-18 16:29:45 +04:00
$info [ 'groups' ] = null ;
if ( $enabled === 'yes' ) {
2013-01-22 01:18:11 +04:00
$active = true ;
2020-04-10 11:35:09 +03:00
} elseif ( $enabled === 'no' ) {
2013-01-22 01:18:11 +04:00
$active = false ;
2014-04-18 16:29:45 +04:00
} else {
$active = true ;
$info [ 'groups' ] = $enabled ;
2013-01-22 01:18:11 +04:00
}
$info [ 'active' ] = $active ;
2017-08-01 19:57:00 +03:00
if ( $appManager -> isShipped ( $app )) {
2014-04-17 17:30:27 +04:00
$info [ 'internal' ] = true ;
2015-03-30 16:58:20 +03:00
$info [ 'level' ] = self :: officialApp ;
2014-05-31 19:50:39 +04:00
$info [ 'removable' ] = false ;
2013-01-22 01:18:11 +04:00
} else {
2014-04-17 17:30:27 +04:00
$info [ 'internal' ] = false ;
2014-05-31 19:50:39 +04:00
$info [ 'removable' ] = true ;
2013-01-22 01:18:11 +04:00
}
2019-05-10 15:50:24 +03:00
if ( in_array ( $app , $supportedApps )) {
$info [ 'level' ] = self :: supportedApp ;
}
2016-02-14 22:57:09 +03:00
$appPath = self :: getAppPath ( $app );
2020-04-10 15:19:56 +03:00
if ( $appPath !== false ) {
2016-02-14 22:57:09 +03:00
$appIcon = $appPath . '/img/' . $app . '.svg' ;
2014-08-14 17:48:38 +04:00
if ( file_exists ( $appIcon )) {
2017-08-01 19:57:00 +03:00
$info [ 'preview' ] = $urlGenerator -> imagePath ( $app , $app . '.svg' );
2014-08-14 17:48:38 +04:00
$info [ 'previewAsIcon' ] = true ;
2016-02-14 22:57:09 +03:00
} else {
$appIcon = $appPath . '/img/app.svg' ;
if ( file_exists ( $appIcon )) {
2017-08-01 19:57:00 +03:00
$info [ 'preview' ] = $urlGenerator -> imagePath ( $app , 'app.svg' );
2016-02-14 22:57:09 +03:00
$info [ 'previewAsIcon' ] = true ;
}
2014-08-14 17:48:38 +04:00
}
}
2016-09-23 22:47:47 +03:00
// fix documentation
if ( isset ( $info [ 'documentation' ]) && is_array ( $info [ 'documentation' ])) {
foreach ( $info [ 'documentation' ] as $key => $url ) {
// If it is not an absolute URL we assume it is a key
// i.e. admin-ldap will get converted to go.php?to=admin-ldap
if ( stripos ( $url , 'https://' ) !== 0 && stripos ( $url , 'http://' ) !== 0 ) {
$url = $urlGenerator -> linkToDocs ( $url );
}
$info [ 'documentation' ][ $key ] = $url ;
}
}
2013-01-22 01:18:11 +04:00
$info [ 'version' ] = OC_App :: getAppVersion ( $app );
$appList [] = $info ;
}
}
2013-10-14 12:54:38 +04:00
2016-10-27 18:41:15 +03:00
return $appList ;
2013-10-14 12:54:38 +04:00
}
2018-02-17 17:37:57 +03:00
public static function shouldUpgrade ( string $app ) : bool {
2014-06-04 18:40:53 +04:00
$versions = self :: getAppVersions ();
$currentVersion = OC_App :: getAppVersion ( $app );
2014-10-30 19:24:25 +03:00
if ( $currentVersion && isset ( $versions [ $app ])) {
2014-06-04 18:40:53 +04:00
$installedVersion = $versions [ $app ];
2016-07-22 15:44:00 +03:00
if ( ! version_compare ( $currentVersion , $installedVersion , '=' )) {
2014-06-04 18:40:53 +04:00
return true ;
}
}
return false ;
}
2014-05-27 13:54:12 +04:00
/**
2014-07-09 12:20:17 +04:00
* Adjust the number of version parts of $version1 to match
2014-05-27 13:54:12 +04:00
* the number of version parts of $version2 .
*
* @ param string $version1 version to adjust
* @ param string $version2 version to take the number of parts from
* @ return string shortened $version1
*/
2018-02-17 17:37:57 +03:00
private static function adjustVersionParts ( string $version1 , string $version2 ) : string {
2014-05-27 13:54:12 +04:00
$version1 = explode ( '.' , $version1 );
$version2 = explode ( '.' , $version2 );
// reduce $version1 to match the number of parts in $version2
while ( count ( $version1 ) > count ( $version2 )) {
array_pop ( $version1 );
}
// if $version1 does not have enough parts, add some
while ( count ( $version1 ) < count ( $version2 )) {
$version1 [] = '0' ;
}
return implode ( '.' , $version1 );
}
2013-02-25 15:38:00 +04:00
/**
2014-05-27 13:54:12 +04:00
* Check whether the current ownCloud version matches the given
* application ' s version requirements .
*
* The comparison is made based on the number of parts that the
* app info version has . For example for ownCloud 6.0 . 3 if the
* app info version is expecting version 6.0 , the comparison is
* made on the first two parts of the ownCloud version .
* This means that it ' s possible to specify " requiremin " => 6
* and " requiremax " => 6 and it will still match ownCloud 6.0 . 3.
*
* @ param string $ocVersion ownCloud version to check against
2015-02-02 16:47:38 +03:00
* @ param array $appInfo app info ( from xml )
2014-05-27 13:54:12 +04:00
*
2013-02-25 15:38:00 +04:00
* @ return boolean true if compatible , otherwise false
*/
2019-03-06 21:59:15 +03:00
public static function isAppCompatible ( string $ocVersion , array $appInfo , bool $ignoreMax = false ) : bool {
2014-05-27 13:54:12 +04:00
$requireMin = '' ;
$requireMax = '' ;
2016-10-31 13:07:54 +03:00
if ( isset ( $appInfo [ 'dependencies' ][ 'nextcloud' ][ '@attributes' ][ 'min-version' ])) {
$requireMin = $appInfo [ 'dependencies' ][ 'nextcloud' ][ '@attributes' ][ 'min-version' ];
} elseif ( isset ( $appInfo [ 'dependencies' ][ 'owncloud' ][ '@attributes' ][ 'min-version' ])) {
2015-01-14 14:48:59 +03:00
$requireMin = $appInfo [ 'dependencies' ][ 'owncloud' ][ '@attributes' ][ 'min-version' ];
2020-04-10 11:35:09 +03:00
} elseif ( isset ( $appInfo [ 'requiremin' ])) {
2014-05-27 13:54:12 +04:00
$requireMin = $appInfo [ 'requiremin' ];
2020-04-10 11:35:09 +03:00
} elseif ( isset ( $appInfo [ 'require' ])) {
2014-05-27 13:54:12 +04:00
$requireMin = $appInfo [ 'require' ];
}
2013-02-25 15:38:00 +04:00
2016-10-31 13:07:54 +03:00
if ( isset ( $appInfo [ 'dependencies' ][ 'nextcloud' ][ '@attributes' ][ 'max-version' ])) {
$requireMax = $appInfo [ 'dependencies' ][ 'nextcloud' ][ '@attributes' ][ 'max-version' ];
} elseif ( isset ( $appInfo [ 'dependencies' ][ 'owncloud' ][ '@attributes' ][ 'max-version' ])) {
2015-01-14 14:48:59 +03:00
$requireMax = $appInfo [ 'dependencies' ][ 'owncloud' ][ '@attributes' ][ 'max-version' ];
2020-04-10 11:35:09 +03:00
} elseif ( isset ( $appInfo [ 'requiremax' ])) {
2014-05-27 13:54:12 +04:00
$requireMax = $appInfo [ 'requiremax' ];
}
2013-02-25 15:38:00 +04:00
2014-05-27 13:54:12 +04:00
if ( ! empty ( $requireMin )
&& version_compare ( self :: adjustVersionParts ( $ocVersion , $requireMin ), $requireMin , '<' )
) {
return false ;
}
2019-03-06 21:59:15 +03:00
if ( ! $ignoreMax && ! empty ( $requireMax )
2014-05-27 13:54:12 +04:00
&& version_compare ( self :: adjustVersionParts ( $ocVersion , $requireMax ), $requireMax , '>' )
) {
return false ;
2013-02-25 15:38:00 +04:00
}
return true ;
}
2012-03-30 15:48:44 +04:00
/**
2012-06-29 00:01:46 +04:00
* get the installed version of all apps
2012-03-30 15:48:44 +04:00
*/
2012-09-07 17:22:01 +04:00
public static function getAppVersions () {
2012-06-26 22:53:28 +04:00
static $versions ;
2016-01-03 18:53:30 +03:00
2020-04-10 15:19:56 +03:00
if ( ! $versions ) {
2016-01-03 18:53:30 +03:00
$appConfig = \OC :: $server -> getAppConfig ();
$versions = $appConfig -> getValues ( false , 'installed_version' );
2012-03-30 15:48:44 +04:00
}
2016-01-03 18:53:30 +03:00
return $versions ;
2012-03-30 15:48:44 +04:00
}
2011-12-12 01:08:01 +04:00
/**
* update the database for the app and call the update script
2014-04-17 17:30:27 +04:00
*
2014-07-09 12:20:17 +04:00
* @ param string $appId
2014-05-21 14:14:10 +04:00
* @ return bool
2011-12-12 01:08:01 +04:00
*/
2018-02-19 12:28:41 +03:00
public static function updateApp ( string $appId ) : bool {
2016-02-14 22:57:09 +03:00
$appPath = self :: getAppPath ( $appId );
2020-04-10 15:19:56 +03:00
if ( $appPath === false ) {
2016-02-14 22:57:09 +03:00
return false ;
}
2017-06-09 15:49:40 +03:00
2018-05-30 17:06:18 +03:00
\OC :: $server -> getAppManager () -> clearAppsCache ();
2016-04-19 16:36:11 +03:00
$appData = self :: getAppInfo ( $appId );
2020-01-13 17:12:04 +03:00
2020-11-26 18:44:20 +03:00
$ignoreMaxApps = \OC :: $server -> getConfig () -> getSystemValue ( 'app_install_overwrite' , []);
$ignoreMax = in_array ( $appId , $ignoreMaxApps , true );
\OC_App :: checkAppDependencies (
\OC :: $server -> getConfig (),
\OC :: $server -> getL10N ( 'core' ),
$appData ,
$ignoreMax
);
2020-01-13 17:14:56 +03:00
self :: registerAutoloading ( $appId , $appPath , true );
2016-04-19 16:36:11 +03:00
self :: executeRepairSteps ( $appId , $appData [ 'repair-steps' ][ 'pre-migration' ]);
2017-06-01 17:56:34 +03:00
2017-06-02 14:22:25 +03:00
if ( file_exists ( $appPath . '/appinfo/database.xml' )) {
OC_DB :: updateDbFromStructure ( $appPath . '/appinfo/database.xml' );
} else {
2017-06-01 17:56:34 +03:00
$ms = new MigrationService ( $appId , \OC :: $server -> getDatabaseConnection ());
$ms -> migrate ();
2011-12-12 01:08:01 +04:00
}
2017-06-01 17:56:34 +03:00
2016-04-19 16:36:11 +03:00
self :: executeRepairSteps ( $appId , $appData [ 'repair-steps' ][ 'post-migration' ]);
2016-04-26 12:56:56 +03:00
self :: setupLiveMigrations ( $appId , $appData [ 'repair-steps' ][ 'live-migration' ]);
2018-01-29 15:09:32 +03:00
// update appversion in app manager
2018-06-02 09:48:33 +03:00
\OC :: $server -> getAppManager () -> clearAppsCache ();
2018-01-29 15:09:32 +03:00
\OC :: $server -> getAppManager () -> getAppVersion ( $appId , false );
2017-06-01 17:56:34 +03:00
2016-05-02 16:26:12 +03:00
self :: setupBackgroundJobs ( $appData [ 'background-jobs' ]);
2012-05-11 22:32:37 +04:00
2012-09-23 04:39:11 +04:00
//set remote/public handlers
2014-07-07 18:57:50 +04:00
if ( array_key_exists ( 'ocsid' , $appData )) {
2015-07-09 13:37:57 +03:00
\OC :: $server -> getConfig () -> setAppValue ( $appId , 'ocsid' , $appData [ 'ocsid' ]);
2020-04-10 15:19:56 +03:00
} elseif ( \OC :: $server -> getConfig () -> getAppValue ( $appId , 'ocsid' , null ) !== null ) {
2015-07-09 13:37:57 +03:00
\OC :: $server -> getConfig () -> deleteAppValue ( $appId , 'ocsid' );
2014-07-07 18:57:50 +04:00
}
2014-04-17 17:30:27 +04:00
foreach ( $appData [ 'remote' ] as $name => $path ) {
2015-07-09 13:37:57 +03:00
\OC :: $server -> getConfig () -> setAppValue ( 'core' , 'remote_' . $name , $appId . '/' . $path );
2012-05-11 22:32:37 +04:00
}
2014-04-17 17:30:27 +04:00
foreach ( $appData [ 'public' ] as $name => $path ) {
2015-07-09 13:37:57 +03:00
\OC :: $server -> getConfig () -> setAppValue ( 'core' , 'public_' . $name , $appId . '/' . $path );
2012-05-11 22:32:37 +04:00
}
2012-05-15 00:49:20 +04:00
2014-07-09 12:20:17 +04:00
self :: setAppTypes ( $appId );
2014-05-21 14:14:10 +04:00
2014-07-25 15:38:44 +04:00
$version = \OC_App :: getAppVersion ( $appId );
2018-01-17 23:14:09 +03:00
\OC :: $server -> getConfig () -> setAppValue ( $appId , 'installed_version' , $version );
2014-07-25 15:38:44 +04:00
2016-08-16 01:52:41 +03:00
\OC :: $server -> getEventDispatcher () -> dispatch ( ManagerEvent :: EVENT_APP_UPDATE , new ManagerEvent (
ManagerEvent :: EVENT_APP_UPDATE , $appId
));
2014-05-21 14:14:10 +04:00
return true ;
2011-12-12 01:08:01 +04:00
}
2012-02-09 00:01:09 +04:00
2016-04-19 16:36:11 +03:00
/**
* @ param string $appId
* @ param string [] $steps
* @ throws \OC\NeedsUpdateException
*/
2018-02-17 17:37:57 +03:00
public static function executeRepairSteps ( string $appId , array $steps ) {
2016-04-19 16:36:11 +03:00
if ( empty ( $steps )) {
return ;
}
// load the app
2017-03-14 03:07:41 +03:00
self :: loadApp ( $appId );
2016-04-19 16:36:11 +03:00
$dispatcher = OC :: $server -> getEventDispatcher ();
// load the steps
$r = new Repair ([], $dispatcher );
foreach ( $steps as $step ) {
try {
$r -> addStep ( $step );
} catch ( Exception $ex ) {
$r -> emit ( '\OC\Repair' , 'error' , [ $ex -> getMessage ()]);
\OC :: $server -> getLogger () -> logException ( $ex );
}
}
// run the steps
$r -> run ();
}
2016-05-02 16:26:12 +03:00
public static function setupBackgroundJobs ( array $jobs ) {
$queue = \OC :: $server -> getJobList ();
foreach ( $jobs as $job ) {
$queue -> add ( $job );
}
}
2016-04-26 12:56:56 +03:00
/**
* @ param string $appId
* @ param string [] $steps
*/
2018-02-17 17:37:57 +03:00
private static function setupLiveMigrations ( string $appId , array $steps ) {
2016-04-26 12:56:56 +03:00
$queue = \OC :: $server -> getJobList ();
foreach ( $steps as $step ) {
$queue -> add ( 'OC\Migration\BackgroundRepair' , [
'app' => $appId ,
'step' => $step ]);
}
}
2012-02-09 00:01:09 +04:00
/**
2014-07-09 12:20:17 +04:00
* @ param string $appId
2015-01-16 21:31:15 +03:00
* @ return \OC\Files\View | false
2012-02-09 00:01:09 +04:00
*/
2018-02-17 17:37:57 +03:00
public static function getStorage ( string $appId ) {
2017-10-24 00:31:17 +03:00
if ( \OC :: $server -> getAppManager () -> isEnabledForUser ( $appId )) { //sanity check
2017-03-02 18:52:05 +03:00
if ( \OC :: $server -> getUserSession () -> isLoggedIn ()) {
2014-04-17 17:30:27 +04:00
$view = new \OC\Files\View ( '/' . OC_User :: getUser ());
2014-07-09 12:20:17 +04:00
if ( ! $view -> file_exists ( $appId )) {
$view -> mkdir ( $appId );
2012-04-24 02:26:33 +04:00
}
2014-07-09 12:20:17 +04:00
return new \OC\Files\View ( '/' . OC_User :: getUser () . '/' . $appId );
2014-04-17 17:30:27 +04:00
} else {
2018-04-25 16:22:28 +03:00
\OCP\Util :: writeLog ( 'core' , 'Can\'t get app storage, app ' . $appId . ', user not logged in' , ILogger :: ERROR );
2012-02-09 00:01:09 +04:00
return false ;
}
2014-04-17 17:30:27 +04:00
} else {
2018-04-25 16:22:28 +03:00
\OCP\Util :: writeLog ( 'core' , 'Can\'t get app storage, app ' . $appId . ' not enabled' , ILogger :: ERROR );
2012-09-18 17:35:27 +04:00
return false ;
2012-02-09 00:01:09 +04:00
}
}
2015-01-13 16:48:30 +03:00
2018-02-17 17:37:57 +03:00
protected static function findBestL10NOption ( array $options , string $lang ) : string {
2018-04-12 13:28:25 +03:00
// only a single option
if ( isset ( $options [ '@value' ])) {
return $options [ '@value' ];
}
2016-10-04 14:29:54 +03:00
$fallback = $similarLangFallback = $englishFallback = false ;
$lang = strtolower ( $lang );
$similarLang = $lang ;
if ( strpos ( $similarLang , '_' )) {
// For "de_DE" we want to find "de" and the other way around
$similarLang = substr ( $lang , 0 , strpos ( $lang , '_' ));
}
2016-09-30 12:00:58 +03:00
foreach ( $options as $option ) {
if ( is_array ( $option )) {
if ( $fallback === false ) {
$fallback = $option [ '@value' ];
}
2016-10-04 14:29:54 +03:00
if ( ! isset ( $option [ '@attributes' ][ 'lang' ])) {
continue ;
}
$attributeLang = strtolower ( $option [ '@attributes' ][ 'lang' ]);
if ( $attributeLang === $lang ) {
2016-09-30 12:00:58 +03:00
return $option [ '@value' ];
}
2016-10-04 14:29:54 +03:00
if ( $attributeLang === $similarLang ) {
$similarLangFallback = $option [ '@value' ];
2020-04-10 11:35:09 +03:00
} elseif ( strpos ( $attributeLang , $similarLang . '_' ) === 0 ) {
2016-10-04 14:29:54 +03:00
if ( $similarLangFallback === false ) {
2020-10-05 16:12:57 +03:00
$similarLangFallback = $option [ '@value' ];
2016-10-04 14:29:54 +03:00
}
}
2016-09-30 12:00:58 +03:00
} else {
$englishFallback = $option ;
}
}
2016-10-04 14:29:54 +03:00
if ( $similarLangFallback !== false ) {
return $similarLangFallback ;
2020-04-10 11:35:09 +03:00
} elseif ( $englishFallback !== false ) {
2016-10-04 14:29:54 +03:00
return $englishFallback ;
}
return ( string ) $fallback ;
2016-09-30 12:00:58 +03:00
}
2015-01-13 16:48:30 +03:00
/**
* parses the app data array and enhanced the 'description' value
*
* @ param array $data the app data
2016-09-30 12:00:58 +03:00
* @ param string $lang
2015-01-13 16:48:30 +03:00
* @ return array improved app data
*/
2018-02-17 17:37:57 +03:00
public static function parseAppInfo ( array $data , $lang = null ) : array {
2016-09-30 12:00:58 +03:00
if ( $lang && isset ( $data [ 'name' ]) && is_array ( $data [ 'name' ])) {
$data [ 'name' ] = self :: findBestL10NOption ( $data [ 'name' ], $lang );
}
if ( $lang && isset ( $data [ 'summary' ]) && is_array ( $data [ 'summary' ])) {
$data [ 'summary' ] = self :: findBestL10NOption ( $data [ 'summary' ], $lang );
}
if ( $lang && isset ( $data [ 'description' ]) && is_array ( $data [ 'description' ])) {
2017-01-11 12:57:26 +03:00
$data [ 'description' ] = trim ( self :: findBestL10NOption ( $data [ 'description' ], $lang ));
2020-04-10 11:35:09 +03:00
} elseif ( isset ( $data [ 'description' ]) && is_string ( $data [ 'description' ])) {
2017-01-11 12:57:26 +03:00
$data [ 'description' ] = trim ( $data [ 'description' ]);
2020-04-10 15:19:56 +03:00
} else {
2016-09-30 15:41:37 +03:00
$data [ 'description' ] = '' ;
2015-01-13 16:48:30 +03:00
}
return $data ;
}
2016-10-07 12:27:33 +03:00
/**
2017-01-11 12:57:26 +03:00
* @ param \OCP\IConfig $config
* @ param \OCP\IL10N $l
* @ param array $info
* @ throws \Exception
2016-10-07 12:27:33 +03:00
*/
2019-03-06 21:59:15 +03:00
public static function checkAppDependencies ( \OCP\IConfig $config , \OCP\IL10N $l , array $info , bool $ignoreMax ) {
2016-10-07 12:27:33 +03:00
$dependencyAnalyzer = new DependencyAnalyzer ( new Platform ( $config ), $l );
2019-03-06 21:59:15 +03:00
$missing = $dependencyAnalyzer -> analyze ( $info , $ignoreMax );
2016-10-07 12:27:33 +03:00
if ( ! empty ( $missing )) {
2017-07-23 22:03:26 +03:00
$missingMsg = implode ( PHP_EOL , $missing );
2016-10-07 12:27:33 +03:00
throw new \Exception (
2018-10-09 15:32:14 +03:00
$l -> t ( 'App "%1$s" cannot be installed because the following dependencies are not fulfilled: %2$s' ,
2016-10-07 12:27:33 +03:00
[ $info [ 'name' ], $missingMsg ]
)
);
}
}
2011-03-03 23:55:32 +03:00
}