diff options
46 files changed, 815 insertions, 171 deletions
diff --git a/3rdparty b/3rdparty -Subproject 338ab170afcb4770e79cc4427a9803cb29a0a23 +Subproject 3cd7b7048e33e0076c1f94cf2ed721934e0c497 diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 4b1b38d783c..ad3dce19778 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -925,7 +925,7 @@ if (this._allowSelection) { td.append( '<input id="select-' + this.id + '-' + fileData.id + - '" type="checkbox" class="selectCheckBox"/><label for="select-' + this.id + '-' + fileData.id + '">' + + '" type="checkbox" class="selectCheckBox checkbox"/><label for="select-' + this.id + '-' + fileData.id + '">' + '<div class="thumbnail" style="background-image:url(' + icon + '); background-size: 32px;"></div>' + '<span class="hidden-visually">' + t('files', 'Select') + '</span>' + '</label>' diff --git a/apps/files/templates/fileexists.html b/apps/files/templates/fileexists.html index c783f9a05c7..e3513237d2b 100644 --- a/apps/files/templates/fileexists.html +++ b/apps/files/templates/fileexists.html @@ -3,14 +3,14 @@ <span class="what">{what}<!-- If you select both versions, the copied file will have a number added to its name. --></span><br/> <br/> <table> - <th><input id="checkbox-allnewfiles" class="allnewfiles" type="checkbox" /><label for="checkbox-allnewfiles">{allnewfiles}<span class="count"></span></label></th> - <th><input id="checkbox-allexistingfiles" class="allexistingfiles" type="checkbox" /><label for="checkbox-allexistingfiles">{allexistingfiles}<span class="count"></span></label></th> + <th><input id="checkbox-allnewfiles" class="allnewfiles checkbox" type="checkbox" /><label for="checkbox-allnewfiles">{allnewfiles}<span class="count"></span></label></th> + <th><input id="checkbox-allexistingfiles" class="allexistingfiles checkbox" type="checkbox" /><label for="checkbox-allexistingfiles">{allexistingfiles}<span class="count"></span></label></th> </table> <div class="conflicts"> <div class="template"> <div class="filename"></div> <div class="replacement"> - <input type="checkbox" class="u-left"/> + <input type="checkbox" class="checkbox u-left"/> <label> <span class="svg icon"></span> <div class="mtime"></div> @@ -18,7 +18,7 @@ </label> </div> <div class="original"> - <input type="checkbox" class="u-left" /> + <input type="checkbox" class="checkbox u-left" /> <label> <span class="svg icon"></span> <div class="mtime"></div> diff --git a/apps/files/templates/list.php b/apps/files/templates/list.php index 15af1970dc3..bbbce8473de 100644 --- a/apps/files/templates/list.php +++ b/apps/files/templates/list.php @@ -56,7 +56,7 @@ <tr> <th id='headerName' class="hidden column-name"> <div id="headerName-container"> - <input type="checkbox" id="select_all_files" class="select-all"/> + <input type="checkbox" id="select_all_files" class="select-all checkbox"/> <label for="select_all_files"> <span class="hidden-visually"><?php p($l->t('Select all'))?></span> </label> diff --git a/apps/files_sharing/lib/activity.php b/apps/files_sharing/lib/activity.php index 1257e7a445c..63ac2e90b2a 100644 --- a/apps/files_sharing/lib/activity.php +++ b/apps/files_sharing/lib/activity.php @@ -58,6 +58,7 @@ class Activity implements IExtension { const SUBJECT_RESHARED_GROUP_BY = 'reshared_group_by'; const SUBJECT_RESHARED_LINK_BY = 'reshared_link_by'; const SUBJECT_RESHARED_USER_BY = 'reshared_user_by'; + const SUBJECT_SHARED_EMAIL = 'shared_with_email'; const SUBJECT_SHARED_WITH_BY = 'shared_with_by'; /** @var IFactory */ @@ -182,6 +183,8 @@ class Activity implements IExtension { return (string) $l->t('%2$s shared %1$s with you', $params); case self::SUBJECT_SHARED_LINK_SELF: return (string) $l->t('You shared %1$s via link', $params); + case self::SUBJECT_SHARED_EMAIL: + return (string) $l->t('You shared %1$s with %2$s', $params); } } @@ -227,6 +230,11 @@ class Activity implements IExtension { 1 => 'username', 2 => '', ]; + case self::SUBJECT_SHARED_EMAIL: + return array( + 0 => 'file', + 1 => '',// 'email' is neither supported nor planned for now + ); case self::SUBJECT_SHARED_USER_SELF: case self::SUBJECT_SHARED_WITH_BY: diff --git a/apps/files_trashbin/tests/expiration.php b/apps/files_trashbin/tests/expiration.php index b3c6fcd95af..76fb24aa4df 100644 --- a/apps/files_trashbin/tests/expiration.php +++ b/apps/files_trashbin/tests/expiration.php @@ -207,6 +207,7 @@ class Expiration_Test extends \PHPUnit_Framework_TestCase { 'setSystemValues', 'setSystemValue', 'getSystemValue', + 'getFilteredSystemValue', 'deleteSystemValue', 'getAppKeys', 'setAppValue', diff --git a/apps/files_versions/lib/storage.php b/apps/files_versions/lib/storage.php index 6aa58c55e9b..bdf1811c5f9 100644 --- a/apps/files_versions/lib/storage.php +++ b/apps/files_versions/lib/storage.php @@ -315,6 +315,9 @@ class Storage { if (self::copyFileContents($users_view, 'files_versions' . $filename . '.v' . $revision, 'files' . $filename)) { $files_view->touch($file, $revision); Storage::scheduleExpire($uid, $file); + \OC_Hook::emit('\OCP\Versions', 'rollback', array( + 'path' => $filename, + )); return true; } else if ($versionCreated) { self::deleteVersion($users_view, $version); diff --git a/apps/files_versions/tests/expirationtest.php b/apps/files_versions/tests/expirationtest.php index 54024b85b78..11a4746c5e7 100644 --- a/apps/files_versions/tests/expirationtest.php +++ b/apps/files_versions/tests/expirationtest.php @@ -177,6 +177,7 @@ class Expiration_Test extends \Test\TestCase { 'setSystemValues', 'setSystemValue', 'getSystemValue', + 'getFilteredSystemValue', 'deleteSystemValue', 'getAppKeys', 'setAppValue', diff --git a/apps/files_versions/tests/versions.php b/apps/files_versions/tests/versions.php index da214ead60a..d4a85c8bffe 100644 --- a/apps/files_versions/tests/versions.php +++ b/apps/files_versions/tests/versions.php @@ -580,6 +580,35 @@ class Test_Files_Versioning extends \Test\TestCase { $this->doTestRestore(); } + /** + * @param string $hookName name of hook called + * @param string $params variable to recieve parameters provided by hook + */ + private function connectMockHooks($hookName, &$params) { + if ($hookName === null) { + return; + } + + $eventHandler = $this->getMockBuilder('\stdclass') + ->setMethods(['callback']) + ->getMock(); + + $eventHandler->expects($this->any()) + ->method('callback') + ->will($this->returnCallback( + function($p) use (&$params) { + $params = $p; + } + )); + + \OCP\Util::connectHook( + '\OCP\Versions', + $hookName, + $eventHandler, + 'callback' + ); + } + private function doTestRestore() { $filePath = self::TEST_VERSIONS_USER . '/files/sub/test.txt'; $this->rootView->file_put_contents($filePath, 'test file'); @@ -608,7 +637,15 @@ class Test_Files_Versioning extends \Test\TestCase { $this->assertEquals('test file', $this->rootView->file_get_contents($filePath)); $info1 = $this->rootView->getFileInfo($filePath); + $params = array(); + $this->connectMockHooks('rollback', $params); + \OCA\Files_Versions\Storage::rollback('sub/test.txt', $t2); + $expectedParams = array( + 'path' => '/sub/test.txt', + ); + + $this->assertEquals($expectedParams, $params); $this->assertEquals('version2', $this->rootView->file_get_contents($filePath)); $info2 = $this->rootView->getFileInfo($filePath); diff --git a/config/config.sample.php b/config/config.sample.php index 51529cdb0cc..e0c486d75c1 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -806,6 +806,21 @@ $CONFIG = array( */ 'ldapUserCleanupInterval' => 51, +/** + * Enforce the existence of the home folder naming rule for all users + * + * Following scenario: + * * a home folder naming rule is set in LDAP advanced settings + * * a user doesn't have the home folder naming rule attribute set + * + * If this is set to **true** (default) it will NOT fallback to the core's + * default naming rule of using the internal user ID as home folder name. + * + * If this is set to **false** it will fallback for the users without the + * attribute set to naming the home folder like the internal user ID. + * + */ +'enforce_home_folder_naming_rule' => true, /** * Maintenance diff --git a/core/ajax/share.php b/core/ajax/share.php index 4546217def3..50c99cb56b4 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -183,6 +183,37 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo $result = $mailNotification->sendLinkShareMail($to_address, $file, $link, $expiration); if(empty($result)) { + // Get the token from the link + $linkParts = explode('/', $link); + $token = array_pop($linkParts); + + // Get the share for the token + $share = \OCP\Share::getShareByToken($token, false); + if ($share !== false) { + $currentUser = \OC::$server->getUserSession()->getUser()->getUID(); + $file = '/' . ltrim($file, '/'); + + // Check whether share belongs to the user and whether the file is the same + if ($share['file_target'] === $file && $share['uid_owner'] === $currentUser) { + + // Get the path for the user + $view = new \OC\Files\View('/' . $currentUser . '/files'); + $fileId = (int) $share['item_source']; + $path = $view->getPath((int) $share['item_source']); + + if ($path !== null) { + $event = \OC::$server->getActivityManager()->generateEvent(); + $event->setApp(\OCA\Files_Sharing\Activity::FILES_SHARING_APP) + ->setType(\OCA\Files_Sharing\Activity::TYPE_SHARED) + ->setAuthor($currentUser) + ->setAffectedUser($currentUser) + ->setObject('files', $fileId, $path) + ->setSubject(\OCA\Files_Sharing\Activity::SUBJECT_SHARED_EMAIL, [$path, $to_address]); + \OC::$server->getActivityManager()->publish($event); + } + } + } + \OCP\JSON::success(); } else { $l = \OC::$server->getL10N('core'); diff --git a/core/command/app/checkcode.php b/core/command/app/checkcode.php index a4e7322460f..9f6e0729ce9 100644 --- a/core/command/app/checkcode.php +++ b/core/command/app/checkcode.php @@ -26,6 +26,8 @@ namespace OC\Core\Command\App; use OC\App\CodeChecker\CodeChecker; use OC\App\CodeChecker\EmptyCheck; +use OC\App\CodeChecker\InfoChecker; +use OC\App\InfoParser; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -33,12 +35,21 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class CheckCode extends Command { + + /** @var InfoParser */ + private $infoParser; + protected $checkers = [ 'private' => '\OC\App\CodeChecker\PrivateCheck', 'deprecation' => '\OC\App\CodeChecker\DeprecationCheck', 'strong-comparison' => '\OC\App\CodeChecker\StrongComparisonCheck', ]; + public function __construct(InfoParser $infoParser) { + parent::__construct(); + $this->infoParser = $infoParser; + } + protected function configure() { $this ->setName('app:check-code') @@ -54,6 +65,12 @@ class CheckCode extends Command { InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'enable the specified checker(s)', [ 'private', 'deprecation', 'strong-comparison' ] + ) + ->addOption( + '--skip-validate-info', + null, + InputOption::VALUE_NONE, + 'skips the info.xml/version check' ); } @@ -84,7 +101,7 @@ class CheckCode extends Command { $output->writeln("<info>Analysing {$filename}</info>"); } - // show error count if there are errros present or the verbosity is high + // show error count if there are errors present or the verbosity is high if($count > 0 || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $output->writeln(" {$count} errors"); } @@ -98,6 +115,53 @@ class CheckCode extends Command { } }); $errors = $codeChecker->analyse($appId); + + if(!$input->getOption('skip-validate-info')) { + $infoChecker = new InfoChecker($this->infoParser); + + $infoChecker->listen('InfoChecker', 'mandatoryFieldMissing', function($key) use ($output) { + $output->writeln("<error>Mandatory field missing: $key</error>"); + }); + + $infoChecker->listen('InfoChecker', 'deprecatedFieldFound', function($key, $value) use ($output) { + if($value === [] || is_null($value) || $value === '') { + $output->writeln("<info>Deprecated field available: $key</info>"); + } else { + $output->writeln("<info>Deprecated field available: $key => $value</info>"); + } + }); + + $infoChecker->listen('InfoChecker', 'differentVersions', function($versionFile, $infoXML) use ($output) { + $output->writeln("<error>Different versions provided (appinfo/version: $versionFile - appinfo/info.xml: $infoXML)</error>"); + }); + + $infoChecker->listen('InfoChecker', 'sameVersions', function($path) use ($output) { + $output->writeln("<info>Version file isn't needed anymore and can be safely removed ($path)</info>"); + }); + + $infoChecker->listen('InfoChecker', 'migrateVersion', function($version) use ($output) { + $output->writeln("<info>Migrate the app version to appinfo/info.xml (add <version>$version</version> to appinfo/info.xml and remove appinfo/version)</info>"); + }); + + if(OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $infoChecker->listen('InfoChecker', 'mandatoryFieldFound', function($key, $value) use ($output) { + $output->writeln("<info>Mandatory field available: $key => $value</info>"); + }); + + $infoChecker->listen('InfoChecker', 'optionalFieldFound', function($key, $value) use ($output) { + $output->writeln("<info>Optional field available: $key => $value</info>"); + }); + + $infoChecker->listen('InfoChecker', 'unusedFieldFound', function($key, $value) use ($output) { + $output->writeln("<info>Unused field available: $key => $value</info>"); + }); + } + + $infoErrors = $infoChecker->analyse($appId); + + $errors = array_merge($errors, $infoErrors); + } + if (empty($errors)) { $output->writeln('<info>App is compliant - awesome job!</info>'); return 0; diff --git a/core/command/config/listconfigs.php b/core/command/config/listconfigs.php index 5796362f2fc..37aeb53c6f5 100644 --- a/core/command/config/listconfigs.php +++ b/core/command/config/listconfigs.php @@ -32,20 +32,6 @@ use Symfony\Component\Console\Output\OutputInterface; class ListConfigs extends Base { protected $defaultOutputFormat = self::OUTPUT_FORMAT_JSON_PRETTY; - /** @var array */ - protected $sensitiveValues = [ - 'dbpassword' => true, - 'dbuser' => true, - 'mail_smtpname' => true, - 'mail_smtppassword' => true, - 'passwordsalt' => true, - 'secret' => true, - 'ldap_agent_password' => true, - 'objectstore' => ['arguments' => ['password' => true]], - ]; - - const SENSITIVE_VALUE = '***REMOVED SENSITIVE VALUE***'; - /** * @var SystemConfig */ protected $systemConfig; @@ -127,10 +113,10 @@ class ListConfigs extends Base { $configs = []; foreach ($keys as $key) { - $value = $this->systemConfig->getValue($key, serialize(null)); - - if ($noSensitiveValues && isset($this->sensitiveValues[$key])) { - $value = $this->removeSensitiveValue($this->sensitiveValues[$key], $value); + if ($noSensitiveValues) { + $value = $this->systemConfig->getFilteredValue($key, serialize(null)); + } else { + $value = $this->systemConfig->getValue($key, serialize(null)); } if ($value !== 'N;') { @@ -140,25 +126,4 @@ class ListConfigs extends Base { return $configs; } - - /** - * @param bool|array $keysToRemove - * @param mixed $value - * @return mixed - */ - protected function removeSensitiveValue($keysToRemove, $value) { - if ($keysToRemove === true) { - return self::SENSITIVE_VALUE; - } - - if (is_array($value)) { - foreach ($keysToRemove as $keyToRemove => $valueToRemove) { - if (isset($value[$keyToRemove])) { - $value[$keyToRemove] = $this->removeSensitiveValue($valueToRemove, $value[$keyToRemove]); - } - } - } - - return $value; - } } diff --git a/core/css/apps.css b/core/css/apps.css index 6dd7e63bb69..23e0c519d00 100644 --- a/core/css/apps.css +++ b/core/css/apps.css @@ -417,7 +417,6 @@ position: relative; height: 100%; overflow-y: auto; - -webkit-overflow-scrolling: touch; } #app-content-wrapper { diff --git a/core/css/fixes.css b/core/css/fixes.css index e5dbeb137ab..7ef44ba6909 100644 --- a/core/css/fixes.css +++ b/core/css/fixes.css @@ -115,8 +115,3 @@ select { line-height: 38px; } -.lte8 input[type="checkbox"] + label:before { background-image: url('../img/actions/checkbox.png'); } -.lte8 input[type="checkbox"].white + label:before { background-image: url('../img/actions/checkbox-white.png'); } -.lte8 input[type="checkbox"]:checked + label:before { background-image: url('../img/actions/checkbox-checked.png'); } -.lte8 input[type="checkbox"].white:checked + label:before { background-image: url('../img/actions/checkbox-checked-white.png'); } - diff --git a/core/css/styles.css b/core/css/styles.css index aefc0ff755a..d952a33c24a 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -158,7 +158,9 @@ textarea:hover, textarea:focus, textarea:active { -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; opacity: 1; } -input[type="checkbox"] { + +/* ie8 doesn't support :checked */ +html:not(.ie8) input[type="checkbox"].checkbox { margin:0; padding:0; height:auto; @@ -166,7 +168,7 @@ input[type="checkbox"] { display: none; } -input[type="checkbox"] + label:before { +html:not(.ie8) input[type="checkbox"].checkbox + label:before { content: ""; display: inline-block; @@ -174,27 +176,27 @@ input[type="checkbox"] + label:before { width: 20px; vertical-align: middle; - background: url('../img/actions/checkbox.svg') left center no-repeat; + background: url('../img/actions/checkbox.svg') left top no-repeat; opacity: 0.7; } -input[type="checkbox"]:disabled +label:before { opacity: .6; } +html:not(.ie8) input[type="checkbox"].checkbox:disabled +label:before { opacity: .6; } -input[type="checkbox"].u-left +label:before { float: left; } +html:not(.ie8) input[type="checkbox"].checkbox.u-left +label:before { float: left; } -input[type="checkbox"].white + label:before { +html:not(.ie8) input[type="checkbox"].checkbox--white + label:before { background-image: url('../img/actions/checkbox-white.svg'); } -input[type="checkbox"]:checked + label:before { +html:not(.ie8) input[type="checkbox"].checkbox:checked + label:before { background-image: url('../img/actions/checkbox-checked.svg'); } -input[type="checkbox"].white:checked + label:before { +html:not(.ie8) input[type="checkbox"].checkbox--white:checked + label:before { background-image: url('../img/actions/checkbox-checked-white.svg'); } -input[type="checkbox"]:hover+label:before, input[type="checkbox"]:focus+label:before { +html:not(.ie8) input[type="checkbox"].checkbox:hover+label:before, input[type="checkbox"]:focus+label:before { color:#111 !important; } diff --git a/core/js/multiselect.js b/core/js/multiselect.js index 41dc68ac051..6d5c54ac0f5 100644 --- a/core/js/multiselect.js +++ b/core/js/multiselect.js @@ -109,6 +109,9 @@ var id='ms'+multiSelectId+'-option-'+item; var input=$('<input type="' + inputType + '"/>'); input.attr('id',id); + if(inputType === 'checkbox') { + input.addClass('checkbox'); + } if(settings.singleSelect) { input.attr('name', 'ms'+multiSelectId+'-option'); } diff --git a/core/register_command.php b/core/register_command.php index 114e115c491..878542f72c9 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -28,7 +28,8 @@ /** @var $application Symfony\Component\Console\Application */ $application->add(new OC\Core\Command\Status); $application->add(new OC\Core\Command\Check(\OC::$server->getConfig())); -$application->add(new OC\Core\Command\App\CheckCode()); +$infoParser = new \OC\App\InfoParser(\OC::$server->getHTTPHelper(), \OC::$server->getURLGenerator()); +$application->add(new OC\Core\Command\App\CheckCode($infoParser)); $application->add(new OC\Core\Command\L10n\CreateJs()); if (\OC::$server->getConfig()->getSystemValue('installed', false)) { diff --git a/core/templates/login.php b/core/templates/login.php index 2057b1034d0..db77f63bbd0 100644 --- a/core/templates/login.php +++ b/core/templates/login.php @@ -65,7 +65,7 @@ script('core', [ <?php endif; ?> <?php if ($_['rememberLoginAllowed'] === true) : ?> <div class="remember-login-container"> - <input type="checkbox" name="remember_login" value="1" id="remember_login" class="white"> + <input type="checkbox" name="remember_login" value="1" id="remember_login" class="checkbox checkbox--white"> <label for="remember_login"><?php p($l->t('remember')); ?></label> </div> <?php endif; ?> diff --git a/lib/base.php b/lib/base.php index 3624a3fbaf9..32b90cad1fa 100644 --- a/lib/base.php +++ b/lib/base.php @@ -376,63 +376,6 @@ class OC { $tmpl->printPage(); } - public static function initTemplateEngine() { - // Add the stuff we need always - // following logic will import all vendor libraries that are - // specified in core/js/core.json - $fileContent = file_get_contents(OC::$SERVERROOT . '/core/js/core.json'); - if($fileContent !== false) { - $coreDependencies = json_decode($fileContent, true); - foreach($coreDependencies['vendor'] as $vendorLibrary) { - // remove trailing ".js" as addVendorScript will append it - OC_Util::addVendorScript( - substr($vendorLibrary, 0, strlen($vendorLibrary) - 3)); - } - } else { - throw new \Exception('Cannot read core/js/core.json'); - } - - OC_Util::addScript("placeholders"); - OC_Util::addScript("compatibility"); - OC_Util::addScript("jquery.ocdialog"); - OC_Util::addScript("oc-dialogs"); - OC_Util::addScript("js"); - OC_Util::addScript("l10n"); - OC_Util::addTranslations("core"); - OC_Util::addScript("octemplate"); - OC_Util::addScript("eventsource"); - OC_Util::addScript("config"); - OC_Util::addScript('search', 'search'); - OC_Util::addScript("oc-requesttoken"); - OC_Util::addScript("apps"); - OC_Util::addScript('mimetype'); - OC_Util::addScript('mimetypelist'); - OC_Util::addVendorScript('snapjs/dist/latest/snap'); - OC_Util::addVendorScript('core', 'backbone/backbone'); - OC_Util::addScript('oc-backbone'); - - // avatars - if (\OC::$server->getSystemConfig()->getValue('enable_avatars', true) === true) { - \OC_Util::addScript('placeholder'); - \OC_Util::addVendorScript('blueimp-md5/js/md5'); - \OC_Util::addScript('jquery.avatar'); - \OC_Util::addScript('avatar'); - } - - OC_Util::addStyle("styles"); - OC_Util::addStyle("header"); - OC_Util::addStyle("mobile"); - OC_Util::addStyle("icons"); - OC_Util::addStyle("fonts"); - OC_Util::addStyle("apps"); - OC_Util::addStyle("fixes"); - OC_Util::addStyle("multiselect"); - OC_Util::addVendorStyle('jquery-ui/themes/base/jquery-ui'); - OC_Util::addStyle('jquery-ui-fixes'); - OC_Util::addStyle("tooltip"); - OC_Util::addStyle("jquery.ocdialog"); - } - public static function initSession() { // prevents javascript from accessing php session cookies ini_set('session.cookie_httponly', true); @@ -611,7 +554,6 @@ class OC { self::initSession(); } \OC::$server->getEventLogger()->end('init_session'); - self::initTemplateEngine(); self::checkConfig(); self::checkInstalled(); @@ -673,7 +615,7 @@ class OC { self::registerFilesystemHooks(); if ($systemConfig->getValue('enable_previews', true)) { self::registerPreviewHooks(); - } + } self::registerShareHooks(); self::registerLogRotate(); self::registerLocalAddressBook(); @@ -686,12 +628,6 @@ class OC { $lockProvider = \OC::$server->getLockingProvider(); register_shutdown_function(array($lockProvider, 'releaseAll')); - if ($systemConfig->getValue('installed', false) && !self::checkUpgrade(false)) { - if (\OC::$server->getConfig()->getAppValue('core', 'backgroundjobs_mode', 'ajax') == 'ajax') { - OC_Util::addScript('backgroundjobs'); - } - } - // Check whether the sample configuration has been copied if($systemConfig->getValue('copied_sample_config', false)) { $l = \OC::$server->getL10N('lib'); @@ -803,8 +739,9 @@ class OC { OC_Hook::connect('\OCP\Versions', 'preDelete', 'OC\Preview', 'prepare_delete'); OC_Hook::connect('\OCP\Trashbin', 'preDelete', 'OC\Preview', 'prepare_delete'); OC_Hook::connect('OC_Filesystem', 'post_delete', 'OC\Preview', 'post_delete_files'); - OC_Hook::connect('\OCP\Versions', 'delete', 'OC\Preview', 'post_delete'); + OC_Hook::connect('\OCP\Versions', 'delete', 'OC\Preview', 'post_delete_versions'); OC_Hook::connect('\OCP\Trashbin', 'delete', 'OC\Preview', 'post_delete'); + OC_Hook::connect('\OCP\Versions', 'rollback', 'OC\Preview', 'post_delete_versions'); } /** diff --git a/lib/private/allconfig.php b/lib/private/allconfig.php index 63cc92601bb..7c2037e8048 100644 --- a/lib/private/allconfig.php +++ b/lib/private/allconfig.php @@ -119,6 +119,17 @@ class AllConfig implements \OCP\IConfig { } /** + * Looks up a system wide defined value and filters out sensitive data + * + * @param string $key the key of the value, under which it was saved + * @param mixed $default the default value to be returned if the value isn't set + * @return mixed the value or $default + */ + public function getFilteredSystemValue($key, $default = '') { + return $this->systemConfig->getFilteredValue($key, $default); + } + + /** * Delete a system wide defined value * * @param string $key the key of the value, under which it was saved diff --git a/lib/private/app/codechecker/infochecker.php b/lib/private/app/codechecker/infochecker.php new file mode 100644 index 00000000000..91580bde07d --- /dev/null +++ b/lib/private/app/codechecker/infochecker.php @@ -0,0 +1,146 @@ +<?php +/** + * @author Morris Jobke <hey@morrisjobke.de> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\App\CodeChecker; + +use OC\App\InfoParser; +use OC\Hooks\BasicEmitter; + +class InfoChecker extends BasicEmitter { + + /** @var InfoParser */ + private $infoParser; + + private $mandatoryFields = [ + 'author', + 'description', + 'id', + 'licence', + 'name', + ]; + private $optionalFields = [ + 'bugs', + 'category', + 'default_enable', + 'dependencies', + 'documentation', + 'namespace', + 'ocsid', + 'public', + 'remote', + 'repository', + 'require', + 'requiremin', + 'types', + 'version', + 'website', + ]; + private $deprecatedFields = [ + 'info', + 'shipped', + 'standalone', + ]; + + public function __construct(InfoParser $infoParser) { + $this->infoParser = $infoParser; + } + + /** + * @param string $appId + * @return array + */ + public function analyse($appId) { + $appPath = \OC_App::getAppPath($appId); + if ($appPath === false) { + throw new \RuntimeException("No app with given id <$appId> known."); + } + + $errors = []; + + $info = $this->infoParser->parse($appPath . '/appinfo/info.xml'); + + foreach ($info as $key => $value) { + if(is_array($value)) { + $value = json_encode($value); + } + if (in_array($key, $this->mandatoryFields)) { + $this->emit('InfoChecker', 'mandatoryFieldFound', [$key, $value]); + continue; + } + + if (in_array($key, $this->optionalFields)) { + $this->emit('InfoChecker', 'optionalFieldFound', [$key, $value]); + continue; + } + + if (in_array($key, $this->deprecatedFields)) { + // skip empty arrays - empty arrays for remote and public are always added + if($value === '[]' && in_array($key, ['public', 'remote', 'info'])) { + continue; + } + $this->emit('InfoChecker', 'deprecatedFieldFound', [$key, $value]); + continue; + } + + $this->emit('InfoChecker', 'unusedFieldFound', [$key, $value]); + } + + foreach ($this->mandatoryFields as $key) { + if(!isset($info[$key])) { + $this->emit('InfoChecker', 'mandatoryFieldMissing', [$key]); + $errors[] = [ + 'type' => 'mandatoryFieldMissing', + 'field' => $key, + ]; + } + } + + $versionFile = $appPath . '/appinfo/version'; + if (is_file($versionFile)) { + $version = trim(file_get_contents($versionFile)); + if(isset($info['version'])) { + if($info['version'] !== $version) { + $this->emit('InfoChecker', 'differentVersions', + [$version, $info['version']]); + $errors[] = [ + 'type' => 'differentVersions', + 'message' => 'appinfo/version: ' . $version . + ' - appinfo/info.xml: ' . $info['version'], + ]; + } else { + $this->emit('InfoChecker', 'sameVersions', [$versionFile]); + } + } else { + $this->emit('InfoChecker', 'migrateVersion', [$version]); + } + } else { + if(!isset($info['version'])) { + $this->emit('InfoChecker', 'mandatoryFieldMissing', ['version']); + $errors[] = [ + 'type' => 'mandatoryFieldMissing', + 'field' => 'version', + ]; + } + } + + return $errors; + } +} diff --git a/lib/private/log.php b/lib/private/log.php index 1b26ece7624..4a0a34b7113 100644 --- a/lib/private/log.php +++ b/lib/private/log.php @@ -3,6 +3,7 @@ * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> + * @author Olivier Paroz <owncloud@oparoz.com> * @author Robin Appelman <icewind@owncloud.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> @@ -26,6 +27,8 @@ namespace OC; +use InterfaSys\LogNormalizer\Normalizer; + use \OCP\ILogger; use OCP\Security\StringUtils; @@ -48,12 +51,15 @@ class Log implements ILogger { /** @var boolean|null cache the result of the log condition check for the request */ private $logConditionSatisfied = null; + /** @var Normalizer */ + private $normalizer; /** * @param string $logger The logger that should be used * @param SystemConfig $config the system config object + * @param null $normalizer */ - public function __construct($logger=null, SystemConfig $config=null) { + public function __construct($logger=null, SystemConfig $config=null, $normalizer = null) { // FIXME: Add this for backwards compatibility, should be fixed at some point probably if($config === null) { $config = \OC::$server->getSystemConfig(); @@ -68,6 +74,11 @@ class Log implements ILogger { } else { $this->logger = $logger; } + if ($normalizer === null) { + $this->normalizer = new Normalizer(); + } else { + $this->normalizer = $normalizer; + } } @@ -175,6 +186,8 @@ class Log implements ILogger { $minLevel = min($this->config->getValue('loglevel', \OCP\Util::WARN), \OCP\Util::ERROR); $logCondition = $this->config->getValue('log.condition', []); + array_walk($context, [$this->normalizer, 'format']); + if (isset($context['app'])) { $app = $context['app']; diff --git a/lib/private/preview.php b/lib/private/preview.php index 978da1161c2..de964b72df2 100644 --- a/lib/private/preview.php +++ b/lib/private/preview.php @@ -1296,6 +1296,13 @@ class Preview { /** * @param array $args + */ + public static function post_delete_versions($args) { + self::post_delete($args, 'files/'); + } + + /** + * @param array $args * @param string $prefix */ public static function post_delete($args, $prefix = '') { diff --git a/lib/private/server.php b/lib/private/server.php index 9f99ead849b..26eb99927fc 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -1098,4 +1098,5 @@ class Server extends SimpleContainer implements IServerContainer { public function getUserStoragesService() { return \OC_Mount_Config::$app->getContainer()->query('OCA\\Files_External\\Service\\UserStoragesService'); } + } diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 07c7f31a853..32389f34868 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -1998,7 +1998,6 @@ class Share extends Constants { $suggestedItemTarget = $result['suggestedItemTarget']; $suggestedFileTarget = $result['suggestedFileTarget']; $filePath = $result['filePath']; - $expirationDate = $result['expirationDate']; } $isGroupShare = false; diff --git a/lib/private/systemconfig.php b/lib/private/systemconfig.php index 13b0959768a..94b815aebd7 100644 --- a/lib/private/systemconfig.php +++ b/lib/private/systemconfig.php @@ -22,12 +22,28 @@ namespace OC; + +use OCP\IConfig; + /** * Class which provides access to the system config values stored in config.php * Internal class for bootstrap only. * fixes cyclic DI: AllConfig needs AppConfig needs Database needs AllConfig */ class SystemConfig { + + /** @var array */ + protected $sensitiveValues = [ + 'dbpassword' => true, + 'dbuser' => true, + 'mail_smtpname' => true, + 'mail_smtppassword' => true, + 'passwordsalt' => true, + 'secret' => true, + 'ldap_agent_password' => true, + 'objectstore' => ['arguments' => ['password' => true]], + ]; + /** * Lists all available config keys * @return array an array of key names @@ -68,6 +84,23 @@ class SystemConfig { } /** + * Looks up a system wide defined value and filters out sensitive data + * + * @param string $key the key of the value, under which it was saved + * @param mixed $default the default value to be returned if the value isn't set + * @return mixed the value or $default + */ + public function getFilteredValue($key, $default = '') { + $value = $this->getValue($key, $default); + + if (isset($this->sensitiveValues[$key])) { + $value = $this->removeSensitiveValue($this->sensitiveValues[$key], $value); + } + + return $value; + } + + /** * Delete a system wide defined value * * @param string $key the key of the value, under which it was saved @@ -75,4 +108,25 @@ class SystemConfig { public function deleteValue($key) { \OC_Config::deleteKey($key); } + + /** + * @param bool|array $keysToRemove + * @param mixed $value + * @return mixed + */ + protected function removeSensitiveValue($keysToRemove, $value) { + if ($keysToRemove === true) { + return IConfig::SENSITIVE_VALUE; + } + + if (is_array($value)) { + foreach ($keysToRemove as $keyToRemove => $valueToRemove) { + if (isset($value[$keyToRemove])) { + $value[$keyToRemove] = $this->removeSensitiveValue($valueToRemove, $value[$keyToRemove]); + } + } + } + + return $value; + } } diff --git a/lib/private/template.php b/lib/private/template.php index 920be71abbf..0300e43edea 100644 --- a/lib/private/template.php +++ b/lib/private/template.php @@ -37,27 +37,40 @@ require_once __DIR__.'/template/functions.php'; * This class provides the templates for ownCloud. */ class OC_Template extends \OC\Template\Base { - private $renderas; // Create a full page? + + /** @var string */ + private $renderAs; // Create a full page? + + /** @var string */ private $path; // The path to the template + + /** @var array */ private $headers = array(); //custom headers + + /** @var string */ protected $app; // app id /** * Constructor * @param string $app app providing the template * @param string $name of the template file (without suffix) - * @param string $renderas = ""; produce a full page + * @param string $renderAs = ""; produce a full page * @param bool $registerCall = true * @return OC_Template object * * This function creates an OC_Template object. * - * If $renderas is set, OC_Template will try to produce a full page in the - * according layout. For now, renderas can be set to "guest", "user" or + * If $renderAs is set, OC_Template will try to produce a full page in the + * according layout. For now, $renderAs can be set to "guest", "user" or * "admin". */ - public function __construct( $app, $name, $renderas = "", $registerCall = true ) { + + protected static $initTemplateEngineFirstRun = true; + + public function __construct( $app, $name, $renderAs = "", $registerCall = true ) { // Read the selected theme from the config file + self::initTemplateEngine(); + $theme = OC_Util::getTheme(); $requesttoken = (OC::$server->getSession() and $registerCall) ? OC_Util::callRegister() : ''; @@ -69,13 +82,85 @@ class OC_Template extends \OC\Template\Base { list($path, $template) = $this->findTemplate($theme, $app, $name); // Set the private data - $this->renderas = $renderas; + $this->renderAs = $renderAs; $this->path = $path; $this->app = $app; parent::__construct($template, $requesttoken, $l10n, $themeDefaults); } + public static function initTemplateEngine() { + if (self::$initTemplateEngineFirstRun){ + + //apps that started before the template initialization can load their own scripts/styles + //so to make sure this scripts/styles here are loaded first we use OC_Util::addScript() with $prepend=true + //meaning the last script/style in this list will be loaded first + if (\OC::$server->getSystemConfig ()->getValue ( 'installed', false ) && ! \OCP\Util::needUpgrade ()) { + if (\OC::$server->getConfig ()->getAppValue ( 'core', 'backgroundjobs_mode', 'ajax' ) == 'ajax') { + OC_Util::addScript ( 'backgroundjobs', null, true ); + } + } + + OC_Util::addStyle("tooltip",null,true); + OC_Util::addStyle('jquery-ui-fixes',null,true); + OC_Util::addVendorStyle('jquery-ui/themes/base/jquery-ui',null,true); + OC_Util::addStyle("multiselect",null,true); + OC_Util::addStyle("fixes",null,true); + OC_Util::addStyle("apps",null,true); + OC_Util::addStyle("fonts",null,true); + OC_Util::addStyle("icons",null,true); + OC_Util::addStyle("mobile",null,true); + OC_Util::addStyle("header",null,true); + OC_Util::addStyle("styles",null,true); + + // avatars + if (\OC::$server->getSystemConfig()->getValue('enable_avatars', true) === true) { + \OC_Util::addScript('avatar', null, true); + \OC_Util::addScript('jquery.avatar', null, true); + \OC_Util::addScript('placeholder', null, true); + } + + OC_Util::addScript('oc-backbone', null, true); + OC_Util::addVendorScript('core', 'backbone/backbone', true); + OC_Util::addVendorScript('snapjs/dist/latest/snap', null, true); + OC_Util::addScript('mimetypelist', null, true); + OC_Util::addScript('mimetype', null, true); + OC_Util::addScript("apps", null, true); + OC_Util::addScript("oc-requesttoken", null, true); + OC_Util::addScript('search', 'search', true); + OC_Util::addScript("config", null, true); + OC_Util::addScript("eventsource", null, true); + OC_Util::addScript("octemplate", null, true); + OC_Util::addTranslations("core", null, true); + OC_Util::addScript("l10n", null, true); + OC_Util::addScript("js", null, true); + OC_Util::addScript("oc-dialogs", null, true); + OC_Util::addScript("jquery.ocdialog", null, true); + OC_Util::addStyle("jquery.ocdialog"); + OC_Util::addScript("compatibility", null, true); + OC_Util::addScript("placeholders", null, true); + + // Add the stuff we need always + // following logic will import all vendor libraries that are + // specified in core/js/core.json + $fileContent = file_get_contents(OC::$SERVERROOT . '/core/js/core.json'); + if($fileContent !== false) { + $coreDependencies = json_decode($fileContent, true); + foreach(array_reverse($coreDependencies['vendor']) as $vendorLibrary) { + // remove trailing ".js" as addVendorScript will append it + OC_Util::addVendorScript( + substr($vendorLibrary, 0, strlen($vendorLibrary) - 3),null,true); + } + } else { + throw new \Exception('Cannot read core/js/core.json'); + } + + self::$initTemplateEngineFirstRun = false; + } + + } + + /** * find the template with the given name * @param string $name of the template file (without suffix) @@ -118,14 +203,14 @@ class OC_Template extends \OC\Template\Base { * Process the template * @return boolean|string * - * This function process the template. If $this->renderas is set, it + * This function process the template. If $this->renderAs is set, it * will produce a full page. */ public function fetchPage() { $data = parent::fetchPage(); - if( $this->renderas ) { - $page = new OC_TemplateLayout($this->renderas, $this->app); + if( $this->renderAs ) { + $page = new OC_TemplateLayout($this->renderAs, $this->app); // Add custom headers $headers = ''; @@ -141,18 +226,20 @@ class OC_Template extends \OC\Template\Base { } } - $page->assign('headers', $headers, false); + $page->assign('headers', $headers); - $page->assign('content', $data, false ); + $page->assign('content', $data); return $page->fetchPage(); } - else{ - return $data; - } + + return $data; } /** * Include template + * + * @param string $file + * @param array|null $additionalParams * @return string returns content of included template * * Includes another template. use <?php echo $this->inc('template'); ?> to @@ -222,7 +309,7 @@ class OC_Template extends \OC\Template\Base { /** * print error page using Exception details - * @param Exception|Error $exception + * @param Exception $exception */ public static function printExceptionErrorPage($exception) { $request = \OC::$server->getRequest(); diff --git a/lib/private/util.php b/lib/private/util.php index 667d358655f..9abaef71a68 100644 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -439,16 +439,23 @@ class OC_Util { * * @param string $application application id * @param string|null $file filename + * @param bool $prepend prepend the Script to the beginning of the list * @return void */ - public static function addScript($application, $file = null) { + public static function addScript($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'js', $file); + //TODO eliminate double code if (!in_array($path, self::$scripts)) { // core js files need separate handling if ($application !== 'core' && $file !== null) { self::addTranslations($application); } - self::$scripts[] = $path; + if ($prepend===true) { + array_unshift(self::$scripts, $path); + } + else { + self::$scripts[] = $path; + } } } @@ -457,12 +464,18 @@ class OC_Util { * * @param string $application application id * @param string|null $file filename + * @param bool $prepend prepend the Script to the beginning of the list * @return void */ - public static function addVendorScript($application, $file = null) { + public static function addVendorScript($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'vendor', $file); - if (!in_array($path, self::$scripts)) { - self::$scripts[] = $path; + //TODO eliminate double code + if (! in_array ( $path, self::$scripts )) { + if ($prepend === true) { + array_unshift ( self::$scripts, $path ); + } else { + self::$scripts [] = $path; + } } } @@ -471,8 +484,9 @@ class OC_Util { * * @param string $application application id * @param string $languageCode language code, defaults to the current language + * @param bool $prepend prepend the Script to the beginning of the list */ - public static function addTranslations($application, $languageCode = null) { + public static function addTranslations($application, $languageCode = null, $prepend = false) { if (is_null($languageCode)) { $languageCode = \OC_L10N::findLanguage($application); } @@ -481,8 +495,13 @@ class OC_Util { } else { $path = "l10n/$languageCode"; } + //TODO eliminate double code if (!in_array($path, self::$scripts)) { - self::$scripts[] = $path; + if ($prepend === true) { + array_unshift ( self::$scripts, $path ); + } else { + self::$scripts [] = $path; + } } } @@ -491,12 +510,18 @@ class OC_Util { * * @param string $application application id * @param string|null $file filename + * @param bool $prepend prepend the Style to the beginning of the list * @return void */ - public static function addStyle($application, $file = null) { + public static function addStyle($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'css', $file); + //TODO eliminate double code if (!in_array($path, self::$styles)) { - self::$styles[] = $path; + if ($prepend === true) { + array_unshift ( self::$styles, $path ); + } else { + self::$styles[] = $path; + } } } @@ -505,12 +530,18 @@ class OC_Util { * * @param string $application application id * @param string|null $file filename + * @param bool $prepend prepend the Style to the beginning of the list * @return void */ - public static function addVendorStyle($application, $file = null) { + public static function addVendorStyle($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'vendor', $file); + //TODO eliminate double code if (!in_array($path, self::$styles)) { - self::$styles[] = $path; + if ($prepend === true) { + array_unshift ( self::$styles, $path ); + } else { + self::$styles[] = $path; + } } } diff --git a/lib/public/iconfig.php b/lib/public/iconfig.php index ff0b6c6a5b0..933eef97ae1 100644 --- a/lib/public/iconfig.php +++ b/lib/public/iconfig.php @@ -41,6 +41,11 @@ namespace OCP; */ interface IConfig { /** + * @since 8.2.0 + */ + const SENSITIVE_VALUE = '***REMOVED SENSITIVE VALUE***'; + + /** * Sets and deletes system wide values * * @param array $configs Associative array with `key => value` pairs @@ -69,6 +74,16 @@ interface IConfig { public function getSystemValue($key, $default = ''); /** + * Looks up a system wide defined value and filters out sensitive data + * + * @param string $key the key of the value, under which it was saved + * @param mixed $default the default value to be returned if the value isn't set + * @return mixed the value or $default + * @since 8.2.0 + */ + public function getFilteredSystemValue($key, $default = ''); + + /** * Delete a system wide defined value * * @param string $key the key of the value, under which it was saved diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index 8be23dff214..e37652c1adc 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -194,6 +194,12 @@ interface IServerContainer { public function getAppConfig(); /** + * @return \OCP\L10N\IFactory + * @since 8.2.0 + */ + public function getL10NFactory(); + + /** * get an L10N instance * @param string $app appid * @param string $lang diff --git a/settings/js/users/users.js b/settings/js/users/users.js index 5b12366ad40..47d63c11b95 100644 --- a/settings/js/users/users.js +++ b/settings/js/users/users.js @@ -813,44 +813,73 @@ $(document).ready(function () { }); }); + if ($('#CheckboxStorageLocation').is(':checked')) { + $("#userlist .storageLocation").show(); + } // Option to display/hide the "Storage location" column $('#CheckboxStorageLocation').click(function() { if ($('#CheckboxStorageLocation').is(':checked')) { $("#userlist .storageLocation").show(); + OC.AppConfig.setValue('core', 'umgmt_show_storage_location', 'true'); } else { $("#userlist .storageLocation").hide(); + OC.AppConfig.setValue('core', 'umgmt_show_storage_location', 'false'); } }); + + if ($('#CheckboxLastLogin').is(':checked')) { + $("#userlist .lastLogin").show(); + } // Option to display/hide the "Last Login" column $('#CheckboxLastLogin').click(function() { if ($('#CheckboxLastLogin').is(':checked')) { $("#userlist .lastLogin").show(); + OC.AppConfig.setValue('core', 'umgmt_show_last_login', 'true'); } else { $("#userlist .lastLogin").hide(); + OC.AppConfig.setValue('core', 'umgmt_show_last_login', 'false'); } }); + + if ($('#CheckboxEmailAddress').is(':checked')) { + $("#userlist .mailAddress").show(); + } // Option to display/hide the "Mail Address" column $('#CheckboxEmailAddress').click(function() { if ($('#CheckboxEmailAddress').is(':checked')) { $("#userlist .mailAddress").show(); + OC.AppConfig.setValue('core', 'umgmt_show_email', 'true'); } else { $("#userlist .mailAddress").hide(); + OC.AppConfig.setValue('core', 'umgmt_show_email', 'false'); } }); + + if ($('#CheckboxUserBackend').is(':checked')) { + $("#userlist .userBackend").show(); + } // Option to display/hide the "User Backend" column $('#CheckboxUserBackend').click(function() { if ($('#CheckboxUserBackend').is(':checked')) { $("#userlist .userBackend").show(); + OC.AppConfig.setValue('core', 'umgmt_show_backend', 'true'); } else { $("#userlist .userBackend").hide(); + OC.AppConfig.setValue('core', 'umgmt_show_backend', 'false'); } }); + + if ($('#CheckboxMailOnUserCreate').is(':checked')) { + $("#newemail").show(); + } // Option to display/hide the "E-Mail" input field $('#CheckboxMailOnUserCreate').click(function() { if ($('#CheckboxMailOnUserCreate').is(':checked')) { $("#newemail").show(); + OC.AppConfig.setValue('core', 'umgmt_send_email', 'true'); } else { $("#newemail").hide(); + OC.AppConfig.setValue('core', 'umgmt_send_email', 'false'); } }); diff --git a/settings/templates/users/main.php b/settings/templates/users/main.php index 73552f8ad2e..0abe31f4a59 100644 --- a/settings/templates/users/main.php +++ b/settings/templates/users/main.php @@ -45,31 +45,36 @@ translation('settings'); <div id="userlistoptions"> <p> - <input type="checkbox" name="StorageLocation" value="StorageLocation" id="CheckboxStorageLocation"> + <input type="checkbox" name="StorageLocation" value="StorageLocation" id="CheckboxStorageLocation" + <?php if ($_['show_storage_location'] === 'true') print_unescaped('checked="checked"'); ?> /> <label for="CheckboxStorageLocation"> <?php p($l->t('Show storage location')) ?> </label> </p> <p> - <input type="checkbox" name="LastLogin" value="LastLogin" id="CheckboxLastLogin"> + <input type="checkbox" name="LastLogin" value="LastLogin" id="CheckboxLastLogin" + <?php if ($_['show_last_login'] === 'true') print_unescaped('checked="checked"'); ?> /> <label for="CheckboxLastLogin"> <?php p($l->t('Show last log in')) ?> </label> </p> <p> - <input type="checkbox" name="UserBackend" value="UserBackend" id="CheckboxUserBackend"> + <input type="checkbox" name="UserBackend" value="UserBackend" id="CheckboxUserBackend" + <?php if ($_['show_backend'] === 'true') print_unescaped('checked="checked"'); ?> /> <label for="CheckboxUserBackend"> <?php p($l->t('Show user backend')) ?> </label> </p> <p> - <input type="checkbox" name="MailOnUserCreate" value="MailOnUserCreate" id="CheckboxMailOnUserCreate"> + <input type="checkbox" name="MailOnUserCreate" value="MailOnUserCreate" id="CheckboxMailOnUserCreate" + <?php if ($_['send_email'] === 'true') print_unescaped('checked="checked"'); ?> /> <label for="CheckboxMailOnUserCreate"> <?php p($l->t('Send email to new user')) ?> </label> </p> <p> - <input type="checkbox" name="EmailAddress" value="EmailAddress" id="CheckboxEmailAddress"> + <input type="checkbox" name="EmailAddress" value="EmailAddress" id="CheckboxEmailAddress" + <?php if ($_['show_email'] === 'true') print_unescaped('checked="checked"'); ?> /> <label for="CheckboxEmailAddress"> <?php p($l->t('Show email address')) ?> </label> diff --git a/settings/users.php b/settings/users.php index 843995a57f9..e0ef56f15c5 100644 --- a/settings/users.php +++ b/settings/users.php @@ -99,4 +99,11 @@ $tmpl->assign('default_quota', $defaultQuota); $tmpl->assign('defaultQuotaIsUserDefined', $defaultQuotaIsUserDefined); $tmpl->assign('recoveryAdminEnabled', $recoveryAdminEnabled); $tmpl->assign('enableAvatars', \OC::$server->getConfig()->getSystemValue('enable_avatars', true)); + +$tmpl->assign('show_storage_location', $config->getAppValue('core', 'umgmt_show_storage_location', 'false')); +$tmpl->assign('show_last_login', $config->getAppValue('core', 'umgmt_show_last_login', 'false')); +$tmpl->assign('show_email', $config->getAppValue('core', 'umgmt_show_email', 'false')); +$tmpl->assign('show_backend', $config->getAppValue('core', 'umgmt_show_backend', 'false')); +$tmpl->assign('send_email', $config->getAppValue('core', 'umgmt_send_email', 'false')); + $tmpl->printPage(); diff --git a/tests/apps/testapp-infoxml-version-different/appinfo/info.xml b/tests/apps/testapp-infoxml-version-different/appinfo/info.xml new file mode 100644 index 00000000000..c765400a76f --- /dev/null +++ b/tests/apps/testapp-infoxml-version-different/appinfo/info.xml @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<info> + <id>testapp-infoxml-version</id> + <version>1.2.3</version> + <author>Jane</author> + <description>A b c</description> + <licence>Abc</licence> + <name>Test app</name> +</info> diff --git a/tests/apps/testapp-infoxml-version-different/appinfo/version b/tests/apps/testapp-infoxml-version-different/appinfo/version new file mode 100644 index 00000000000..e8ea05db814 --- /dev/null +++ b/tests/apps/testapp-infoxml-version-different/appinfo/version @@ -0,0 +1 @@ +1.2.4 diff --git a/tests/apps/testapp-infoxml-version/appinfo/info.xml b/tests/apps/testapp-infoxml-version/appinfo/info.xml new file mode 100644 index 00000000000..c765400a76f --- /dev/null +++ b/tests/apps/testapp-infoxml-version/appinfo/info.xml @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<info> + <id>testapp-infoxml-version</id> + <version>1.2.3</version> + <author>Jane</author> + <description>A b c</description> + <licence>Abc</licence> + <name>Test app</name> +</info> diff --git a/tests/apps/testapp-infoxml-version/appinfo/version b/tests/apps/testapp-infoxml-version/appinfo/version new file mode 100644 index 00000000000..0495c4a88ca --- /dev/null +++ b/tests/apps/testapp-infoxml-version/appinfo/version @@ -0,0 +1 @@ +1.2.3 diff --git a/tests/apps/testapp-infoxml/appinfo/info.xml b/tests/apps/testapp-infoxml/appinfo/info.xml new file mode 100644 index 00000000000..cb63a0fc76e --- /dev/null +++ b/tests/apps/testapp-infoxml/appinfo/info.xml @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<info> + <id>testapp-infoxml</id> + <version>1.2.3</version> + <author>Jane</author> + <description>A b c</description> + <licence>Abc</licence> + <name>Test app</name> +</info> diff --git a/tests/apps/testapp-name-missing/appinfo/info.xml b/tests/apps/testapp-name-missing/appinfo/info.xml new file mode 100644 index 00000000000..f0a62b8d380 --- /dev/null +++ b/tests/apps/testapp-name-missing/appinfo/info.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<info> + <id>testapp-version</id> + <version>1.1.1</version> + <author>Jane</author> + <description>A b c</description> + <licence>Abc</licence> +</info> diff --git a/tests/apps/testapp-version-missing/appinfo/info.xml b/tests/apps/testapp-version-missing/appinfo/info.xml new file mode 100644 index 00000000000..d7da3e07e36 --- /dev/null +++ b/tests/apps/testapp-version-missing/appinfo/info.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<info> + <id>testapp-version</id> + <author>Jane</author> + <description>A b c</description> + <licence>Abc</licence> + <name>Test app</name> +</info> diff --git a/tests/apps/testapp-version/appinfo/info.xml b/tests/apps/testapp-version/appinfo/info.xml new file mode 100644 index 00000000000..d7da3e07e36 --- /dev/null +++ b/tests/apps/testapp-version/appinfo/info.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<info> + <id>testapp-version</id> + <author>Jane</author> + <description>A b c</description> + <licence>Abc</licence> + <name>Test app</name> +</info> diff --git a/tests/apps/testapp-version/appinfo/version b/tests/apps/testapp-version/appinfo/version new file mode 100644 index 00000000000..0495c4a88ca --- /dev/null +++ b/tests/apps/testapp-version/appinfo/version @@ -0,0 +1 @@ +1.2.3 diff --git a/tests/core/command/config/listconfigstest.php b/tests/core/command/config/listconfigstest.php index 7492701cce3..bde6a1b0db3 100644 --- a/tests/core/command/config/listconfigstest.php +++ b/tests/core/command/config/listconfigstest.php @@ -23,6 +23,7 @@ namespace Tests\Core\Command\Config; use OC\Core\Command\Config\ListConfigs; +use OCP\IConfig; use Test\TestCase; class ListConfigsTest extends TestCase { @@ -66,7 +67,7 @@ class ListConfigsTest extends TestCase { 'overwrite.cli.url', ], [ - ['secret', 'N;', 'my secret'], + ['secret', 'N;', IConfig::SENSITIVE_VALUE], ['overwrite.cli.url', 'N;', 'http://localhost'], ], // app config @@ -81,7 +82,7 @@ class ListConfigsTest extends TestCase { false, json_encode([ 'system' => [ - 'secret' => ListConfigs::SENSITIVE_VALUE, + 'secret' => IConfig::SENSITIVE_VALUE, 'overwrite.cli.url' => 'http://localhost', ], 'apps' => [ @@ -139,12 +140,12 @@ class ListConfigsTest extends TestCase { 'overwrite.cli.url', ], [ - ['secret', 'N;', 'my secret'], + ['secret', 'N;', IConfig::SENSITIVE_VALUE], ['objectstore', 'N;', [ 'class' => 'OC\\Files\\ObjectStore\\Swift', 'arguments' => [ 'username' => 'facebook100000123456789', - 'password' => 'Secr3tPaSSWoRdt7', + 'password' => IConfig::SENSITIVE_VALUE, ], ]], ['overwrite.cli.url', 'N;', 'http://localhost'], @@ -161,12 +162,12 @@ class ListConfigsTest extends TestCase { false, json_encode([ 'system' => [ - 'secret' => ListConfigs::SENSITIVE_VALUE, + 'secret' => IConfig::SENSITIVE_VALUE, 'objectstore' => [ 'class' => 'OC\\Files\\ObjectStore\\Swift', 'arguments' => [ 'username' => 'facebook100000123456789', - 'password' => ListConfigs::SENSITIVE_VALUE, + 'password' => IConfig::SENSITIVE_VALUE, ], ], 'overwrite.cli.url' => 'http://localhost', @@ -276,9 +277,15 @@ class ListConfigsTest extends TestCase { $this->systemConfig->expects($this->any()) ->method('getKeys') ->willReturn($systemConfigs); - $this->systemConfig->expects($this->any()) - ->method('getValue') - ->willReturnMap($systemConfigMap); + if ($private) { + $this->systemConfig->expects($this->any()) + ->method('getValue') + ->willReturnMap($systemConfigMap); + } else { + $this->systemConfig->expects($this->any()) + ->method('getFilteredValue') + ->willReturnMap($systemConfigMap); + } $this->appConfig->expects($this->any()) ->method('getApps') diff --git a/tests/lib/app/codechecker/infocheckertest.php b/tests/lib/app/codechecker/infocheckertest.php new file mode 100644 index 00000000000..59c1316b769 --- /dev/null +++ b/tests/lib/app/codechecker/infocheckertest.php @@ -0,0 +1,73 @@ +<?php +/** + * @author Morris Jobke <hey@morrisjobke.de> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\App\CodeChecker; + +use OC\App\InfoParser; +use Test\TestCase; + +class InfoCheckerTest extends TestCase { + /** @var InfoChecker */ + protected $infoChecker; + + public static function setUpBeforeClass() { + \OC::$APPSROOTS[] = [ + 'path' => \OC::$SERVERROOT . '/tests/apps', + 'url' => '/apps-test', + 'writable' => false, + ]; + } + + public static function tearDownAfterClass() { + // remove last element + array_pop(\OC::$APPSROOTS); + } + + protected function setUp() { + parent::setUp(); + $infoParser = new InfoParser(\OC::$server->getHTTPHelper(), \OC::$server->getURLGenerator()); + + $this->infoChecker = new InfoChecker($infoParser); + } + + public function appInfoData() { + return [ + ['testapp-infoxml', []], + ['testapp-version', []], + ['testapp-infoxml-version', []], + ['testapp-infoxml-version-different', [['type' => 'differentVersions', 'message' => 'appinfo/version: 1.2.4 - appinfo/info.xml: 1.2.3']]], + ['testapp-version-missing', [['type' => 'mandatoryFieldMissing', 'field' => 'version']]], + ['testapp-name-missing', [['type' => 'mandatoryFieldMissing', 'field' => 'name']]], + ]; + } + + /** + * @dataProvider appInfoData + * + * @param $appId + * @param $expectedErrors + */ + public function testApps($appId, $expectedErrors) { + $errors = $this->infoChecker->analyse($appId); + + $this->assertEquals($expectedErrors, $errors); + } +} diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index ef0d9822085..58a76470afa 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -1567,6 +1567,43 @@ class Test_Share extends \Test\TestCase { $this->setHttpHelper($oldHttpHelper); } + /** + * Test case for #19119 + */ + public function testReshareWithLinkDefaultExpirationDate() { + $config = \OC::$server->getConfig(); + $config->setAppValue('core', 'shareapi_default_expire_date', 'yes'); + $config->setAppValue('core', 'shareapi_expire_after_n_days', '2'); + + // Expiration date + $expireAt = time() + 2 * 24*60*60; + $date = new DateTime(); + $date->setTimestamp($expireAt); + $date->setTime(0, 0, 0); + + //Share a file from user 1 to user 2 + $this->shareUserTestFileWithUser($this->user1, $this->user2); + + //User 2 shares as link + OC_User::setUserId($this->user2); + $result = OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_LINK, null, \OCP\Constants::PERMISSION_READ); + $this->assertTrue(is_string($result)); + + //Check if expire date is correct + $result = OCP\Share::getItemShared('test', 'test.txt'); + $this->assertCount(1, $result); + $result = reset($result); + $this->assertNotEmpty($result['expiration']); + $expireDate = new DateTime($result['expiration']); + $this->assertEquals($date, $expireDate); + + //Unshare + $this->assertTrue(OCP\Share::unshareAll('test', 'test.txt')); + + //Reset config + $config->deleteAppValue('core', 'shareapi_default_expire_date'); + $config->deleteAppValue('core', 'shareapi_expire_after_n_days'); + } } class DummyShareClass extends \OC\Share\Share { |