This commit is contained in:
dartcafe 2018-10-14 13:55:51 +02:00
Родитель cfcd1a054f
Коммит 541612d271
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: CCE73CEF3035D3C8
7 изменённых файлов: 298 добавлений и 250 удалений

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -116,7 +116,9 @@ class ApiController extends Controller {
'id' => $user->getUID(), 'id' => $user->getUID(),
'type' => 'user', 'type' => 'user',
'displayName' => $user->getDisplayName(), 'displayName' => $user->getDisplayName(),
'avatarURL' => '' 'avatarURL' => '',
'lastLogin' => $user->getLastLogin(),
'cloudId' => $user->getCloudId()
]; ];
} }
} }

Просмотреть файл

@ -23,7 +23,7 @@
"axios": "^0.17.1", "axios": "^0.17.1",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"moment": "^2.22.1", "moment": "^2.22.1",
"nextcloud-vue": "^0.1.5", "nextcloud-vue": "^0.2.0",
"velocity-animate": "^1.5.1", "velocity-animate": "^1.5.1",
"vue": "^2.5.16", "vue": "^2.5.16",
"vue-js-modal": "^1.3.26" "vue-js-modal": "^1.3.26"

Просмотреть файл

@ -83,7 +83,6 @@
<div class="header"> <div class="header">
<div class="pollInformation flex-column"> <div class="pollInformation flex-column">
<user-div :description="t('polls', 'Owner')" :user-id="poll.event.owner"></user-div> <user-div :description="t('polls', 'Owner')" :user-id="poll.event.owner"></user-div>
<cloud-div v-bind:options="poll.event"></cloud-div>
</div> </div>
</div> </div>
@ -95,7 +94,7 @@
<div> <div>
<div class="flex-wrap align-centered space-between" v-if="protect"> <div class="flex-wrap align-centered space-between" v-if="protect">
<span>{{ t('polls', 'Configuration is locked. Changing options may result in unwanted behaviour,but you can unlock it anyway.') }}</span> <span>{{ t('polls', 'Configuration is locked. Changing options may result in unwanted behaviour, but you can unlock it anyway.') }}</span>
<button @click="protect=false" > {{ t('polls', 'Unlock configuration ') }} </button> <button @click="protect=false" > {{ t('polls', 'Unlock configuration ') }} </button>
</div> </div>
<div id="configurationsTabView" class="tab configurationsTabView flex-wrap"> <div id="configurationsTabView" class="tab configurationsTabView flex-wrap">
@ -145,8 +144,7 @@
:placeholder="t('polls', 'Name of user or group')" :placeholder="t('polls', 'Name of user or group')"
:active-shares="poll.shares" :active-shares="poll.shares"
v-show="poll.event.access === 'select'" v-show="poll.event.access === 'select'"
@add-share="addShare" :shares="poll.shares"/>
@remove-share="removeShare"/>
</div> </div>
</div> </div>
</div> </div>
@ -199,9 +197,8 @@
} }
}, },
system:[], system:[],
lang: OC.getLanguage(), lang: '',
locale: OC.getLocale(), locale: '',
localeData: moment.localeData(moment.locale(OC.getLocale())),
placeholder: '', placeholder: '',
newPollDate: '', newPollDate: '',
newPollTime: '', newPollTime: '',
@ -212,35 +209,27 @@
sidebar: false, sidebar: false,
titleEmpty: false, titleEmpty: false,
indexPage: '', indexPage: '',
longDateFormat: moment.localeData().longDateFormat('L'), longDateFormat: '',
dateTimeFormat: moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT'), dateTimeFormat: '',
expirationDatePicker: {
editable: true,
minuteStep: 1,
type: 'datetime',
format: moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT'),
lang: OC.getLanguage().split("-")[0],
placeholder: t('polls', 'Expiration date')
},
optionDatePicker: {
editable: false,
minuteStep: 1,
type: 'datetime',
format: moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT'),
lang: OC.getLanguage().split("-")[0],
placeholder: t('polls', 'Click to add a date'),
timePickerOptions: {
start: '00:00',
step: '00:05',
end: '23:55'
}
}
} }
}, },
created: function() { created: function() {
this.indexPage = OC.generateUrl('apps/polls/'); this.indexPage = OC.generateUrl('apps/polls/');
this.getSystemValues(); this.getSystemValues();
this.lang = OC.getLanguage();
try {
this.locale = OC.getLocale();
} catch (e) {
if (e instanceof TypeError) {
this.locale = this.lang;
} else {
console.log(e)
}
}
moment.locale(this.locale);
this.longDateFormat = moment.localeData().longDateFormat('L');
this.dateTimeFormat = moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT');
var urlArray = window.location.pathname.split( '/' ); var urlArray = window.location.pathname.split( '/' );
if (urlArray[urlArray.length - 1] === 'create') { if (urlArray[urlArray.length - 1] === 'create') {
@ -258,7 +247,7 @@
computed: { computed: {
langShort: function () { langShort: function () {
return OC.getLanguage().split("-")[0] return this.lang.split("-")[0]
}, },
title: function() { title: function() {
@ -276,8 +265,39 @@
} else { } else {
return t('polls', 'Create new poll') return t('polls', 'Create new poll')
} }
},
localeData: function () {
return moment.localeData(moment.locale(this.locale))
},
expirationDatePicker: function () {
return {
editable: true,
minuteStep: 1,
type: 'datetime',
lang: this.lang.split("-")[0],
format: moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT'),
placeholder: t('polls', 'Expiration date')
}
},
optionDatePicker: function () {
return {
editable: false,
minuteStep: 1,
type: 'datetime',
format: moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT'),
lang: this.lang.split("-")[0],
placeholder: t('polls', 'Click to add a date'),
timePickerOptions: {
start: '00:00',
step: '00:05',
end: '23:55'
}
}
} }
}, },
watch: { watch: {
@ -312,12 +332,14 @@
}, },
addNewPollDate: function (newPollDate) { addNewPollDate: function (newPollDate) {
this.newPollDate = moment(newPollDate); if (newPollDate != null) {
this.poll.options.pollDates.push({ this.newPollDate = moment(newPollDate);
id: this.nextPollDateId++, this.poll.options.pollDates.push({
timestamp: moment(newPollDate).unix(), id: this.nextPollDateId++,
}); timestamp: moment(newPollDate).unix(),
this.poll.options.pollDates = _.sortBy(this.poll.options.pollDates, 'timestamp'); });
this.poll.options.pollDates = _.sortBy(this.poll.options.pollDates, 'timestamp');
}
}, },
addNewPollText: function () { addNewPollText: function () {
@ -378,6 +400,7 @@
} }
</script> </script>
<style lang="scss"> <style lang="scss">
#content { #content {
display: flex; display: flex;
@ -491,10 +514,12 @@
border-bottom: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border);
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
&:hover, &:active { &:hover, &:active {
transition: var(--background-dark) 0.3s ease; transition: var(--background-dark) 0.3s ease;
background-color: var(--color-loading-light); //$hover-color; background-color: var(--color-loading-light); //$hover-color;
} }
> div { > div {
display: flex; display: flex;
flex-grow: 1; flex-grow: 1;
@ -525,7 +550,10 @@
.autocomplete { .autocomplete {
position: relative; position: relative;
} }
.configurationsTabView {
display: flex;
}
#share-list { #share-list {
.user-list { .user-list {
max-height: 180px; max-height: 180px;
@ -539,5 +567,4 @@
width: 99%; width: 99%;
} }
} }
</style> </style>

Просмотреть файл

@ -1,22 +1,18 @@
/* global Vue, oc_userconfig */ /* global Vue, oc_userconfig */
<template> <template>
<div class="userRow"> <div class="user-row" :class="type">
<div v-show="description" class="description">{{description}}</div> <div v-show="description" class="description">{{description}}</div>
<div class="avatar"> <div class="avatar"><img :src="avatarURL" :width="size" :height="size" :title="computedDisplayName"></div>
<img :src="avatarURL" :width="size" :height="size"> <div v-show="!onlyAvatar" class="user-name">{{ computedDisplayName }}</div>
</div>
<div v-show="nothidden" class="avatar imageplaceholderseed" :data-username="userId" :data-displayname="computedDisplayName" data-seed="Poll users 1">
{{ computedDisplayName.toUpperCase().substr(0,1) }}
</div>
<div class="user">{{ computedDisplayName }}</div>
</div> </div>
<div class="avatar imageplaceholderseed"</div>
</template> </template>
<script> <script>
export default { export default {
props: { props: {
onlyAvatar: {
default: false
},
userId: { userId: {
type: String, type: String,
default: OC.getCurrentUser().uid default: OC.getCurrentUser().uid
@ -81,36 +77,33 @@
} }
</script> </script>
<style scoped> <style lang="scss">
.userRow { .user-row {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-grow: 1; flex-grow: 1;
align-items: center; align-items: center;
margin-left: 0; margin-left: 0;
margin-top: 0; margin-top: 0;
}
.description { > div {
opacity: 0.7; margin: 0 4px 0 4px;
margin-right: 4px; }
}
.avatar { .description {
height: 32px; opacity: 0.7;
width: 32px; flex-grow: 0;
} }
.user {
margin-left: 8px; .avatar {
opacity: 0.5; height: 32px;
flex-grow: 1; width: 32px;
} flex-grow: 0;
.imageplaceholderseed { }
height: 32px;
width: 32px; .user-name {
background-color: rgb(185, 185, 185); opacity: 0.5;
color: rgb(255, 255, 255); flex-grow: 1;
font-weight: normal; }
text-align: center;
line-height: 32px;
font-size: 17.6px;
} }
</style> </style>

Просмотреть файл

@ -1,36 +1,35 @@
<template> <template>
<div> <div>
<h2> {{ t('polls', 'Share with') }}</h2> <h2> {{ t('polls', 'Share with') }}</h2>
<div class="autocomplete"> <multiselect
<input class="shareWithField" v-model="shares"
autocomplete="off" :options="users"
type="text" :option-height=32
:placeholder="placeholder" :multiple="true"
v-model="query" :close-on-select="false"
@input="onInput" :clear-on-select="false"
@focus="onInput"> :preserve-search="true"
label="displayName"
<transition-group v-show="openList" tag="ul" v-bind:css="false" class="user-list suggestion"> track-by="id"
<li v-for="(item, index) in sortedSiteusers" :preselect-first="true"
v-bind:key="item.displayName" :placeholder="placeholder">
v-bind:data-index="index" <template slot="selection" slot-scope="{ values, search, isOpen }">
class="flex-row" <span class="multiselect__single" v-if="values.length &amp;&amp; !isOpen">
v-on:click="addShare(index, item)"> {{ values.length }} users selected
<user-div :user-id="item.id" :display-name="item.displayName" :type="item.type"></user-div> </span>
</li> </template>
</transition-group> <template slot="option" slot-scope="props">
</div> <div class="option__desc">
<user-div :user-id="props.option.id" :display-name="props.option.displayName" :type="props.option.type"></user-div>
</div>
</template>
</multiselect>
<transition-group tag="ul" v-bind:css="false" class="shared-list"> <transition-group tag="ul" v-bind:css="false" class="shared-list">
<li v-for="(item, index) in sortedShares" <li v-for="(item, index) in sortedShares"
v-bind:key="item.displayName" v-bind:key="item.displayName"
v-bind:data-index="index" v-bind:data-index="index">
class="flex-row"> <user-div :user-id="item.id" :display-name="item.displayName" :type="item.type" only-avatar="true"></user-div>
<user-div :user-id="item.id" :display-name="item.displayName" :type="item.type"></user-div>
<div class="flex-row options">
<a @click="removeShare(index, item)" class="icon icon-delete svg delete-poll"></a>
</div>
</li> </li>
</transition-group> </transition-group>
</div> </div>
@ -38,8 +37,13 @@
<script> <script>
import axios from 'axios'; import axios from 'axios';
import { Multiselect } from 'nextcloud-vue';
export default { export default {
components: {
Multiselect
},
props: { props: {
placeholder: { placeholder: {
type: String type: String
@ -52,14 +56,11 @@
data: function () { data: function () {
return { return {
query: '', query: '',
shares: [],
users: [], users: [],
openList: false,
siteUsersLoaded: false,
siteUsersListOptions: { siteUsersListOptions: {
getUsers: true, getUsers: true,
getGroups: true, getGroups: true,
skipUsers: [],
skipGroups: []
} }
} }
}, },
@ -68,54 +69,20 @@
this.loadSiteUsers(); this.loadSiteUsers();
}, },
mounted: function() {
document.addEventListener('click', this.handleClickOutside)
},
destroyed: function() {
document.removeEventListener('click', this.handleClickOutside)
},
computed: { computed: {
filteredSiteusers: function() {
var vm = this;
return this.users.filter(function (item) {
return item.displayName.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1
})
},
sortedSiteusers: function() {
return this.filteredSiteusers.sort(this.sortByDisplayname);
},
sortedShares: function() { sortedShares: function() {
return this.activeShares.sort(this.sortByDisplayname); return this.shares.sort(this.sortByDisplayname);
} }
}, },
methods: { methods: {
addShare: function (index, item){
this.$emit('add-share', item);
this.users.splice(this.users.indexOf(item), 1);
},
removeShare: function (index, item){ removeShare: function (index, item){
this.$emit('remove-share', item); this.$emit('remove-share', item);
this.users.push(item); this.users.push(item);
}, },
loadSiteUsers: function () { loadSiteUsers: function () {
var vm = this;
vm.siteUsersListOptions.skipUsers = [];
vm.siteUsersListOptions.skipGroups = [];
this.activeShares.forEach(function(item) {
if (item.type === 'group') {
vm.siteUsersListOptions.skipGroups.push(item.id)
} else if (item.type === 'user') {
vm.siteUsersListOptions.skipUsers.push(item.id)
}
});
axios.post(OC.generateUrl('apps/polls/get/siteusers'), this.siteUsersListOptions) axios.post(OC.generateUrl('apps/polls/get/siteusers'), this.siteUsersListOptions)
.then((response) => { .then((response) => {
this.users = response.data.siteusers; this.users = response.data.siteusers;
@ -124,24 +91,75 @@
}); });
}, },
onInput: function() {
this.loadSiteUsers();
if (this.query !== '') {
this.openList = true;
}
},
sortByDisplayname: function (a, b) { sortByDisplayname: function (a, b) {
if (a.displayName.toLowerCase() < b.displayName.toLowerCase()) return -1; if (a.displayName.toLowerCase() < b.displayName.toLowerCase()) return -1;
if (a.displayName.toLowerCase() > b.displayName.toLowerCase()) return 1; if (a.displayName.toLowerCase() > b.displayName.toLowerCase()) return 1;
return 0; return 0;
}, }
handleClickOutside: function(evt) {
if (!this.$el.contains(evt.target)) {
this.openList = false;
}
}
} }
} }
</script> </script>
<style lang="scss">
.shared-list {
display: flex;
padding-top: 8px;
flex-grow: 0;
justify-content: flex-start;
> li {
display: flex;
flex-grow: 0;
}
}
div, select {
&.multiselect:not(.multiselect-vue), &.multiselect:not(.multiselect-vue) {
max-width: unset;
}
}
.multiselect {
width: 100%;
.multiselect__content-wrapper li > span {
height: unset;
}
.option__desc {
flex-grow: 1;
}
.multiselect__option--highlight {
background: #41b883;
outline: none;
color: #fff;
&::after {
content: attr(data-select);
background: #41b883;
color: #fff;
}
}
.multiselect__option--selected {
&::after {
content: attr(data-selected);
color: silver;
}
&.multiselect__option--highlight {
background: #ff6a6a;
color: #fff;
&::after {
background: #ff6a6a;
content: attr(data-deselect);
color: #fff;
}
}
}
}
</style>

Просмотреть файл

@ -98,11 +98,7 @@
$owner = $poll->getOwner(); $owner = $poll->getOwner();
$expiry_style = ''; $expiry_style = '';
if ($poll->getType() === 0) { $participated = $_['votes'];
$participated = $_['participations'];
} else {
$participated = $_['participations_text'];
}
$participated_class = 'partic_no'; $participated_class = 'partic_no';
$participated_title = 'You did not vote'; $participated_title = 'You did not vote';
$participated_count = count($participated); $participated_count = count($participated);