diff options
author | Vinzenz Rosenkranz <v1r0x@users.noreply.github.com> | 2020-01-20 01:33:55 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-20 01:33:55 +0300 |
commit | c74ca32e493e5e39cfa772cceb4fd36af105ad6c (patch) | |
tree | 8d80ab9831f4f03af1ea134432532bb44296c01d | |
parent | 11957d19e103b61a225ef87ed02475c94aad806a (diff) | |
parent | 5b08fae10c3d01dfe892d658c14826ed4545c399 (diff) |
Merge pull request #751 from nextcloud/release-1.0.0release-1.0
Release 1.0.0
24 files changed, 163 insertions, 66 deletions
@@ -55,9 +55,8 @@ lint-fix: .PHONY: clean clean: rm -rf $(build_dir) - rm -f js/polls.js - rm -f js/polls.js.map - + rm -rf js/ + mkdir -p js clean-dev: clean rm -rf node_modules rm -rf ./vendor @@ -5,18 +5,15 @@ [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/nextcloud/polls.svg?style=flat-square)](https://scrutinizer-ci.com/g/nextcloud/polls) [![Software License](https://img.shields.io/badge/license-AGPL-brightgreen.svg?style=flat-square)](LICENSE) -## This branch is in alpha state, do not use for production needs - This is a poll app, similar to doodle or dudle, for Nextcloud written in PHP and JS / Vue. -It is a rework of the already existing [polls app](https://github.com/raduvatav/polls) written by @raduvatav. **Note**: ownCloud is **no longer** supported! Last (confirmed) working version is 0.8.1 and is released in the oC marketplace. **Note**: IE11 users will face some CSS problems (see #541). Please change to a compatible browser (Firefox, Chrome, Edge, etc.). Or better: don't even try this browser ### Features -- :bar_chart: Create / edit polls (datetimes _and_ texts) +- :bar_chart: Create / edit polls (datetimes and texts) - :date: Set expiration date -- :lock: Restrict access (only logged in users, certain groups / users, hidden and public) +- :lock: Restrict access (all site users or invited users only) - :speech_balloon: Comments ### Bugs @@ -27,23 +24,22 @@ Create a new poll from the navigation bar and get an overview of your polls ![Overview](screenshots/overview.png) Vote and comment -![Vote](screenshots/vote.png) +![Vote](screenshots/comment.png) -Edit poll on the vote page as owner or an admin -![Edit poll](screenshots/edit-poll.png) -![Edit options](screenshots/edit-options.png) +Edit poll on the vote page +![Edit poll](screenshots/configurations.png) +![Edit options](screenshots/options.png) Add shared links to your poll -![Share poll](screenshots/edit-shares.png) +![Share poll](screenshots/shares.png) -View the vote page on mobiles -![Vote mobile portrait](screenshots/vote-mobile-portrait.png) +View the vote page on mobiles (Turn phone to landscape to see th full table) +![Vote mobile portrait](screenshots/mobile-portrait.png) -Turn phone to landscape to see details -![Vote mobile landscape](screenshots/vote-mobile-landscape.png) +Only the owner can edit the poll. Granting access to admin users will be available in the next version. ## Installation / Update -This app is supposed to work on Nextcloud version 13+. +This app is supposed to work on Nextcloud version 16+. ### Install latest release You can download and install the latest release from the [Nextcloud app store](https://apps.nextcloud.com/apps/polls). diff --git a/lib/Migration/Version0010Date20191227063812.php b/lib/Migration/Version0010Date20191227063812.php index 08dff52a..4b5bfd76 100644 --- a/lib/Migration/Version0010Date20191227063812.php +++ b/lib/Migration/Version0010Date20191227063812.php @@ -242,11 +242,13 @@ class Version0010Date20191227063812 extends SimpleMigrationStep { /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); - if ($schema->hasTable('polls_polls')) { + if ($schema->hasTable('polls_polls') && + $schema->hasTable('polls_events')) { $this->migrateEvents(); } - if ($schema->hasTable('polls_share')) { + if ($schema->hasTable('polls_share') && + $schema->hasTable('polls_events')) { $this->copyTokens(); } } @@ -343,7 +345,8 @@ class Version0010Date20191227063812 extends SimpleMigrationStep { 'type' => $insert->createParameter('type'), 'poll_id' => $insert->createParameter('poll_id'), 'user_id' => $insert->createParameter('user_id'), - 'user_email' => $insert->createParameter('user_email') + 'user_email' => $insert->createParameter('user_email'), + 'user' => $insert->createParameter('user') ]); $query = $this->connection->getQueryBuilder(); $query->select('*') @@ -358,7 +361,8 @@ class Version0010Date20191227063812 extends SimpleMigrationStep { ->setParameter('type', 'public') ->setParameter('poll_id', $row['id']) ->setParameter('user_id', null) - ->setParameter('user_email', null); + ->setParameter('user_email', null) + ->setParameter('user', ''); $insert->execute(); } elseif ($row['access'] == 'hidden') { // copy the hash to a public share @@ -368,7 +372,8 @@ class Version0010Date20191227063812 extends SimpleMigrationStep { ->setParameter('type', 'public') ->setParameter('poll_id', $row['id']) ->setParameter('user_id', null) - ->setParameter('user_email', null); + ->setParameter('user_email', null) + ->setParameter('user', ''); $insert->execute(); } elseif ($row['access'] == 'registered') { // copy the hash to a public share @@ -378,7 +383,8 @@ class Version0010Date20191227063812 extends SimpleMigrationStep { ->setParameter('type', 'public') ->setParameter('poll_id', $row['id']) ->setParameter('user_id', null) - ->setParameter('user_email', null); + ->setParameter('user_email', null) + ->setParameter('user', ''); } else { // create a personal share for invitated users @@ -398,7 +404,8 @@ class Version0010Date20191227063812 extends SimpleMigrationStep { ->setParameter('type', $parts[0]) ->setParameter('poll_id', $row['id']) ->setParameter('user_id', $parts[1]) - ->setParameter('user_email', null); + ->setParameter('user_email', null) + ->setParameter('user', ''); $insert->execute(); } } diff --git a/lib/Migration/Version0010Date20200119101800.php b/lib/Migration/Version0010Date20200119101800.php new file mode 100644 index 00000000..0152c972 --- /dev/null +++ b/lib/Migration/Version0010Date20200119101800.php @@ -0,0 +1,73 @@ +<?php +/** + * @copyright Copyright (c) 2017 René Gieling <github@dartcafe.de> + * + * @author René Gieling <github@dartcafe.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Polls\Migration; + +use OCP\DB\ISchemaWrapper; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\Migration\SimpleMigrationStep; +use OCP\Migration\IOutput; + +/** + * Installation class for the polls app. + * Initial db creation + */ +class Version0010Date20200119101800 extends SimpleMigrationStep { + + /** @var IDBConnection */ + protected $connection; + + /** @var IConfig */ + protected $config; + + /** + * @param IDBConnection $connection + * @param IConfig $config + */ + public function __construct(IDBConnection $connection, IConfig $config) { + $this->connection = $connection; + $this->config = $config; + } + + /** + * @param IOutput $output + * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + * @since 13.0.0 + */ + public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + if ($schema->hasTable('polls_polls') && + $schema->hasTable('polls_events')) { + + $schema->dropTable('polls_events'); + } + + return $schema; + } +} diff --git a/screenshots/comment.png b/screenshots/comment.png Binary files differnew file mode 100644 index 00000000..b5a66580 --- /dev/null +++ b/screenshots/comment.png diff --git a/screenshots/configuration.png b/screenshots/configuration.png Binary files differnew file mode 100644 index 00000000..9d064328 --- /dev/null +++ b/screenshots/configuration.png diff --git a/screenshots/edit-options.png b/screenshots/edit-options.png Binary files differdeleted file mode 100644 index f97343d9..00000000 --- a/screenshots/edit-options.png +++ /dev/null diff --git a/screenshots/edit-poll.png b/screenshots/edit-poll.png Binary files differdeleted file mode 100644 index b84fa767..00000000 --- a/screenshots/edit-poll.png +++ /dev/null diff --git a/screenshots/edit-shares.png b/screenshots/edit-shares.png Binary files differdeleted file mode 100644 index 7eedd030..00000000 --- a/screenshots/edit-shares.png +++ /dev/null diff --git a/screenshots/mobile-portrait.png b/screenshots/mobile-portrait.png Binary files differnew file mode 100644 index 00000000..b9698b52 --- /dev/null +++ b/screenshots/mobile-portrait.png diff --git a/screenshots/options.png b/screenshots/options.png Binary files differnew file mode 100644 index 00000000..3f878757 --- /dev/null +++ b/screenshots/options.png diff --git a/screenshots/overview.png b/screenshots/overview.png Binary files differindex 8d19ad16..e1b90977 100644 --- a/screenshots/overview.png +++ b/screenshots/overview.png diff --git a/screenshots/shares.png b/screenshots/shares.png Binary files differnew file mode 100644 index 00000000..a2dfd082 --- /dev/null +++ b/screenshots/shares.png diff --git a/screenshots/vote-mobile-landscape.png b/screenshots/vote-mobile-landscape.png Binary files differdeleted file mode 100644 index 45858793..00000000 --- a/screenshots/vote-mobile-landscape.png +++ /dev/null diff --git a/screenshots/vote-mobile-portrait.png b/screenshots/vote-mobile-portrait.png Binary files differdeleted file mode 100644 index b1932016..00000000 --- a/screenshots/vote-mobile-portrait.png +++ /dev/null diff --git a/screenshots/vote.png b/screenshots/vote.png Binary files differdeleted file mode 100644 index 53cb13f8..00000000 --- a/screenshots/vote.png +++ /dev/null diff --git a/src/js/components/Base/PollInformation.vue b/src/js/components/Base/PollInformation.vue index b20bac28..6c22627f 100644 --- a/src/js/components/Base/PollInformation.vue +++ b/src/js/components/Base/PollInformation.vue @@ -23,7 +23,7 @@ <template> <div class="poll-information"> <UserBubble :user="poll.owner" :display-name="poll.owner" /> - {{ t('polls', ' started this poll on %n. ', 1, moment.unix(poll.created).format('LLLL')) }} + {{ t('polls', 'started this poll on %n. ', 1, moment.unix(poll.created).format('LLLL')) }} <span v-if="expired">{{ t('polls', 'Voting is no more possible, because this poll expired since %n.', 1, moment.unix(poll.expire).format('LLLL')) }}</span> <span v-if="!expired && poll.expire && acl.allowVote">{{ t('polls', 'You can place your vote until %n. ', 1, moment.unix(poll.expire).format('LLLL')) }}</span> <span v-if="poll.anonymous">{{ t('polls', 'The names of other participants are hidden, as this is an anonymous poll. ') }}</span> @@ -33,10 +33,15 @@ <script> import { mapState, mapGetters } from 'vuex' +import { UserBubble } from '@nextcloud/vue' export default { name: 'PollInformation', + components: { + UserBubble + }, + computed: { ...mapState({ acl: state => state.acl, diff --git a/src/js/components/PollList/PollListItem.vue b/src/js/components/PollList/PollListItem.vue index 3b6a828a..213acbe1 100644 --- a/src/js/components/PollList/PollListItem.vue +++ b/src/js/components/PollList/PollListItem.vue @@ -62,7 +62,7 @@ <div class="toggleUserActions"> <div v-click-outside="hideMenu" class="icon-more" @click="toggleMenu" /> <div class="popovermenu" :class="{ 'open': openedMenu }"> - <popover-menu :menu="menuItems" /> + <PopoverMenu :menu="menuItems" /> </div> </div> </div> @@ -87,9 +87,15 @@ </template> <script> +import { PopoverMenu } from '@nextcloud/vue' + export default { name: 'PollListItem', + components: { + PopoverMenu + }, + props: { header: { type: Boolean, diff --git a/src/js/components/SideBar/SideBarTabConfiguration.vue b/src/js/components/SideBar/SideBarTabConfiguration.vue index 29410328..7a8fd207 100644 --- a/src/js/components/SideBar/SideBarTabConfiguration.vue +++ b/src/js/components/SideBar/SideBarTabConfiguration.vue @@ -56,7 +56,7 @@ type="checkbox" class="checkbox"> <label class="title" for="expiration"> {{ t('polls', 'Expires') }} </label> - <DatePicker v-show="pollExpiration" + <DatetimePicker v-show="pollExpiration" v-model="pollExpire" v-bind="expirationDatePicker" style="width:170px" /> </div> @@ -82,10 +82,15 @@ <script> import debounce from 'lodash/debounce' import { mapState, mapMutations, mapActions } from 'vuex' +import { DatetimePicker } from '@nextcloud/vue' export default { name: 'SideBarTabConfiguration', + components: { + DatetimePicker + }, + data() { return { writingPoll: false, @@ -101,6 +106,15 @@ export default { acl: state => state.acl }), + firstDOW() { + // vue2-datepicker needs 7 for sunday + if (moment.localeData()._week.dow === 0) { + return 7 + } else { + return moment.localeData()._week.dow + } + }, + // Add bindings pollDescription: { get() { @@ -183,18 +197,6 @@ export default { } }, - langPicker() { - return { - formatLocale: { - months: moment.months(), - monthsShort: moment.monthsShort(), - weekdays: moment.weekdays(), - weekdaysMin: moment.weekdaysMin(), - firstDayOfWeek: moment.localeData()._week.dow - } - } - }, - expirationDatePicker() { return { editable: true, @@ -204,12 +206,12 @@ export default { // TODO: use this for version 2.x lang: OC.getLanguage().split('-')[0], - firstDayOfWeek: moment.localeData()._week.dow, + firstDayOfWeek: this.firstDOW, // TODO: use this from version 3.x on // lang: { // formatLocale: { - // firstDayOfWeek: moment.localeData()._week.dow, + // firstDayOfWeek: this.firstDOW, // months: moment.months(), // monthsShort: moment.monthsShort(), // weekdays: moment.weekdays(), diff --git a/src/js/components/SideBar/SideBarTabDateOptions.vue b/src/js/components/SideBar/SideBarTabDateOptions.vue index 7fb91125..c8e9b154 100644 --- a/src/js/components/SideBar/SideBarTabDateOptions.vue +++ b/src/js/components/SideBar/SideBarTabDateOptions.vue @@ -26,7 +26,7 @@ <label class="title icon-calendar"> {{ t('polls', 'Add a date option') }} </label> - <DatePicker v-model="lastOption" + <DatetimePicker v-model="lastOption" v-bind="optionDatePicker" style="width:100%" confirm @@ -63,7 +63,7 @@ </template> <script> -import { Multiselect } from '@nextcloud/vue' +import { DatetimePicker, Multiselect } from '@nextcloud/vue' import { mapGetters, mapState } from 'vuex' import DatePollItem from '../Base/DatePollItem' @@ -72,7 +72,8 @@ export default { components: { Multiselect, - DatePollItem + DatePollItem, + DatetimePicker }, data() { @@ -93,6 +94,15 @@ export default { ...mapGetters(['sortedOptions']), + firstDOW() { + // vue2-datepicker needs 7 for sunday + if (moment.localeData()._week.dow === 0) { + return 7 + } else { + return moment.localeData()._week.dow + } + }, + optionDatePicker() { return { editable: false, @@ -102,12 +112,12 @@ export default { // TODO: use this for version 2.x lang: OC.getLanguage().split('-')[0], - firstDayOfWeek: moment.localeData()._week.dow, + firstDayOfWeek: this.firstDOW, // TODO: use this from version 3.x on // lang: { // formatLocale: { - // firstDayOfWeek: moment.localeData()._week.dow, + // firstDayOfWeek: this.firstDOW, // months: moment.months(), // monthsShort: moment.monthsShort(), // weekdays: moment.weekdays(), diff --git a/src/js/main.js b/src/js/main.js index c0e0acc7..6dfb0fed 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -32,7 +32,7 @@ import ClickOutside from 'v-click-outside' import VueClipboard from 'vue-clipboard2' import moment from 'moment' -import { PopoverMenu, Tooltip, DatetimePicker, AppContent, UserBubble } from '@nextcloud/vue' +import { Tooltip } from '@nextcloud/vue' import UserDiv from './components/Base/UserDiv' import LoadingOverlay from './components/Base/LoadingOverlay' @@ -54,13 +54,8 @@ Vue.prototype.$http = axios Vue.prototype.OC = OC Vue.prototype.OCA = OCA -Vue.component('PopoverMenu', PopoverMenu) -Vue.component('AppContent', AppContent) -Vue.component('DatePicker', DatetimePicker) Vue.component('UserDiv', UserDiv) Vue.component('LoadingOverlay', LoadingOverlay) -Vue.component('UserBubble', UserBubble) - Vue.directive('tooltip', Tooltip) Vue.use(ClickOutside) diff --git a/src/js/views/PollList.vue b/src/js/views/PollList.vue index 5ed44ded..325ec93a 100644 --- a/src/js/views/PollList.vue +++ b/src/js/views/PollList.vue @@ -21,7 +21,7 @@ --> <template> - <app-content> + <AppContent> <div class="main-container"> <div v-if="noPolls" class=""> <div class="icon-polls" /> @@ -45,16 +45,18 @@ </div> <loading-overlay v-if="loading" /> <!-- <modal-dialog /> --> - </app-content> + </AppContent> </template> <script> +import { AppContent } from '@nextcloud/vue' import PollListItem from '../components/PollList/PollListItem' import { mapGetters } from 'vuex' export default { name: 'PollList', components: { + AppContent, PollListItem }, diff --git a/src/js/views/PublicVote.vue b/src/js/views/PublicVote.vue index e45971e8..9c782e4d 100644 --- a/src/js/views/PublicVote.vue +++ b/src/js/views/PublicVote.vue @@ -22,7 +22,7 @@ <template> <AppContent> - <div v-if="poll.id > 0" class="main-container"> + <div v-if="poll.id > 0" v-show="!isLoading" class="main-container"> <div class="header-actions"> <button class="button btn primary" @click="tableMode = !tableMode"> <span>{{ t('polls', 'Switch view') }}</span> @@ -34,8 +34,8 @@ <PollInformation /> <VoteHeaderPublic /> <PollDescription /> - <VoteList v-show="!isLoading && !tableMode" /> - <VoteTable v-show="!isLoading && tableMode" /> + <VoteList v-show="!tableMode" /> + <VoteTable v-show="tableMode" /> <div class="additional"> <ParticipantsList v-if="acl.allowSeeUsernames" /> @@ -50,6 +50,7 @@ <script> // import Comments from '../components/Comments/Comments' +import { AppContent } from '@nextcloud/vue' import ParticipantsList from '../components/Base/ParticipantsList' import PollDescription from '../components/Base/PollDescription' import PollInformation from '../components/Base/PollInformation' @@ -64,6 +65,7 @@ import { mapState } from 'vuex' export default { name: 'Vote', components: { + AppContent, ParticipantsList, PollDescription, PollInformation, @@ -111,7 +113,7 @@ export default { methods: { loadPoll() { - this.isLoading = false + this.isLoading = true this.$store.dispatch('loadPollMain', { token: this.$route.params.token }) .then(() => { this.$store.dispatch('loadPoll', { token: this.$route.params.token }) @@ -165,8 +167,6 @@ export default { display: block; width: 44px; height: 44px; - right: 0; - position: absolute; } </style> diff --git a/src/js/views/Vote.vue b/src/js/views/Vote.vue index 023f9fc4..53f1e9ea 100644 --- a/src/js/views/Vote.vue +++ b/src/js/views/Vote.vue @@ -22,7 +22,7 @@ <template> <AppContent> - <div v-if="poll.id > 0" class="main-container"> + <div v-if="poll.id > 0" v-show="!isLoading" class="main-container"> <div class="header-actions"> <button class="button btn primary" @click="tableMode = !tableMode"> <span>{{ t('polls', 'Switch view') }}</span> @@ -33,8 +33,8 @@ <PollTitle /> <PollInformation /> <PollDescription /> - <VoteList v-show="!isLoading && !tableMode && options.list.length" /> - <VoteTable v-show="!isLoading && tableMode && options.list.length" /> + <VoteList v-show="!tableMode && options.list.length" /> + <VoteTable v-show="tableMode && options.list.length" /> <div v-if="!options.list.length" class="emptycontent"> <div class="icon-toggle-filelist" /> <p> {{ t('polls', 'There are no vote options, add some.') }}</p> @@ -54,6 +54,7 @@ <script> // import Comments from '../components/Comments/Comments' +import { AppContent } from '@nextcloud/vue' import Subscription from '../components/Subscription/Subscription' import ParticipantsList from '../components/Base/ParticipantsList' import PollDescription from '../components/Base/PollDescription' @@ -68,6 +69,7 @@ import { mapState } from 'vuex' export default { name: 'Vote', components: { + AppContent, Subscription, ParticipantsList, PollDescription, @@ -88,7 +90,7 @@ export default { isLoading: false, initialTab: 'comments', newName: '', - tableMode: false + tableMode: true } }, @@ -117,7 +119,7 @@ export default { methods: { loadPoll() { - this.isLoading = false + this.isLoading = true this.$store.dispatch({ type: 'loadPollMain', pollId: this.$route.params.id }) .then(() => { this.$store.dispatch({ type: 'loadPoll', pollId: this.$route.params.id }) |