Signed-off-by: Christopher Ng <chrng8@gmail.com>tags/v24.0.0beta1
$options = array_merge($opts, $post, $errors); | $options = array_merge($opts, $post, $errors); | ||||
$this->display($options); | $this->display($options); | ||||
} else { | } else { | ||||
$this->finishSetup(isset($post['install-recommended-apps'])); | |||||
$this->finishSetup(); | |||||
} | } | ||||
} else { | } else { | ||||
$options = array_merge($opts, $post); | $options = array_merge($opts, $post); | ||||
\OC_Template::printGuestPage('', 'installation', $parameters); | \OC_Template::printGuestPage('', 'installation', $parameters); | ||||
} | } | ||||
private function finishSetup(bool $installRecommended) { | |||||
private function finishSetup() { | |||||
if (file_exists($this->autoConfigFile)) { | if (file_exists($this->autoConfigFile)) { | ||||
unlink($this->autoConfigFile); | unlink($this->autoConfigFile); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if ($installRecommended) { | |||||
header('Location: ' . \OC::$server->getURLGenerator()->getAbsoluteURL('index.php/core/apps/recommended')); | |||||
exit(); | |||||
} else { | |||||
header('Location: ' . \OC::$server->getURLGenerator()->linkToDefaultPageUrl()); | |||||
exit(); | |||||
} | |||||
header('Location: ' . \OC::$server->getURLGenerator()->getAbsoluteURL('index.php/core/apps/recommended')); | |||||
exit(); | |||||
} | } | ||||
public function loadAutoConfig($post) { | public function loadAutoConfig($post) { |
margin: 5px 0 !important; | margin: 5px 0 !important; | ||||
} | } | ||||
#install-recommended-apps + label span { | |||||
display: inline-block; | |||||
opacity: .7; | |||||
} | |||||
/* Errors */ | /* Errors */ | ||||
/* Warnings and errors are the same */ | /* Warnings and errors are the same */ | ||||
.body-login-container, | .body-login-container, | ||||
.warning, | .warning, | ||||
.update, | .update, | ||||
.error { | .error { | ||||
display: block; | |||||
display: flex; | |||||
flex-direction: column; | |||||
margin-top: 15px; | margin-top: 15px; | ||||
padding: 15px; | padding: 15px; | ||||
background-color: rgba(0,0,0,.3); | background-color: rgba(0,0,0,.3); |
<!-- | |||||
- @copyright 2022 Christopher Ng <chrng8@gmail.com> | |||||
- | |||||
- @author Christopher Ng <chrng8@gmail.com> | |||||
- | |||||
- @license GNU AGPL version 3 or any later version | |||||
- | |||||
- This program is free software: you can redistribute it and/or modify | |||||
- it under the terms of the GNU Affero General Public License as | |||||
- published by the Free Software Foundation, either version 3 of the | |||||
- License, or (at your option) any later version. | |||||
- | |||||
- 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 | |||||
- along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
- | |||||
--> | |||||
<template> | |||||
<button | |||||
class="primary" | |||||
:autofocus="true" | |||||
v-on="$listeners"> | |||||
{{ t('core', 'Install recommended apps') }} | |||||
</button> | |||||
</template> | |||||
<script> | |||||
export default { | |||||
name: 'InstallButton', | |||||
} | |||||
</script> | |||||
<style lang="scss" scoped> | |||||
button { | |||||
margin: 24px auto 10px auto; | |||||
padding: 10px 20px; | |||||
font-size: 20px; | |||||
} | |||||
</style> |
<p v-else-if="loadingAppsError" class="loading-error text-center"> | <p v-else-if="loadingAppsError" class="loading-error text-center"> | ||||
{{ t('core', 'Could not fetch list of apps from the App Store.') }} | {{ t('core', 'Could not fetch list of apps from the App Store.') }} | ||||
</p> | </p> | ||||
<p v-else class="text-center"> | |||||
<p v-else-if="installingApps" class="text-center"> | |||||
{{ t('core', 'Installing apps …') }} | {{ t('core', 'Installing apps …') }} | ||||
</p> | </p> | ||||
<div v-for="app in recommendedApps" :key="app.id" class="app"> | <div v-for="app in recommendedApps" :key="app.id" class="app"> | ||||
<img :src="customIcon(app.id)" alt=""> | <img :src="customIcon(app.id)" alt=""> | ||||
<div class="info"> | <div class="info"> | ||||
</p> | </p> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<InstallButton v-if="showInstallButton" | |||||
@click.stop.prevent="installApps" /> | |||||
<p class="text-center"> | <p class="text-center"> | ||||
<a :href="defaultPageUrl">{{ t('core', 'Cancel') }}</a> | <a :href="defaultPageUrl">{{ t('core', 'Cancel') }}</a> | ||||
</p> | </p> | ||||
import pLimit from 'p-limit' | import pLimit from 'p-limit' | ||||
import { translate as t } from '@nextcloud/l10n' | import { translate as t } from '@nextcloud/l10n' | ||||
// TODO replace with Button component when @nextcloud/vue is upgraded to v5 | |||||
import InstallButton from './InstallButton' | |||||
import logger from '../../logger' | import logger from '../../logger' | ||||
const recommended = { | const recommended = { | ||||
export default { | export default { | ||||
name: 'RecommendedApps', | name: 'RecommendedApps', | ||||
components: { | |||||
InstallButton, | |||||
}, | |||||
data() { | data() { | ||||
return { | return { | ||||
showInstallButton: false, | |||||
installingApps: false, | |||||
loadingApps: true, | loadingApps: true, | ||||
loadingAppsError: false, | loadingAppsError: false, | ||||
apps: [], | apps: [], | ||||
return this.apps.filter(app => recommendedIds.includes(app.id)) | return this.apps.filter(app => recommendedIds.includes(app.id)) | ||||
}, | }, | ||||
}, | }, | ||||
mounted() { | |||||
return axios.get(generateUrl('settings/apps/list')) | |||||
.then(resp => resp.data) | |||||
.then(data => { | |||||
logger.info(`${data.apps.length} apps fetched`) | |||||
this.apps = data.apps.map(app => Object.assign(app, { loading: false, installationError: false })) | |||||
logger.debug(`${this.recommendedApps.length} recommended apps found`, { apps: this.recommendedApps }) | |||||
this.installApps() | |||||
}) | |||||
.catch(error => { | |||||
logger.error('could not fetch app list', { error }) | |||||
this.loadingAppsError = true | |||||
}) | |||||
.then(() => { | |||||
this.loadingApps = false | |||||
}) | |||||
async mounted() { | |||||
try { | |||||
const { data } = await axios.get(generateUrl('settings/apps/list')) | |||||
logger.info(`${data.apps.length} apps fetched`) | |||||
this.apps = data.apps.map(app => Object.assign(app, { loading: false, installationError: false })) | |||||
logger.debug(`${this.recommendedApps.length} recommended apps found`, { apps: this.recommendedApps }) | |||||
this.showInstallButton = true | |||||
} catch (error) { | |||||
logger.error('could not fetch app list', { error }) | |||||
this.loadingAppsError = true | |||||
} finally { | |||||
this.loadingApps = false | |||||
} | |||||
}, | }, | ||||
methods: { | methods: { | ||||
installApps() { | installApps() { | ||||
this.showInstallButton = false | |||||
this.installingApps = true | |||||
const limit = pLimit(1) | const limit = pLimit(1) | ||||
const installing = this.recommendedApps | const installing = this.recommendedApps | ||||
.filter(app => !app.active && app.isCompatible && app.canInstall) | .filter(app => !app.active && app.isCompatible && app.canInstall) | ||||
} | } | ||||
p.loading, p.loading-error { | |||||
height: 100px; | |||||
p { | |||||
&.loading, | |||||
&.loading-error { | |||||
height: 100px; | |||||
} | |||||
&:last-child { | |||||
margin-top: 10px; | |||||
} | |||||
} | } | ||||
.text-center { | .text-center { |
</fieldset> | </fieldset> | ||||
<?php endif ?> | <?php endif ?> | ||||
<fieldset> | |||||
<p class="info"> | |||||
<input type="checkbox" id="install-recommended-apps" name="install-recommended-apps" class="checkbox checkbox--white" checked> | |||||
<label for="install-recommended-apps"> | |||||
<?php p($l->t('Install recommended apps')); ?> | |||||
<span><?php p($l->t('Calendar, Contacts, Talk, Mail & Collaborative editing')); ?></span> | |||||
</label> | |||||
</p> | |||||
</fieldset> | |||||
<div class="icon-loading-dark float-spinner"> </div> | <div class="icon-loading-dark float-spinner"> </div> | ||||
<div class="buttons"><input type="submit" class="primary" value="<?php p($l->t('Finish setup')); ?>" data-finishing="<?php p($l->t('Finishing …')); ?>"></div> | |||||
<div class="buttons"><input type="submit" class="primary" value="<?php p($l->t('Install')); ?>" data-finishing="<?php p($l->t('Installing …')); ?>"></div> | |||||
<p class="info"> | <p class="info"> | ||||
<span class="icon-info-white"></span> | <span class="icon-info-white"></span> |