Reworked admin maintenance area (needed for restarting screen).
This commit is contained in:
parent
5bacada8ca
commit
cb5fd0c2c0
31 changed files with 1025 additions and 407 deletions
|
@ -132,10 +132,14 @@ class App.SettingsAreaLogo extends App.Controller
|
|||
@render()
|
||||
|
||||
render: ->
|
||||
@html App.view('settings/logo')(
|
||||
localElement = $(App.view('settings/logo')(
|
||||
setting: @setting
|
||||
))
|
||||
localElement.find('.js-loginPreview').html( App.view('generic/login_preview')(
|
||||
logoUrl: @logoUrl()
|
||||
)
|
||||
logoChange: true
|
||||
))
|
||||
@html localElement
|
||||
|
||||
onLogoPick: (event) =>
|
||||
reader = new FileReader()
|
||||
|
@ -146,8 +150,7 @@ class App.SettingsAreaLogo extends App.Controller
|
|||
file = event.target.files[0]
|
||||
|
||||
# if no file is given, about in file upload was used
|
||||
if !file
|
||||
return
|
||||
return if !file
|
||||
|
||||
maxSiteInMb = 8
|
||||
if file.size && file.size > 1024 * 1024 * maxSiteInMb
|
||||
|
@ -189,9 +192,9 @@ class App.SettingsAreaLogo extends App.Controller
|
|||
msg: App.i18n.translateContent('Update successful!')
|
||||
timeout: 2000
|
||||
}
|
||||
|
||||
for key, value of data.settings
|
||||
App.Config.set( key, value )
|
||||
for setting in data.settings
|
||||
value = App.Setting.get(setting.name)
|
||||
App.Config.set(name, value)
|
||||
else
|
||||
App.Event.trigger 'notify', {
|
||||
type: 'error'
|
||||
|
|
|
@ -21,6 +21,20 @@ class Index extends App.ControllerContent
|
|||
@render()
|
||||
@navupdate '#login'
|
||||
|
||||
# observe config changes related to login page
|
||||
@bind('config_update_local', (data) =>
|
||||
return if data.name != 'maintenance_mode' &&
|
||||
data.name != 'maintenance_login' &&
|
||||
data.name != 'maintenance_login_message' &&
|
||||
data.name != 'user_lost_password' &&
|
||||
data.name != 'user_create_account' &&
|
||||
data.name != 'product_name' &&
|
||||
data.name != 'product_logo' &&
|
||||
data.name != 'fqdn'
|
||||
@render()
|
||||
'rerender'
|
||||
)
|
||||
|
||||
render: (data = {}) ->
|
||||
auth_provider_all = {
|
||||
facebook: {
|
||||
|
@ -100,11 +114,15 @@ class Index extends App.ControllerContent
|
|||
@navigate '#/'
|
||||
|
||||
error: (xhr, statusText, error) =>
|
||||
detailsRaw = xhr.responseText
|
||||
details = {}
|
||||
if !_.isEmpty(detailsRaw)
|
||||
details = JSON.parse(detailsRaw)
|
||||
|
||||
# add notify
|
||||
@notify
|
||||
type: 'error'
|
||||
msg: App.i18n.translateContent('Wrong Username and Password combination.')
|
||||
msg: App.i18n.translateContent(details.error || 'Wrong Username and Password combination.')
|
||||
removeAll: true
|
||||
|
||||
# rerender login page
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
class Index extends App.ControllerContent
|
||||
events:
|
||||
'submit form': 'sendMessage'
|
||||
'change .js-modeSetting input': 'setMode'
|
||||
'change .js-loginSetting input': 'setLogin'
|
||||
'blur .js-Login': 'updateMessage'
|
||||
'submit .js-Message': 'sendMessage'
|
||||
|
||||
elements:
|
||||
'.js-modeSetting input': 'modeSetting'
|
||||
'.js-loginSetting input': 'loginSetting'
|
||||
|
||||
constructor: ->
|
||||
super
|
||||
|
@ -10,19 +17,55 @@ class Index extends App.ControllerContent
|
|||
|
||||
@title 'Maintenance', true
|
||||
|
||||
@render()
|
||||
@subscribeId = App.Setting.subscribe(@render, initFetch: true, clear: false)
|
||||
|
||||
render: ->
|
||||
@html App.view('maintenance')()
|
||||
release: =>
|
||||
App.Setting.unsubscribe(@subscribeId)
|
||||
|
||||
render: =>
|
||||
localElement = $(App.view('maintenance')())
|
||||
localElement.find('.js-loginPreview').html( App.view('generic/login_preview')(
|
||||
logoUrl: @logoUrl()
|
||||
))
|
||||
|
||||
localElement.find('.js-textarea').ce({
|
||||
mode: 'richtext'
|
||||
multiline: true
|
||||
maxlength: 20000
|
||||
})
|
||||
|
||||
@html localElement
|
||||
|
||||
setMode: (e) =>
|
||||
value = @modeSetting.prop('checked')
|
||||
return if value && !confirm('Sure?')
|
||||
App.Setting.set('maintenance_mode', value)
|
||||
App.WebSocket.send(
|
||||
event:'maintenance'
|
||||
data:
|
||||
type: 'mode'
|
||||
on: value
|
||||
)
|
||||
|
||||
setLogin: (e) =>
|
||||
value = @loginSetting.prop('checked')
|
||||
App.Setting.set('maintenance_login', value)
|
||||
|
||||
updateMessage: (e) =>
|
||||
e.preventDefault()
|
||||
params = @formParam(e.target)
|
||||
App.Setting.set('maintenance_login_message', params.message)
|
||||
@notify
|
||||
type: 'success'
|
||||
msg: App.i18n.translateContent('Update successful!')
|
||||
removeAll: true
|
||||
|
||||
sendMessage: (e) ->
|
||||
e.preventDefault()
|
||||
params = @formParam(e.target)
|
||||
App.Event.trigger(
|
||||
'ws:send'
|
||||
event: 'broadcast'
|
||||
data:
|
||||
event: 'session:maintenance'
|
||||
params.type = 'message'
|
||||
App.WebSocket.send(
|
||||
event:'maintenance'
|
||||
data: params
|
||||
)
|
||||
@notify
|
||||
|
@ -31,4 +74,4 @@ class Index extends App.ControllerContent
|
|||
removeAll: true
|
||||
@render()
|
||||
|
||||
App.Config.set( 'Maintenance', { prio: 3600, name: 'Maintenance', parent: '#system', target: '#system/maintenance', controller: Index, role: ['Admin'] }, 'NavBarAdmin' )
|
||||
App.Config.set('Maintenance', { prio: 3600, name: 'Maintenance', parent: '#system', target: '#system/maintenance', controller: Index, role: ['Admin'] }, 'NavBarAdmin')
|
||||
|
|
|
@ -81,18 +81,21 @@ class Index extends App.ControllerContent
|
|||
# add notify
|
||||
@notify
|
||||
type: 'success'
|
||||
msg: 'Thanks for joining. Email sent to "' + @params.email + '". Please verify your email address.'
|
||||
msg: App.i18n.translateContent('Thanks for joining. Email sent to "%s". Please verify your email address.', @params.email)
|
||||
removeAll: true
|
||||
|
||||
# redirect to #
|
||||
@navigate '#'
|
||||
|
||||
error: (xhr, statusText, error) =>
|
||||
detailsRaw = xhr.responseText
|
||||
details = {}
|
||||
if !_.isEmpty(detailsRaw)
|
||||
details = JSON.parse(detailsRaw)
|
||||
|
||||
# add notify
|
||||
@notify
|
||||
type: 'warning'
|
||||
msg: 'Wrong Username and Password combination.'
|
||||
type: 'error'
|
||||
msg: App.i18n.translateContent(details.error || 'Wrong Username and Password combination.')
|
||||
removeAll: true
|
||||
|
||||
App.Config.set( 'signup', Index, 'Routes' )
|
||||
App.Config.set('signup', Index, 'Routes')
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
class Widget extends App.Controller
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
App.Event.bind(
|
||||
'config_update'
|
||||
(data) ->
|
||||
App.Config.set(data.name, data.value)
|
||||
App.Event.trigger('config_update_local', data)
|
||||
)
|
||||
|
||||
App.Config.set('app_config_update', Widget, 'Widgets')
|
|
@ -1,33 +0,0 @@
|
|||
class Widget extends App.Controller
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
App.Event.bind(
|
||||
'app_version'
|
||||
(data) =>
|
||||
@render(data)
|
||||
'app_version'
|
||||
)
|
||||
|
||||
render: (data) =>
|
||||
return if @message
|
||||
return if @appVersion is data.app_version
|
||||
if !@appVersion
|
||||
@appVersion = data.app_version
|
||||
return
|
||||
@appVersion = data.app_version
|
||||
localAppVersion = @appVersion.split(':')
|
||||
return if localAppVersion[1] isnt 'true'
|
||||
message = =>
|
||||
@message = new App.SessionMessage(
|
||||
head: 'New Version'
|
||||
message: 'A new version of Zammad is available, please reload your browser.'
|
||||
keyboard: false
|
||||
backdrop: true
|
||||
buttonClose: false
|
||||
buttonSubmit: 'Continue session'
|
||||
forceReload: true
|
||||
)
|
||||
@delay(message, 2000)
|
||||
|
||||
App.Config.set('app_version', Widget, 'Widgets')
|
|
@ -2,11 +2,19 @@ class Widget extends App.Controller
|
|||
constructor: ->
|
||||
super
|
||||
|
||||
# bind on event to show message
|
||||
App.Event.bind(
|
||||
'session:maintenance'
|
||||
'maintenance'
|
||||
(data) =>
|
||||
if data.type is 'message'
|
||||
@showMessage(data)
|
||||
if data.type is 'mode'
|
||||
@maintanaceMode(data)
|
||||
if data.type is 'app_version'
|
||||
@maintanaceAppVersion(data)
|
||||
if data.type is 'config_changed'
|
||||
@maintanaceConfigChanged(data)
|
||||
if data.type is 'restart'
|
||||
@maintanaceRestart(data)
|
||||
'maintenance'
|
||||
)
|
||||
|
||||
|
@ -17,13 +25,10 @@ class Widget extends App.Controller
|
|||
else
|
||||
button = 'Close'
|
||||
|
||||
# convert to html and linkify
|
||||
message.message = App.Utils.textCleanup(message.message)
|
||||
message.message = App.Utils.text2html(message.message)
|
||||
|
||||
new App.SessionMessage(
|
||||
head: message.head
|
||||
contentInline: message.message
|
||||
small: true
|
||||
keyboard: true
|
||||
backdrop: true
|
||||
buttonClose: true
|
||||
|
@ -31,4 +36,62 @@ class Widget extends App.Controller
|
|||
forceReload: message.reload
|
||||
)
|
||||
|
||||
maintanaceMode: (data = {}) =>
|
||||
return if data.on isnt true
|
||||
return if !@authenticate(true)
|
||||
@navigate '#logout'
|
||||
|
||||
#App.Event.trigger('maintenance', {type:'restart'})
|
||||
maintanaceRestart: (data) =>
|
||||
return if @messageRestart
|
||||
@messageRestart = new App.SessionMessage(
|
||||
head: 'Restarting...'
|
||||
message: 'Zammad is restarting... waiting...'
|
||||
keyboard: false
|
||||
backdrop: false
|
||||
buttonClose: false
|
||||
buttonSubmit: false
|
||||
small: true
|
||||
forceReload: true
|
||||
)
|
||||
|
||||
# disconnect
|
||||
|
||||
# try if backend is reachable again
|
||||
|
||||
# reload app
|
||||
|
||||
maintanaceConfigChanged: (data) =>
|
||||
return if @messageConfigChanged
|
||||
@messageConfigChanged = new App.SessionMessage(
|
||||
head: 'Config has changed'
|
||||
message: 'The configuration of Zammad has changed, please reload your browser.'
|
||||
keyboard: false
|
||||
backdrop: true
|
||||
buttonClose: false
|
||||
buttonSubmit: 'Continue session'
|
||||
forceReload: true
|
||||
)
|
||||
|
||||
maintanaceAppVersion: (data) =>
|
||||
return if @messageAppVersion
|
||||
return if @appVersion is data.app_version
|
||||
if !@appVersion
|
||||
@appVersion = data.app_version
|
||||
return
|
||||
@appVersion = data.app_version
|
||||
localAppVersion = @appVersion.split(':')
|
||||
return if localAppVersion[1] isnt 'true'
|
||||
message = =>
|
||||
@messageAppVersion = new App.SessionMessage(
|
||||
head: 'New Version'
|
||||
message: 'A new version of Zammad is available, please reload your browser.'
|
||||
keyboard: false
|
||||
backdrop: true
|
||||
buttonClose: false
|
||||
buttonSubmit: 'Continue session'
|
||||
forceReload: true
|
||||
)
|
||||
@delay(message, 2000)
|
||||
|
||||
App.Config.set('maintenance', Widget, 'Widgets')
|
||||
|
|
|
@ -27,8 +27,8 @@ class App.Setting extends App.Model
|
|||
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!')
|
||||
timeout: 2000
|
||||
}
|
||||
setting.save(options)
|
||||
App.Config.set(name, value)
|
||||
setting.save(options)
|
||||
|
||||
@preferencesPost: (setting) ->
|
||||
return if !setting.preferences
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<div class="login branding centered darkBackground vertical">
|
||||
|
||||
<% if @C('maintenance_mode'): %>
|
||||
<div class="hero-unit alert alert--danger"><%- @T('Zammad is currently in maintenance mode. Only administrators can login. Please wait until the maintenance window is over.') %></div>
|
||||
<% end %>
|
||||
|
||||
<% if !@logoChange || @C('maintenance_login'): %>
|
||||
<form>
|
||||
<div contenteditable id="maintenance-message" data-name="message" class="hero-unit alert alert--success js-textarea js-Login" <% if !@C('maintenance_login'): %>style="opacity: 0.5;"<% end %>><%- @C('maintenance_login_message') %></div>
|
||||
</form>
|
||||
<% end %>
|
||||
|
||||
<div class="hero-unit">
|
||||
|
||||
<% if @logoChange: %>
|
||||
<img class="logo-preview" src="<%= @logoUrl %>">
|
||||
<div class="logo-preview-placeholder"><%- @T('Your Logo') %></div>
|
||||
<div class="centered">
|
||||
<div class="btn btn--success fileUpload"><%- @T('Change') %><input type="file" class="js-upload" name="logo" accept="image/*"></div>
|
||||
</div>
|
||||
<% else: %>
|
||||
<img class="company-logo" src="<%= @logoUrl %>">
|
||||
<% end %>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="username"><%- @Ti('Username / email') %></label>
|
||||
<input id="username" name="username" type="text" class="form-control" value="<%= @S('login') %>" autocapitalize="off" disabled="disabled"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password"><%- @Ti('Password') %></label>
|
||||
<input id="password" name="password" type="password" class="form-control" value="some_pass" disabled="disabled"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label><input name="remember_me" value="1" type="checkbox" disabled="disabled"/> <%- @T('Remember me') %></label>
|
||||
</div>
|
||||
|
||||
<div class="form-controls">
|
||||
<button class="btn btn--primary" type="submit" disabled="disabled"><%- @T('Sign in') %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,56 +1,63 @@
|
|||
<div class="login fullscreen">
|
||||
<div class="fullscreen-center">
|
||||
<div class="fullscreen-body">
|
||||
<p><%- @T( 'Login with %s', @C( 'fqdn' ) ) %></p>
|
||||
<p><%- @T('Login with %s', @C('fqdn')) %></p>
|
||||
|
||||
<% if @C('maintenance_mode'): %>
|
||||
<div class="hero-unit alert alert--danger js-maintenanceMode"><%- @T('Zammad is currently in maintenance mode. Only administrators can login. Please wait until the maintenance window is over.') %></div>
|
||||
<% end %>
|
||||
<% if @C('maintenance_login') && @C('maintenance_login_message'): %>
|
||||
<div class="hero-unit alert alert--success js-maintenanceLogin"><%- @C('maintenance_login_message') %></div>
|
||||
<% end %>
|
||||
|
||||
<div class="hero-unit">
|
||||
<img class="company-logo" src="<%= @logoUrl %>" alt="<%= @C( 'product_name' ) %>">
|
||||
<img class="company-logo" src="<%= @logoUrl %>" alt="<%= @C('product_name') %>">
|
||||
<form id="login">
|
||||
<div class="form-group">
|
||||
<div class="formGroup-label">
|
||||
<label for="username"><%- @Ti( 'Username / email' ) %></label>
|
||||
<label for="username"><%- @Ti('Username / email') %></label>
|
||||
</div>
|
||||
<input id="username" name="username" type="text" class="form-control" value="<%= @item.username %>" autocapitalize="off" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="formGroup-label">
|
||||
<label for="password"><%- @Ti( 'Password' ) %></label>
|
||||
<label for="password"><%- @Ti('Password') %></label>
|
||||
</div>
|
||||
<input id="password" name="password" type="password" class="form-control"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<!--
|
||||
<label for="remember_me"><%- @Ti( 'Remember me' ) %></label>
|
||||
<label for="remember_me"><%- @Ti('Remember me') %></label>
|
||||
<input id="remember_me" name="remember_me" value="1" type="checkbox"/>
|
||||
-->
|
||||
<label class="inline-label checkbox-replacement">
|
||||
<input name="remember_me" value="1" type="checkbox">
|
||||
<%- @Icon('checkbox', 'icon-unchecked') %>
|
||||
<%- @Icon('checkbox-checked', 'icon-checked') %>
|
||||
<span class="label-text"><%- @T( 'Remember me' ) %></span>
|
||||
<span class="label-text"><%- @T('Remember me') %></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-controls">
|
||||
<button class="btn btn--primary" type="submit"><%- @T( 'Sign in' ) %></button>
|
||||
<button class="btn btn--primary" type="submit"><%- @T('Sign in') %></button>
|
||||
|
||||
<% if @C('user_lost_password'): %>
|
||||
<a href="#password_reset" class="btn btn--text btn--secondary align-right"><%- @T( 'Forgot password?' ) %></a>
|
||||
<a href="#password_reset" class="btn btn--text btn--secondary align-right"><%- @T('Forgot password?') %></a>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if !_.isEmpty( @auth_providers ): %>
|
||||
<% if !_.isEmpty(@auth_providers): %>
|
||||
<div class="separator">
|
||||
<span class="separator-text"><%- @T( 'or sign in using' ) %></span>
|
||||
<span class="separator-text"><%- @T('or sign in using') %></span>
|
||||
</div>
|
||||
|
||||
<div class="auth-providers">
|
||||
<% for auth_provider in @auth_providers: %>
|
||||
<a class="auth-provider auth-provider--<%= auth_provider.class %>" href="<%= auth_provider.url %>">
|
||||
<%- @Icon("#{auth_provider.class}-button", 'provider-icon') %>
|
||||
<span class="provider-name"><%- @T( auth_provider.name ) %></span>
|
||||
<span class="provider-name"><%- @T(auth_provider.name) %></span>
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
|
@ -59,23 +66,23 @@
|
|||
</div>
|
||||
|
||||
<p>
|
||||
<%- @T( "You're already registered with your email adress if you've been in touch with our support team.") %><br>
|
||||
<%- @T("You're already registered with your email adress if you've been in touch with our support team.") %><br>
|
||||
<% if @C('user_lost_password'): %>
|
||||
<%- @T( "You can request your password") %> <a href="#password_reset"><%- @T( "here") %></a>.
|
||||
<%- @T('You can request your password') %> <a href="#password_reset"><%- @T('here') %></a>.
|
||||
<% end %>
|
||||
</p>
|
||||
|
||||
<% if @C('user_create_account'): %>
|
||||
<hr>
|
||||
<p>
|
||||
<a href="#signup"><%- @T( 'Register as a new customer' ) %></a>
|
||||
<a href="#signup"><%- @T('Register as a new customer') %></a>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="poweredBy">
|
||||
<%- @Icon('logo') %>
|
||||
<%- @T("Powered by") %>
|
||||
<%- @T('Powered by') %>
|
||||
<%- @Icon('logotype', 'logotype') %>
|
||||
</div>
|
||||
</div>
|
|
@ -1,10 +1,37 @@
|
|||
<div class="page-header">
|
||||
<div class="page-header-title">
|
||||
<h1><%- @T('Maintenance Message') %><small></small></h1>
|
||||
<h1><%- @T('Maintenance') %><small></small></h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-content">
|
||||
<form id="maintenanceForm">
|
||||
|
||||
<div class="settings-entry">
|
||||
<div class="page-header-title">
|
||||
<div class="zammad-switch zammad-switch--small js-modeSetting">
|
||||
<input name="chat" type="checkbox" id="setting-mode" <% if @C('maintenance_mode'): %>checked<% end %>>
|
||||
<label for="setting-mode"></label>
|
||||
</div>
|
||||
<h2><%- @T('Mode') %></h2>
|
||||
</div>
|
||||
<p>⚠ <%- @T('Enable or disable the maintenance mode of Zammad. If enabled, _all non-administrators get logged out_ and _only administrators can start a new session_.') %></p>
|
||||
</div>
|
||||
|
||||
<div class="settings-entry">
|
||||
<div class="page-header-title">
|
||||
<div class="zammad-switch zammad-switch--small js-loginSetting">
|
||||
<input name="chat" type="checkbox" id="setting-login" <% if @C('maintenance_login'): %>checked<% end %>>
|
||||
<label for="setting-login"></label>
|
||||
</div>
|
||||
<h2>@<%- @T('Login') %></h2>
|
||||
</div>
|
||||
<p><%- @T('Put a message on the login page. To change it, click on the text area below and change it inline.') %></p>
|
||||
<div class="js-loginPreview"></div>
|
||||
</div>
|
||||
|
||||
<div class="settings-entry">
|
||||
<h2><%- @T('Message') %></h2>
|
||||
<p><%- @T('Send a message to all logged in users.') %></p>
|
||||
<form class="js-Message">
|
||||
<div class="form-group">
|
||||
<label for="maintenance-title"><%- @T('Title') %></label>
|
||||
<div class="controls">
|
||||
|
@ -14,7 +41,7 @@
|
|||
<div class="form-group">
|
||||
<label for="maintenance-message"><%- @T('Message') %></label>
|
||||
<div class="controls">
|
||||
<textarea id="maintenance-message" name="message" class="form-control" rows="8" required></textarea>
|
||||
<div contenteditable id="maintenance-message" data-name="message" class="form-control form-control--multiline js-textarea richtext-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -25,6 +52,7 @@
|
|||
<span class="label-text"><%- @T('Reload application') %></span>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn--primary submit"><%- @T('Send to clients') %></button>
|
||||
<button class="btn btn--primary js-submit"><%- @T('Send to clients') %></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -1,36 +1,8 @@
|
|||
<form class="settings-entry" id="<%= @setting.name %>">
|
||||
<h2><%- @T( @setting.title ) %></h2>
|
||||
<p><%- @T( @setting.description ) %></p>
|
||||
|
||||
<div class="login branding centered darkBackground">
|
||||
<div class="hero-unit">
|
||||
<img class="logo-preview" src="<%= @logoUrl %>">
|
||||
<div class="logo-preview-placeholder"><%- @T('Your Logo') %></div>
|
||||
<div class="centered">
|
||||
<div class="btn btn--success fileUpload"><%- @T('Change') %><input type="file" class="js-upload" name="logo" accept="image/*"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="username"><%- @Ti( 'Username / email' ) %></label>
|
||||
<input id="username" name="username" type="text" class="form-control" value="<%= @S('login') %>" autocapitalize="off" disabled="disabled"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password"><%- @Ti( 'Password' ) %></label>
|
||||
<input id="password" name="password" type="password" class="form-control" value="some_pass" disabled="disabled"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label><input name="remember_me" value="1" type="checkbox" disabled="disabled"/> <%- @T( 'Remember me' ) %></label>
|
||||
</div>
|
||||
|
||||
<div class="form-controls">
|
||||
<button class="btn btn--primary" type="submit" disabled="disabled"><%- @T( 'Sign in' ) %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2><%- @T(@setting.title) %></h2>
|
||||
<p><%- @T(@setting.description) %></p>
|
||||
<div class="js-loginPreview"></div>
|
||||
<div class="setting-controls">
|
||||
<button type="submit" class="btn btn--primary"><%- @T( 'Submit' ) %></button>
|
||||
<button type="submit" class="btn btn--primary"><%- @T('Submit') %></button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -243,6 +243,12 @@ class ApplicationController < ActionController::Base
|
|||
# check sso based authentication
|
||||
sso_userdata = User.sso(params)
|
||||
if sso_userdata
|
||||
if check_maintenance_only(sso_userdata)
|
||||
return {
|
||||
auth: false,
|
||||
message: 'Maintenance mode enabled!',
|
||||
}
|
||||
end
|
||||
session[:persistent] = true
|
||||
return {
|
||||
auth: true
|
||||
|
@ -254,6 +260,12 @@ class ApplicationController < ActionController::Base
|
|||
logger.debug "http basic auth check '#{username}'"
|
||||
userdata = User.authenticate(username, password)
|
||||
next if !userdata
|
||||
if check_maintenance_only(userdata)
|
||||
return {
|
||||
auth: false,
|
||||
message: 'Maintenance mode enabled!',
|
||||
}
|
||||
end
|
||||
current_user_set(userdata)
|
||||
user_device_log(userdata, 'basic_auth')
|
||||
logger.debug "http basic auth for '#{userdata.login}'"
|
||||
|
@ -271,6 +283,12 @@ class ApplicationController < ActionController::Base
|
|||
name: token,
|
||||
)
|
||||
next if !userdata
|
||||
if check_maintenance_only(userdata)
|
||||
return {
|
||||
auth: false,
|
||||
message: 'Maintenance mode enabled!',
|
||||
}
|
||||
end
|
||||
current_user_set(userdata)
|
||||
user_device_log(userdata, 'token_auth')
|
||||
logger.debug "token auth for '#{userdata.login}'"
|
||||
|
@ -345,7 +363,7 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
# config
|
||||
config = {}
|
||||
Setting.select('name').where(frontend: true ).each { |setting|
|
||||
Setting.select('name').where(frontend: true).each { |setting|
|
||||
config[setting.name] = Setting.get(setting.name)
|
||||
}
|
||||
|
||||
|
@ -480,4 +498,19 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
data
|
||||
end
|
||||
|
||||
# check maintenance mode
|
||||
def check_maintenance_only(user)
|
||||
return false if Setting.get('maintenance_mode') != true
|
||||
return false if user.role?('Admin')
|
||||
Rails.logger.info "Maintenance mode enabled, denied login for user #{user.login}, it's no admin user."
|
||||
true
|
||||
end
|
||||
|
||||
def check_maintenance(user)
|
||||
return false if !check_maintenance_only(user)
|
||||
render json: { error: 'Maintenance mode enabled!' }, status: :unauthorized
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -11,9 +11,12 @@ class SessionsController < ApplicationController
|
|||
# authenticate user
|
||||
user = User.authenticate(params[:username], params[:password])
|
||||
|
||||
# check maintenance mode
|
||||
return if check_maintenance(user)
|
||||
|
||||
# auth failed
|
||||
if !user
|
||||
render json: { error: 'login failed' }, status: :unauthorized
|
||||
render json: { error: 'Wrong Username and Password combination.' }, status: :unauthorized
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -144,6 +147,12 @@ class SessionsController < ApplicationController
|
|||
authorization = Authorization.create_from_hash(auth, current_user)
|
||||
end
|
||||
|
||||
# check maintenance mode
|
||||
if check_maintenance_only(authorization.user)
|
||||
redirect_to '/#'
|
||||
return
|
||||
end
|
||||
|
||||
# set current session user
|
||||
current_user_set(authorization.user)
|
||||
|
||||
|
@ -167,6 +176,12 @@ class SessionsController < ApplicationController
|
|||
# Log the authorizing user in.
|
||||
if user
|
||||
|
||||
# check maintenance mode
|
||||
if check_maintenance_only(user)
|
||||
redirect_to '/#'
|
||||
return
|
||||
end
|
||||
|
||||
# set current session user
|
||||
current_user_set(user)
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ class SettingsController < ApplicationController
|
|||
file = StaticAssets.data_url_attributes(params[:logo_resize])
|
||||
|
||||
# store image 1:1
|
||||
setting.state = StaticAssets.store( file[:content], file[:mime_type] )
|
||||
setting.state = StaticAssets.store(file[:content], file[:mime_type])
|
||||
setting.save
|
||||
end
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ class Setting < ApplicationModel
|
|||
store :state_current
|
||||
store :state_initial
|
||||
store :preferences
|
||||
before_create :state_check, :set_initial
|
||||
before_update :state_check
|
||||
before_create :state_check, :set_initial, :check_broadcast
|
||||
before_update :state_check, :check_broadcast
|
||||
after_create :reset_cache
|
||||
after_update :reset_cache
|
||||
after_destroy :reset_cache
|
||||
|
@ -168,4 +168,20 @@ reload config settings
|
|||
return if state && state.respond_to?('has_key?') && state.key?(:value)
|
||||
self.state_current = { value: state }
|
||||
end
|
||||
|
||||
# notify clients about public config changes
|
||||
def check_broadcast
|
||||
return if frontend != true
|
||||
value = state_current
|
||||
if state_current.key?(:value)
|
||||
value = state_current[:value]
|
||||
end
|
||||
Sessions.broadcast(
|
||||
{
|
||||
event: 'config_update',
|
||||
data: { name: name, value: value }
|
||||
},
|
||||
'public'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
35
db/migrate/20160550000001_update_maintenance.rb
Normal file
35
db/migrate/20160550000001_update_maintenance.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
class UpdateMaintenance < ActiveRecord::Migration
|
||||
def up
|
||||
# can be deleted later, db/seeds.rb already updated
|
||||
Setting.create_if_not_exists(
|
||||
title: 'Maintenance Mode',
|
||||
name: 'maintenance_mode',
|
||||
area: 'Core::WebApp',
|
||||
description: 'Enable or disable the maintenance mode of Zammad. If enabled, all non-administrators get logged out and only administrators can start a new session.',
|
||||
options: {},
|
||||
state: false,
|
||||
preferences: {},
|
||||
frontend: true
|
||||
)
|
||||
Setting.create_if_not_exists(
|
||||
title: 'Maintenance Login',
|
||||
name: 'maintenance_login',
|
||||
area: 'Core::WebApp',
|
||||
description: 'Put a message on the login page. To change it, click on the text area below and change it inline.',
|
||||
options: {},
|
||||
state: false,
|
||||
preferences: {},
|
||||
frontend: true
|
||||
)
|
||||
Setting.create_if_not_exists(
|
||||
title: 'Maintenance Login',
|
||||
name: 'maintenance_login_message',
|
||||
area: 'Core::WebApp',
|
||||
description: 'Message for login page.',
|
||||
options: {},
|
||||
state: 'Something about to share. Click here to change.',
|
||||
preferences: {},
|
||||
frontend: true
|
||||
)
|
||||
end
|
||||
end
|
30
db/seeds.rb
30
db/seeds.rb
|
@ -26,6 +26,36 @@ Setting.create_if_not_exists(
|
|||
preferences: { online_service_disable: true },
|
||||
frontend: false
|
||||
)
|
||||
Setting.create_if_not_exists(
|
||||
title: 'Maintenance Mode',
|
||||
name: 'maintenance_mode',
|
||||
area: 'Core::WebApp',
|
||||
description: 'Enable or disable the maintenance mode of Zammad. If enabled, all non-administrators get logged out and only administrators can start a new session.',
|
||||
options: {},
|
||||
state: false,
|
||||
preferences: {},
|
||||
frontend: true
|
||||
)
|
||||
Setting.create_if_not_exists(
|
||||
title: 'Maintenance Login',
|
||||
name: 'maintenance_login',
|
||||
area: 'Core::WebApp',
|
||||
description: 'Put a message on the login page. To change it, click on the text area below and change it inline.',
|
||||
options: {},
|
||||
state: false,
|
||||
preferences: {},
|
||||
frontend: true
|
||||
)
|
||||
Setting.create_if_not_exists(
|
||||
title: 'Maintenance Login',
|
||||
name: 'maintenance_login_message',
|
||||
area: 'Core::WebApp',
|
||||
description: 'Message for login page.',
|
||||
options: {},
|
||||
state: 'Something about to share. Click here to change.',
|
||||
preferences: {},
|
||||
frontend: true
|
||||
)
|
||||
Setting.create_if_not_exists(
|
||||
title: 'Developer System',
|
||||
name: 'developer_mode',
|
||||
|
|
|
@ -44,8 +44,9 @@ get event data
|
|||
returnes
|
||||
|
||||
{
|
||||
event: 'app_version'
|
||||
event: 'maintenance'
|
||||
data: {
|
||||
type: 'app_version',
|
||||
app_version: app_version,
|
||||
}
|
||||
}
|
||||
|
@ -54,8 +55,9 @@ returnes
|
|||
|
||||
def self.event_data
|
||||
{
|
||||
event: 'app_version',
|
||||
event: 'maintenance',
|
||||
data: {
|
||||
type: 'app_version',
|
||||
app_version: get,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -353,17 +353,32 @@ returns
|
|||
|
||||
true|false
|
||||
|
||||
broadcase also to not authenticated client
|
||||
|
||||
Sessions.broadcast(data, 'public')
|
||||
|
||||
broadcase also not to sender
|
||||
|
||||
Sessions.broadcast(data, 'public', sender_user_id)
|
||||
|
||||
=end
|
||||
|
||||
def self.broadcast(data)
|
||||
def self.broadcast(data, recipient = 'autenticated', sender_user_id = nil)
|
||||
|
||||
# list all current clients
|
||||
client_list = sessions
|
||||
client_list.each {|client_id|
|
||||
session = Sessions.get(client_id)
|
||||
next if !session
|
||||
|
||||
if recipient != 'public'
|
||||
next if !session[:user]
|
||||
next if !session[:user]['id']
|
||||
end
|
||||
|
||||
if sender_user_id
|
||||
next if session[:user] && session[:user]['id'] && session[:user]['id'].to_i == sender_user_id.to_i
|
||||
end
|
||||
Sessions.send(client_id, data)
|
||||
}
|
||||
true
|
||||
|
|
|
@ -49,6 +49,51 @@ class Sessions::Event::Base
|
|||
true
|
||||
end
|
||||
|
||||
def role_permission_check(role, event)
|
||||
if !@session
|
||||
error = {
|
||||
event: "#{event}_error",
|
||||
data: {
|
||||
state: 'no_session',
|
||||
},
|
||||
}
|
||||
Sessions.send(@client_id, error)
|
||||
return
|
||||
end
|
||||
if !@session['id']
|
||||
error = {
|
||||
event: "#{event}_error",
|
||||
data: {
|
||||
state: 'no_session_user_id',
|
||||
},
|
||||
}
|
||||
Sessions.send(@client_id, error)
|
||||
return
|
||||
end
|
||||
user = User.lookup(id: @session['id'])
|
||||
if !user
|
||||
error = {
|
||||
event: "#{event}_error",
|
||||
data: {
|
||||
state: 'no_such_user',
|
||||
},
|
||||
}
|
||||
Sessions.send(@client_id, error)
|
||||
return
|
||||
end
|
||||
if !user.role?(role)
|
||||
error = {
|
||||
event: "#{event}_error",
|
||||
data: {
|
||||
state: 'no_permission',
|
||||
},
|
||||
}
|
||||
Sessions.send(@client_id, error)
|
||||
return
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def log(level, data, client_id = nil)
|
||||
if !@options[:v]
|
||||
return if level == 'debug'
|
||||
|
|
|
@ -4,7 +4,7 @@ class Sessions::Event::ChatAgentState < Sessions::Event::ChatBase
|
|||
return super if super
|
||||
|
||||
# check if user has permissions
|
||||
return if !agent_permission_check
|
||||
return if !role_permission_check('Agent', 'chat')
|
||||
|
||||
Chat::Agent.state(@session['id'], @payload['data']['active'])
|
||||
|
||||
|
|
|
@ -23,51 +23,6 @@ class Sessions::Event::ChatBase < Sessions::Event::Base
|
|||
}
|
||||
end
|
||||
|
||||
def agent_permission_check
|
||||
if !@session
|
||||
error = {
|
||||
event: 'chat_error',
|
||||
data: {
|
||||
state: 'no_session',
|
||||
},
|
||||
}
|
||||
Sessions.send(@client_id, error)
|
||||
return
|
||||
end
|
||||
if !@session['id']
|
||||
error = {
|
||||
event: 'chat_error',
|
||||
data: {
|
||||
state: 'no_session_user_id',
|
||||
},
|
||||
}
|
||||
Sessions.send(@client_id, error)
|
||||
return
|
||||
end
|
||||
user = User.lookup(id: @session['id'])
|
||||
if !user
|
||||
error = {
|
||||
event: 'chat_error',
|
||||
data: {
|
||||
state: 'no_such_user',
|
||||
},
|
||||
}
|
||||
Sessions.send(@client_id, error)
|
||||
return
|
||||
end
|
||||
if !user.role?('Agent')
|
||||
error = {
|
||||
event: 'chat_error',
|
||||
data: {
|
||||
state: 'no_permission',
|
||||
},
|
||||
}
|
||||
Sessions.send(@client_id, error)
|
||||
return
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def current_chat_session
|
||||
Chat::Session.find_by(session_id: @payload['data']['session_id'])
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ class Sessions::Event::ChatSessionStart < Sessions::Event::ChatBase
|
|||
|
||||
def run
|
||||
return super if super
|
||||
agent_permission_check
|
||||
return if !role_permission_check('Agent', 'chat')
|
||||
|
||||
# find first in waiting list
|
||||
chat_session = Chat::Session.where(state: 'waiting').order('created_at ASC').first
|
||||
|
|
|
@ -4,7 +4,7 @@ class Sessions::Event::ChatStatusAgent < Sessions::Event::ChatBase
|
|||
return super if super
|
||||
|
||||
# check if user has permissions
|
||||
return if !agent_permission_check
|
||||
return if !role_permission_check('Agent', 'chat')
|
||||
|
||||
# renew timestamps
|
||||
state = Chat::Agent.state(@session['id'])
|
||||
|
|
22
lib/sessions/event/maintenance.rb
Normal file
22
lib/sessions/event/maintenance.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
class Sessions::Event::Maintenance < Sessions::Event::Base
|
||||
|
||||
def initialize(params)
|
||||
super(params)
|
||||
return if !@is_web_socket
|
||||
ActiveRecord::Base.establish_connection
|
||||
end
|
||||
|
||||
def destroy
|
||||
return if !@is_web_socket
|
||||
ActiveRecord::Base.remove_connection
|
||||
end
|
||||
|
||||
def run
|
||||
|
||||
# check if sender is admin
|
||||
return if !role_permission_check('Admin', 'maintenance')
|
||||
Sessions.broadcast(@payload, 'public', @session['id'])
|
||||
false
|
||||
end
|
||||
|
||||
end
|
|
@ -33,8 +33,7 @@ if [ "$LEVEL" == '1' ]; then
|
|||
rm test/browser/first_steps_test.rb
|
||||
# test/browser/form_test.rb
|
||||
rm test/browser/keyboard_shortcuts_test.rb
|
||||
# test/browser/maintenance_app_version_test.rb
|
||||
# test/browser/maintenance_message_test.rb
|
||||
# test/browser/maintenance_test.rb
|
||||
rm test/browser/prefereces_test.rb
|
||||
rm test/browser/setting_test.rb
|
||||
# test/browser/signup_password_change_and_reset_test.rb
|
||||
|
@ -76,7 +75,7 @@ elif [ "$LEVEL" == '2' ]; then
|
|||
rm test/browser/first_steps_test.rb
|
||||
rm test/browser/form_test.rb
|
||||
rm test/browser/keyboard_shortcuts_test.rb
|
||||
rm test/browser/maintenance_*.rb
|
||||
rm test/browser/maintenance_test.rb
|
||||
rm test/browser/manage_test.rb
|
||||
rm test/browser/prefereces_test.rb
|
||||
rm test/browser/setting_test.rb
|
||||
|
@ -119,7 +118,7 @@ elif [ "$LEVEL" == '3' ]; then
|
|||
rm test/browser/first_steps_test.rb
|
||||
rm test/browser/form_test.rb
|
||||
rm test/browser/keyboard_shortcuts_test.rb
|
||||
rm test/browser/maintenance_*.rb
|
||||
rm test/browser/maintenance_test.rb
|
||||
rm test/browser/manage_test.rb
|
||||
rm test/browser/prefereces_test.rb
|
||||
rm test/browser/setting_test.rb
|
||||
|
@ -162,7 +161,7 @@ elif [ "$LEVEL" == '4' ]; then
|
|||
rm test/browser/first_steps_test.rb
|
||||
rm test/browser/form_test.rb
|
||||
rm test/browser/keyboard_shortcuts_test.rb
|
||||
rm test/browser/maintenance_*.rb
|
||||
rm test/browser/maintenance_test.rb
|
||||
rm test/browser/manage_test.rb
|
||||
rm test/browser/prefereces_test.rb
|
||||
rm test/browser/setting_test.rb
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
# encoding: utf-8
|
||||
require 'browser_test_helper'
|
||||
|
||||
class MaintenanceAppVersionTest < TestCase
|
||||
def test_app_version
|
||||
@browser = browser_instance
|
||||
login(
|
||||
username: 'master@example.com',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
)
|
||||
|
||||
sleep 10
|
||||
|
||||
execute(
|
||||
js: 'App.Event.trigger("app_version", {app_version:"1234:false"})',
|
||||
)
|
||||
sleep 10
|
||||
|
||||
match_not(
|
||||
css: 'body',
|
||||
value: 'new version',
|
||||
)
|
||||
|
||||
execute(
|
||||
js: 'App.Event.trigger("app_version", {app_version:"1235:true"})',
|
||||
)
|
||||
sleep 5
|
||||
|
||||
match(
|
||||
css: 'body',
|
||||
value: 'new version',
|
||||
)
|
||||
|
||||
end
|
||||
end
|
|
@ -1,174 +0,0 @@
|
|||
# encoding: utf-8
|
||||
require 'browser_test_helper'
|
||||
|
||||
class MaintenanceMessageTest < TestCase
|
||||
def test_websocket
|
||||
string = rand(99_999_999_999_999_999).to_s
|
||||
title_html = "test <b>#{string}</b>"
|
||||
title_text = "test <b>#{string}<\/b>"
|
||||
message_html = "message <b>1äöüß</b> #{string}\n\n\nhttp://zammad.org"
|
||||
message_text = "message <b>1äöüß<\/b> #{string}\n\nhttp:\/\/zammad.org"
|
||||
|
||||
# check #1
|
||||
browser1 = browser_instance
|
||||
login(
|
||||
browser: browser1,
|
||||
username: 'master@example.com',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
)
|
||||
|
||||
browser2 = browser_instance
|
||||
login(
|
||||
browser: browser2,
|
||||
username: 'agent1@example.com',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#manage"]',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#system/maintenance"]',
|
||||
)
|
||||
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content input[name="head"]',
|
||||
value: title_html,
|
||||
)
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content textarea[name="message"]',
|
||||
value: message_html,
|
||||
)
|
||||
|
||||
click(
|
||||
browser: browser1,
|
||||
css: '#content button[type="submit"]',
|
||||
)
|
||||
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: title_text,
|
||||
)
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: message_text,
|
||||
)
|
||||
|
||||
match_not(
|
||||
browser: browser1,
|
||||
css: 'body',
|
||||
value: message_text,
|
||||
)
|
||||
|
||||
click(
|
||||
browser: browser2,
|
||||
css: 'div.modal-header .js-close',
|
||||
)
|
||||
|
||||
# check #2
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#manage"]',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#system/maintenance"]',
|
||||
)
|
||||
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content input[name="head"]',
|
||||
value: title_html + ' #2',
|
||||
)
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content textarea[name="message"]',
|
||||
value: message_html + ' #2',
|
||||
)
|
||||
|
||||
click(
|
||||
browser: browser1,
|
||||
css: '#content button[type="submit"]',
|
||||
)
|
||||
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: title_text + ' #2',
|
||||
)
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: message_text + ' #2',
|
||||
)
|
||||
|
||||
match_not(
|
||||
browser: browser1,
|
||||
css: 'body',
|
||||
value: message_text,
|
||||
)
|
||||
|
||||
click(
|
||||
browser: browser2,
|
||||
css: 'div.modal-header .js-close',
|
||||
)
|
||||
|
||||
# check #3
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#manage"]',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#system/maintenance"]',
|
||||
)
|
||||
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content input[name="head"]',
|
||||
value: title_html + ' #3',
|
||||
)
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content textarea[name="message"]',
|
||||
value: message_html + ' #3',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: '#content input[name="reload"] + .icon-checkbox.icon-unchecked',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: '#content button[type="submit"]',
|
||||
)
|
||||
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: title_text + ' #3',
|
||||
)
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: message_text + ' #3',
|
||||
)
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: 'Continue session',
|
||||
)
|
||||
|
||||
match_not(
|
||||
browser: browser1,
|
||||
css: 'body',
|
||||
value: message_text,
|
||||
)
|
||||
end
|
||||
end
|
413
test/browser/maintenance_test.rb
Normal file
413
test/browser/maintenance_test.rb
Normal file
|
@ -0,0 +1,413 @@
|
|||
# encoding: utf-8
|
||||
require 'browser_test_helper'
|
||||
|
||||
class MaintenanceTest < TestCase
|
||||
def test_message
|
||||
string = rand(99_999_999_999_999_999).to_s
|
||||
title_html = "test <b>#{string}</b>"
|
||||
title_text = "test <b>#{string}<\/b>"
|
||||
message_html = "message <b>1äöüß</b> #{string}\n\n\nhttp://zammad.org"
|
||||
message_text = "message <b>1äöüß</b> #{string}\n\n\nhttp://zammad.org"
|
||||
|
||||
# check #1
|
||||
browser1 = browser_instance
|
||||
login(
|
||||
browser: browser1,
|
||||
username: 'master@example.com',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
)
|
||||
|
||||
browser2 = browser_instance
|
||||
login(
|
||||
browser: browser2,
|
||||
username: 'agent1@example.com',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#manage"]',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#system/maintenance"]',
|
||||
)
|
||||
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content .js-Message input[name="head"]',
|
||||
value: title_html,
|
||||
)
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content .js-Message .js-textarea[data-name="message"]',
|
||||
value: message_html,
|
||||
)
|
||||
|
||||
click(
|
||||
browser: browser1,
|
||||
css: '#content .js-Message button.js-submit',
|
||||
)
|
||||
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: title_text,
|
||||
)
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: message_text,
|
||||
)
|
||||
|
||||
match_not(
|
||||
browser: browser1,
|
||||
css: 'body',
|
||||
value: message_text,
|
||||
)
|
||||
|
||||
click(
|
||||
browser: browser2,
|
||||
css: 'div.modal-header .js-close',
|
||||
)
|
||||
|
||||
# check #2
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#manage"]',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#system/maintenance"]',
|
||||
)
|
||||
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content .js-Message input[name="head"]',
|
||||
value: title_html + ' #2',
|
||||
)
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content .js-Message .js-textarea[data-name="message"]',
|
||||
value: message_html + ' #2',
|
||||
)
|
||||
|
||||
click(
|
||||
browser: browser1,
|
||||
css: '#content .js-Message button.js-submit',
|
||||
)
|
||||
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: title_text + ' #2',
|
||||
)
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: message_text + ' #2',
|
||||
)
|
||||
|
||||
match_not(
|
||||
browser: browser1,
|
||||
css: 'body',
|
||||
value: message_text,
|
||||
)
|
||||
|
||||
click(
|
||||
browser: browser2,
|
||||
css: 'div.modal-header .js-close',
|
||||
)
|
||||
|
||||
# check #3
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#manage"]',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#system/maintenance"]',
|
||||
)
|
||||
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content .js-Message input[name="head"]',
|
||||
value: title_html + ' #3',
|
||||
)
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content .js-Message .js-textarea[data-name="message"]',
|
||||
value: message_html + ' #3',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: '#content .js-Message input[name="reload"] + .icon-checkbox.icon-unchecked',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: '#content .js-Message button.js-submit',
|
||||
)
|
||||
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: title_text + ' #3',
|
||||
)
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: message_text + ' #3',
|
||||
)
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.modal',
|
||||
value: 'Continue session',
|
||||
)
|
||||
|
||||
match_not(
|
||||
browser: browser1,
|
||||
css: 'body',
|
||||
value: message_text,
|
||||
)
|
||||
end
|
||||
|
||||
def test_login_message
|
||||
browser1 = browser_instance
|
||||
login(
|
||||
browser: browser1,
|
||||
username: 'master@example.com',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
)
|
||||
|
||||
browser2 = browser_instance
|
||||
location(
|
||||
browser: browser2,
|
||||
url: browser_url,
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#manage"]',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#system/maintenance"]',
|
||||
)
|
||||
|
||||
exists_not(
|
||||
browser: browser2,
|
||||
css: '.js-maintenanceLogin',
|
||||
)
|
||||
|
||||
string = rand(99_999_999_999_999_999).to_s
|
||||
message = "test <b>#{string}</b>"
|
||||
set(
|
||||
browser: browser1,
|
||||
css: '#content .js-loginPreview [data-name="message"]',
|
||||
value: message,
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: '#global-search',
|
||||
)
|
||||
|
||||
sleep 3
|
||||
switch(
|
||||
browser: browser1,
|
||||
css: '#content .js-loginSetting',
|
||||
type: 'on',
|
||||
)
|
||||
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.js-maintenanceLogin',
|
||||
value: message
|
||||
)
|
||||
|
||||
switch(
|
||||
browser: browser1,
|
||||
css: '#content .js-loginSetting',
|
||||
type: 'off',
|
||||
)
|
||||
|
||||
watch_for_disappear(
|
||||
browser: browser2,
|
||||
css: '.js-maintenanceLogin',
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
def test_mode
|
||||
browser1 = browser_instance
|
||||
login(
|
||||
browser: browser1,
|
||||
username: 'master@example.com',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
)
|
||||
|
||||
browser2 = browser_instance
|
||||
location(
|
||||
browser: browser2,
|
||||
url: browser_url,
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#manage"]',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#system/maintenance"]',
|
||||
)
|
||||
|
||||
exists_not(
|
||||
browser: browser2,
|
||||
css: '.js-maintenanceMode',
|
||||
)
|
||||
|
||||
switch(
|
||||
browser: browser1,
|
||||
css: '#content .js-modeSetting',
|
||||
type: 'on',
|
||||
)
|
||||
|
||||
# check warning
|
||||
alert = browser1.switch_to.alert
|
||||
#alert.dismiss()
|
||||
alert.accept()
|
||||
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.js-maintenanceMode',
|
||||
)
|
||||
|
||||
# try to logon with normal agent, should not work
|
||||
login(
|
||||
browser: browser2,
|
||||
username: 'agent1@example.com',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
success: false,
|
||||
)
|
||||
login(
|
||||
browser: browser2,
|
||||
username: 'nicole.braun@zammad.org',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
success: false,
|
||||
)
|
||||
|
||||
# logout with admin and logon again
|
||||
logout(
|
||||
browser: browser1,
|
||||
)
|
||||
sleep 4
|
||||
login(
|
||||
browser: browser1,
|
||||
username: 'master@example.com',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#manage"]',
|
||||
)
|
||||
click(
|
||||
browser: browser1,
|
||||
css: 'a[href="#system/maintenance"]',
|
||||
)
|
||||
|
||||
switch(
|
||||
browser: browser1,
|
||||
css: '#content .js-modeSetting',
|
||||
type: 'off',
|
||||
)
|
||||
|
||||
watch_for_disappear(
|
||||
browser: browser2,
|
||||
css: '.js-maintenanceMode',
|
||||
)
|
||||
|
||||
# try to logon with normal agent, should work again
|
||||
login(
|
||||
browser: browser2,
|
||||
username: 'agent1@example.com',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
)
|
||||
logout(
|
||||
browser: browser2,
|
||||
)
|
||||
sleep 4
|
||||
login(
|
||||
browser: browser2,
|
||||
username: 'nicole.braun@zammad.org',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
)
|
||||
|
||||
switch(
|
||||
browser: browser1,
|
||||
css: '#content .js-modeSetting',
|
||||
type: 'on',
|
||||
)
|
||||
# check warning
|
||||
alert = browser1.switch_to.alert
|
||||
#alert.dismiss()
|
||||
alert.accept()
|
||||
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '#login',
|
||||
)
|
||||
watch_for(
|
||||
browser: browser2,
|
||||
css: '.js-maintenanceMode',
|
||||
)
|
||||
|
||||
switch(
|
||||
browser: browser1,
|
||||
css: '#content .js-modeSetting',
|
||||
type: 'off',
|
||||
)
|
||||
|
||||
watch_for_disappear(
|
||||
browser: browser2,
|
||||
css: '.js-maintenanceMode',
|
||||
)
|
||||
end
|
||||
|
||||
def test_app_version
|
||||
@browser = browser_instance
|
||||
login(
|
||||
username: 'master@example.com',
|
||||
password: 'test',
|
||||
url: browser_url,
|
||||
)
|
||||
|
||||
sleep 8
|
||||
|
||||
execute(
|
||||
js: 'App.Event.trigger("maintenance", {type:"app_version", app_version:"1234:false"} )',
|
||||
)
|
||||
sleep 8
|
||||
|
||||
match_not(
|
||||
css: 'body',
|
||||
value: 'new version',
|
||||
)
|
||||
|
||||
execute(
|
||||
js: 'App.Event.trigger("maintenance", {type:"app_version", app_version:"1235:true"}) ',
|
||||
)
|
||||
sleep 5
|
||||
|
||||
match(
|
||||
css: 'body',
|
||||
value: 'new version',
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -133,6 +133,7 @@ class TestCase < Test::Unit::TestCase
|
|||
url: 'some url', # optional
|
||||
remember_me: true, # optional
|
||||
auto_wizard: false, # optional, in case of auto wizard, skip login
|
||||
success: false, #optional
|
||||
)
|
||||
|
||||
=end
|
||||
|
@ -194,12 +195,28 @@ class TestCase < Test::Unit::TestCase
|
|||
instance.find_elements(css: '#login button')[0].click
|
||||
|
||||
sleep 4
|
||||
login_failed = false
|
||||
if !instance.find_elements(css: '.user-menu .user a')[0]
|
||||
login_failed = true
|
||||
else
|
||||
login = instance.find_elements(css: '.user-menu .user a')[0].attribute('title')
|
||||
if login != params[:username]
|
||||
login_failed = true
|
||||
end
|
||||
end
|
||||
if login_failed
|
||||
if params[:success] == false
|
||||
assert(true, 'login not successfull, like wanted')
|
||||
return true
|
||||
end
|
||||
screenshot(browser: instance, comment: 'login_failed')
|
||||
raise 'login failed'
|
||||
end
|
||||
|
||||
if params[:success] == false
|
||||
raise 'login successfull but should not'
|
||||
end
|
||||
|
||||
clues_close(
|
||||
browser: instance,
|
||||
optional: true,
|
||||
|
@ -2693,6 +2710,78 @@ wait untill text in selector disabppears
|
|||
raise 'group creation failed'
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
object_manager_attribute_create(
|
||||
browser: browser2,
|
||||
data: {
|
||||
name: 'field_name' + random,
|
||||
display: 'Display Name of Field',
|
||||
},
|
||||
error: 'already exists'
|
||||
)
|
||||
|
||||
=end
|
||||
|
||||
def object_manager_attribute_create(params = {})
|
||||
switch_window_focus(params)
|
||||
log('object_manager_attribute_create', params)
|
||||
|
||||
instance = params[:browser] || @browser
|
||||
data = params[:data]
|
||||
|
||||
click(
|
||||
browser: instance,
|
||||
css: 'a[href="#manage"]',
|
||||
mute_log: true,
|
||||
)
|
||||
click(
|
||||
browser: instance,
|
||||
css: 'a[href="#system/object_manager"]',
|
||||
mute_log: true,
|
||||
)
|
||||
sleep 4
|
||||
click(
|
||||
browser: instance,
|
||||
css: '#content .js-new',
|
||||
mute_log: true,
|
||||
)
|
||||
modal_ready
|
||||
element = instance.find_elements(css: '.modal input[name=name]')[0]
|
||||
element.clear
|
||||
element.send_keys(data[:name])
|
||||
element = instance.find_elements(css: '.modal input[name=display]')[0]
|
||||
element.clear
|
||||
element.send_keys(data[:display])
|
||||
instance.find_elements(css: '.modal button.js-submit')[0].click
|
||||
if params[:error]
|
||||
sleep 4
|
||||
watch_for(
|
||||
css: '.modal',
|
||||
value: params[:error],
|
||||
)
|
||||
click(
|
||||
browser: instance,
|
||||
css: '.modal .js-close',
|
||||
mute_log: true,
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
(1..12).each {
|
||||
element = instance.find_elements(css: 'body')[0]
|
||||
text = element.text
|
||||
if text =~ /#{Regexp.quote(data[:name])}/
|
||||
assert(true, 'object manager attribute created')
|
||||
sleep 1
|
||||
return true
|
||||
end
|
||||
sleep 1
|
||||
}
|
||||
screenshot(browser: instance, comment: 'object_manager_attribute_create_failed')
|
||||
raise 'object manager attribute creation failed'
|
||||
end
|
||||
|
||||
def quote(string)
|
||||
string_quoted = string
|
||||
string_quoted.gsub!(/&/, '&')
|
||||
|
|
Loading…
Reference in a new issue