mirror of
https://github.com/nextcloud/server.git
synced 2026-02-03 20:41:22 -05:00
refactor(user_ldap): migrate jQuery UI of password renewal to Vue
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
5f33fee58f
commit
29e31ffdb1
13 changed files with 135 additions and 1009 deletions
|
|
@ -1,148 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
#personal-show + label {
|
||||
inset-inline-start: 230px !important;
|
||||
margin-top: 8px !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#renewpassword .strengthify-wrapper {
|
||||
inset-inline-start: 10px;
|
||||
margin-top: 65px;
|
||||
position: absolute;
|
||||
width: 219px;
|
||||
}
|
||||
|
||||
#cancel-container p.info {
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#renewpassword .title {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position:absolute;
|
||||
display:block;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
font-style:normal;
|
||||
font-weight:400;
|
||||
letter-spacing:normal;
|
||||
line-break:auto;
|
||||
line-height:1.6;
|
||||
text-align:start;
|
||||
text-decoration:none;
|
||||
text-shadow:none;
|
||||
text-transform:none;
|
||||
white-space:normal;
|
||||
word-break:normal;
|
||||
word-spacing:normal;
|
||||
word-wrap:normal;
|
||||
font-size:12px;
|
||||
opacity:0;
|
||||
z-index:100000;
|
||||
filter:drop-shadow(0 1px 10px rgba(77, 77, 77, 0.75));
|
||||
}
|
||||
|
||||
.tooltip.in {
|
||||
opacity:1
|
||||
}
|
||||
|
||||
.tooltip.top {
|
||||
margin-top:-3px;
|
||||
padding:10px 0
|
||||
}
|
||||
|
||||
.tooltip.bottom {
|
||||
margin-top:3px;
|
||||
padding:10px 0
|
||||
}
|
||||
|
||||
.tooltip.right {
|
||||
margin-inline-start:3px;
|
||||
padding:0 10px
|
||||
}
|
||||
|
||||
.tooltip.right .tooltip-arrow {
|
||||
top:50%;
|
||||
inset-inline-start:0;
|
||||
margin-top:-10px;
|
||||
border-width:10px 10px 10px 0;
|
||||
border-inline-end-color:#fff
|
||||
}
|
||||
|
||||
.tooltip.left {
|
||||
margin-inline-start:-3px;
|
||||
padding:0 5px
|
||||
}
|
||||
|
||||
.tooltip.left .tooltip-arrow {
|
||||
top:50%;
|
||||
inset-inline-end:0;
|
||||
margin-top:-10px;
|
||||
border-width:10px 0 10px 10px;
|
||||
border-inline-start-color:#fff
|
||||
}
|
||||
|
||||
.tooltip.top .tooltip-arrow,.tooltip.top-left .tooltip-arrow,.tooltip.top-right .tooltip-arrow {
|
||||
bottom:0;
|
||||
border-width:10px 10px 0;
|
||||
border-top-color:#fff
|
||||
}
|
||||
|
||||
.tooltip.top .tooltip-arrow {
|
||||
inset-inline-start:50%;
|
||||
margin-inline-start:-10px
|
||||
}
|
||||
|
||||
.tooltip.top-left .tooltip-arrow {
|
||||
inset-inline-end:10px;
|
||||
margin-bottom:-10px
|
||||
}
|
||||
|
||||
.tooltip.top-right .tooltip-arrow {
|
||||
inset-inline-start:10px;
|
||||
margin-bottom:-10px
|
||||
}
|
||||
|
||||
.tooltip.bottom .tooltip-arrow,.tooltip.bottom-left .tooltip-arrow,.tooltip.bottom-right .tooltip-arrow {
|
||||
top:0;
|
||||
border-width:0 10px 10px;
|
||||
border-bottom-color:#fff
|
||||
}
|
||||
|
||||
.tooltip.bottom .tooltip-arrow {
|
||||
inset-inline-start:50%;
|
||||
margin-inline-start:-10px
|
||||
}
|
||||
|
||||
.tooltip.bottom-left .tooltip-arrow {
|
||||
inset-inline-end:10px;
|
||||
margin-top:-10px
|
||||
}
|
||||
|
||||
.tooltip.bottom-right .tooltip-arrow {
|
||||
inset-inline-start:10px;
|
||||
margin-top:-10px
|
||||
}
|
||||
|
||||
.tooltip-inner {
|
||||
max-width:350px;
|
||||
padding:5px 8px !important;
|
||||
background-color:#fff;
|
||||
color:#000 !important;
|
||||
text-align:center !important;
|
||||
font-weight:normal !important;
|
||||
border-radius:3px
|
||||
}
|
||||
|
||||
.tooltip-arrow {
|
||||
position:absolute;
|
||||
width:0;
|
||||
height:0;
|
||||
border-color:transparent;
|
||||
border-style:solid
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2012 Eric Hynds
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
.ui-multiselect { padding:2px 0 2px 4px; text-align:left; }
|
||||
.ui-multiselect span.ui-icon { float:right; }
|
||||
.ui-multiselect-single .ui-multiselect-checkboxes input { position:absolute !important; top: auto !important; left:-9999px; }
|
||||
.ui-multiselect-single .ui-multiselect-checkboxes label { padding:5px !important; }
|
||||
|
||||
.ui-multiselect-header { margin-bottom:3px; padding:3px 0 3px 4px; }
|
||||
.ui-multiselect-header ul { font-size:14px; }
|
||||
.ui-multiselect-header ul li { float:left; padding:0 10px 0 0; }
|
||||
.ui-multiselect-header a { text-decoration:none; }
|
||||
.ui-multiselect-header a:hover { text-decoration:underline; }
|
||||
.ui-multiselect-header span.ui-icon { float:left;}
|
||||
.ui-multiselect-header li.ui-multiselect-close { float:right; text-align:right; padding-right:0; }
|
||||
|
||||
.ui-multiselect-menu { display:none; padding:3px; position:absolute; z-index:10000; text-align: left; }
|
||||
.ui-multiselect-checkboxes { position:relative /* fixes bug in IE6/7 */; overflow-y:scroll; }
|
||||
.ui-multiselect-checkboxes label { cursor:default; display:block; border:1px solid transparent; padding:3px 1px; }
|
||||
.ui-multiselect-checkboxes label input { position:relative; top:1px; }
|
||||
.ui-multiselect-checkboxes li { clear:both; font-size:14px; padding-right:3px; }
|
||||
.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label { text-align:center; font-weight:bold; border-bottom:1px solid; }
|
||||
.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label a { display:block; padding:3px; margin:1px 0; text-decoration:none; }
|
||||
|
||||
/* remove label borders in IE6 because IE6 does not support transparency */
|
||||
* html .ui-multiselect-checkboxes label { border:none; }
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {}
|
||||
OCA.LDAP = _.extend(OC.LDAP || {}, {
|
||||
onRenewPassword: function() {
|
||||
$('#submit')
|
||||
.removeClass('icon-confirm-white')
|
||||
.addClass('icon-loading-small')
|
||||
.attr('value', t('core', 'Renewing …'))
|
||||
return true
|
||||
},
|
||||
})
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
$('form[name=renewpassword]').submit(OCA.LDAP.onRenewPassword)
|
||||
|
||||
if ($('#newPassword').length) {
|
||||
$('#newPassword').showPassword().keyup()
|
||||
}
|
||||
$('#newPassword').strengthify({
|
||||
zxcvbn: OC.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,
|
||||
$addAfter: $('input[name="newPassword-clone"]'),
|
||||
})
|
||||
})
|
||||
|
|
@ -1,707 +0,0 @@
|
|||
/* jshint forin:true, noarg:true, noempty:true, eqeqeq:true, boss:true, undef:true, curly:true, browser:true, jquery:true */
|
||||
/*
|
||||
* jQuery MultiSelect UI Widget 1.13
|
||||
* Copyright (c) 2012 Eric Hynds
|
||||
*
|
||||
* http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/
|
||||
*
|
||||
* Depends:
|
||||
* - jQuery 1.4.2+
|
||||
* - jQuery UI 1.8 widget factory
|
||||
*
|
||||
* Optional:
|
||||
* - jQuery UI effects
|
||||
* - jQuery UI position utility
|
||||
*
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2012 Eric Hynds
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
(function($, undefined) {
|
||||
let multiselectID = 0
|
||||
|
||||
$.widget('ech.multiselect', {
|
||||
|
||||
// default options
|
||||
options: {
|
||||
header: true,
|
||||
height: 175,
|
||||
minWidth: 225,
|
||||
classes: '',
|
||||
checkAllText: 'Check all',
|
||||
uncheckAllText: 'Uncheck all',
|
||||
noneSelectedText: 'Select options',
|
||||
selectedText: '# selected',
|
||||
selectedList: 0,
|
||||
show: null,
|
||||
hide: null,
|
||||
autoOpen: false,
|
||||
multiple: true,
|
||||
position: {},
|
||||
},
|
||||
|
||||
_create: function() {
|
||||
const el = this.element.hide(),
|
||||
o = this.options
|
||||
|
||||
this.speed = $.fx.speeds._default // default speed for effects
|
||||
this._isOpen = false // assume no
|
||||
|
||||
const
|
||||
button = (this.button = $('<button type="button"><span class="ui-icon ui-icon-triangle-2-n-s"></span></button>'))
|
||||
.addClass('ui-multiselect ui-widget ui-state-default ui-corner-all')
|
||||
.addClass(o.classes)
|
||||
.attr({ title: el.attr('title'), 'aria-haspopup': true, tabIndex: el.attr('tabIndex') })
|
||||
.insertAfter(el),
|
||||
|
||||
buttonlabel = (this.buttonlabel = $('<span />'))
|
||||
.html(o.noneSelectedText)
|
||||
.appendTo(button),
|
||||
|
||||
menu = (this.menu = $('<div />'))
|
||||
.addClass('ui-multiselect-menu ui-widget ui-widget-content ui-corner-all')
|
||||
.addClass(o.classes)
|
||||
.appendTo(document.body),
|
||||
|
||||
header = (this.header = $('<div />'))
|
||||
.addClass('ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix')
|
||||
.appendTo(menu),
|
||||
|
||||
headerLinkContainer = (this.headerLinkContainer = $('<ul />'))
|
||||
.addClass('ui-helper-reset')
|
||||
.html(function() {
|
||||
if (o.header === true) {
|
||||
return '<li><a class="ui-multiselect-all" href="#"><span class="ui-icon ui-icon-check"></span><span>' + o.checkAllText + '</span></a></li><li><a class="ui-multiselect-none" href="#"><span class="ui-icon ui-icon-closethick"></span><span>' + o.uncheckAllText + '</span></a></li>'
|
||||
} else if (typeof o.header === 'string') {
|
||||
return '<li>' + o.header + '</li>'
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
})
|
||||
.append('<li class="ui-multiselect-close"><a href="#" class="ui-multiselect-close"><span class="ui-icon ui-icon-circle-close"></span></a></li>')
|
||||
.appendTo(header),
|
||||
|
||||
checkboxContainer = (this.checkboxContainer = $('<ul />'))
|
||||
.addClass('ui-multiselect-checkboxes ui-helper-reset')
|
||||
.appendTo(menu)
|
||||
|
||||
// perform event bindings
|
||||
this._bindEvents()
|
||||
|
||||
// build menu
|
||||
this.refresh(true)
|
||||
|
||||
// some addl. logic for single selects
|
||||
if (!o.multiple) {
|
||||
menu.addClass('ui-multiselect-single')
|
||||
}
|
||||
},
|
||||
|
||||
_init: function() {
|
||||
if (this.options.header === false) {
|
||||
this.header.hide()
|
||||
}
|
||||
if (!this.options.multiple) {
|
||||
this.headerLinkContainer.find('.ui-multiselect-all, .ui-multiselect-none').hide()
|
||||
}
|
||||
if (this.options.autoOpen) {
|
||||
this.open()
|
||||
}
|
||||
if (this.element.is(':disabled')) {
|
||||
this.disable()
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function(init) {
|
||||
let el = this.element,
|
||||
o = this.options,
|
||||
menu = this.menu,
|
||||
checkboxContainer = this.checkboxContainer,
|
||||
optgroups = [],
|
||||
html = '',
|
||||
id = el.attr('id') || multiselectID++ // unique ID for the label & option tags
|
||||
|
||||
// build items
|
||||
el.find('option').each(function(i) {
|
||||
let $this = $(this),
|
||||
parent = this.parentNode,
|
||||
title = this.innerHTML,
|
||||
description = this.title,
|
||||
value = this.value,
|
||||
inputID = 'ui-multiselect-' + (this.id || id + '-option-' + i),
|
||||
isDisabled = this.disabled,
|
||||
isSelected = this.selected,
|
||||
labelClasses = ['ui-corner-all'],
|
||||
liClasses = (isDisabled ? 'ui-multiselect-disabled ' : ' ') + this.className,
|
||||
optLabel
|
||||
|
||||
// is this an optgroup?
|
||||
if (parent.tagName === 'OPTGROUP') {
|
||||
optLabel = parent.getAttribute('label')
|
||||
|
||||
// has this optgroup been added already?
|
||||
if ($.inArray(optLabel, optgroups) === -1) {
|
||||
html += '<li class="ui-multiselect-optgroup-label ' + parent.className + '"><a href="#">' + optLabel + '</a></li>'
|
||||
optgroups.push(optLabel)
|
||||
}
|
||||
}
|
||||
|
||||
if (isDisabled) {
|
||||
labelClasses.push('ui-state-disabled')
|
||||
}
|
||||
|
||||
// browsers automatically select the first option
|
||||
// by default with single selects
|
||||
if (isSelected && !o.multiple) {
|
||||
labelClasses.push('ui-state-active')
|
||||
}
|
||||
|
||||
html += '<li class="' + liClasses + '">'
|
||||
|
||||
// create the label
|
||||
html += '<label for="' + inputID + '" title="' + description + '" class="' + labelClasses.join(' ') + '">'
|
||||
html += '<input id="' + inputID + '" name="multiselect_' + id + '" type="' + (o.multiple ? 'checkbox' : 'radio') + '" value="' + value + '" title="' + title + '"'
|
||||
|
||||
// pre-selected?
|
||||
if (isSelected) {
|
||||
html += ' checked="checked"'
|
||||
html += ' aria-selected="true"'
|
||||
}
|
||||
|
||||
// disabled?
|
||||
if (isDisabled) {
|
||||
html += ' disabled="disabled"'
|
||||
html += ' aria-disabled="true"'
|
||||
}
|
||||
|
||||
// add the title and close everything off
|
||||
html += ' /><span>' + title + '</span></label></li>'
|
||||
})
|
||||
|
||||
// insert into the DOM
|
||||
checkboxContainer.html(html)
|
||||
|
||||
// cache some moar useful elements
|
||||
this.labels = menu.find('label')
|
||||
this.inputs = this.labels.children('input')
|
||||
|
||||
// set widths
|
||||
this._setButtonWidth()
|
||||
this._setMenuWidth()
|
||||
|
||||
// remember default value
|
||||
this.button[0].defaultValue = this.update()
|
||||
|
||||
// broadcast refresh event; useful for widgets
|
||||
if (!init) {
|
||||
this._trigger('refresh')
|
||||
}
|
||||
},
|
||||
|
||||
// updates the button text. call refresh() to rebuild
|
||||
update: function() {
|
||||
let o = this.options,
|
||||
$inputs = this.inputs,
|
||||
$checked = $inputs.filter(':checked'),
|
||||
numChecked = $checked.length,
|
||||
value
|
||||
|
||||
if (numChecked === 0) {
|
||||
value = o.noneSelectedText
|
||||
} else {
|
||||
if ($.isFunction(o.selectedText)) {
|
||||
value = o.selectedText.call(this, numChecked, $inputs.length, $checked.get())
|
||||
} else if (/\d/.test(o.selectedList) && o.selectedList > 0 && numChecked <= o.selectedList) {
|
||||
value = $checked.map(function() { return $(this).next().html() }).get().join(', ')
|
||||
} else {
|
||||
value = o.selectedText.replace('#', numChecked).replace('#', $inputs.length)
|
||||
}
|
||||
}
|
||||
|
||||
this.buttonlabel.html(value)
|
||||
return value
|
||||
},
|
||||
|
||||
// binds events
|
||||
_bindEvents: function() {
|
||||
const self = this, button = this.button
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function clickHandler() {
|
||||
self[self._isOpen ? 'close' : 'open']()
|
||||
return false
|
||||
}
|
||||
|
||||
// webkit doesn't like it when you click on the span :(
|
||||
button
|
||||
.find('span')
|
||||
.bind('click.multiselect', clickHandler)
|
||||
|
||||
// button events
|
||||
button.bind({
|
||||
click: clickHandler,
|
||||
keypress: function(e) {
|
||||
switch (e.which) {
|
||||
case 27: // esc
|
||||
case 38: // up
|
||||
case 37: // left
|
||||
self.close()
|
||||
break
|
||||
case 39: // right
|
||||
case 40: // down
|
||||
self.open()
|
||||
break
|
||||
}
|
||||
},
|
||||
mouseenter: function() {
|
||||
if (!button.hasClass('ui-state-disabled')) {
|
||||
$(this).addClass('ui-state-hover')
|
||||
}
|
||||
},
|
||||
mouseleave: function() {
|
||||
$(this).removeClass('ui-state-hover')
|
||||
},
|
||||
focus: function() {
|
||||
if (!button.hasClass('ui-state-disabled')) {
|
||||
$(this).addClass('ui-state-focus')
|
||||
}
|
||||
},
|
||||
blur: function() {
|
||||
$(this).removeClass('ui-state-focus')
|
||||
},
|
||||
})
|
||||
|
||||
// header links
|
||||
this.header
|
||||
.delegate('a', 'click.multiselect', function(e) {
|
||||
// close link
|
||||
if ($(this).hasClass('ui-multiselect-close')) {
|
||||
self.close()
|
||||
|
||||
// check all / uncheck all
|
||||
} else {
|
||||
self[$(this).hasClass('ui-multiselect-all') ? 'checkAll' : 'uncheckAll']()
|
||||
}
|
||||
|
||||
e.preventDefault()
|
||||
})
|
||||
|
||||
// optgroup label toggle support
|
||||
this.menu
|
||||
.delegate('li.ui-multiselect-optgroup-label a', 'click.multiselect', function(e) {
|
||||
e.preventDefault()
|
||||
|
||||
const $this = $(this),
|
||||
$inputs = $this.parent().nextUntil('li.ui-multiselect-optgroup-label').find('input:visible:not(:disabled)'),
|
||||
nodes = $inputs.get(),
|
||||
label = $this.parent().text()
|
||||
|
||||
// trigger event and bail if the return is false
|
||||
if (self._trigger('beforeoptgrouptoggle', e, { inputs: nodes, label }) === false) {
|
||||
return
|
||||
}
|
||||
|
||||
// toggle inputs
|
||||
self._toggleChecked(
|
||||
$inputs.filter(':checked').length !== $inputs.length,
|
||||
$inputs,
|
||||
)
|
||||
|
||||
self._trigger('optgrouptoggle', e, {
|
||||
inputs: nodes,
|
||||
label,
|
||||
checked: nodes[0].checked,
|
||||
})
|
||||
})
|
||||
.delegate('label', 'mouseenter.multiselect', function() {
|
||||
if (!$(this).hasClass('ui-state-disabled')) {
|
||||
self.labels.removeClass('ui-state-hover')
|
||||
$(this).addClass('ui-state-hover').find('input').focus()
|
||||
}
|
||||
})
|
||||
.delegate('label', 'keydown.multiselect', function(e) {
|
||||
e.preventDefault()
|
||||
|
||||
switch (e.which) {
|
||||
case 9: // tab
|
||||
case 27: // esc
|
||||
self.close()
|
||||
break
|
||||
case 38: // up
|
||||
case 40: // down
|
||||
case 37: // left
|
||||
case 39: // right
|
||||
self._traverse(e.which, this)
|
||||
break
|
||||
case 13: // enter
|
||||
$(this).find('input')[0].click()
|
||||
break
|
||||
}
|
||||
})
|
||||
.delegate('input[type="checkbox"], input[type="radio"]', 'click.multiselect', function(e) {
|
||||
const $this = $(this),
|
||||
val = this.value,
|
||||
checked = this.checked,
|
||||
tags = self.element.find('option')
|
||||
|
||||
// bail if this input is disabled or the event is cancelled
|
||||
if (this.disabled || self._trigger('click', e, { value: val, text: this.title, checked }) === false) {
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
|
||||
// make sure the input has focus. otherwise, the esc key
|
||||
// won't close the menu after clicking an item.
|
||||
$this.focus()
|
||||
|
||||
// toggle aria state
|
||||
$this.attr('aria-selected', checked)
|
||||
|
||||
// change state on the original option tags
|
||||
tags.each(function() {
|
||||
if (this.value === val) {
|
||||
this.selected = checked
|
||||
} else if (!self.options.multiple) {
|
||||
this.selected = false
|
||||
}
|
||||
})
|
||||
|
||||
// some additional single select-specific logic
|
||||
if (!self.options.multiple) {
|
||||
self.labels.removeClass('ui-state-active')
|
||||
$this.closest('label').toggleClass('ui-state-active', checked)
|
||||
|
||||
// close menu
|
||||
self.close()
|
||||
}
|
||||
|
||||
// fire change on the select box
|
||||
self.element.trigger('change')
|
||||
|
||||
// setTimeout is to fix multiselect issue #14 and #47. caused by jQuery issue #3827
|
||||
// http://bugs.jquery.com/ticket/3827
|
||||
setTimeout($.proxy(self.update, self), 10)
|
||||
})
|
||||
|
||||
// close each widget when clicking on any other element/anywhere else on the page
|
||||
$(document).bind('mousedown.multiselect', function(e) {
|
||||
if (self._isOpen && !$.contains(self.menu[0], e.target) && !$.contains(self.button[0], e.target) && e.target !== self.button[0]) {
|
||||
self.close()
|
||||
}
|
||||
})
|
||||
|
||||
// deal with form resets. the problem here is that buttons aren't
|
||||
// restored to their defaultValue prop on form reset, and the reset
|
||||
// handler fires before the form is actually reset. delaying it a bit
|
||||
// gives the form inputs time to clear.
|
||||
$(this.element[0].form).bind('reset.multiselect', function() {
|
||||
setTimeout($.proxy(self.refresh, self), 10)
|
||||
})
|
||||
},
|
||||
|
||||
// set button width
|
||||
_setButtonWidth: function() {
|
||||
let width = this.element.outerWidth(),
|
||||
o = this.options
|
||||
|
||||
if (/\d/.test(o.minWidth) && width < o.minWidth) {
|
||||
width = o.minWidth
|
||||
}
|
||||
|
||||
// set widths
|
||||
this.button.width(width)
|
||||
},
|
||||
|
||||
// set menu width
|
||||
_setMenuWidth: function() {
|
||||
const m = this.menu,
|
||||
width = this.button.outerWidth()
|
||||
- parseInt(m.css('padding-left'), 10)
|
||||
- parseInt(m.css('padding-right'), 10)
|
||||
- parseInt(m.css('border-right-width'), 10)
|
||||
- parseInt(m.css('border-left-width'), 10)
|
||||
|
||||
m.width(width || this.button.outerWidth())
|
||||
},
|
||||
|
||||
// move up or down within the menu
|
||||
_traverse: function(which, start) {
|
||||
const $start = $(start),
|
||||
moveToLast = which === 38 || which === 37,
|
||||
|
||||
// select the first li that isn't an optgroup label / disabled
|
||||
$next = $start.parent()[moveToLast ? 'prevAll' : 'nextAll']('li:not(.ui-multiselect-disabled, .ui-multiselect-optgroup-label)')[moveToLast ? 'last' : 'first']()
|
||||
|
||||
// if at the first/last element
|
||||
if (!$next.length) {
|
||||
const $container = this.menu.find('ul').last()
|
||||
|
||||
// move to the first/last
|
||||
this.menu.find('label')[moveToLast ? 'last' : 'first']().trigger('mouseover')
|
||||
|
||||
// set scroll position
|
||||
$container.scrollTop(moveToLast ? $container.height() : 0)
|
||||
} else {
|
||||
$next.find('label').trigger('mouseover')
|
||||
}
|
||||
},
|
||||
|
||||
// This is an internal function to toggle the checked property and
|
||||
// other related attributes of a checkbox.
|
||||
//
|
||||
// The context of this function should be a checkbox; do not proxy it.
|
||||
_toggleState: function(prop, flag) {
|
||||
return function() {
|
||||
if (!this.disabled) {
|
||||
this[prop] = flag
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
this.setAttribute('aria-selected', true)
|
||||
} else {
|
||||
this.removeAttribute('aria-selected')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_toggleChecked: function(flag, group) {
|
||||
const $inputs = (group && group.length) ? group : this.inputs,
|
||||
self = this
|
||||
|
||||
// toggle state on inputs
|
||||
$inputs.each(this._toggleState('checked', flag))
|
||||
|
||||
// give the first input focus
|
||||
$inputs.eq(0).focus()
|
||||
|
||||
// update button text
|
||||
this.update()
|
||||
|
||||
// gather an array of the values that actually changed
|
||||
const values = $inputs.map(function() {
|
||||
return this.value
|
||||
}).get()
|
||||
|
||||
// toggle state on original option tags
|
||||
this.element
|
||||
.find('option')
|
||||
.each(function() {
|
||||
if (!this.disabled && $.inArray(this.value, values) > -1) {
|
||||
self._toggleState('selected', flag).call(this)
|
||||
}
|
||||
})
|
||||
|
||||
// trigger the change event on the select
|
||||
if ($inputs.length) {
|
||||
this.element.trigger('change')
|
||||
}
|
||||
},
|
||||
|
||||
_toggleDisabled: function(flag) {
|
||||
this.button
|
||||
.attr({ disabled: flag, 'aria-disabled': flag })[flag ? 'addClass' : 'removeClass']('ui-state-disabled')
|
||||
|
||||
let inputs = this.menu.find('input')
|
||||
const key = 'ech-multiselect-disabled'
|
||||
|
||||
if (flag) {
|
||||
// remember which elements this widget disabled (not pre-disabled)
|
||||
// elements, so that they can be restored if the widget is re-enabled.
|
||||
inputs = inputs.filter(':enabled')
|
||||
.data(key, true)
|
||||
} else {
|
||||
inputs = inputs.filter(function() {
|
||||
return $.data(this, key) === true
|
||||
}).removeData(key)
|
||||
}
|
||||
|
||||
inputs
|
||||
.attr({ disabled: flag, 'arial-disabled': flag })
|
||||
.parent()[flag ? 'addClass' : 'removeClass']('ui-state-disabled')
|
||||
|
||||
this.element
|
||||
.attr({ disabled: flag, 'aria-disabled': flag })
|
||||
},
|
||||
|
||||
// open the menu
|
||||
open: function(e) {
|
||||
let self = this,
|
||||
button = this.button,
|
||||
menu = this.menu,
|
||||
speed = this.speed,
|
||||
o = this.options,
|
||||
args = []
|
||||
|
||||
// bail if the multiselectopen event returns false, this widget is disabled, or is already open
|
||||
if (this._trigger('beforeopen') === false || button.hasClass('ui-state-disabled') || this._isOpen) {
|
||||
return
|
||||
}
|
||||
|
||||
let $container = menu.find('ul').last(),
|
||||
effect = o.show,
|
||||
pos = button.offset()
|
||||
|
||||
// figure out opening effects/speeds
|
||||
if ($.isArray(o.show)) {
|
||||
effect = o.show[0]
|
||||
speed = o.show[1] || self.speed
|
||||
}
|
||||
|
||||
// if there's an effect, assume jQuery UI is in use
|
||||
// build the arguments to pass to show()
|
||||
if (effect) {
|
||||
args = [effect, speed]
|
||||
}
|
||||
|
||||
// set the scroll of the checkbox container
|
||||
$container.scrollTop(0).height(o.height)
|
||||
|
||||
// position and show menu
|
||||
if ($.ui.position && !$.isEmptyObject(o.position)) {
|
||||
o.position.of = o.position.of || button
|
||||
|
||||
menu
|
||||
.show()
|
||||
.position(o.position)
|
||||
.hide()
|
||||
|
||||
// if position utility is not available...
|
||||
} else {
|
||||
menu.css({
|
||||
top: pos.top + button.outerHeight(),
|
||||
'inset-inline-start': pos.left,
|
||||
})
|
||||
}
|
||||
|
||||
// show the menu, maybe with a speed/effect combo
|
||||
$.fn.show.apply(menu, args)
|
||||
|
||||
// select the first option
|
||||
// triggering both mouseover and mouseover because 1.4.2+ has a bug where triggering mouseover
|
||||
// will actually trigger mouseenter. the mouseenter trigger is there for when it's eventually fixed
|
||||
this.labels.eq(0).trigger('mouseover').trigger('mouseenter').find('input').trigger('focus')
|
||||
|
||||
button.addClass('ui-state-active')
|
||||
this._isOpen = true
|
||||
this._trigger('open')
|
||||
},
|
||||
|
||||
// close the menu
|
||||
close: function() {
|
||||
if (this._trigger('beforeclose') === false) {
|
||||
return
|
||||
}
|
||||
|
||||
let o = this.options,
|
||||
effect = o.hide,
|
||||
speed = this.speed,
|
||||
args = []
|
||||
|
||||
// figure out opening effects/speeds
|
||||
if ($.isArray(o.hide)) {
|
||||
effect = o.hide[0]
|
||||
speed = o.hide[1] || this.speed
|
||||
}
|
||||
|
||||
if (effect) {
|
||||
args = [effect, speed]
|
||||
}
|
||||
|
||||
$.fn.hide.apply(this.menu, args)
|
||||
this.button.removeClass('ui-state-active').trigger('blur').trigger('mouseleave')
|
||||
this._isOpen = false
|
||||
this._trigger('close')
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this._toggleDisabled(false)
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this._toggleDisabled(true)
|
||||
},
|
||||
|
||||
checkAll: function(e) {
|
||||
this._toggleChecked(true)
|
||||
this._trigger('checkAll')
|
||||
},
|
||||
|
||||
uncheckAll: function() {
|
||||
this._toggleChecked(false)
|
||||
this._trigger('uncheckAll')
|
||||
},
|
||||
|
||||
getChecked: function() {
|
||||
return this.menu.find('input').filter(':checked')
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
// remove classes + data
|
||||
$.Widget.prototype.destroy.call(this)
|
||||
|
||||
this.button.remove()
|
||||
this.menu.remove()
|
||||
this.element.show()
|
||||
|
||||
return this
|
||||
},
|
||||
|
||||
isOpen: function() {
|
||||
return this._isOpen
|
||||
},
|
||||
|
||||
widget: function() {
|
||||
return this.menu
|
||||
},
|
||||
|
||||
getButton: function() {
|
||||
return this.button
|
||||
},
|
||||
|
||||
// react to option changes after initialization
|
||||
_setOption: function(key, value) {
|
||||
const menu = this.menu
|
||||
|
||||
switch (key) {
|
||||
case 'header':
|
||||
menu.find('div.ui-multiselect-header')[value ? 'show' : 'hide']()
|
||||
break
|
||||
case 'checkAllText':
|
||||
menu.find('a.ui-multiselect-all span').eq(-1).text(value)
|
||||
break
|
||||
case 'uncheckAllText':
|
||||
menu.find('a.ui-multiselect-none span').eq(-1).text(value)
|
||||
break
|
||||
case 'height':
|
||||
menu.find('ul').last().height(parseInt(value, 10))
|
||||
break
|
||||
case 'minWidth':
|
||||
this.options[key] = parseInt(value, 10)
|
||||
this._setButtonWidth()
|
||||
this._setMenuWidth()
|
||||
break
|
||||
case 'selectedText':
|
||||
case 'selectedList':
|
||||
case 'noneSelectedText':
|
||||
this.options[key] = value // these all needs to update immediately for the update() call
|
||||
this.update()
|
||||
break
|
||||
case 'classes':
|
||||
menu.add(this.button).removeClass(this.options.classes).addClass(value)
|
||||
break
|
||||
case 'multiple':
|
||||
menu.toggleClass('ui-multiselect-single', !value)
|
||||
this.options.multiple = value
|
||||
this.element[0].multiple = value
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
$.Widget.prototype._setOption.apply(this, arguments)
|
||||
},
|
||||
})
|
||||
})(jQuery)
|
||||
|
|
@ -30,6 +30,7 @@ use OCP\AppFramework\Bootstrap\IBootstrap;
|
|||
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||
use OCP\AppFramework\IAppContainer;
|
||||
use OCP\AppFramework\Services\IAppConfig;
|
||||
use OCP\AppFramework\Services\IInitialState;
|
||||
use OCP\Config\IUserConfig;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IAvatarManager;
|
||||
|
|
@ -67,6 +68,7 @@ class Application extends App implements IBootstrap {
|
|||
$appContainer->get(IL10N::class),
|
||||
$appContainer->get('Session'),
|
||||
$appContainer->get(IURLGenerator::class),
|
||||
$appContainer->get(IInitialState::class),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
namespace OCA\User_LDAP\Controller;
|
||||
|
||||
use OCA\User_LDAP\AppInfo\Application;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||
use OCP\AppFramework\Http\Attribute\OpenAPI;
|
||||
|
|
@ -13,6 +14,7 @@ use OCP\AppFramework\Http\Attribute\PublicPage;
|
|||
use OCP\AppFramework\Http\Attribute\UseSession;
|
||||
use OCP\AppFramework\Http\RedirectResponse;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\AppFramework\Services\IInitialState;
|
||||
use OCP\Config\IUserConfig;
|
||||
use OCP\HintException;
|
||||
use OCP\IConfig;
|
||||
|
|
@ -20,8 +22,8 @@ use OCP\IL10N;
|
|||
use OCP\IRequest;
|
||||
use OCP\ISession;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Util;
|
||||
|
||||
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
|
||||
class RenewPasswordController extends Controller {
|
||||
|
|
@ -34,6 +36,7 @@ class RenewPasswordController extends Controller {
|
|||
protected IL10N $l10n,
|
||||
private ISession $session,
|
||||
private IURLGenerator $urlGenerator,
|
||||
private IInitialState $initialState,
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
}
|
||||
|
|
@ -51,7 +54,7 @@ class RenewPasswordController extends Controller {
|
|||
if (!$this->userConfig->getValueBool($user, 'user_ldap', 'needsPasswordReset')) {
|
||||
return new RedirectResponse($this->urlGenerator->linkToRouteAbsolute('core.login.showLoginForm'));
|
||||
}
|
||||
$parameters = [];
|
||||
|
||||
$renewPasswordMessages = $this->session->get('renewPasswordMessages');
|
||||
$errors = [];
|
||||
$messages = [];
|
||||
|
|
@ -59,25 +62,23 @@ class RenewPasswordController extends Controller {
|
|||
[$errors, $messages] = $renewPasswordMessages;
|
||||
}
|
||||
$this->session->remove('renewPasswordMessages');
|
||||
foreach ($errors as $value) {
|
||||
$parameters[$value] = true;
|
||||
}
|
||||
|
||||
$parameters['messages'] = $messages;
|
||||
$parameters['user'] = $user;
|
||||
|
||||
$parameters['canResetPassword'] = true;
|
||||
$parameters['resetPasswordLink'] = $this->config->getSystemValue('lost_password_link', '');
|
||||
if (!$parameters['resetPasswordLink']) {
|
||||
$userObj = $this->userManager->get($user);
|
||||
if ($userObj instanceof IUser) {
|
||||
$parameters['canResetPassword'] = $userObj->canChangePassword();
|
||||
}
|
||||
}
|
||||
$parameters['cancelLink'] = $this->urlGenerator->linkToRouteAbsolute('core.login.showLoginForm');
|
||||
$this->initialState->provideInitialState('renewPasswordParameters',
|
||||
[
|
||||
'user' => $user,
|
||||
'errors' => $errors,
|
||||
'messages' => $messages,
|
||||
'cancelRenewUrl' => $this->urlGenerator->linkToRouteAbsolute('core.login.showLoginForm'),
|
||||
'tryRenewPasswordUrl' => $this->urlGenerator->linkToRouteAbsolute('user_ldap.renewPassword.tryRenewPassword'),
|
||||
],
|
||||
);
|
||||
|
||||
Util::addStyle(Application::APP_ID, 'renewPassword');
|
||||
Util::addScript(Application::APP_ID, 'renewPassword');
|
||||
return new TemplateResponse(
|
||||
$this->appName, 'renewpassword', $parameters, 'guest'
|
||||
Application::APP_ID,
|
||||
'renewpassword',
|
||||
renderAs: 'guest',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<Settings />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Settings from './views/Settings.vue'
|
||||
</script>
|
||||
10
apps/user_ldap/src/renewPassword.ts
Normal file
10
apps/user_ldap/src/renewPassword.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import RenewPasswordView from './views/RenewPassword.vue'
|
||||
|
||||
const app = createApp(RenewPasswordView)
|
||||
app.mount('#user_ldap-renewPassword')
|
||||
|
|
@ -2,8 +2,9 @@
|
|||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import LDAPSettingsApp from './LDAPSettingsApp.vue'
|
||||
import LDAPSettingsApp from './views/LDAPSettingsApp.vue'
|
||||
import { pinia } from './store/index.ts'
|
||||
|
||||
const app = createApp(LDAPSettingsApp)
|
||||
|
|
|
|||
|
|
@ -87,8 +87,6 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
/* eslint vue/multi-word-component-names: "warn" */
|
||||
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { NcButton, NcCheckboxRadioSwitch, NcNoteCard, NcSelect } from '@nextcloud/vue'
|
||||
|
|
@ -132,8 +130,9 @@ const selectedConfigHasServerInfo = computed(() => {
|
|||
})
|
||||
|
||||
/**
|
||||
* Request to clear the mapping.
|
||||
*
|
||||
* @param subject
|
||||
* @param subject - The subject to clear
|
||||
*/
|
||||
async function requestClearMapping(subject: 'user' | 'group') {
|
||||
try {
|
||||
97
apps/user_ldap/src/views/RenewPassword.vue
Normal file
97
apps/user_ldap/src/views/RenewPassword.vue
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getRequestToken } from '@nextcloud/auth'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { ref } from 'vue'
|
||||
import NcButton from '@nextcloud/vue/components/NcButton'
|
||||
import NcGuestContent from '@nextcloud/vue/components/NcGuestContent'
|
||||
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
|
||||
import NcPasswordField from '@nextcloud/vue/components/NcPasswordField'
|
||||
|
||||
const renewPasswordParameters = loadState<{
|
||||
user: string
|
||||
errors: string[]
|
||||
messages: string[]
|
||||
cancelRenewUrl: string
|
||||
tryRenewPasswordUrl: string
|
||||
}>('user_ldap', 'renewPasswordParameters')
|
||||
|
||||
const hasInvalidPassword = renewPasswordParameters.errors.includes('invalidpassword')
|
||||
|
||||
const requestToken = getRequestToken()
|
||||
const isRenewing = ref(false)
|
||||
|
||||
/**
|
||||
* Handle the form submission.
|
||||
*/
|
||||
function onSubmit() {
|
||||
isRenewing.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NcGuestContent>
|
||||
<h2>{{ t('user_ldap', 'Please renew your password') }}</h2>
|
||||
<NcNoteCard v-if="renewPasswordParameters.messages.length" type="warning">
|
||||
<p v-for="(message, index) in renewPasswordParameters.messages" :key="index">
|
||||
{{ message }}
|
||||
</p>
|
||||
</NcNoteCard>
|
||||
<NcNoteCard
|
||||
v-if="renewPasswordParameters.errors.includes('internalexception')"
|
||||
:heading="t('user_ldap', 'An internal error occurred.')"
|
||||
:text="t('user_ldap', 'Please try again or contact your administrator.')"
|
||||
type="warning" />
|
||||
|
||||
<form
|
||||
method="post"
|
||||
name="renewpassword"
|
||||
:action="renewPasswordParameters.tryRenewPasswordUrl"
|
||||
@submit="onSubmit">
|
||||
<NcPasswordField
|
||||
autofocus
|
||||
autocomplete="off"
|
||||
autocapitalize="off"
|
||||
:error="hasInvalidPassword"
|
||||
:helper-text="hasInvalidPassword ? t('user_ldap', 'Wrong password.') : ''"
|
||||
:label="t('user_ldap', 'Current password')"
|
||||
required
|
||||
spellcheck="false"
|
||||
name="oldPassword" />
|
||||
<NcPasswordField
|
||||
autofocus
|
||||
autocomplete="off"
|
||||
autocapitalize="off"
|
||||
:label="t('user_ldap', 'New password')"
|
||||
required
|
||||
spellcheck="false"
|
||||
name="newPassword" />
|
||||
|
||||
<div :class="$style.renewPassword__actions">
|
||||
<NcButton :href="renewPasswordParameters.cancelRenewUrl" variant="error">
|
||||
{{ t('user_ldap', 'Cancel') }}
|
||||
</NcButton>
|
||||
<NcButton :disabled="isRenewing" type="submit" variant="primary">
|
||||
{{ isRenewing ? t('user_ldap', 'Renewing…') : t('user_ldap', 'Renew password') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="user" :value="renewPasswordParameters.user">
|
||||
<input type="hidden" name="requesttoken" :value="requestToken">
|
||||
</form>
|
||||
</NcGuestContent>
|
||||
</template>
|
||||
|
||||
<style module>
|
||||
.renewPassword__actions {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
gap: var(--default-grid-baseline);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,66 +1,9 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
/** @var \OCP\IL10N $l */
|
||||
|
||||
\OCP\Util::addScript('user_ldap', 'renewPassword', 'core');
|
||||
style('user_ldap', 'renewPassword');
|
||||
?>
|
||||
|
||||
<form method="post" name="renewpassword" id="renewpassword" action="<?php p(\OCP\Server::get(\OCP\IURLGenerator::class)->linkToRoute('user_ldap.renewPassword.tryRenewPassword')); ?>">
|
||||
<fieldset>
|
||||
<div class="warning title">
|
||||
<?php p($l->t('Please renew your password.')); ?><br>
|
||||
</div>
|
||||
<?php foreach ($_['messages'] as $message): ?>
|
||||
<div class="warning">
|
||||
<?php p($message); ?><br>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php if (isset($_['internalexception']) && $_['internalexception']): ?>
|
||||
<div class="warning">
|
||||
<?php p($l->t('An internal error occurred.')); ?><br>
|
||||
<small><?php p($l->t('Please try again or contact your administrator.')); ?></small>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div id="message" class="hidden">
|
||||
<img class="float-spinner" alt=""
|
||||
src="<?php p(image_path('core', 'loading-dark.gif'));?>">
|
||||
<span id="messageText"></span>
|
||||
<!-- the following div ensures that the spinner is always inside the #message div -->
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
<p class="grouptop">
|
||||
<input type="password" id="oldPassword" name="oldPassword"
|
||||
placeholder="<?php echo $l->t('Current password');?>"
|
||||
autofocus autocomplete="off" autocapitalize="off" spellcheck="false" required/>
|
||||
<label for="oldPassword" class="infield"><?php p($l->t('Current password')); ?></label>
|
||||
</p>
|
||||
|
||||
<p class="groupbottom">
|
||||
<input type="checkbox" id="personal-show" name="show" class="hidden-visually" /><label for="personal-show"></label>
|
||||
<label id="newPassword-label" for="newPassword" class="infield"><?php p($l->t('New password')); ?></label>
|
||||
<input type="password" id="newPassword" name="newPassword"
|
||||
placeholder="<?php echo $l->t('New password');?>"
|
||||
data-typetoggle="#personal-show" autofocus autocomplete="off" autocapitalize="off" spellcheck="false" required/>
|
||||
</p>
|
||||
|
||||
<input type="submit" id="submit" class="login primary icon-confirm-white" value="<?php p($l->t('Renew password')); ?>"/>
|
||||
|
||||
<?php if (!empty($_['invalidpassword'])) { ?>
|
||||
<p class="warning">
|
||||
<?php p($l->t('Wrong password.')); ?>
|
||||
</p>
|
||||
<?php } ?>
|
||||
<p id="cancel-container" class="info">
|
||||
<a id="cancel" href="<?php p($_['cancelLink']); ?>">
|
||||
<?php p($l->t('Cancel')); ?>
|
||||
</a>
|
||||
</p>
|
||||
<input type="hidden" name="user" id="user" value="<?php p($_['user']) ?>">
|
||||
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>">
|
||||
</fieldset>
|
||||
</form>
|
||||
<div id="user_ldap-renewPassword"></div>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ const modules = {
|
|||
'settings-personal': resolve(import.meta.dirname, 'apps/twofactor_backupcodes/src', 'settings-personal.ts'),
|
||||
},
|
||||
user_ldap: {
|
||||
renewPassword: resolve(import.meta.dirname, 'apps/user_ldap/src', 'renewPassword.ts'),
|
||||
'settings-admin': resolve(import.meta.dirname, 'apps/user_ldap/src', 'settings-admin.ts'),
|
||||
},
|
||||
user_status: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue