fix layout Add generic way of handling input change events Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>tags/v11.0RC2
@@ -8,8 +8,6 @@ input#openid, input#webdav { width:20em; } | |||
/* PERSONAL */ | |||
#avatar { | |||
display: inline-block; | |||
float: left; | |||
width: 160px; | |||
padding-right: 0; | |||
} | |||
@@ -63,6 +61,46 @@ input#openid, input#webdav { width:20em; } | |||
float: right; | |||
} | |||
#personal-settings-avatar-container { | |||
float: left; | |||
} | |||
#personal-settings-container { | |||
position: relative; | |||
float: left; | |||
min-width: 280px; | |||
width: calc(100% - 200px); | |||
} | |||
#personal-settings-container:after { | |||
clear: both; | |||
} | |||
#personal-settings-container > div { | |||
float: left; | |||
height: 100px; | |||
min-width: 300px; | |||
} | |||
#personal-settings-container > div h2 span[class^="icon-"] { | |||
display: inline-block; | |||
margin-left: 5px; | |||
background-size: 110%; | |||
opacity: 0.3; | |||
cursor: pointer; | |||
} | |||
.personal-settings-setting-box input[type="text"], | |||
.personal-settings-setting-box input[type="email"], | |||
.personal-settings-setting-box input[type="tel"] { | |||
width: 17em; | |||
} | |||
.federationScopeMenu { | |||
top: 66px; | |||
} | |||
.federationScopeMenu.bubble::after { | |||
left: 45px; | |||
} | |||
.federationScopeMenu.popovermenu { | |||
font-weight: 100; | |||
font-size: medium; | |||
} | |||
#displaynameform, | |||
#lostpassword, | |||
#groups, | |||
@@ -104,10 +142,6 @@ input#openid, input#webdav { width:20em; } | |||
input#identity { | |||
width: 20em; | |||
} | |||
#displayName, | |||
#email { | |||
width: 17em; | |||
} | |||
#showWizard { | |||
display: inline-block; |
@@ -0,0 +1,114 @@ | |||
/* | |||
* Copyright (c) 2016 | |||
* | |||
* This file is licensed under the Affero General Public License version 3 | |||
* or later. | |||
* | |||
* See the COPYING-README file. | |||
* | |||
*/ | |||
/* global OC, Handlebars */ | |||
(function() { | |||
var TEMPLATE_MENU = | |||
'<ul>' + | |||
'{{#each items}}' + | |||
'<li>' + | |||
'<a href="#" class="menuitem action action-{{name}} permanent" data-action="{{name}}">' + | |||
'{{#if icon}}<img class="icon" src="{{icon}}"/>' + | |||
'{{else}}'+ | |||
'{{#if iconClass}}' + | |||
'<span class="icon {{iconClass}}"></span>' + | |||
'{{else}}' + | |||
'<span class="no-icon"></span>' + | |||
'{{/if}}' + | |||
'{{/if}}' + | |||
'<span>{{displayName}}</span></a>' + | |||
'</li>' + | |||
'{{/each}}' + | |||
'</ul>'; | |||
/** | |||
* Construct a new FederationScopeMenu instance | |||
* @constructs FederationScopeMenu | |||
* @memberof OC.Settings | |||
*/ | |||
var FederationScopeMenu = OC.Backbone.View.extend({ | |||
tagName: 'div', | |||
className: 'federationScopeMenu popovermenu bubble hidden open menu', | |||
_scopes: [ | |||
{ | |||
name: 'private', | |||
displayName: t('core', 'Private'), | |||
icon: OC.imagePath('core', 'actions/password') | |||
}, | |||
{ | |||
name: 'contacts', | |||
displayName: t('core', 'Contacts'), | |||
icon: OC.imagePath('core', 'places/contacts-dark') | |||
}, | |||
{ | |||
name: 'public', | |||
displayName: t('core', 'Public'), | |||
icon: OC.imagePath('core', 'places/link') | |||
} | |||
], | |||
/** | |||
* Current context | |||
* | |||
* @type OCA.Files.FileActionContext | |||
*/ | |||
_context: null, | |||
events: { | |||
'click a.action': '_onClickAction' | |||
}, | |||
template: Handlebars.compile(TEMPLATE_MENU), | |||
/** | |||
* Event handler whenever an action has been clicked within the menu | |||
* | |||
* @param {Object} event event object | |||
*/ | |||
_onClickAction: function(event) { | |||
var $target = $(event.currentTarget); | |||
if (!$target.hasClass('menuitem')) { | |||
$target = $target.closest('.menuitem'); | |||
} | |||
this.trigger('select:scope', $target.data('action')); | |||
OC.hideMenus(); | |||
}, | |||
/** | |||
* Renders the menu with the currently set items | |||
*/ | |||
render: function() { | |||
this.$el.html(this.template({ | |||
items: this._scopes | |||
})); | |||
}, | |||
/** | |||
* Displays the menu | |||
*/ | |||
show: function(context) { | |||
this._context = context; | |||
this.render(); | |||
this.$el.removeClass('hidden'); | |||
OC.showMenu(null, this.$el); | |||
} | |||
}); | |||
OC.Settings = OC.Settings || {}; | |||
OC.Settings.FederationScopeMenu = FederationScopeMenu; | |||
})(); | |||
@@ -0,0 +1,116 @@ | |||
/* global OC */ | |||
/** | |||
* Copyright (c) 2016, Christoph Wurst <christoph@owncloud.com> | |||
* | |||
* This file is licensed under the Affero General Public License version 3 or later. | |||
* See the COPYING-README file. | |||
*/ | |||
(function() { | |||
'use strict'; | |||
var FederationSettingsView = OC.Backbone.View.extend({ | |||
_inputFields: undefined, | |||
/** @var Backbone.Model */ | |||
_config: undefined, | |||
initialize: function(options) { | |||
options = options || {}; | |||
if (options.config) { | |||
this._config = options.config; | |||
} else { | |||
this._config = new OC.Backbone.Model() | |||
} | |||
this._inputFields = [ | |||
'displayname', | |||
'phone', | |||
'email', | |||
'website', | |||
'address' | |||
]; | |||
this._registerEvents(); | |||
}, | |||
render: function() { | |||
var self = this; | |||
_.each(this._inputFields, function(field) { | |||
var $heading = self.$('#' + field + 'form > h2'); | |||
var $icon = self.$('#' + field + 'form > h2 > span'); | |||
var scopeMenu = new OC.Settings.FederationScopeMenu(); | |||
self.listenTo(scopeMenu, 'select:scope', function(scope) { | |||
self._onScopeChanged(field, scope); | |||
}); | |||
$heading.append(scopeMenu.$el); | |||
$icon.on('click', _.bind(scopeMenu.show, scopeMenu)); | |||
// Fix absolute position according to the heading text length | |||
// TODO: fix position without magic numbers | |||
var pos = ($heading.width() - $heading.find('label').width()) - 68; | |||
scopeMenu.$el.css('right', pos); | |||
}); | |||
}, | |||
_registerEvents: function() { | |||
var self = this; | |||
_.each(this._inputFields, function(field) { | |||
self.$('#' + field).keyUpDelayedOrEnter(_.bind(self._onInputChanged, self)); | |||
}); | |||
}, | |||
_onInputChanged: function(e) { | |||
OC.msg.startSaving('#personal-settings-container .msg'); | |||
var $target = $(e.target); | |||
var value = $target.val(); | |||
var field = $target.attr('id'); | |||
console.log(field + ' changed to ' + value); | |||
this._config.set(field, value); | |||
console.log(this._config.toJSON()); | |||
// TODO: this._config.save(); | |||
// TODO: OC.msg.finishedSaving('#personal-settings-container .msg', result); | |||
// TODO: call _updateDisplayName after successful update | |||
}, | |||
_updateDisplayName: function(displayName) { | |||
// update displayName on the top right expand button | |||
$('#expandDisplayName').text($('#displayName').val()); | |||
// update avatar if avatar is available | |||
if(!$('#removeavatar').hasClass('hidden')) { | |||
updateAvatar(); | |||
} | |||
}, | |||
_onScopeChanged: function(field, scope) { | |||
// TODO: save changes to the server | |||
console.log(field + ' changed to ' + scope); | |||
this._setFieldScopeIcon(field, scope); | |||
}, | |||
_setFieldScopeIcon: function(field, scope) { | |||
var $icon = this.$('#' + field + 'form > h2 > span'); | |||
$icon.removeClass('icon-password'); | |||
$icon.removeClass('icon-contacts-dark'); | |||
$icon.removeClass('icon-link'); | |||
switch (scope) { | |||
case 'private': | |||
$icon.addClass('icon-password'); | |||
break; | |||
case 'contacts': | |||
$icon.addClass('icon-contacts-dark'); | |||
break; | |||
case 'public': | |||
$icon.addClass('icon-link'); | |||
break; | |||
} | |||
} | |||
}); | |||
OC.Settings = OC.Settings || {}; | |||
OC.Settings.FederationSettingsView = FederationSettingsView; | |||
})(); |
@@ -1,10 +1,15 @@ | |||
/* global OC */ | |||
/** | |||
* Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com> | |||
* 2013, Morris Jobke <morris.jobke@gmail.com> | |||
* 2016, Christoph Wurst <christoph@owncloud.com> | |||
* This file is licensed under the Affero General Public License version 3 or later. | |||
* See the COPYING-README file. | |||
*/ | |||
OC.Settings = OC.Settings || {}; | |||
/** | |||
* The callback will be fired as soon as enter is pressed by the | |||
* user or 1 second after the last data entry | |||
@@ -21,21 +26,21 @@ jQuery.fn.keyUpDelayedOrEnter = function (callback, allowEmptyValue) { | |||
return; | |||
} | |||
if (allowEmptyValue || that.val() !== '') { | |||
cb(); | |||
cb(event); | |||
} | |||
}, 1000)); | |||
this.keypress(function (event) { | |||
if (event.keyCode === 13 && (allowEmptyValue || that.val() !== '')) { | |||
event.preventDefault(); | |||
cb(); | |||
cb(event); | |||
} | |||
}); | |||
this.bind('paste', null, function (e) { | |||
if(!e.keyCode){ | |||
this.bind('paste', null, function (event) { | |||
if(!event.keyCode){ | |||
if (allowEmptyValue || that.val() !== '') { | |||
cb(); | |||
cb(event); | |||
} | |||
} | |||
}); | |||
@@ -265,8 +270,10 @@ $(document).ready(function () { | |||
} | |||
}); | |||
$('#displayName').keyUpDelayedOrEnter(changeDisplayName); | |||
$('#email').keyUpDelayedOrEnter(changeEmailAddress, true); | |||
var federationSettingsView = new OC.Settings.FederationSettingsView({ | |||
el: '#personal-settings-container' | |||
}); | |||
federationSettingsView.render(); | |||
$("#languageinput").change(function () { | |||
// Serialize the data | |||
@@ -452,3 +459,5 @@ OC.Encryption.msg = { | |||
} | |||
} | |||
}; | |||
OC.Settings.updateAvatar = updateAvatar; |
@@ -47,7 +47,9 @@ $urlGenerator = \OC::$server->getURLGenerator(); | |||
OC_Util::addScript('settings', 'authtoken'); | |||
OC_Util::addScript('settings', 'authtoken_collection'); | |||
OC_Util::addScript('settings', 'authtoken_view'); | |||
OC_Util::addScript( 'settings', 'personal' ); | |||
OC_Util::addScript('settings', 'federationsettingsview'); | |||
OC_Util::addScript('settings', 'federationscopemenu'); | |||
OC_Util::addScript('settings', 'personal'); | |||
OC_Util::addScript('settings', 'certificates'); | |||
OC_Util::addStyle( 'settings', 'settings' ); | |||
\OC_Util::addVendorScript('strengthify/jquery.strengthify'); |
@@ -33,44 +33,94 @@ | |||
</div> | |||
<?php if ($_['enableAvatars']): ?> | |||
<form id="avatar" class="section" method="post" action="<?php p(\OC::$server->getURLGenerator()->linkToRoute('core.avatar.postAvatar')); ?>"> | |||
<h2><?php p($l->t('Profile picture')); ?></h2> | |||
<div id="displayavatar"> | |||
<div class="avatardiv"></div> | |||
<div class="warning hidden"></div> | |||
<?php if ($_['avatarChangeSupported']): ?> | |||
<label for="uploadavatar" class="inlineblock button icon-upload" id="uploadavatarbutton" title="<?php p($l->t('Upload new')); ?>"></label> | |||
<div class="inlineblock button icon-folder" id="selectavatar" title="<?php p($l->t('Select from Files')); ?>"></div> | |||
<div class="hidden button icon-delete" id="removeavatar" title="<?php p($l->t('Remove image')); ?>"></div> | |||
<input type="file" name="files[]" id="uploadavatar" class="hiddenuploadfield"> | |||
<p><em><?php p($l->t('png or jpg, max. 20 MB')); ?></em></p> | |||
<?php else: ?> | |||
<?php p($l->t('Picture provided by original account')); ?> | |||
<?php endif; ?> | |||
</div> | |||
<div id="personal-settings-avatar-container"> | |||
<form id="avatar" class="section" method="post" action="<?php p(\OC::$server->getURLGenerator()->linkToRoute('core.avatar.postAvatar')); ?>"> | |||
<h2><?php p($l->t('Profile picture')); ?></h2> | |||
<div id="displayavatar"> | |||
<div class="avatardiv"></div> | |||
<div class="warning hidden"></div> | |||
<?php if ($_['avatarChangeSupported']): ?> | |||
<label for="uploadavatar" class="inlineblock button icon-upload svg" id="uploadavatarbutton" title="<?php p($l->t('Upload new')); ?>"></label> | |||
<div class="inlineblock button icon-folder svg" id="selectavatar" title="<?php p($l->t('Select from Files')); ?>"></div> | |||
<div class="hidden button icon-delete svg" id="removeavatar" title="<?php p($l->t('Remove image')); ?>"></div> | |||
<input type="file" name="files[]" id="uploadavatar" class="hiddenuploadfield"> | |||
<p><em><?php p($l->t('png or jpg, max. 20 MB')); ?></em></p> | |||
<?php else: ?> | |||
<?php p($l->t('Picture provided by original account')); ?> | |||
<?php endif; ?> | |||
</div> | |||
<div id="cropper" class="hidden"> | |||
<div class="inner-container"> | |||
<div id="cropper" class="hidden"> | |||
<div class="inlineblock button" id="abortcropperbutton"><?php p($l->t('Cancel')); ?></div> | |||
<div class="inlineblock button primary" id="sendcropperbutton"><?php p($l->t('Choose as profile picture')); ?></div> | |||
</div> | |||
</div> | |||
</form> | |||
</form> | |||
</div> | |||
<?php endif; ?> | |||
<?php | |||
if($_['displayNameChangeSupported']) { | |||
?> | |||
<form id="displaynameform" class="section"> | |||
<h2> | |||
<label for="displayName"><?php echo $l->t('Full name');?></label> | |||
</h2> | |||
<input type="text" id="displayName" name="displayName" class="password-confirm-required" | |||
value="<?php p($_['displayName'])?>" | |||
autocomplete="on" autocapitalize="off" autocorrect="off" /> | |||
<span class="msg"></span> | |||
<input type="hidden" id="oldDisplayName" name="oldDisplayName" value="<?php p($_['displayName'])?>" /> | |||
</form> | |||
<div id="personal-settings-container"> | |||
<div class="personal-settings-setting-box"> | |||
<form id="displaynameform" class="section"> | |||
<h2> | |||
<label for="displayname"><?php p($l->t('Full name')); ?></label> | |||
<span class="icon-password"/> | |||
</h2> | |||
<input type="text" id="displayname" name="displayname" | |||
value="<?php p($_['displayName']) ?>" | |||
autocomplete="on" autocapitalize="off" autocorrect="off" /> | |||
</form> | |||
</div> | |||
<div class="personal-settings-setting-box"> | |||
<form id="phoneform" class="section"> | |||
<h2> | |||
<label for="phone"><?php p($l->t('Phone name')); ?></label> | |||
<span class="icon-password"/> | |||
</h2> | |||
<input type="tel" id="phone" name="phone" | |||
value="<?php p($_['phone']) ?>" | |||
autocomplete="on" autocapitalize="off" autocorrect="off" /> | |||
</form> | |||
</div> | |||
<div class="personal-settings-setting-box"> | |||
<form id="emailform" class="section"> | |||
<h2> | |||
<label for="email"><?php p($l->t('Email')); ?></label> | |||
<span class="icon-password"/> | |||
</h2> | |||
<input type="email" name="email" id="email" value="<?php p($_['email']); ?>" | |||
placeholder="<?php p($l->t('Your email address')); ?>" | |||
autocomplete="on" autocapitalize="off" autocorrect="off" /> | |||
<br /> | |||
<em><?php p($l->t('For password recovery and notifications')); ?></em> | |||
</form> | |||
</div> | |||
<div class="personal-settings-setting-box"> | |||
<form id="websiteform" class="section"> | |||
<h2> | |||
<label for="website"><?php p($l->t('Website')); ?></label> | |||
<span class="icon-password"/> | |||
</h2> | |||
<input type="text" name="website" id="website" value="<?php p($_['website']); ?>" | |||
placeholder="<?php p($l->t('Your website')); ?>" | |||
autocomplete="on" autocapitalize="off" autocorrect="off" /> | |||
</form> | |||
</div> | |||
<div class="personal-settings-setting-box"> | |||
<form id="addressform" class="section"> | |||
<h2> | |||
<label for="address"><?php echo $l->t('Address'); ?></label> | |||
<span class="icon-password"/> | |||
</h2> | |||
<input type="text" id="address" name="address" | |||
value="<?php p($_['address']) ?>" | |||
autocomplete="on" autocapitalize="off" autocorrect="off" /> | |||
</form> | |||
</div> | |||
<span class="msg"></span> | |||
</div> | |||
<?php | |||
} else { | |||
?> | |||
@@ -99,10 +149,13 @@ if($_['displayNameChangeSupported']) { | |||
<?php | |||
} else { | |||
?> | |||
======= | |||
>>>>>>> Add more personal information fields to the settings page for enhanced federated sharing | |||
<div id="lostpassword" class="section"> | |||
<h2><?php echo $l->t('Email'); ?></h2> | |||
<span><?php if(isset($_['email'][0])) { p($_['email']); } else { p($l->t('No email address set')); }?></span> | |||
</div> | |||
<!-- TODO: show phone/address --> | |||
<?php | |||
} | |||
?> |