Skip to content

Commit

Permalink
XWIKI-15415: Create a compact user picker widget and use it in platform
Browse files Browse the repository at this point in the history
* Update the usage of the user picker in XWikiGroupSheet, MessageSenderMacro, SharePage, AdminWikiDescriptorSheet, CreateWiki and WikiUsers
* Fix AWM.ClassEditSheet to be able to load the user picker styles when a user picker field is added
* Update AWM.LiveTableGenerator (no need to load the user displayer styles since we moved them to the skin)
* Add users.less to hold the styles for the user/group displayer since they are used in losts of places
* Update the user displayer usage in SolrUserFacet
* Use the Bootstrap modal in Share Page dialog (also rewrite the JavaScript code using jQuery)
* Modify the displayUser and displayGroup velocity macros to have a compact display
* Modify the userPicker/groupPicker velocity macros to use the compact user picker
* Add support for JSON output in uorgsuggest.vm
  • Loading branch information
mflorea committed Jul 5, 2018
1 parent 5939c0f commit 8fc372b
Show file tree
Hide file tree
Showing 18 changed files with 580 additions and 519 deletions.
Expand Up @@ -194,17 +194,18 @@
var addMembers = function(event) {
event && event.stop();
var form = event.element().form;
if (form.select('ul.accepted-suggestions li').length == 0 && $('userInput').value.strip() == ''
&& $('groupInput').value.strip() == '') {
var parameters = form.serialize(true);
var names = Object.isArray(parameters.name) ? parameters.name : [parameters.name];
if (names.join('').strip() === '') {
// Nothing to add.
return;
}
new Ajax.Request(form.action, {
parameters: form.serialize(),
parameters: parameters,
onSuccess: function() {
// Clear the list of selected users and groups.
XWiki.widgets.UserPicker.instances.groupInput.clear();
XWiki.widgets.UserPicker.instances.userInput.clear();
jQuery('#userInput')[0].selectize.clear(true);
jQuery('#groupInput')[0].selectize.clear(true);
// Reload the live table.
var end = editgrouptable.limit;
var start = Math.max(editgrouptable.lastOffset, 1);
Expand Down
Expand Up @@ -340,8 +340,10 @@ xcontext.put('propertyCustomDisplayer', new PropertyCustomDisplayer(xcontext))
*#
#macro(displayNewField)
## Output the SkinExtension hooks to allow field displayers to pull JavaScript/CSS resources.
## Output also the LinkExtension hook because $xwiki.linkx.use() is used to load CSS files from WebJars.
## The class editor moves this resource includes in the HTML page head.
{{html}}
<!-- com.xpn.xwiki.plugin.skinx.LinkExtensionPlugin -->

This comment has been minimized.

Copy link
@atallahade

atallahade Jul 5, 2018

Member

I think this should be more generic as the AWM is not the only place where a CSS file is not loaded.
For instance, if you create a page and add the object AppWithinMinutes.Users, you'll end up with a missing CSS file.
I've made a PR to fix this problem, you can see the commit here: 7c8aabf

#skinExtensionHooks
{{/html}}

Expand Down
Expand Up @@ -120,25 +120,13 @@ ${escapetool.h}set (${escapetool.d}columnsProperties = {
#end
#end

#macro(generateColumnExtraSSX)
#set($userPicker = false)
#foreach($column in $columns)
#set($field = $xclass.get($column))
#if($field && ($field.classType == 'Users' || $field.classType == 'Groups') && !$userPicker)
#set($userPicker = true)
${escapetool.h}set (${escapetool.d}discard = ${escapetool.d}xwiki.ssfx.use('uicomponents/widgets/userpicker/userPicker.css'))
#end
#end
#end

#macro(generateLiveTable)
{{{##
{{velocity}}
#set($className = $liveTableObj.getProperty('class').value)
#set($classDoc = $xwiki.getDocument($className))
#set($xclass = $classDoc.getxWikiClass())
#set($shortName = $classDoc.name.replaceAll("Class", "").toLowerCase())
#generateColumnExtraSSX()
#generateColumnProperties()
${escapetool.h}set (${escapetool.d}options = {
'className': '#escapeSingleQuotes($className)',
Expand Down
Expand Up @@ -58,22 +58,6 @@ table {
height: 1em;
}

// Avatars ====================================================================

.avatar_30, .avatar_50 {
border-radius: @thumbnail-border-radius;
}

.avatar_30 {
max-width: 30px;
max-height: 30px;
}

.avatar_50 {
max-width: 50px;
max-height: 50px;
}

// Code =======================================================================

pre { /* Resetting values from bootstrap/code.less and bootstrap/normalize.less, since 'pre' are generated by our syntax in verbatim blocks */
Expand All @@ -94,4 +78,4 @@ pre { /* Resetting values from bootstrap/code.less and bootstrap/normalize.less,

img {
max-width: 100%;
}
}
Expand Up @@ -14,6 +14,7 @@
@import "breadcrumbs";
@import "drawer";
@import "edit";
@import "figure";
@import "forms";
@import "general";
@import "grid";
Expand All @@ -26,9 +27,9 @@
@import "tables";
@import "tree";
@import "type";
@import "users";
@import "vertical-menus";
@import "xlist";
@import "figure";
// In order to let custom skins easily override styling, we import a custom.less that is empty by default.
// Make sure the custom.less import is the last one in the imports list.
@import "custom";
Expand Down
@@ -0,0 +1,48 @@
// Avatars ====================================================================

.avatar_30, .avatar_50 {
border-radius: @thumbnail-border-radius;
}

.avatar_30 {
max-width: 30px;
max-height: 30px;
}

.avatar_50 {
max-width: 50px;
max-height: 50px;
}

// Users and Groups Displayer =================================================

ul.users,
ul.groups {
list-style-type: none;
margin: 0;
padding: 0;
}

li.user,
li.group {
display: inline-block;
margin-right: .6em;
}

.user,
.group {
white-space: nowrap;
}

.user .user-avatar,
.group .group-avatar {
margin-right: .3em;
}

.user img.user-avatar,
.group img.group-avatar {
border-radius: 3px;
max-height: 18px;
max-width: 18px;
vertical-align: text-top;
}
Expand Up @@ -122,75 +122,40 @@
<cache>long</cache>
</property>
<property>
<code>#set ($userSuggestScope = 'local')
#if ($services.wiki.user &amp;&amp; $services.wiki.user.userScope != "LOCAL_ONLY")
#set ($userSuggestScope = 'global')
#end
var XWiki = (function (XWiki) {
<code>var XWiki = (function (XWiki) {
// Start XWiki augmentation.
/**
* Extends the UserPicker in order to display the selected user/group inline after the target text input.
*/
var CustomUserPicker = Class.create(XWiki.widgets.UserPicker, {
// @Override
_createSelectionManager: function($super, options) {
var selectionManager = $super(Object.extend(options, {
listInsertionPosition : 'after',
acceptFreeText : false
}));
// Overwrite the way the selected item is displayed.
selectionManager.displayItem = function(suggestion) {
var targetInfo = new Element('span').update(suggestion.info).insert(this.createDeleteTool());
var targetClass = this.suggest.sources[0].script.indexOf('uorg=group') &gt; 0 ? 'target-group' : 'target-user';
return new Element('li', {'class': targetClass}).insert(targetInfo).insert(this.createItemInput(suggestion));
}.bind(selectionManager);
return selectionManager;
}
});

XWiki.MessageStream = Class.create({
targetsWithName : ['user', 'group'],
suggestParameters : {
user : {
script: '$doc.getURL("get", "xpage=uorgsuggest&amp;uorg=user&amp;wiki=${userSuggestScope}")&amp;',
noresults: "$escapetool.javascript($services.localization.render('core.widgets.userPicker.noResults'))"
},
group : {
script: "$doc.getURL('get', 'xpage=uorgsuggest&amp;uorg=group')&amp;",
noresults: "$escapetool.javascript($services.localization.render('core.widgets.groupPicker.noResults'))"
}
},
initialize : function() {
this.prepareForms();
this.enhanceSelect();
},
prepareTargetInput : function(event, element) {
var targetType = element.options[element.selectedIndex].value;
element.className = targetType;
if (!element.__targetNameInput) {return;}
if (this.targetsWithName.indexOf(targetType) &lt; 0) {
element.__targetNameInput.addClassName('hidden');
if (element.__targetNameInput.__x_suggest) {
element.__targetNameInput.__x_suggest.detach();
}
} else {
// Focus the text input so that the place-holder value is cleared if present. Otherwise the user picker will treat
// the place-holder value as if it was typed by the user and will automatically select the corresponding suggestion.
element.__targetNameInput.removeClassName('hidden').activate();
// Defer the creation of the user picker to allow the focus listeners to be called first (in order to clear the
// place-holder value).
this.suggestParameters[targetType] &amp;&amp; function() {
new CustomUserPicker(element.__targetNameInput, this.suggestParameters[targetType]);
}.bind(this).defer();
var messageStream = element.up('.messagestream');
var targetInputContainer = messageStream.down('.message-target-' + targetType);
if (!targetInputContainer) {
targetInputContainer = messageStream.down('.message-target-default');
}
messageStream.select('.message-target').forEach(function(messageTargetContainer) {
var fields = messageTargetContainer.select('input, select, textarea');
if (messageTargetContainer === targetInputContainer) {
messageTargetContainer.removeClassName('hidden');
fields.forEach(function(field) {
field.enable();
field.hasClassName('selectized') &amp;&amp; field.selectize.enable();
});
} else {
messageTargetContainer.addClassName('hidden');
fields.forEach(function(field) {
field.disable();
field.hasClassName('selectized') &amp;&amp; field.selectize.disable();
});
}
});
},
enhanceSelect: function () {
$$('.messagestream select[name="visibilityLevel"]').each(function(element) {
element.addClassName(element.options[element.selectedIndex].value);
element.__targetNameInput = element.up('.messagestream').down('input[name="targetName"]');
if (element.__targetNameInput &amp;&amp; this.targetsWithName.indexOf(element.options[element.selectedIndex].value) &lt; 0) {
element.__targetNameInput.addClassName('hidden');
}
element.observe('change', this.prepareTargetInput.bindAsEventListener(this, element));
this.prepareTargetInput(null, element);
}.bind(this));
Expand Down Expand Up @@ -369,34 +334,17 @@ return XWiki;
<cache>long</cache>
</property>
<property>
<code>#template('colorThemeInit.vm')

.messagestream-tools {
<code>.messagestream-tools {
display: flex;
position: relative;
margin: .5em 0 1em;
}
.messagestream-tools .accepted-suggestions {
display: inline;
}
.messagestream-tools .accepted-suggestions .target-user,
.messagestream-tools .accepted-suggestions .target-group {
background: none no-repeat scroll left center transparent;
display: inline-block;
margin-right: 1em;
padding: 0 1px 0 18px;
}
.messagestream-tools .accepted-suggestions .target-user {
background-image: url("$xwiki.getSkinFile('icons/silk/user.png')");
}
.messagestream-tools .accepted-suggestions .target-group {
background-image: url("$xwiki.getSkinFile('icons/silk/group.png')");
}
.messagestream select {
width: auto;
}
.messagestream input.targetName {
margin-right: .3em;
width: auto;
.messagestream .message-target {
flex-grow: 1;
margin-left: .3em;
}</code>
</property>
<property>
Expand All @@ -406,7 +354,7 @@ return XWiki;
<name>Message stream form styling</name>
</property>
<property>
<parse>1</parse>
<parse>0</parse>
</property>
<property>
<use>onDemand</use>
Expand Down Expand Up @@ -568,8 +516,6 @@ return XWiki;
##
## Skin Extensions
##--------------------------------------------------------------
#set ($discard = $xwiki.jsfx.use('uicomponents/suggest/suggestPicker.js'))
#set ($discard = $xwiki.jsfx.use('uicomponents/widgets/userpicker/userPicker.js'))
#set ($discard = $xwiki.ssx.use('Main.Activity'))
#set ($discard = $xwiki.ssx.use('Main.MessageSenderMacro'))
#set ($discard = $xwiki.jsx.use('Main.MessageSenderMacro'))
Expand Down Expand Up @@ -694,16 +640,28 @@ return XWiki;
#end
&lt;/select&gt;
&lt;/label&gt;
&lt;label for="targetName$!{targetNameCounter}"&gt;
&lt;span class='hidden'&gt;Name:&lt;/span&gt;
&lt;input type="text" name="targetName" id="targetName$!targetNameCounter" class="targetName withTip useTitleAsTip" value="$!targetName" size="15"
title="$services.localization.render('xe.activity.messages.visibility.targetName.tip')" #if ($inEditMode) disabled="disabled"#end/&gt;
&lt;/label&gt;
#if (!$targetNameCounter)
#set ($targetNameCounter = 1)
#else
#set ($targetNameCounter = $targetNameCounter + 1)
#set ($userPickerParams = {
'name': 'targetName',
'value': $targetName,
'title': $services.localization.render('xe.activity.messages.visibility.targetName.tip')
})
#if ($possibleTargets.contains('user'))
&lt;div class="message-target message-target-user#if ($defaultTarget != 'user') hidden#end"&gt;
#set ($discard = $userPickerParams.put('class', 'targetName'))
#set ($userPickerParams.disabled = $inEditMode || $defaultTarget != 'user')
#userPicker(true, $userPickerParams)
&lt;/div&gt;
#end
#if ($possibleTargets.contains('group'))
&lt;div class="message-target message-target-group#if ($defaultTarget != 'group') hidden#end"&gt;
#set ($discard = $userPickerParams.put('class', 'targetName'))
#set ($userPickerParams.disabled = $inEditMode || $defaultTarget != 'group')
#groupPicker(true, $userPickerParams)
&lt;/div&gt;
#end
&lt;div class="message-target message-target-default"&gt;
&lt;input type="hidden" name="targetName" value="$!escapetool.xml($targetName)" class="targetName" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class='messagestream-buttons'&gt;
&lt;span class='butonwrapper'&gt;&lt;input type='submit' value="$services.localization.render('xe.activity.messages.submit')" class='button'#if($inEditMode) disabled='disabled'#end/&gt;&lt;/span&gt;
Expand Down
Expand Up @@ -42,25 +42,14 @@
<content>{{velocity}}
#macro (displaySearchFacetValue_user $user)
#set ($userReference = $services.model.resolveDocument($user))
## The string returned by XWiki#getUserName() is XML encoded.
#set ($userName = $xwiki.getUserName($user, false))
&lt;span class="user"&gt;
&lt;span class="user-avatar-wrapper"&gt;
#getUserAvatarURL($userReference $avatarURL 30)
&lt;img src="$escapetool.xml($avatarURL.url)" class="icon" alt="$userName" /&gt;
&lt;/span&gt;
&lt;span class="user-name"&gt;$userName&lt;/span&gt;
&lt;span&gt;
&lt;span class="user-alias"&gt;$escapetool.xml($userReference.name)&lt;/span&gt;
## Display the wiki only for local users.
#if ($userReference.wikiReference.name != $xcontext.getMainWikiName())
&lt;span class="user-wiki"&gt;$escapetool.xml($userReference.wikiReference.name)&lt;/span&gt;
#end
&lt;/span&gt;
&lt;/span&gt;
#set ($escapedUserName = $escapetool.xml($xwiki.getPlainUserName($userReference)))
#getUserAvatarURL($userReference $avatarURL 30)
&lt;span class="user"&gt;##
&lt;img class="user-avatar" src="$escapetool.xml($avatarURL.url)" alt="$escapedUserName" /&gt;##
&lt;span class="user-name"&gt;$escapedUserName&lt;/span&gt;##
&lt;/span&gt;##
#end
#if ($facetValues)
#set ($discard = $xwiki.ssfx.use('uicomponents/widgets/userpicker/userPicker.css', true))
#set ($discard = $xwiki.ssx.use('Main.SolrUserFacet'))
{{html clean="false"}}
&lt;ul class="users"&gt;
Expand Down Expand Up @@ -168,10 +157,6 @@
<code>.users .itemName .user {
display: block;
margin: 0;
}

.users .itemName .user-name {
display: block;
overflow: hidden;
text-overflow: ellipsis;
}</code>
Expand Down

0 comments on commit 8fc372b

Please sign in to comment.