Merge pull request #51464 from nextcloud/feat/setup

feat(core): migrate setup to vue
This commit is contained in:
John Molakvoæ 2025-03-13 16:34:14 +01:00 committed by GitHub
commit a71ef1114d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 543 additions and 338 deletions

View file

@ -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);
}

View file

@ -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')
}
})
})

49
core/src/install.ts Normal file
View file

@ -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')

420
core/src/views/Setup.vue Normal file
View file

@ -0,0 +1,420 @@
<!--
- 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 v-if="Object.keys(config.databases).length > 1" class="setup-form__database-type-select">
<NcCheckboxRadioSwitch v-for="(name, db) in config.databases"
:key="db"
v-model="config.dbtype"
: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>

View file

@ -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>

4
dist/core-common.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,56 +1,95 @@
SPDX-License-Identifier: MIT
SPDX-License-Identifier: ISC
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-License-Identifier: BSD-3-Clause
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: (MPL-2.0 OR Apache-2.0)
SPDX-FileCopyrightText: escape-html developers
SPDX-FileCopyrightText: debounce developers
SPDX-FileCopyrightText: Tobias Koppers @sokra
SPDX-FileCopyrightText: T. Jameson Little <t.jameson.little@gmail.com>
SPDX-FileCopyrightText: Roman Shtylman <shtylman@gmail.com>
SPDX-FileCopyrightText: OpenJS Foundation and other contributors
SPDX-FileCopyrightText: Rob Cresswell <robcresswell@pm.me>
SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
SPDX-FileCopyrightText: Morris Jobke
SPDX-FileCopyrightText: Matt Zabriskie
SPDX-FileCopyrightText: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
SPDX-FileCopyrightText: GitHub Inc.
SPDX-FileCopyrightText: Feross Aboukhadijeh
SPDX-FileCopyrightText: Evan You
SPDX-FileCopyrightText: Dr.-Ing. Mario Heiderich, Cure53 <mario@cure53.de> (https://cure53.de/)
SPDX-FileCopyrightText: Christoph Wurst
This file is generated from multiple sources. Included packages:
- @nextcloud/auth
- version: 2.4.0
- license: GPL-3.0-or-later
- @nextcloud/axios
- version: 2.5.1
- license: GPL-3.0-or-later
- @nextcloud/browser-storage
- version: 0.4.0
- license: GPL-3.0-or-later
- semver
- version: 7.6.3
- license: ISC
- @nextcloud/event-bus
- version: 3.3.1
- license: GPL-3.0-or-later
- @nextcloud/initial-state
- version: 2.2.0
- license: GPL-3.0-or-later
- @nextcloud/l10n
- version: 3.2.0
- license: GPL-3.0-or-later
- @nextcloud/logger
- version: 3.0.2
- license: GPL-3.0-or-later
- @nextcloud/router
- version: 3.0.1
- license: GPL-3.0-or-later
- @nextcloud/vue
- version: 8.23.1
- license: AGPL-3.0-or-later
- axios
- version: 1.8.1
- license: MIT
- base64-js
- version: 1.5.1
- license: MIT
- css-loader
- version: 7.1.2
- license: MIT
- debounce
- version: 2.2.0
- license: MIT
- dompurify
- version: 3.2.4
- license: (MPL-2.0 OR Apache-2.0)
- escape-html
- version: 1.0.3
- license: MIT
- jquery-ui
- version: 1.13.3
- license: MIT
- jquery
- version: 3.7.1
- ieee754
- version: 1.2.1
- license: BSD-3-Clause
- buffer
- version: 6.0.3
- license: MIT
- process
- version: 0.11.10
- license: MIT
- strengthify
- version: 0.5.9
- license: MIT
- style-loader
- version: 4.0.0
- license: MIT
- vue-loader
- version: 15.11.1
- license: MIT
- vue-material-design-icons
- version: 5.3.1
- license: MIT
- vue
- version: 2.7.16
- license: MIT
- webpack
- version: 5.94.0
- license: MIT

File diff suppressed because one or more lines are too long

View file

@ -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'),