aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorskjnldsv <skjnldsv@protonmail.com>2025-03-13 08:55:37 +0100
committerskjnldsv <skjnldsv@protonmail.com>2025-03-13 15:44:37 +0100
commit4d0680b605d756f9ae0b14dce2db82155f2c17a5 (patch)
tree56c7e18401825b7b79f7b075b937e4f13cffd812
parent9dea6185ada1f9f891f2c64d256551c6ad171d29 (diff)
downloadnextcloud-server-feat/setup.tar.gz
nextcloud-server-feat/setup.zip
feat(core): migrate setup to vuefeat/setup
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
-rw-r--r--core/Controller/SetupController.php18
-rw-r--r--core/src/install.js156
-rw-r--r--core/src/install.ts49
-rw-r--r--core/src/views/Setup.vue421
-rw-r--r--core/templates/installation.php165
-rw-r--r--webpack.modules.js2
6 files changed, 489 insertions, 322 deletions
diff --git a/core/Controller/SetupController.php b/core/Controller/SetupController.php
index 4b5902fdd3c..8474f6edf87 100644
--- a/core/Controller/SetupController.php
+++ b/core/Controller/SetupController.php
@@ -8,6 +8,8 @@
namespace OC\Core\Controller;
use OC\Setup;
+use OCP\IInitialStateService;
+use OCP\IURLGenerator;
use OCP\Template\ITemplateManager;
use OCP\Util;
use Psr\Log\LoggerInterface;
@@ -19,6 +21,8 @@ class SetupController {
protected Setup $setupHelper,
protected LoggerInterface $logger,
protected ITemplateManager $templateManager,
+ protected IInitialStateService $initialStateService,
+ protected IURLGenerator $urlGenerator,
) {
$this->autoConfigFile = \OC::$configDir . 'autoconfig.php';
}
@@ -72,6 +76,8 @@ class SetupController {
'dbtablespace' => '',
'dbhost' => 'localhost',
'dbtype' => '',
+ 'hasAutoconfig' => false,
+ 'serverRoot' => \OC::$SERVERROOT,
];
$parameters = array_merge($defaults, $post);
@@ -80,9 +86,18 @@ class SetupController {
// include common nextcloud webpack bundle
Util::addScript('core', 'common');
Util::addScript('core', 'main');
+ Util::addScript('core', 'install');
Util::addTranslations('core');
- $this->templateManager->printGuestPage('', 'installation', $parameters);
+ $this->initialStateService->provideInitialState('core', 'config', $parameters);
+ $this->initialStateService->provideInitialState('core', 'data', false);
+ $this->initialStateService->provideInitialState('core', 'links', [
+ 'adminInstall' => $this->urlGenerator->linkToDocs('admin-install'),
+ 'adminSourceInstall' => $this->urlGenerator->linkToDocs('admin-source_install'),
+ 'adminDBConfiguration' => $this->urlGenerator->linkToDocs('admin-db-configuration'),
+ ]);
+
+ $this->templateManager->printGuestPage('', 'installation');
}
private function finishSetup(): void {
@@ -107,6 +122,7 @@ class SetupController {
$this->logger->info('Autoconfig file found, setting up Nextcloud…');
$AUTOCONFIG = [];
include $this->autoConfigFile;
+ $post['hasAutoconfig'] = count($AUTOCONFIG) > 0;
$post = array_merge($post, $AUTOCONFIG);
}
diff --git a/core/src/install.js b/core/src/install.js
deleted file mode 100644
index ea2e2996a2a..00000000000
--- a/core/src/install.js
+++ /dev/null
@@ -1,156 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-import $ from 'jquery'
-import { translate as t } from '@nextcloud/l10n'
-import { linkTo } from '@nextcloud/router'
-
-import { getToken } from './OC/requesttoken.js'
-import getURLParameter from './Util/get-url-parameter.js'
-
-import './jquery/showpassword.js'
-
-import 'jquery-ui/ui/widgets/button.js'
-import 'jquery-ui/themes/base/theme.css'
-import 'jquery-ui/themes/base/button.css'
-
-import 'strengthify'
-import 'strengthify/strengthify.css'
-
-window.addEventListener('DOMContentLoaded', function() {
- const dbtypes = {
- sqlite: !!$('#hasSQLite').val(),
- mysql: !!$('#hasMySQL').val(),
- postgresql: !!$('#hasPostgreSQL').val(),
- oracle: !!$('#hasOracle').val(),
- }
-
- $('#selectDbType').buttonset()
- // change links inside an info box back to their default appearance
- $('#selectDbType p.info a').button('destroy')
-
- if ($('#hasSQLite').val()) {
- $('#use_other_db').hide()
- $('#use_oracle_db').hide()
- } else {
- $('#sqliteInformation').hide()
- }
- $('#adminlogin').change(function() {
- $('#adminlogin').val($.trim($('#adminlogin').val()))
- })
- $('#sqlite').click(function() {
- $('#use_other_db').slideUp(250)
- $('#use_oracle_db').slideUp(250)
- $('#sqliteInformation').show()
- $('#dbname').attr('pattern', '[0-9a-zA-Z$_-]+')
- })
-
- $('#mysql,#pgsql').click(function() {
- $('#use_other_db').slideDown(250)
- $('#use_oracle_db').slideUp(250)
- $('#sqliteInformation').hide()
- $('#dbname').attr('pattern', '[0-9a-zA-Z$_-]+')
- })
-
- $('#oci').click(function() {
- $('#use_other_db').slideDown(250)
- $('#use_oracle_db').show(250)
- $('#sqliteInformation').hide()
- $('#dbname').attr('pattern', '[0-9a-zA-Z$_-.]+')
- })
-
- $('#showAdvanced').click(function(e) {
- e.preventDefault()
- $('#datadirContent').slideToggle(250)
- $('#databaseBackend').slideToggle(250)
- $('#databaseField').slideToggle(250)
- })
- $('form').submit(function() {
- // Save form parameters
- const post = $(this).serializeArray()
-
- // Show spinner while finishing setup
- $('.float-spinner').show(250)
-
- // Disable inputs
- $('input[type="submit"]').attr('disabled', 'disabled').val($('input[type="submit"]').data('finishing'))
- $('input', this).addClass('ui-state-disabled').attr('disabled', 'disabled')
- // only disable buttons if they are present
- if ($('#selectDbType').find('.ui-button').length > 0) {
- $('#selectDbType').buttonset('disable')
- }
- $('.strengthify-wrapper, .tipsy')
- .css('filter', 'alpha(opacity=30)')
- .css('opacity', 0.3)
-
- // Create the form
- const form = $('<form>')
- form.attr('action', $(this).attr('action'))
- form.attr('method', 'POST')
-
- for (let i = 0; i < post.length; i++) {
- const input = $('<input type="hidden">')
- input.attr(post[i])
- form.append(input)
- }
-
- // Add redirect_url
- const redirectURL = getURLParameter('redirect_url')
- if (redirectURL) {
- const redirectURLInput = $('<input type="hidden">')
- redirectURLInput.attr({
- name: 'redirect_url',
- value: redirectURL,
- })
- form.append(redirectURLInput)
- }
-
- // Submit the form
- form.appendTo(document.body)
- form.submit()
- return false
- })
-
- // Expand latest db settings if page was reloaded on error
- const currentDbType = $('input[type="radio"]:checked').val()
-
- if (currentDbType === undefined) {
- $('input[type="radio"]').first().click()
- }
-
- if (
- currentDbType === 'sqlite'
- || (dbtypes.sqlite && currentDbType === undefined)
- ) {
- $('#datadirContent').hide(250)
- $('#databaseBackend').hide(250)
- $('#databaseField').hide(250)
- $('.float-spinner').hide(250)
- }
-
- $('#adminpass').strengthify({
- zxcvbn: linkTo('core', 'vendor/zxcvbn/dist/zxcvbn.js'),
- titles: [
- t('core', 'Very weak password'),
- t('core', 'Weak password'),
- t('core', 'So-so password'),
- t('core', 'Good password'),
- t('core', 'Strong password'),
- ],
- drawTitles: true,
- nonce: btoa(getToken()),
- })
-
- $('#dbpass').showPassword().keyup()
- $('.toggle-password').click(function(event) {
- event.preventDefault()
- const currentValue = $(this).parent().children('input').attr('type')
- if (currentValue === 'password') {
- $(this).parent().children('input').attr('type', 'text')
- } else {
- $(this).parent().children('input').attr('type', 'password')
- }
- })
-})
diff --git a/core/src/install.ts b/core/src/install.ts
new file mode 100644
index 00000000000..61c3747d02a
--- /dev/null
+++ b/core/src/install.ts
@@ -0,0 +1,49 @@
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import Vue from 'vue'
+import Setup from './views/Setup.vue'
+
+type Error = {
+ error: string
+ hint: string
+}
+
+export type DbType = 'sqlite' | 'mysql' | 'pgsql' | 'oci'
+
+export type SetupConfig = {
+ adminlogin: string
+ adminpass: string
+ dbuser: string
+ dbpass: string
+ dbname: string
+ dbtablespace: string
+ dbhost: string
+ dbtype: DbType | ''
+
+ hasSQLite: boolean
+ hasMySQL: boolean
+ hasPostgreSQL: boolean
+ hasOracle: boolean
+ databases: Record<DbType, string>
+
+ dbIsSet: boolean
+ directory: string
+ directoryIsSet: boolean
+ hasAutoconfig: boolean
+ htaccessWorking: boolean
+ serverRoot: string
+
+ errors: string[]|Error[]
+}
+
+export type SetupLinks = {
+ adminInstall: string
+ adminSourceInstall: string
+ adminDBConfiguration: string
+}
+
+const SetupVue = Vue.extend(Setup)
+new SetupVue().$mount('#content')
diff --git a/core/src/views/Setup.vue b/core/src/views/Setup.vue
new file mode 100644
index 00000000000..c03fc81f99b
--- /dev/null
+++ b/core/src/views/Setup.vue
@@ -0,0 +1,421 @@
+<!--
+ - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <form ref="form"
+ class="setup-form"
+ :class="{ 'setup-form--loading': loading }"
+ action=""
+ method="POST"
+ @submit="onSubmit">
+ <!-- Autoconfig info -->
+ <NcNoteCard v-if="config.hasAutoconfig"
+ :heading="t('core', 'Autoconfig file detected')"
+ type="success">
+ {{ t('core', 'The setup form below is pre-filled with the values from the config file.') }}
+ </NcNoteCard>
+
+ <!-- Htaccess warning -->
+ <NcNoteCard v-if="config.htaccessWorking === false"
+ :heading="t('core', 'Security warning')"
+ type="warning">
+ <p v-html="htaccessWarning" />
+ </NcNoteCard>
+
+ <!-- Various errors -->
+ <NcNoteCard v-for="(error, index) in errors"
+ :key="index"
+ :heading="error.heading"
+ type="error">
+ {{ error.message }}
+ </NcNoteCard>
+
+ <!-- Admin creation -->
+ <fieldset class="setup-form__administration">
+ <legend>{{ t('core', 'Create administration account') }}</legend>
+
+ <!-- Username -->
+ <NcTextField v-model="config.adminlogin"
+ :label="t('core', 'Administration account name')"
+ name="adminlogin"
+ required />
+
+ <!-- Password -->
+ <NcPasswordField v-model="config.adminpass"
+ :label="t('core', 'Administration account password')"
+ name="adminpass"
+ required />
+
+ <!-- Password entropy -->
+ <NcNoteCard v-show="config.adminpass !== ''" :type="passwordHelperType">
+ {{ passwordHelperText }}
+ </NcNoteCard>
+ </fieldset>
+
+ <!-- Autoconfig toggle -->
+ <details :open="!isValidAutoconfig">
+ <summary>{{ t('core', 'Advanced settings') }}</summary>
+
+ <!-- Data folder -->
+ <fieldset class="setup-form__data-folder">
+ <legend>{{ t('core', 'Data folder') }}</legend>
+ <NcTextField v-model="config.directory"
+ :label="t('core', 'Data folder')"
+ :placeholder="config.serverRoot + '/data'"
+ required
+ autocomplete="off"
+ autocapitalize="none"
+ name="directory"
+ spellcheck="false" />
+ </fieldset>
+
+ <!-- Database -->
+ <fieldset class="setup-form__database">
+ <legend>{{ t('core', 'Database configuration') }}</legend>
+
+ <!-- Database type select -->
+ <fieldset class="setup-form__database-type">
+ <legend>{{ t('core', 'Database type') }}</legend>
+ <p class="setup-form__database-type-select" v-if="Object.keys(config.databases).length > 1">
+ <NcCheckboxRadioSwitch v-for="(name, db) in config.databases"
+ v-model="config.dbtype"
+ :key="db"
+ :button-variant="true"
+ :value="db"
+ name="dbtype"
+ button-variant-grouped="horizontal"
+ type="radio">
+ {{ name }}
+ </NcCheckboxRadioSwitch>
+ </p>
+
+ <NcNoteCard v-else type="warning">
+ {{ t('core', 'Only {db} is available.', { db: Object.values(config.databases).at(0) }) }}<br>
+ {{ t('core', 'Install and activate additional PHP modules to choose other database types.') }}<br>
+ <a :href="links.adminSourceInstall" target="_blank" rel="noreferrer noopener">
+ {{ t('core', 'For more details check out the documentation.') }} ↗
+ </a>
+ </NcNoteCard>
+
+ <NcNoteCard v-if="config.dbtype === 'sqlite'"
+ :heading="t('core', 'Performance warning')"
+ type="warning">
+ {{ t('core', 'You chose SQLite as database.') }}<br>
+ {{ t('core', 'SQLite should only be used for minimal and development instances. For production we recommend a different database backend.') }}<br>
+ {{ t('core', 'If you use clients for file syncing, the use of SQLite is highly discouraged.') }}
+ </NcNoteCard>
+ </fieldset>
+
+ <!-- Database configuration -->
+ <fieldset v-if="config.dbtype !== 'sqlite'">
+ <NcTextField v-model="config.dbuser"
+ :label="t('core', 'Database user')"
+ autocapitalize="none"
+ autocomplete="off"
+ name="dbuser"
+ spellcheck="false"
+ required />
+
+ <NcPasswordField v-model="config.dbpass"
+ :label="t('core', 'Database password')"
+ autocapitalize="none"
+ autocomplete="off"
+ name="dbpass"
+ spellcheck="false"
+ required />
+
+ <NcTextField v-model="config.dbname"
+ :label="t('core', 'Database name')"
+ autocapitalize="none"
+ autocomplete="off"
+ name="dbname"
+ pattern="[0-9a-zA-Z\$_\-]+"
+ spellcheck="false"
+ required />
+
+ <NcTextField v-if="config.dbtype === 'oci'"
+ v-model="config.dbtablespace"
+ :label="t('core', 'Database tablespace')"
+ autocapitalize="none"
+ autocomplete="off"
+ name="dbtablespace"
+ spellcheck="false" />
+
+ <NcTextField v-model="config.dbhost"
+ :helper-text="t('core', 'Please specify the port number along with the host name (e.g., localhost:5432).')"
+ :label="t('core', 'Database host')"
+ :placeholder="t('core', 'localhost')"
+ autocapitalize="none"
+ autocomplete="off"
+ name="dbhost"
+ spellcheck="false" />
+ </fieldset>
+ </fieldset>
+ </details>
+
+ <!-- Submit -->
+ <NcButton
+ class="setup-form__button"
+ :class="{ 'setup-form__button--loading': loading }"
+ :disabled="loading"
+ :loading="loading"
+ :wide="true"
+ alignment="center-reverse"
+ native-type="submit"
+ type="primary">
+ <template #icon>
+ <NcLoadingIcon v-if="loading" />
+ <IconArrowRight v-else />
+ </template>
+ {{ loading ? t('core', 'Installing …') : t('core', 'Install') }}
+ </NcButton>
+
+ <!-- Help note -->
+ <NcNoteCard type="info">
+ {{ t('core', 'Need help?') }}
+ <a target="_blank" rel="noreferrer noopener" :href="links.adminInstall">{{ t('core', 'See the documentation') }} ↗</a>
+ </NcNoteCard>
+ </form>
+</template>
+<script lang="ts">
+import type { DbType, SetupConfig, SetupLinks } from '../install'
+
+import { defineComponent } from 'vue'
+import { loadState } from '@nextcloud/initial-state'
+import { t } from '@nextcloud/l10n'
+import DomPurify from 'dompurify'
+
+import NcButton from '@nextcloud/vue/components/NcButton'
+import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
+import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
+import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
+import NcPasswordField from '@nextcloud/vue/components/NcPasswordField'
+import NcTextField from '@nextcloud/vue/components/NcTextField'
+
+import IconArrowRight from 'vue-material-design-icons/ArrowRight.vue'
+
+const config = loadState<SetupConfig>('core', 'config')
+const links = loadState<SetupLinks>('core', 'links')
+
+enum PasswordStrength {
+ VeryWeak,
+ Weak,
+ Moderate,
+ Strong,
+ VeryStrong,
+ ExtremelyStrong,
+}
+
+export default defineComponent({
+ name: 'Setup',
+
+ components: {
+ IconArrowRight,
+ NcButton,
+ NcCheckboxRadioSwitch,
+ NcLoadingIcon,
+ NcNoteCard,
+ NcPasswordField,
+ NcTextField,
+ },
+
+ setup() {
+ return {
+ links,
+ t,
+ }
+ },
+
+ data() {
+ return {
+ config,
+ isValidAutoconfig: false,
+ loading: false,
+ }
+ },
+
+ computed: {
+ passwordHelperText(): string {
+ if (this.config.adminpass === '') {
+ return ''
+ }
+
+ const passwordStrength = this.checkPasswordEntropy(this.config.adminpass)
+ switch (passwordStrength) {
+ case PasswordStrength.VeryWeak:
+ return t('core', 'Password is too weak')
+ case PasswordStrength.Weak:
+ return t('core', 'Password is weak')
+ case PasswordStrength.Moderate:
+ return t('core', 'Password is average')
+ case PasswordStrength.Strong:
+ return t('core', 'Password is strong')
+ case PasswordStrength.VeryStrong:
+ return t('core', 'Password is very strong')
+ case PasswordStrength.ExtremelyStrong:
+ return t('core', 'Password is extremely strong')
+ }
+
+ return t('core', 'Unknown password strength')
+ },
+ passwordHelperType() {
+ if (this.checkPasswordEntropy(this.config.adminpass) < PasswordStrength.Moderate) {
+ return 'error'
+ }
+ if (this.checkPasswordEntropy(this.config.adminpass) < PasswordStrength.Strong) {
+ return 'warning'
+ }
+ return 'success'
+ },
+
+ htaccessWarning(): string {
+ // We use v-html, let's make sure we're safe
+ const message = [
+ t('core', 'Your data directory and files are probably accessible from the internet because the <code>.htaccess</code> file does not work.'),
+ t('core', 'For information how to properly configure your server, please {linkStart}see the documentation{linkEnd}', {
+ linkStart: '<a href="' + links.adminInstall + '" target="_blank" rel="noreferrer noopener">',
+ linkEnd: '</a>',
+ }, { escape: false }),
+ ].join('<br>')
+ return DomPurify.sanitize(message)
+ },
+
+ errors() {
+ return this.config.errors.map(error => {
+ if (typeof error === 'string') {
+ return {
+ heading: '',
+ message: error,
+ }
+ }
+
+ // f no hint is set, we don't want to show a heading
+ if (error.hint === '') {
+ return {
+ heading: '',
+ message: error.error,
+ }
+ }
+
+ return {
+ heading: error.error,
+ message: error.hint,
+ }
+ })
+ },
+ },
+
+ mounted() {
+ if (this.config.dbtype === '') {
+ this.config.dbtype = Object.keys(this.config.databases).at(0) as DbType
+ }
+
+ // Validate the legitimacy of the autoconfig
+ if (this.config.hasAutoconfig) {
+ const form = this.$refs.form as HTMLFormElement
+
+ // Check the form without the administration account fields
+ form.querySelectorAll('input[name="adminlogin"], input[name="adminpass"]').forEach(input => {
+ input.removeAttribute('required')
+ })
+
+ if (form.checkValidity() && this.config.errors.length === 0) {
+ this.isValidAutoconfig = true
+ } else {
+ this.isValidAutoconfig = false
+ }
+
+ // Restore the required attribute
+ // Check the form without the administration account fields
+ form.querySelectorAll('input[name="adminlogin"], input[name="adminpass"]').forEach(input => {
+ input.setAttribute('required', 'true')
+ })
+ }
+ },
+
+ methods: {
+ async onSubmit() {
+ this.loading = true
+ },
+
+ checkPasswordEntropy(password: string): PasswordStrength {
+ const uniqueCharacters = new Set(password)
+ const entropy = parseInt(Math.log2(Math.pow(parseInt(uniqueCharacters.size.toString()), password.length)).toFixed(2))
+ if (entropy < 16) {
+ return PasswordStrength.VeryWeak
+ } else if (entropy < 31) {
+ return PasswordStrength.Weak
+ } else if (entropy < 46) {
+ return PasswordStrength.Moderate
+ } else if (entropy < 61) {
+ return PasswordStrength.Strong
+ } else if (entropy < 76) {
+ return PasswordStrength.VeryStrong
+ }
+
+ return PasswordStrength.ExtremelyStrong
+ },
+ },
+})
+</script>
+<style lang="scss">
+form {
+ padding: calc(3 * var(--default-grid-baseline));
+ color: var(--color-main-text);
+ border-radius: var(--border-radius-container);
+ background-color: var(--color-main-background-blur);
+ box-shadow: 0 0 10px var(--color-box-shadow);
+ -webkit-backdrop-filter: var(--filter-background-blur);
+ backdrop-filter: var(--filter-background-blur);
+
+ max-width: 300px;
+ margin-bottom: 30px;
+
+ > fieldset:first-child,
+ > .notecard:first-child {
+ margin-top: 0;
+ }
+
+ > .notecard:last-child {
+ margin-bottom: 0;
+ }
+
+ > fieldset,
+ > details {
+ margin-block: 1rem;
+ }
+
+ .setup-form__button:not(.setup-form__button--loading) {
+ .material-design-icon {
+ transition: all linear var(--animation-quick);
+ }
+
+ &:hover .material-design-icon {
+ transform: translateX(0.2em);
+ }
+ }
+
+ // Db select required styling
+ .setup-form__database-type-select {
+ display: flex;
+ }
+
+}
+
+code {
+ background-color: var(--color-background-dark);
+ margin-top: 1rem;
+ padding: 0 0.3em;
+ border-radius: var(--border-radius);
+}
+
+// Various overrides
+.input-field {
+ margin-block-start: 1rem !important;
+}
+
+.notecard__heading {
+ font-size: inherit !important;
+}
+</style>
diff --git a/core/templates/installation.php b/core/templates/installation.php
index 320fb9df7b7..b002ee400cc 100644
--- a/core/templates/installation.php
+++ b/core/templates/installation.php
@@ -4,168 +4,5 @@
* SPDX-FileCopyrightText: 2011-2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
-script('core', 'install');
?>
-<input type='hidden' id='hasMySQL' value='<?php p($_['hasMySQL']) ?>'>
-<input type='hidden' id='hasSQLite' value='<?php p($_['hasSQLite']) ?>'>
-<input type='hidden' id='hasPostgreSQL' value='<?php p($_['hasPostgreSQL']) ?>'>
-<input type='hidden' id='hasOracle' value='<?php p($_['hasOracle']) ?>'>
-<form method="post" class="guest-box install-form">
-<input type="hidden" name="install" value="true">
- <?php if (count($_['errors']) > 0): ?>
- <fieldset class="warning">
- <legend><strong><?php p($l->t('Error'));?></strong></legend>
- <?php foreach ($_['errors'] as $err): ?>
- <p>
- <?php if (is_array($err)):?>
- <?php p($err['error']); ?>
- <span class='hint'><?php p($err['hint']); ?></span>
- <?php else: ?>
- <?php p($err); ?>
- <?php endif; ?>
- </p>
- <?php endforeach; ?>
- </fieldset>
- <?php endif; ?>
- <?php if (!$_['htaccessWorking']): ?>
- <fieldset class="warning">
- <legend><strong><?php p($l->t('Security warning'));?></strong></legend>
- <p><?php p($l->t('Your data directory and files are probably accessible from the internet because the .htaccess file does not work.'));?><br>
- <?php print_unescaped($l->t(
- 'For information how to properly configure your server, please see the <a href="%s" target="_blank" rel="noreferrer noopener">documentation</a>.',
- [link_to_docs('admin-install')]
- )); ?></p>
- </fieldset>
- <?php endif; ?>
- <fieldset id="adminaccount">
- <legend><?php print_unescaped($l->t('<strong>Create an admin account</strong>')); ?></legend>
- <p>
- <label for="adminlogin"><?php p($l->t('New admin account name')); ?></label>
- <input type="text" name="adminlogin" id="adminlogin"
- value="<?php p($_['adminlogin']); ?>"
- autocomplete="off" autocapitalize="none" spellcheck="false" autofocus required>
- </p>
- <p class="groupbottom">
- <label for="adminpass"><?php p($l->t('New admin password')); ?></label>
- <input type="password" name="adminpass" data-typetoggle="#show" id="adminpass"
- value="<?php p($_['adminpass']); ?>"
- autocomplete="off" autocapitalize="none" spellcheck="false" required>
- <button id="show" class="toggle-password" aria-label="<?php p($l->t('Show password')); ?>">
- <img src="<?php print_unescaped(image_path('', 'actions/toggle.svg')); ?>" alt="<?php p($l->t('Toggle password visibility')); ?>">
- </button>
- </p>
- </fieldset>
-
- <?php if (!$_['directoryIsSet'] or !$_['dbIsSet'] or count($_['errors']) > 0): ?>
- <fieldset id="advancedHeader">
- <legend><a id="showAdvanced" tabindex="0" href="#"><?php p($l->t('Storage & database')); ?><img src="<?php print_unescaped(image_path('core', 'actions/caret.svg')); ?>" /></a></legend>
- </fieldset>
- <?php endif; ?>
-
- <?php if (!$_['directoryIsSet'] or count($_['errors']) > 0): ?>
- <fieldset id="datadirField">
- <div id="datadirContent">
- <label for="directory"><?php p($l->t('Data folder')); ?></label>
- <input type="text" name="directory" id="directory"
- placeholder="<?php p(OC::$SERVERROOT . '/data'); ?>"
- value="<?php p($_['directory']); ?>"
- autocomplete="off" autocapitalize="none" spellcheck="false">
- </div>
- </fieldset>
- <?php endif; ?>
-
- <?php if (!$_['dbIsSet'] or count($_['errors']) > 0): ?>
- <fieldset id='databaseBackend'>
- <?php if ($_['hasMySQL'] or $_['hasPostgreSQL'] or $_['hasOracle']) {
- $hasOtherDB = true;
- } else {
- $hasOtherDB = false;
- } //other than SQLite?>
- <legend><?php p($l->t('Configure the database')); ?></legend>
- <div id="selectDbType">
- <?php foreach ($_['databases'] as $type => $label): ?>
- <?php if (count($_['databases']) === 1): ?>
- <p class="info">
- <?php p($l->t('Only %s is available.', [$label])); ?>
- <?php p($l->t('Install and activate additional PHP modules to choose other database types.')); ?><br>
- <a href="<?php print_unescaped(link_to_docs('admin-source_install')); ?>" target="_blank" rel="noreferrer noopener">
- <?php p($l->t('For more details check out the documentation.')); ?> ↗</a>
- </p>
- <input type="hidden" id="dbtype" name="dbtype" value="<?php p($type) ?>">
- <?php else: ?>
- <input type="radio" name="dbtype" value="<?php p($type) ?>" id="<?php p($type) ?>"
- <?php print_unescaped($_['dbtype'] === $type ? 'checked="checked" ' : '') ?>/>
- <label class="<?php p($type) ?>" for="<?php p($type) ?>"><?php p($label) ?></label>
- <?php endif; ?>
- <?php endforeach; ?>
- </div>
- </fieldset>
-
- <?php if ($hasOtherDB): ?>
- <fieldset id='databaseField'>
- <div id="use_other_db">
- <p class="grouptop">
- <label for="dbuser"><?php p($l->t('Database account')); ?></label>
- <input type="text" name="dbuser" id="dbuser"
- value="<?php p($_['dbuser']); ?>"
- autocomplete="off" autocapitalize="none" spellcheck="false">
- </p>
- <p class="groupmiddle">
- <label for="dbpass"><?php p($l->t('Database password')); ?></label>
- <input type="password" name="dbpass" id="dbpass"
- value="<?php p($_['dbpass']); ?>"
- autocomplete="off" autocapitalize="none" spellcheck="false">
- <button id="show" class="toggle-password" aria-label="<?php p($l->t('Show password')); ?>">
- <img src="<?php print_unescaped(image_path('', 'actions/toggle.svg')); ?>" alt="<?php p($l->t('Toggle password visibility')); ?>">
- </button>
- </p>
- <p class="groupmiddle">
- <label for="dbname"><?php p($l->t('Database name')); ?></label>
- <input type="text" name="dbname" id="dbname"
- value="<?php p($_['dbname']); ?>"
- autocomplete="off" autocapitalize="none" spellcheck="false"
- pattern="[0-9a-zA-Z$_-]+">
- </p>
- <?php if ($_['hasOracle']): ?>
- <div id="use_oracle_db">
- <p class="groupmiddle">
- <label for="dbtablespace" class="infield"><?php p($l->t('Database tablespace')); ?></label>
- <input type="text" name="dbtablespace" id="dbtablespace"
- value="<?php p($_['dbtablespace']); ?>"
- autocomplete="off" autocapitalize="none" spellcheck="false">
- </p>
- </div>
- <?php endif; ?>
- <p class="groupbottom">
- <label for="dbhost"><?php p($l->t('Database host')); ?></label>
- <input type="text" name="dbhost" id="dbhost"
- value="<?php p($_['dbhost']); ?>"
- autocomplete="off" autocapitalize="none" spellcheck="false">
- </p>
- <p class="info">
- <?php p($l->t('Please specify the port number along with the host name (e.g., localhost:5432).')); ?>
- </p>
- </div>
- </fieldset>
- <?php endif; ?>
- <?php endif; ?>
-
- <?php if (!$_['dbIsSet'] or count($_['errors']) > 0): ?>
- <div id="sqliteInformation" class="notecard warning">
- <legend><?php p($l->t('Performance warning'));?></legend>
- <p><?php p($l->t('You chose SQLite as database.'));?></p>
- <p><?php p($l->t('SQLite should only be used for minimal and development instances. For production we recommend a different database backend.'));?></p>
- <p><?php p($l->t('If you use clients for file syncing, the use of SQLite is highly discouraged.')); ?></p>
- </div>
- <?php endif ?>
-
- <div class="icon-loading-dark float-spinner">&nbsp;</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">
- <span class="icon-info-white"></span>
- <?php p($l->t('Need help?'));?>
- <a target="_blank" rel="noreferrer noopener" href="<?php p(link_to_docs('admin-install')); ?>"><?php p($l->t('See the documentation'));?> ↗</a>
- </p>
-</form>
+<div id="content"></div>
diff --git a/webpack.modules.js b/webpack.modules.js
index 13ceea2b0cf..b05afa6d3f5 100644
--- a/webpack.modules.js
+++ b/webpack.modules.js
@@ -14,7 +14,7 @@ module.exports = {
'ajax-cron': path.join(__dirname, 'core/src', 'ajax-cron.ts'),
files_client: path.join(__dirname, 'core/src', 'files/client.js'),
files_fileinfo: path.join(__dirname, 'core/src', 'files/fileinfo.js'),
- install: path.join(__dirname, 'core/src', 'install.js'),
+ install: path.join(__dirname, 'core/src', 'install.ts'),
login: path.join(__dirname, 'core/src', 'login.js'),
main: path.join(__dirname, 'core/src', 'main.js'),
maintenance: path.join(__dirname, 'core/src', 'maintenance.js'),