-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make shipped apps updatable via appstore #8808
Changes from all commits
2bcfd8e
c8636ca
020255b
c6dc9ae
c8a8c7e
eea501b
19129b3
56a8010
2c00ab1
724d027
00b7f36
6aab50f
fad3bd7
0fe8f77
498aa66
a110973
1ab9bdc
5d4f3ba
6bf0689
602404c
5e9fa64
0890ce4
c378e76
65028c4
86f546f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,12 +89,11 @@ public function findClass($class) { | |
} elseif (strpos($class, 'OCA\\') === 0) { | ||
list(, $app, $rest) = explode('\\', $class, 3); | ||
$app = strtolower($app); | ||
foreach (\OC::$APPSROOTS as $appDir) { | ||
if (stream_resolve_include_path($appDir['path'] . '/' . $app)) { | ||
$paths[] = $appDir['path'] . '/' . $app . '/' . strtolower(str_replace('\\', '/', $rest) . '.php'); | ||
// If not found in the root of the app directory, insert '/lib' after app id and try again. | ||
$paths[] = $appDir['path'] . '/' . $app . '/lib/' . strtolower(str_replace('\\', '/', $rest) . '.php'); | ||
} | ||
$appPath = \OC_App::getAppPath($app); | ||
if ($appPath && stream_resolve_include_path($appPath)) { | ||
$paths[] = $appPath . '/' . strtolower(str_replace('\\', '/', $rest) . '.php'); | ||
// If not found in the root of the app directory, insert '/lib' after app id and try again. | ||
$paths[] = $appPath . '/lib/' . strtolower(str_replace('\\', '/', $rest) . '.php'); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bantu @icewind1991 can you help double-checking these auto-loader changes ? (I'm not familiar with autoloader, so not sure if this could break something else) |
||
} elseif (strpos($class, 'Test_') === 0) { | ||
$paths[] = 'tests/lib/' . strtolower(str_replace('_', '/', substr($class, 5)) . '.php'); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,13 @@ | |
* ownCloud | ||
* | ||
* @author Frank Karlitschek | ||
* @copyright 2012 Frank Karlitschek <frank@owncloud.org> | ||
* | ||
* @author Jakob Sack | ||
* @copyright 2012 Frank Karlitschek frank@owncloud.org | ||
* @copyright 2012 Jakob Sack <mail@jakobsack.de> | ||
* | ||
* @author Georg Ehrke | ||
* @copyright 2014 Georg Ehrke <georg@ownCloud.com> | ||
* | ||
* This library is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | ||
|
@@ -221,64 +226,53 @@ public static function isEnabled($app) { | |
public static function enable($app, $groups = null) { | ||
self::$enabledAppsCache = array(); // flush | ||
if (!OC_Installer::isInstalled($app)) { | ||
// check if app is a shipped app or not. OCS apps have an integer as id, shipped apps use a string | ||
if (!is_numeric($app)) { | ||
$app = OC_Installer::installShippedApp($app); | ||
} else { | ||
$appdata = OC_OCSClient::getApplication($app); | ||
$download = OC_OCSClient::getApplicationDownload($app, 1); | ||
if (isset($download['downloadlink']) and $download['downloadlink'] != '') { | ||
// Replace spaces in download link without encoding entire URL | ||
$download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']); | ||
$info = array('source' => 'http', 'href' => $download['downloadlink'], 'appdata' => $appdata); | ||
$app = OC_Installer::installApp($info); | ||
} | ||
} | ||
$app = self::installApp($app); | ||
} | ||
$l = OC_L10N::get('core'); | ||
if ($app !== false) { | ||
// check if the app is compatible with this version of ownCloud | ||
$info = OC_App::getAppInfo($app); | ||
$version = OC_Util::getVersion(); | ||
if(!self::isAppCompatible($version, $info)) { | ||
throw new \Exception( | ||
$l->t("App \"%s\" can't be installed because it is not compatible with this version of ownCloud.", | ||
array($info['name']) | ||
) | ||
); | ||
} else { | ||
if (!is_null($groups)) { | ||
OC_Appconfig::setValue($app, 'enabled', json_encode($groups)); | ||
}else{ | ||
OC_Appconfig::setValue($app, 'enabled', 'yes'); | ||
} | ||
if (isset($appdata['id'])) { | ||
OC_Appconfig::setValue($app, 'ocsid', $appdata['id']); | ||
} | ||
\OC_Hook::emit('OC_App', 'post_enable', array('app' => $app)); | ||
} | ||
} else { | ||
throw new \Exception($l->t("No app name specified")); | ||
|
||
if (!is_null($groups)) { | ||
OC_Appconfig::setValue($app, 'enabled', json_encode($groups)); | ||
}else{ | ||
OC_Appconfig::setValue($app, 'enabled', 'yes'); | ||
} | ||
} | ||
|
||
/** | ||
* disables an app | ||
* @param string $app app | ||
* @return boolean|null | ||
* | ||
* @param string $app | ||
* @return int | ||
*/ | ||
public static function downloadApp($app) { | ||
$appdata=OC_OCSClient::getApplication($app); | ||
$download=OC_OCSClient::getApplicationDownload($app, 1); | ||
if(isset($download['downloadlink']) and $download['downloadlink']!='') { | ||
// Replace spaces in download link without encoding entire URL | ||
$download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we sure there aren't any other special characters in the download links? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just from moved this code here from another place (https://github.com/owncloud/core/pull/8808/files#diff-4828f39ae4935d14b1dc1a6ffe323725L232) works in current versions, so apparently it's just fine to escape spaces |
||
$info = array('source'=>'http', 'href'=>$download['downloadlink'], 'appdata'=>$appdata); | ||
$app=OC_Installer::installApp($info); | ||
} | ||
return $app; | ||
} | ||
|
||
/** | ||
* @param string $app | ||
* @return bool | ||
*/ | ||
public static function removeApp($app) { | ||
if (self::isShipped($app)) { | ||
return false; | ||
} | ||
|
||
return OC_Installer::removeApp($app); | ||
} | ||
|
||
/** | ||
* This function set an app as disabled in appconfig. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Merge this line with the line above |
||
* @param string $app app | ||
*/ | ||
public static function disable($app) { | ||
self::$enabledAppsCache = array(); // flush | ||
// check if app is a shipped app or not. if not delete | ||
\OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app)); | ||
OC_Appconfig::setValue($app, 'enabled', 'no'); | ||
|
||
// check if app is a shipped app or not. if not delete | ||
if (!OC_App::isShipped($app)) { | ||
OC_Installer::removeApp($app); | ||
} | ||
OC_Appconfig::setValue($app, 'enabled', 'no' ); | ||
} | ||
|
||
/** | ||
|
@@ -461,17 +455,46 @@ public static function getInstallPath() { | |
} | ||
|
||
|
||
protected static function findAppInDirectories($appid) { | ||
/** | ||
* search for an app in all app-directories | ||
* @param $appId | ||
* @return mixed (bool|string) | ||
*/ | ||
protected static function findAppInDirectories($appId) { | ||
static $app_dir = array(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. static should be in the object instead of the method? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. works both, didn't write this piece of code. This is how it is in master |
||
if (isset($app_dir[$appid])) { | ||
return $app_dir[$appid]; | ||
|
||
if (isset($app_dir[$appId])) { | ||
return $app_dir[$appId]; | ||
} | ||
foreach (OC::$APPSROOTS as $dir) { | ||
if (file_exists($dir['path'] . '/' . $appid)) { | ||
return $app_dir[$appid] = $dir; | ||
|
||
$possibleApps = array(); | ||
foreach(OC::$APPSROOTS as $dir) { | ||
if(file_exists($dir['path'] . '/' . $appId)) { | ||
$possibleApps[] = $dir; | ||
} | ||
} | ||
return false; | ||
|
||
if (empty($possibleApps)) { | ||
return false; | ||
} elseif(count($possibleApps) === 1) { | ||
$dir = array_shift($possibleApps); | ||
$app_dir[$appId] = $dir; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this line, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
return $dir; | ||
} else { | ||
$versionToLoad = array(); | ||
foreach($possibleApps as $possibleApp) { | ||
$version = self::getAppVersionByPath($possibleApp['path']); | ||
if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) { | ||
$versionToLoad = array( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but we want to load the newest version, that's why we need to check all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should load the "running" version, not the newest one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can't run multiple app versions that use the same appid, that's why we search for the app with the highest version num There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah okay, is that the same way the core finds the classes of apps? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
'dir' => $possibleApp, | ||
'version' => $version, | ||
); | ||
} | ||
} | ||
$app_dir[$appId] = $versionToLoad['dir']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. useless assignment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. invalid (it's a static variable of the method) |
||
return $versionToLoad['dir']; | ||
//TODO - write test | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -487,6 +510,17 @@ public static function getAppPath($appid) { | |
return false; | ||
} | ||
|
||
|
||
/** | ||
* check if an app's directory is writable | ||
* @param $appid | ||
* @return bool | ||
*/ | ||
public static function isAppDirWritable($appid) { | ||
$path = self::getAppPath($appid); | ||
return ($path !== false) ? is_writable($path) : false; | ||
} | ||
|
||
/** | ||
* 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) | ||
|
@@ -506,15 +540,27 @@ public static function getAppWebPath($appid) { | |
* @return string | ||
*/ | ||
public static function getAppVersion($appid) { | ||
$file = self::getAppPath($appid) . '/appinfo/version'; | ||
if (is_file($file) && $version = trim(file_get_contents($file))) { | ||
return $version; | ||
} else { | ||
$appData = self::getAppInfo($appid); | ||
$file = self::getAppPath($appid); | ||
return ($file !== false) ? self::getAppVersionByPath($file) : '0'; | ||
} | ||
|
||
/** | ||
* get app's version based on it's path | ||
* @param string $path | ||
* @return string | ||
*/ | ||
public static function getAppVersionByPath($path) { | ||
$versionFile = $path . '/appinfo/version'; | ||
$infoFile = $path . '/appinfo/info.xml'; | ||
if(is_file($versionFile)) { | ||
return trim(file_get_contents($versionFile)); | ||
}else{ | ||
$appData = self::getAppInfo($infoFile, true); | ||
return isset($appData['version']) ? $appData['version'] : ''; | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Read all app metadata from the info.xml file | ||
* @param string $appid id of the app or the path of the info.xml file | ||
|
@@ -619,7 +665,6 @@ public static function getCurrentApp() { | |
} | ||
} | ||
|
||
|
||
/** | ||
* get the forms for either settings, admin or personal | ||
*/ | ||
|
@@ -745,18 +790,20 @@ public static function listAllApps() { | |
|
||
$info['active'] = $active; | ||
|
||
if (isset($info['shipped']) and ($info['shipped'] == 'true')) { | ||
if(isset($info['shipped']) and ($info['shipped'] == 'true')) { | ||
$info['internal'] = true; | ||
$info['internallabel'] = 'Internal App'; | ||
$info['internalclass'] = ''; | ||
$info['update'] = false; | ||
$info['removable'] = false; | ||
} else { | ||
$info['internal'] = false; | ||
$info['internallabel'] = '3rd Party'; | ||
$info['internalclass'] = 'externalapp'; | ||
$info['update'] = OC_Installer::isUpdateAvailable($app); | ||
$info['removable'] = true; | ||
} | ||
|
||
$info['update'] = OC_Installer::isUpdateAvailable($app); | ||
|
||
$info['preview'] = OC_Helper::imagePath('settings', 'trans.png'); | ||
$info['version'] = OC_App::getAppVersion($app); | ||
$appList[] = $info; | ||
|
@@ -841,6 +888,7 @@ public static function getAppstoreApps($filter = 'approved') { | |
$app1[$i]['ocs_id'] = $app['id']; | ||
$app1[$i]['internal'] = $app1[$i]['active'] = 0; | ||
$app1[$i]['update'] = false; | ||
$app1[$i]['removable'] = false; | ||
if ($app['label'] == 'recommended') { | ||
$app1[$i]['internallabel'] = 'Recommended'; | ||
$app1[$i]['internalclass'] = 'recommendedapp'; | ||
|
@@ -851,17 +899,29 @@ public static function getAppstoreApps($filter = 'approved') { | |
|
||
|
||
// rating img | ||
if ($app['score'] >= 0 and $app['score'] < 5) $img = OC_Helper::imagePath("core", "rating/s1.png"); | ||
elseif ($app['score'] >= 5 and $app['score'] < 15) $img = OC_Helper::imagePath("core", "rating/s2.png"); | ||
elseif ($app['score'] >= 15 and $app['score'] < 25) $img = OC_Helper::imagePath("core", "rating/s3.png"); | ||
elseif ($app['score'] >= 25 and $app['score'] < 35) $img = OC_Helper::imagePath("core", "rating/s4.png"); | ||
elseif ($app['score'] >= 35 and $app['score'] < 45) $img = OC_Helper::imagePath("core", "rating/s5.png"); | ||
elseif ($app['score'] >= 45 and $app['score'] < 55) $img = OC_Helper::imagePath("core", "rating/s6.png"); | ||
elseif ($app['score'] >= 55 and $app['score'] < 65) $img = OC_Helper::imagePath("core", "rating/s7.png"); | ||
elseif ($app['score'] >= 65 and $app['score'] < 75) $img = OC_Helper::imagePath("core", "rating/s8.png"); | ||
elseif ($app['score'] >= 75 and $app['score'] < 85) $img = OC_Helper::imagePath("core", "rating/s9.png"); | ||
elseif ($app['score'] >= 85 and $app['score'] < 95) $img = OC_Helper::imagePath("core", "rating/s10.png"); | ||
elseif ($app['score'] >= 95 and $app['score'] < 100) $img = OC_Helper::imagePath("core", "rating/s11.png"); | ||
if ($app['score'] < 5) { | ||
$img = OC_Helper::imagePath( "core", "rating/s1.png" ); | ||
} elseif ($app['score'] < 15) { | ||
$img = OC_Helper::imagePath( "core", "rating/s2.png" ); | ||
} elseif($app['score'] < 25) { | ||
$img = OC_Helper::imagePath( "core", "rating/s3.png" ); | ||
} elseif($app['score'] < 35) { | ||
$img = OC_Helper::imagePath( "core", "rating/s4.png" ); | ||
} elseif($app['score'] < 45) { | ||
$img = OC_Helper::imagePath( "core", "rating/s5.png" ); | ||
} elseif($app['score'] < 55) { | ||
$img = OC_Helper::imagePath( "core", "rating/s6.png" ); | ||
} elseif($app['score'] < 65) { | ||
$img = OC_Helper::imagePath( "core", "rating/s7.png" ); | ||
} elseif($app['score'] < 75) { | ||
$img = OC_Helper::imagePath( "core", "rating/s8.png" ); | ||
} elseif($app['score'] < 85) { | ||
$img = OC_Helper::imagePath( "core", "rating/s9.png" ); | ||
} elseif($app['score'] < 95) { | ||
$img = OC_Helper::imagePath( "core", "rating/s10.png" ); | ||
} elseif($app['score'] < 100) { | ||
$img = OC_Helper::imagePath( "core", "rating/s11.png" ); | ||
} | ||
|
||
$app1[$i]['score'] = '<img src="' . $img . '"> Score: ' . $app['score'] . '%'; | ||
$i++; | ||
|
@@ -1043,10 +1103,58 @@ public static function getAppVersions() { | |
return $versions; | ||
} | ||
|
||
|
||
/** | ||
* @param mixed $app | ||
* @return bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just @throws \Exception or is it supposed to have a description in which certain cases an exception is thrown There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the case would be great |
||
* @throws Exception if app is not compatible with this version of ownCloud | ||
* @throws Exception if no app-name was specified | ||
*/ | ||
public static function installApp($app) { | ||
$l = OC_L10N::get('core'); | ||
$appdata=OC_OCSClient::getApplication($app); | ||
|
||
// check if app is a shipped app or not. OCS apps have an integer as id, shipped apps use a string | ||
if(!is_numeric($app)) { | ||
$shippedVersion=self::getAppVersion($app); | ||
if($appdata && version_compare($shippedVersion, $appdata['version'], '<')) { | ||
$app = self::downloadApp($app); | ||
} else { | ||
$app = OC_Installer::installShippedApp($app); | ||
} | ||
}else{ | ||
$app = self::downloadApp($app); | ||
} | ||
|
||
if($app!==false) { | ||
// check if the app is compatible with this version of ownCloud | ||
$info = self::getAppInfo($app); | ||
$version=OC_Util::getVersion(); | ||
if(!self::isAppCompatible($version, $info)) { | ||
throw new \Exception( | ||
$l->t('App \"%s\" can\'t be installed because it is not compatible with this version of ownCloud.', | ||
array($info['name']) | ||
) | ||
); | ||
}else{ | ||
OC_Appconfig::setValue( $app, 'enabled', 'yes' ); | ||
if(isset($appdata['id'])) { | ||
OC_Appconfig::setValue( $app, 'ocsid', $appdata['id'] ); | ||
} | ||
\OC_Hook::emit('OC_App', 'post_enable', array('app' => $app)); | ||
} | ||
}else{ | ||
throw new \Exception($l->t("No app name specified")); | ||
} | ||
|
||
return $app; | ||
} | ||
|
||
/** | ||
* update the database for the app and call the update script | ||
* | ||
* @param string $appid | ||
* @return bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't comment on the line bellow: In line 1166 there is just a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed with 6bf0689 |
||
*/ | ||
public static function updateApp($appid) { | ||
if (file_exists(self::getAppPath($appid) . '/appinfo/preupdate.php')) { | ||
|
@@ -1057,7 +1165,7 @@ public static function updateApp($appid) { | |
OC_DB::updateDbFromStructure(self::getAppPath($appid) . '/appinfo/database.xml'); | ||
} | ||
if (!self::isEnabled($appid)) { | ||
return; | ||
return false; | ||
} | ||
if (file_exists(self::getAppPath($appid) . '/appinfo/update.php')) { | ||
self::loadApp($appid); | ||
|
@@ -1074,6 +1182,8 @@ public static function updateApp($appid) { | |
} | ||
|
||
self::setAppTypes($appid); | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like $appPath defined by \OC_App::getAppPath($app) on line 92 can also be of type false; however, stream_resolve_include_path() does only seem to accept string, maybe add an additional type check?