Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/polls.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordartcafe <github@dartcafe.de>2019-12-09 01:17:11 +0300
committerdartcafe <github@dartcafe.de>2019-12-09 01:17:11 +0300
commit1d4a9334c7298416032b2f453e174c35d70217ce (patch)
tree44feef8a7f7439b8789ed9e5b7260fd4fb816663
parentb26f96c7e932ef9d178cdd6e041cdee11218e354 (diff)
* some enhancements
-rw-r--r--appinfo/info.xml2
-rw-r--r--appinfo/routes.php8
-rw-r--r--img/expired-unvoted-vote.svg14
-rw-r--r--img/expired-voted-vote.svg11
-rw-r--r--img/no-comment.svg5
-rw-r--r--img/open-unvoted-vote.svg14
-rw-r--r--img/open-voted-vote.svg11
-rw-r--r--img/save.svg35
-rw-r--r--img/share.svg6
-rw-r--r--img/toggle.svg86
-rw-r--r--img/yes-comment.svg5
-rw-r--r--lib/Controller/AclController.php1
-rw-r--r--lib/Controller/EventController.php225
-rw-r--r--lib/Controller/PageController.php11
-rw-r--r--lib/Controller/ShareController.php2
-rw-r--r--lib/Db/Event.php16
-rw-r--r--lib/Db/EventMapper.php3
-rw-r--r--lib/Migration/Version0010Date20190801063812.php84
-rw-r--r--lib/Model/Acl.php91
-rw-r--r--package.json10
-rw-r--r--src/js/assets/app.pngbin0 -> 195 bytes
-rw-r--r--src/js/assets/app.svg38
-rw-r--r--src/js/components/PollList/PollListItem.vue68
-rw-r--r--src/js/components/VoteTable/VoteHeader.vue (renamed from src/js/components/base/sideBarClose.vue)46
-rw-r--r--src/js/components/VoteTable/VoteHeaderPublic.vue221
-rw-r--r--src/js/components/VoteTable/VoteTable.vue11
-rw-r--r--src/js/components/VoteTable/VoteTableHeader.vue5
-rw-r--r--src/js/components/VoteTable/VoteTableItem.vue5
-rw-r--r--src/js/components/base/sideBar.vue45
-rw-r--r--src/js/components/create/createDlg.vue47
-rw-r--r--src/js/components/navigation/navigation.vue32
-rw-r--r--src/js/components/navigation/store/polls.js6
-rw-r--r--src/js/components/sidebar/SideBar.vue109
-rw-r--r--src/js/components/sidebar/SideBarTabConfiguration.vue94
-rw-r--r--src/js/router.js12
-rw-r--r--src/js/store/index.js4
-rw-r--r--src/js/store/modules/acl.js82
-rw-r--r--src/js/store/modules/currentPoll.js59
-rw-r--r--src/js/store/modules/event.js66
-rw-r--r--src/js/store/modules/shares.js24
-rw-r--r--src/js/store/modules/votes.js8
-rw-r--r--src/js/views/PollList.vue38
-rw-r--r--src/js/views/PublicVote.vue252
-rw-r--r--src/js/views/Vote.vue92
44 files changed, 999 insertions, 1005 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml
index d66c0b08..9dc6f0da 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -23,6 +23,6 @@
<screenshot>https://raw.githubusercontent.com/nextcloud/polls/master/screenshots/vote.png</screenshot>
<screenshot>https://raw.githubusercontent.com/nextcloud/polls/master/screenshots/edit-poll.png</screenshot>
<dependencies>
- <nextcloud min-version="14" max-version="17" />
+ <nextcloud min-version="14" max-version="18" />
</dependencies>
</info>
diff --git a/appinfo/routes.php b/appinfo/routes.php
index b3065269..f86a10b1 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -24,16 +24,13 @@
return [
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
-
['name' => 'page#vote_poll', 'url' => '/vote/{pollId}', 'verb' => 'GET'],
['name' => 'page#vote_public', 'url' => '/s/{pollId}', 'verb' => 'GET'],
- ['name' => 'PublicAPI#get', 'url' => '/api/{token}', 'verb' => 'GET' ],
['name' => 'notification#get', 'url' => '/get/notification/{pollId}', 'verb' => 'GET'],
['name' => 'notification#set', 'url' => '/set/notification/', 'verb' => 'POST'],
['name' => 'comment#get', 'url' => '/get/comments/{pollId}', 'verb' => 'GET'],
- ['name' => 'comment#test', 'url' => '/get/test/{pollId}', 'verb' => 'GET'],
['name' => 'comment#write', 'url' => '/write/comment', 'verb' => 'POST'],
['name' => 'comment#getByToken', 'url' => '/get/comments/s/{token}', 'verb' => 'GET'],
@@ -51,8 +48,7 @@ return [
['name' => 'event#list', 'url' => '/get/events', 'verb' => 'GET'],
['name' => 'event#get', 'url' => '/get/event/{pollId}', 'verb' => 'GET'],
- ['name' => 'event#write', 'url' => '/write/event/', 'verb' => 'POST'],
- ['name' => 'event#add', 'url' => '/add/event/', 'verb' => 'POST'],
+ ['name' => 'event#write', 'url' => '/write/event', 'verb' => 'POST'],
['name' => 'event#getByToken', 'url' => '/get/event/s/{token}', 'verb' => 'GET'],
['name' => 'share#getShares', 'url' => '/get/shares/{pollId}', 'verb' => 'GET'],
@@ -61,7 +57,7 @@ return [
['name' => 'share#remove', 'url' => '/remove/share', 'verb' => 'POST'],
['name' => 'share#get', 'url' => '/get/share/{token}', 'verb' => 'GET'],
- ['name' => 'acl#getByToken', 'url' => '/get/aclbytoken/{token}', 'verb' => 'GET'],
+ ['name' => 'acl#getByToken', 'url' => '/get/acl/s/{token}', 'verb' => 'GET'],
['name' => 'acl#get', 'url' => '/get/acl/{id}', 'verb' => 'GET'],
['name' => 'system#get_site_users_and_groups', 'url' => '/get/siteusers', 'verb' => 'POST'],
diff --git a/img/expired-unvoted-vote.svg b/img/expired-unvoted-vote.svg
deleted file mode 100644
index ca46c722..00000000
--- a/img/expired-unvoted-vote.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
- <ellipse
- style="opacity:1;fill:none;fill-opacity:1;stroke:#f45573;stroke-width:1.49987304;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- cx="8"
- cy="8"
- rx="6.2500634"
- ry="6.2500639" />
- <rect
- style="fill:none;fill-opacity:1;stroke:#ffc107;stroke-width:0.86666656;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- width="5.6333332"
- height="5.6333332"
- x="5.1833334"
- y="5.1833334" />
-</svg>
diff --git a/img/expired-voted-vote.svg b/img/expired-voted-vote.svg
deleted file mode 100644
index a58f41b0..00000000
--- a/img/expired-voted-vote.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
- <ellipse
- style="opacity:1;fill:none;fill-opacity:1;stroke:#f45573;stroke-width:1.49987304;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- cx="8"
- cy="8"
- rx="6.2500634"
- ry="6.2500639" />
- <path
- d="M 6.9766048,11.334813 3.39273,7.7509392 4.4157633,6.7271819 6.9766048,9.285851 11.569757,4.6651869 12.60727,5.7034244 Z"
- style="fill:#49bc49;fill-opacity:1" />
-</svg>
diff --git a/img/no-comment.svg b/img/no-comment.svg
deleted file mode 100644
index 1ac7b557..00000000
--- a/img/no-comment.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
- <path
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#f45573;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.39999998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
- d="M 2.7395492,2.1143382 C 2.406783,2.1147393 2.1371235,2.3821537 2.1367191,2.7121496 l 0,6.7829882 c 4.043e-4,0.329996 0.2700638,0.5974112 0.6028301,0.5978122 l 0.5846175,0 -1.1049559,3.792712 3.2785391,-3.792712 7.7627011,0 c 0.332766,-4.01e-4 0.602426,-0.2678152 0.60283,-0.5978122 l 0,-6.7829882 c -4.04e-4,-0.329996 -0.270064,-0.5974105 -0.60283,-0.5978114 z" />
-</svg>
diff --git a/img/open-unvoted-vote.svg b/img/open-unvoted-vote.svg
deleted file mode 100644
index 4376c065..00000000
--- a/img/open-unvoted-vote.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
- <path
- style="opacity:1;fill:none;fill-opacity:1;stroke:#49bc49;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 14.244065,8.0059972 c 0,3.4518138 -2.798249,6.2500608 -6.2500623,6.2500608 -3.4518138,0 -6.250062,-2.798247 -6.2500622,-6.2500608 0,-3.4518134 2.7982482,-6.2500612 6.2500622,-6.2500612" />
- <path
- style="fill:#49bc49;fill-opacity:1;stroke:none;stroke-width:0.11827402;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 7.9774453,0.41685427 11.667719,1.8149286 7.9774453,3.2780296 v 0 z" />
- <rect
- style="fill:none;fill-opacity:1;stroke:#ffc107;stroke-width:0.86666656;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- width="5.6333332"
- height="5.6333332"
- x="5.1833334"
- y="5.1833334" />
-</svg>
diff --git a/img/open-voted-vote.svg b/img/open-voted-vote.svg
deleted file mode 100644
index 58e5764f..00000000
--- a/img/open-voted-vote.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
- <path
- d="M 6.9766048,11.334813 3.39273,7.750939 4.4157633,6.7271817 6.9766048,9.2858508 11.569757,4.6651867 12.60727,5.7034243 Z"
- style="fill:#49bc49;fill-opacity:1" />
- <path
- style="opacity:1;fill:none;fill-opacity:1;stroke:#49bc49;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 14.250063,7.9999999 c 0,3.4518141 -2.798249,6.2500611 -6.2500635,6.2500611 -3.4518138,0 -6.250062,-2.798247 -6.2500622,-6.2500611 0,-3.4518134 2.7982482,-6.2500609 6.2500622,-6.2500609" />
- <path
- style="fill:#49bc49;fill-opacity:1;stroke:none;stroke-width:0.11827402;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 7.977,0.4168247 11.667274,1.8148987 7.977,3.278 v 0 z" />
-</svg>
diff --git a/img/save.svg b/img/save.svg
deleted file mode 100644
index f8fbaae9..00000000
--- a/img/save.svg
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- id="svg4"
- height="16"
- width="16"
- viewbox="0 0 16 16"
- version="1.1">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <g
- id="XMLID_2_"
- transform="matrix(0.22613747,0,0,0.23288366,-2.8432917,-3.1784157)">
- <path
- d="M 19.4,18 V 78 H 76.5 V 31.4 L 65.3,18 H 58.4 V 40.9 H 28 V 18 Z m 22.9,2.9 V 35.2 H 53.7 V 20.9 Z M 28,50.4 H 68 V 72.3 H 28 Z m 5.7,4.7 V 58 h 28.6 v -2.9 z m 0,8.6 v 2.9 h 28.6 v -2.9 z"
- class="st0"
- id="XMLID_9_" />
- </g>
-</svg>
diff --git a/img/share.svg b/img/share.svg
deleted file mode 100644
index d67d35c6..00000000
--- a/img/share.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
- <g transform="translate(0 -1036.4)">
- <path d="m12.228 1037.4c-1.3565 0-2.4592 1.0977-2.4592 2.4542 0 0.075 0.0084 0.1504 0.0149 0.2236l-4.7346 2.4145c-0.4291-0.3667-0.98611-0.5863-1.5947-0.5863-1.3565 0-2.4542 1.0977-2.4542 2.4543 0 1.3565 1.0977 2.4542 2.4542 2.4542 0.54607 0 1.0528-0.1755 1.4606-0.477l4.8637 2.4741c-0.0024 0.044-0.0099 0.089-0.0099 0.1342 0 1.3565 1.1027 2.4542 2.4592 2.4542s2.4542-1.0977 2.4542-2.4542-1.0977-2.4592-2.4542-2.4592c-0.63653 0-1.218 0.2437-1.6544 0.6409l-4.6953-2.4c0.01892-0.1228 0.03478-0.2494 0.03478-0.3775 0-0.072-0.0089-0.1437-0.0149-0.2137l4.7395-2.4145c0.42802 0.3627 0.98488 0.5813 1.5898 0.5813 1.3565 0 2.4542-1.1027 2.4542-2.4592s-1.0977-2.4542-2.4542-2.4542z"/>
- </g>
-</svg>
diff --git a/img/toggle.svg b/img/toggle.svg
deleted file mode 100644
index 71d260f7..00000000
--- a/img/toggle.svg
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- id="svg4"
- height="20"
- width="4"
- viewbox="0 0 16 16"
- version="1.1"
- sodipodi:docname="toggle.svg"
- inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1920"
- inkscape:window-height="1017"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="32"
- inkscape:cx="-2.3727798"
- inkscape:cy="9.3760237"
- inkscape:window-x="-8"
- inkscape:window-y="-8"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg4" />
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <rect
- style="fill:#00000d;fill-opacity:1;stroke:#000000;stroke-width:0.5714286;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:19.89999962;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
- id="rect4513"
- width="1.4285715"
- height="1.4285713"
- x="1.2857143"
- y="13.285715" />
- <rect
- style="fill:#00000d;fill-opacity:1;stroke:#000000;stroke-width:0.5714286;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:19.89999962;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
- id="rect4513-2"
- width="1.4285715"
- height="1.4285715"
- x="1.2857143"
- y="17.285715" />
- <rect
- style="fill:#00000d;fill-opacity:1;stroke:#000000;stroke-width:0.5714286;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:19.89999962;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
- id="rect4513-6"
- width="1.4285715"
- height="1.4285715"
- x="1.2857143"
- y="9.2857151" />
- <rect
- style="fill:#00000d;fill-opacity:1;stroke:#000000;stroke-width:0.5714286;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:19.89999962;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
- id="rect4513-26"
- width="1.4285716"
- height="1.4285715"
- x="1.2857141"
- y="5.2857151" />
- <rect
- style="fill:#00000d;fill-opacity:1;stroke:#000000;stroke-width:0.5714286;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:19.89999962;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
- id="rect4513-9"
- width="1.4285715"
- height="1.4285715"
- x="1.2857143"
- y="1.2857143" />
-</svg>
diff --git a/img/yes-comment.svg b/img/yes-comment.svg
deleted file mode 100644
index 57d21f20..00000000
--- a/img/yes-comment.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
- <path
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#49bc49;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.39999998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
- d="M 2.7395491,2.114338 C 2.4067829,2.1147391 2.1371234,2.3821535 2.136719,2.7121494 l 0,6.7829882 c 4.043e-4,0.329996 0.2700638,0.5974114 0.6028301,0.5978124 l 0.5846175,0 -1.1049559,3.792712 3.2785391,-3.792712 7.7627012,0 c 0.332766,-4.01e-4 0.602426,-0.2678154 0.60283,-0.5978124 l 0,-6.7829882 C 13.862877,2.3821534 13.593217,2.1147389 13.260451,2.114338 Z" />
-</svg>
diff --git a/lib/Controller/AclController.php b/lib/Controller/AclController.php
index 0986084d..4d3467d7 100644
--- a/lib/Controller/AclController.php
+++ b/lib/Controller/AclController.php
@@ -66,6 +66,7 @@ class AclController extends Controller {
/**
* Read acl with share token
* @NoAdminRequired
+ * @PublicPage
* @NoCSRFRequired
* @param integer $pollId
* @return array
diff --git a/lib/Controller/EventController.php b/lib/Controller/EventController.php
index 794594c9..61a0f053 100644
--- a/lib/Controller/EventController.php
+++ b/lib/Controller/EventController.php
@@ -42,17 +42,16 @@ use OCA\Polls\Db\EventMapper;
use OCA\Polls\Service\EventService;
use OCA\Polls\Model\Acl;
-
-
class EventController extends Controller {
- private $userId;
- private $mapper;
- private $logger;
- private $groupManager;
- private $userManager;
- private $eventService;
- private $acl;
+ // private $userId;
+ // private $mapper;
+ // private $logger;
+ // private $groupManager;
+ // private $userManager;
+ // private $eventService;
+ // private $acl;
+ // private $event;
/**
* CommentController constructor.
@@ -73,6 +72,7 @@ class EventController extends Controller {
IRequest $request,
ILogger $logger,
EventMapper $mapper,
+ Event $event,
IGroupManager $groupManager,
IUserManager $userManager,
EventService $eventService,
@@ -85,6 +85,7 @@ class EventController extends Controller {
$this->groupManager = $groupManager;
$this->userManager = $userManager;
$this->eventService = $eventService;
+ $this->event = $event;
$this->acl = $acl;
}
@@ -99,7 +100,14 @@ class EventController extends Controller {
$events = [];
if (\OC::$server->getUserSession()->isLoggedIn()) {
try {
- $events = $this->mapper->findAll();
+
+ $events = array_filter($this->mapper->findAll(), function($item) {
+ if ($this->acl->setPollId($item->getId())->getAllowView()) {
+ return true;
+ } else {
+ return false;
+ }
+ });
} catch (DoesNotExistException $e) {
$events = [];
// return new DataResponse($e, Http::STATUS_NOT_FOUND);
@@ -117,56 +125,56 @@ class EventController extends Controller {
* @return array
*/
public function get($pollId) {
- $data = array();
try {
if (!$this->acl->getFoundByToken()) {
$this->acl->setPollId($pollId);
}
- $event = $this->mapper->find($pollId);
+ $this->event = $this->mapper->find($pollId);
} catch (DoesNotExistException $e) {
$this->logger->info('Poll ' . $pollId . ' not found!', ['app' => 'polls']);
return new DataResponse($e, Http::STATUS_NOT_FOUND);
}
- if ($event->getType() == 0) {
+ if ($this->event->getType() == 0) {
$pollType = 'datePoll';
} else {
$pollType = 'textPoll';
}
- $accessType = $event->getAccess();
- if (!strpos('|public|hidden|registered', $accessType)) {
- $accessType = 'select';
- }
- if ($event->getExpire() == null) {
- $expired = false;
- $expiration = false;
+ // TODO: add migration for this
+ if ($this->event->getAccess() === 'public') {
+ $accessType = 'public';
+ } else if ($this->event->getAccess() === 'hidden') {
+ $accessType = 'hidden';
+ } else if ($this->event->getAccess() === 'registered') {
+ $accessType = 'public';
+ $this->event->setAccess('public');
} else {
- $expired = time() > strtotime($event->getExpire());
- $expiration = true;
+ $accessType = 'hidden';
+ $this->event->setAccess('hidden');
}
return new DataResponse((object) [
- 'id' => $event->getId(),
- 'type' => $pollType,
- 'title' => $event->getTitle(),
- 'description' => $event->getDescription(),
- 'owner' => $event->getOwner(),
- 'ownerDisplayName' => $this->userManager->get($event->getOwner())->getDisplayName(),
- 'created' => $event->getCreated(),
- 'access' => $accessType,
- 'expiration' => $expiration,
- 'expired' => $expired,
- 'expirationDate' => $event->getExpire(),
- 'isAnonymous' => boolval($event->getIsAnonymous()),
- 'fullAnonymous' => boolval($event->getFullAnonymous()),
- 'allowMaybe' => boolval($event->getAllowMaybe()),
- 'acl' => $this->acl
- ],
- Http::STATUS_OK);
+ 'id' => $this->event->getId(),
+ 'type' => $pollType,
+ 'title' => $this->event->getTitle(),
+ 'description' => $this->event->getDescription(),
+ 'owner' => $this->event->getOwner(),
+ 'created' => $this->event->getCreated(),
+ 'access' => $this->event->getAccess(),
+ 'expire' => $this->event->getExpire(),
+ 'isAnonymous' => boolval($this->event->getIsAnonymous()),
+ 'fullAnonymous' => boolval($this->event->getFullAnonymous()),
+ 'allowMaybe' => boolval($this->event->getAllowMaybe()),
+ 'voteLimit' => $this->event->getVoteLimit(),
+ 'showResults' =>$this->event->getShowResults(),
+ 'deleted' => boolval($this->event->getDeleted()),
+ 'deleteDate' => $this->event->getDeleteDate()
+ ],
+ Http::STATUS_OK);
}
@@ -190,126 +198,63 @@ class EventController extends Controller {
}
-
-
/**
* Write poll (create/update)
* @NoAdminRequired
* @param Array $event
- * @param Array $options
- * @param Array $shares
- * @param string $mode
* @return DataResponse
*/
- public function add($event) {
- if (!\OC::$server->getUserSession()->isLoggedIn()) {
- return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
- }
-
- $NewEvent = new Event();
-
- // Set the configuration options entered by the user
- $NewEvent->setTitle($event['title']);
- $NewEvent->setDescription($event['description']);
-
- if ($event['type'] === 'datePoll') {
- $NewEvent->setType(0);
- } elseif ($event['type'] === 'textPoll') {
- $NewEvent->setType(1);
- }
-
- $NewEvent->setAccess('hidden');
- $NewEvent->setIsAnonymous(0);
- $NewEvent->setFullAnonymous(0);
- $NewEvent->setAllowMaybe(0);
-
- $NewEvent->setOwner($this->userId);
- $NewEvent->setCreated(date('Y-m-d H:i:s'));
- $NewEvent = $this->mapper->insert($NewEvent);
-
- return new DataResponse($this->get($NewEvent->getId()), Http::STATUS_OK);
-
- }
-
- /**
- * Write poll (create/update)
- * @NoAdminRequired
- * @param Array $event
- * @param Array $options
- * @param Array $shares
- * @param string $mode
- * @return DataResponse
- */
-
- public function write($event, $mode) {
- if (!\OC::$server->getUserSession()->isLoggedIn()) {
- return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
- } else {
- $adminAccess = $this->groupManager->isAdmin($this->userId);
- }
-
- $NewEvent = new Event();
-
- // Set the configuration options entered by the user
- $NewEvent->setTitle($event['title']);
- $NewEvent->setDescription($event['description']);
-
- $NewEvent->setIsAnonymous(intval($event['isAnonymous']));
- $NewEvent->setFullAnonymous(intval($event['fullAnonymous']));
- $NewEvent->setAllowMaybe(intval($event['allowMaybe']));
-
- if ($event['access'] === 'select') {
- } else {
- $NewEvent->setAccess($event['access']);
- }
-
- if ($event['expiration']) {
- $NewEvent->setExpire(date('Y-m-d H:i:s', strtotime($event['expirationDate'])));
- } else {
- $NewEvent->setExpire(null);
- }
-
- if ($event['type'] === 'datePoll') {
- $NewEvent->setType(0);
- } elseif ($event['type'] === 'textPoll') {
- $NewEvent->setType(1);
- }
+ public function write($event) {
try {
// Find existing poll
- $oldEvent = $this->mapper->find($event['id']);
+ $this->event = $this->mapper->find($event['id']);
+ $this->acl->setPollId($this->event->getId());
- // Check if current user is allowed to edit existing poll
- if ($oldEvent->getOwner() !== $this->userId && !$adminAccess) {
- // If current user is not owner of existing poll deny access
- return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
+ if (!$this->acl->getAllowEdit()) {
+ $this->logger->alert('Unauthorized delete attempt from user ' . $this->userId);
+ return new DataResponse('Unauthorized delete attempt.', Http::STATUS_UNAUTHORIZED);
}
- // else take owner, and id of existing poll
- $NewEvent->setOwner($oldEvent->getOwner());
- $NewEvent->setId($oldEvent->getId());
- try {
- $this->mapper->update($NewEvent);
-
- } catch (Exception $e) {
- $this->logger->alert('Poll ' . $oldEvent['id'] . ' not found!', ['app' => 'polls']);
- return new DataResponse(null, Http::STATUS_NOT_FOUND);
+ } catch (Exception $e) {
+ $this->event = new Event();
+
+ // $this->event->setType($event['type']);
+ if ($event['type'] === 'datePoll') {
+ $this->event->setType(0);
+ } elseif ($event['type'] === 'textPoll') {
+ $this->event->setType(1);
+ } else {
+ $this->event->setType($event['type']);
}
- } catch (Exception $e) {
+ $this->event->setOwner($this->userId);
+ $this->event->setCreated(date('Y-m-d H:i:s',time()));
+ $this->logger->error(date('Y-m-d H:i:s',time()));
+ } finally {
- // Create new poll
- // Define current user as owner, set new creation date
- $NewEvent->setOwner($this->userId);
- $NewEvent->setCreated(date('Y-m-d H:i:s'));
- $NewEvent = $this->mapper->insert($NewEvent);
+ $this->event->setTitle($event['title']);
+ $this->event->setDescription($event['description']);
- } finally {
+ $this->event->setAccess($event['access']);
+ // $this->event->setExpire($event['expire']);
+ if ($event['expire']) {
+ $this->event->setExpire(date('Y-m-d H:i:s', strtotime($event['expire'])));
+ } else {
+ $this->event->setExpire(null);
+ }
+ $this->event->setIsAnonymous(intval($event['isAnonymous']));
+ $this->event->setFullAnonymous(intval($event['fullAnonymous']));
+ $this->event->setAllowMaybe(intval($event['allowMaybe']));
+ $this->event->setDeleted($event['deleted']);
+ // $this->event->setDeleteDate(time());
+ $this->event->setVoteLimit(intval($event['voteLimit']));
+ $this->event->setShowResults($event['showResults']);
- return new DataResponse($this->get($NewEvent->getId()), Http::STATUS_OK);
+ $this->mapper->insertOrUpdate($this->event);
+ return new DataResponse($this->event, Http::STATUS_OK);
}
-
}
}
diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php
index 06b88078..5d7e2602 100644
--- a/lib/Controller/PageController.php
+++ b/lib/Controller/PageController.php
@@ -58,7 +58,16 @@ class PageController extends Controller {
* @NoAdminRequired
* @NoCSRFRequired
*/
- public function index() {
+ public function index($path = 'all') {
+ return new TemplateResponse('polls', 'polls.tmpl',
+ ['urlGenerator' => $this->urlGenerator]);
+ }
+
+ /**
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ */
+ public function all($path = 'all') {
return new TemplateResponse('polls', 'polls.tmpl',
['urlGenerator' => $this->urlGenerator]);
}
diff --git a/lib/Controller/ShareController.php b/lib/Controller/ShareController.php
index ab00c1ce..d1c4aae8 100644
--- a/lib/Controller/ShareController.php
+++ b/lib/Controller/ShareController.php
@@ -110,7 +110,7 @@ class ShareController extends Controller {
* @return DataResponse
*/
public function getShares($pollId) {
- // $this->acl->setPollId($pollId)
+
if ($this->acl->setPollId($pollId)->getAllowEdit()) {
try {
$event = $this->eventMapper->find($pollId);
diff --git a/lib/Db/Event.php b/lib/Db/Event.php
index a6aa7080..289c1c5d 100644
--- a/lib/Db/Event.php
+++ b/lib/Db/Event.php
@@ -50,6 +50,8 @@ use OCP\AppFramework\Db\Entity;
* @method void setFullAnonymous(integer $value)
* @method integer getAllowMaybe()
* @method void setAllowMaybe(integer $value)
+ * @method integer getData()
+ * @method void setData(integer $value)
*/
class Event extends Entity implements JsonSerializable {
protected $type;
@@ -63,6 +65,10 @@ class Event extends Entity implements JsonSerializable {
protected $isAnonymous;
protected $fullAnonymous;
protected $allowMaybe;
+ protected $showResults;
+ protected $voteLimit;
+ protected $deleted;
+ protected $deleteDate;
public function jsonSerialize() {
return [
@@ -74,9 +80,13 @@ class Event extends Entity implements JsonSerializable {
'created' => $this->created,
'access' => $this->access,
'expire' => $this->expire,
- 'isAnonymous' => $this->isAnonymous,
- 'fullAnonymous' => $this->fullAnonymous,
- 'allowMaybe' => $this->allowMaybe
+ 'isAnonymous' => boolval($this->isAnonymous),
+ 'fullAnonymous' => boolval($this->fullAnonymous),
+ 'allowMaybe' => boolval($this->allowMaybe),
+ 'voteLimit' => $this->showResults,
+ 'showResults' => $this->showResults,
+ 'deleted' => boolval($this->deleted),
+ 'deleteDate' => $this->deleteDate
];
}
}
diff --git a/lib/Db/EventMapper.php b/lib/Db/EventMapper.php
index f8fcd3c3..b9788cd2 100644
--- a/lib/Db/EventMapper.php
+++ b/lib/Db/EventMapper.php
@@ -65,7 +65,8 @@ class EventMapper extends QBMapper {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
- ->from($this->getTableName());
+ ->from($this->getTableName())
+;
return $this->findEntities($qb);
}
diff --git a/lib/Migration/Version0010Date20190801063812.php b/lib/Migration/Version0010Date20190801063812.php
index a334e056..c6c2fecd 100644
--- a/lib/Migration/Version0010Date20190801063812.php
+++ b/lib/Migration/Version0010Date20190801063812.php
@@ -32,6 +32,7 @@ use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Migration\SimpleMigrationStep;
use OCP\Migration\IOutput;
+use OCP\Security\ISecureRandom;
/**
* Installation class for the polls app.
@@ -65,6 +66,33 @@ class Version0010Date20190801063812 extends SimpleMigrationStep {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
+ if ($schema->hasTable('polls_events')) {
+ $table = $schema->getTable('polls_events');
+ if (!$table->hasColumn('deleted')) {
+ $table->addColumn('deleted', Type::BOOLEAN, [
+ 'notnull' => false,
+ 'default' => 0
+ ]);
+ }
+ if (!$table->hasColumn('delete_date')) {
+ $table->addColumn('delete_date', Type::DATETIME, [
+ 'notnull' => true
+ ]);
+ }
+ if (!$table->hasColumn('vote_limit')) {
+ $table->addColumn('vote_limit', Type::INTEGER, [
+ 'notnull' => false,
+ 'default' => 0
+ ]);
+ }
+ if (!$table->hasColumn('show_results')) {
+ $table->addColumn('show_results', Type::STRING, [
+ 'notnull' => true,
+ 'lenght' => 64
+ ]);
+ }
+
+ }
if (!$schema->hasTable('polls_share')) {
$table = $schema->createTable('polls_share');
$table->addColumn('id', Type::INTEGER, [
@@ -79,9 +107,8 @@ class Version0010Date20190801063812 extends SimpleMigrationStep {
'notnull' => true,
'length' => 128,
]);
- $table->addColumn('poll_id', Type::STRING, [
- 'notnull' => true,
- 'length' => 128,
+ $table->addColumn('poll_id', Type::INTEGER, [
+ 'notnull' => true
]);
$table->addColumn('user_id', Type::STRING, [
'notnull' => false,
@@ -109,11 +136,12 @@ class Version0010Date20190801063812 extends SimpleMigrationStep {
if ($schema->hasTable('polls_share')) {
$this->copyTokens();
+ // $this->copyInvitationTokens();
}
}
/**
- * Copy date options
+ * Copy public tokens
*/
protected function copyTokens() {
$insert = $this->connection->getQueryBuilder();
@@ -123,23 +151,67 @@ class Version0010Date20190801063812 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')
]);
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('polls_events');
$result = $query->execute();
+
while ($row = $result->fetch()) {
if ($row['access'] == 'public') {
+ // copy the hash to a public share
+ $insert
+ ->setParameter('token', $row['hash'])
+ ->setParameter('type', 'public')
+ ->setParameter('poll_id', $row['id'])
+ ->setParameter('user_id', null)
+ ->setParameter('user_email', null);
+ $insert->execute();
+ } elseif ($row['access'] == 'hidden') {
+ // copy the hash to a public share
+ // poll stays hidden for registered users
$insert
->setParameter('token', $row['hash'])
- ->setParameter('type', $row['access'])
+ ->setParameter('type', 'public')
->setParameter('poll_id', $row['id'])
->setParameter('user_id', null)
->setParameter('user_email', null);
$insert->execute();
+ } elseif ($row['access'] == 'registered') {
+ // copy the hash to a public share
+ // to keep the hash
+ $insert
+ ->setParameter('token', $row['hash'])
+ ->setParameter('type', 'public')
+ ->setParameter('poll_id', $row['id'])
+ ->setParameter('user_id', null)
+ ->setParameter('user_email', null);
+ } else {
+ // create a personal share for invitated users
+
+ // explode the access entry to single access strings
+ $users = explode(';', $row['access']);
+ foreach ($users as $value) {
+ // separate 'user' and 'group' from user names and create
+ // a share for every entry
+ $parts = explode('_', $value);
+ $insert
+ ->setParameter('token', \OC::$server->getSecureRandom()->generate(
+ 16,
+ ISecureRandom::CHAR_DIGITS .
+ ISecureRandom::CHAR_LOWER .
+ ISecureRandom::CHAR_UPPER
+ ))
+ ->setParameter('type', $parts[0])
+ ->setParameter('poll_id', $row['id'])
+ ->setParameter('user_id', $parts[1])
+ ->setParameter('user_email', null);
+ $insert->execute();
+ }
}
}
$result->closeCursor();
}
+
}
diff --git a/lib/Model/Acl.php b/lib/Model/Acl.php
index 035ca50d..245b0d08 100644
--- a/lib/Model/Acl.php
+++ b/lib/Model/Acl.php
@@ -29,7 +29,9 @@ use Exception;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\IGroupManager;
+use OCP\ILogger;
use OCA\Polls\Db\Event;
+use OCA\Polls\Db\Share;
use OCA\Polls\Db\EventMapper;
use OCA\Polls\Db\ShareMapper;
@@ -42,6 +44,10 @@ class Acl implements JsonSerializable {
/** @var int */
private $pollId = 0;
+ private $logger;
+
+ /** @var array */
+ private $shares = [];
/** @var string */
private $token = '';
@@ -66,6 +72,7 @@ class Acl implements JsonSerializable {
public function __construct(
string $appName,
$userId,
+ ILogger $logger,
IGroupManager $groupManager,
EventMapper $eventMapper,
ShareMapper $shareMapper,
@@ -76,11 +83,12 @@ class Acl implements JsonSerializable {
$this->eventMapper = $eventMapper;
$this->shareMapper = $shareMapper;
$this->event = $event;
+ $this->logger = $logger;
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return string
*/
public function getUserId() {
@@ -88,7 +96,7 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return string
*/
public function setUserId($userId): Acl {
@@ -97,7 +105,7 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return int
*/
public function getPollId(): int {
@@ -105,18 +113,19 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return int
*/
public function setPollId(int $pollId): Acl {
$this->pollId = $pollId;
$this->event = $this->eventMapper->find($this->pollId);
+ $this->shares = $this->shareMapper->findByPoll($this->pollId);
return $this;
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return bool
*/
public function getIsOwner(): bool {
@@ -128,7 +137,7 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return bool
*/
public function getIsAdmin(): bool {
@@ -140,23 +149,58 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return bool
*/
public function getAllowView(): bool {
- if ($this->pollId) {
- return true;
- } else {
- return false;
- }
+ return (
+ $this->getIsOwner()
+ || $this->getIsAdmin()
+ || ($this->getGroupShare() && !$this->event->getDeleted())
+ || ($this->getPersonalShare() && !$this->event->getDeleted())
+ || $this->event->getAccess() !== 'hidden'
+ );
+ }
+
+ /**
+ * @NoAdminRequired
+ * @return bool
+ */
+ public function getGroupShare(): bool {
+ return count(
+ array_filter($this->shareMapper->findByPoll($this->getPollId()), function($item) {
+ if ($item->getType() === 'group' && $this->groupManager->isInGroup($this->getUserId(),$item->getUserId())) {
+ return true;
+ }
+ })
+ );
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
+ * @return bool
+ */
+ public function getPersonalShare(): bool {
+
+ return count(
+ array_filter($this->shareMapper->findByPoll($this->getPollId()), function($item) {
+ if ($item->getType() === 'user' && $item->getUserId() === $this->getUserId()) {
+ return true;
+ }
+ })
+ );
+ }
+
+ /**
+ * @NoAdminRequired
* @return bool
*/
public function getAllowVote(): bool {
- if ($this->pollId) {
+ // $this->logger->error('Expiration: ' . $this->event->getExpire());
+ // $this->logger->error('Expiration time: ' . strtotime($this->event->getExpire()));
+ // $this->logger->error('low: ' . time());
+ // $this->logger->error('compare: ' . (strtotime($this->event->getExpire()) > time()));
+ if ($this->getAllowView() && strtotime($this->event->getExpire()) > time()) {
return true;
} else {
return false;
@@ -164,7 +208,7 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return bool
*/
public function getAllowComment(): bool {
@@ -172,7 +216,7 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return bool
*/
public function getAllowEdit(): bool {
@@ -180,7 +224,7 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return bool
*/
public function getAllowSeeUsernames(): bool {
@@ -188,7 +232,7 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return bool
*/
public function getAllowSeeAllVotes(): bool {
@@ -201,7 +245,7 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return bool
*/
public function getFoundByToken(): bool {
@@ -209,7 +253,7 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
+ * @NoAdminRequired
* @return string
*/
public function getToken(): string {
@@ -217,6 +261,7 @@ class Acl implements JsonSerializable {
}
/**
+ * @NoAdminRequired
* @return string
*/
public function setToken(string $token): Acl {
@@ -248,8 +293,8 @@ class Acl implements JsonSerializable {
}
/**
- * @NoAdminRequired
- * @return string
+ * @NoAdminRequired
+ * @return string
*/
public function getAccessLevel(): string {
if ($this->getIsOwner()) {
@@ -283,6 +328,8 @@ class Acl implements JsonSerializable {
'allowEdit' => $this->getAllowEdit(),
'allowSeeUsernames' => $this->getAllowSeeUsernames(),
'allowSeeAllVotes' => $this->getAllowSeeAllVotes(),
+ 'groupShare' => $this->getGroupShare(),
+ 'personalShare' => $this->getPersonalShare(),
'foundByToken' => $this->getFoundByToken(),
'accessLevel' => $this->getAccessLevel()
];
diff --git a/package.json b/package.json
index 41d92896..fc081bea 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
"test:coverage": "jest --coverage"
},
"dependencies": {
- "@babel/runtime": "^7.7.4",
+ "@babel/runtime": "^7.7.6",
"@nextcloud/vue": "^1.2.2",
"acorn": "^7.1.0",
"acorn-jsx": "^5.1.0",
@@ -66,16 +66,16 @@
"node": ">=10.0.0"
},
"devDependencies": {
- "@babel/core": "^7.7.4",
+ "@babel/core": "^7.7.5",
"@babel/plugin-syntax-dynamic-import": "^7.7.4",
- "@babel/preset-env": "^7.7.4",
+ "@babel/preset-env": "^7.7.6",
"babel-eslint": "^10.0.2",
"babel-loader": "^8.0.6",
- "css-loader": "^3.2.0",
+ "css-loader": "^3.2.1",
"eslint": "^5.16.0",
"eslint-config-standard": "^12.0.0",
"eslint-friendly-formatter": "^4.0.1",
- "eslint-loader": "^3.0.2",
+ "eslint-loader": "^3.0.3",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-node": "^10.0.0",
"eslint-plugin-promise": "^4.2.1",
diff --git a/src/js/assets/app.png b/src/js/assets/app.png
new file mode 100644
index 00000000..84a879bb
--- /dev/null
+++ b/src/js/assets/app.png
Binary files differ
diff --git a/src/js/assets/app.svg b/src/js/assets/app.svg
new file mode 100644
index 00000000..cbf61396
--- /dev/null
+++ b/src/js/assets/app.svg
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ id="svg8"
+ viewBox="0 0 32 32"
+ x="0px"
+ y="0px"
+ enable-background="new 0 0 595.275 311.111"
+ width="32"
+ height="32"
+ xml:space="preserve"
+ version="1.1"><metadata
+ id="metadata14"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs12" /><rect
+ id="rect2"
+ y="2"
+ x="3"
+ height="26"
+ width="7"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.93541431" /><rect
+ id="rect4"
+ y="12"
+ x="12"
+ height="16"
+ width="7"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.93541431" /><rect
+ id="rect6"
+ y="8"
+ x="21"
+ height="20"
+ width="7"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.8918826" /></svg> \ No newline at end of file
diff --git a/src/js/components/PollList/PollListItem.vue b/src/js/components/PollList/PollListItem.vue
index 151a18b4..95c0468a 100644
--- a/src/js/components/PollList/PollListItem.vue
+++ b/src/js/components/PollList/PollListItem.vue
@@ -45,7 +45,7 @@
</div>
<div v-else class="pollListItem poll">
- <div v-tooltip.auto="pollType" class="thumbnail" :class="[pType, {expired : poll.event.expired}]">
+ <div v-tooltip.auto="pollType" class="thumbnail" :class="[pType, {expired : poll.expired}]">
{{ pollType }}
</div>
@@ -53,10 +53,10 @@
<router-link :to="{name: 'vote', params: {id: poll.id}}" class="title">
<div class="name">
- {{ poll.event.title }}
+ {{ poll.title }}
</div>
<div class="description">
- {{ poll.event.description }}
+ {{ poll.description }}
</div>
</router-link>
@@ -78,14 +78,14 @@
</div>
<div class="owner">
- <user-div :user-id="poll.event.owner" :display-name="poll.event.ownerDisplayName" />
+ <user-div :user-id="poll.owner" :display-name="poll.ownerDisplayName" />
</div>
<div class="dates">
<div class="created ">
{{ timeSpanCreated }}
</div>
- <div class="expiry" :class="{ expired : poll.event.expired }">
+ <div class="expiry" :class="{ expired : poll.expired }">
{{ timeSpanExpiration }}
</div>
</div>
@@ -120,17 +120,27 @@ export default {
// TODO: dity hack
aType() {
- if (this.poll.event.access === 'public') {
- return this.poll.event.access
- } else if (this.poll.event.access === 'registered') {
- return this.poll.event.access
- } else if (this.poll.event.access === 'hidden') {
- return this.poll.event.access
+ if (this.poll.access === 'public') {
+ return this.poll.access
+ } else if (this.poll.access === 'registered') {
+ return this.poll.access
+ } else if (this.poll.access === 'hidden') {
+ return this.poll.access
} else {
return 'select'
}
},
+ expired() {
+ if (this.poll.expire === null) {
+ return false
+ } else if (Date.parse(this.poll.expire) < Date.now()) {
+ return false
+ } else {
+ return true
+ }
+ },
+
accessType() {
if (this.aType === 'public') {
return t('polls', 'Public access')
@@ -147,7 +157,7 @@ export default {
// TODO: dity hack
pType() {
- if (this.poll.event.type === '1') {
+ if (this.poll.type === '1') {
// TRANSLATORS This means that this is the type of the poll. Another type is a 'date poll'.
return t('polls', 'textPoll')
} else {
@@ -168,41 +178,19 @@ export default {
},
timeSpanCreated() {
- return moment(this.poll.event.created).fromNow()
+ return moment(this.poll.created).fromNow()
},
timeSpanExpiration() {
- if (this.poll.event.expiration) {
- return moment(this.poll.event.expirationDate).fromNow()
+ if (this.poll.expire) {
+ return moment(this.poll.expire).fromNow()
} else {
return t('polls', 'never')
}
},
- participants() {
- return this.poll.votes
- .map(item => item.userId)
- .filter((value, index, self) => self.indexOf(value) === index)
- },
- countvotes() {
- return this.participants.length
- },
- countComments() {
- if (this.poll.comments.length > 999) {
- return '999+'
- }
- return this.poll.comments.length
- },
- countCommentsHint() {
- return n('polls', 'There is %n comment', 'There are %n comments', this.poll.comments.length)
- },
- countShares() {
- return this.poll.shares.length
- },
- votedBycurrentUser() {
- return this.participants.indexOf(OC.getCurrentUser().uid) > -1
- },
+
voteUrl() {
- return OC.generateUrl('apps/polls/poll/') + this.poll.event.id
+ return OC.generateUrl('apps/polls/poll/') + this.poll.id
},
menuItems() {
@@ -221,7 +209,7 @@ export default {
}
]
- if (this.poll.event.owner === OC.getCurrentUser().uid) {
+ if (this.poll.owner === OC.getCurrentUser().uid) {
// items.push({
// key: 'editPoll',
// icon: 'icon-rename',
diff --git a/src/js/components/base/sideBarClose.vue b/src/js/components/VoteTable/VoteHeader.vue
index 6ff1c14f..d8eb7fc7 100644
--- a/src/js/components/base/sideBarClose.vue
+++ b/src/js/components/VoteTable/VoteHeader.vue
@@ -21,27 +21,57 @@
-->
<template>
- <div class="close flex-row">
- <a id="closeDetails" :title="closeDetailLabel" :alt="closeDetailLabelAlt"
- class="close icon-close has-tooltip-bottom" href="#" @:click="hideSidebar" />
+ <div class="voteHeader">
+ <h2>
+ {{ event.title }}
+ <span v-if="expired" class="label error">{{ t('polls', 'Expired since %n', 1, timeSpanExpiration) }}</span>
+ <span v-if="!expired && isExpirationSet" class="label success">{{ t('polls', 'Place your votes until %n', 1, event.expire) }}</span>
+ <span v-if="!isExpirationSet" class="label success">{{ t('polls', 'No expiration date set') }}</span>
+ </h2>
+ <h3>
+ {{ event.description }}
+ </h3>
</div>
</template>
<script>
+import { mapState, mapGetters } from 'vuex'
+
export default {
- name: 'SideBarClose',
+ name: 'VoteHeader',
data() {
return {
- closeDetailLabel: t('Close details'),
- closeDetailLabelAlt: t('Close')
+ voteSaved: false,
+ delay: 50,
+ newName: ''
}
},
+ computed: {
+ ...mapState({
+ event: state => state.event
+ }),
+
+ ...mapGetters([
+ 'isExpirationSet',
+ 'expired',
+ 'timeSpanExpiration'
+ ])
+
+ },
+
methods: {
- hideSidebar() {
- OC.Apps.hideAppSidebar()
+ indicateVoteSaved() {
+ this.voteSaved = true
+ window.setTimeout(this.timer, this.delay)
}
}
}
</script>
+
+<style lang="scss" scoped>
+ .voteHeader {
+ margin: 8px 24px;
+ }
+</style>
diff --git a/src/js/components/VoteTable/VoteHeaderPublic.vue b/src/js/components/VoteTable/VoteHeaderPublic.vue
new file mode 100644
index 00000000..4c679729
--- /dev/null
+++ b/src/js/components/VoteTable/VoteHeaderPublic.vue
@@ -0,0 +1,221 @@
+<!--
+ - @copyright Copyright (c) 2018 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/>.
+ -
+ -->
+
+<template>
+ <div class="voteHeader">
+ <div v-if="!isValidUser" class="getUsername">
+ <!-- <label>
+ {{ t('polls', 'Enter a valid username, to participate in this poll.') }}
+ </label> -->
+
+ <form v-if="!redirecting">
+ <input v-model="userName" :class="{ error: (!isValidName && userName.length > 0), success: isValidName }" type="text"
+ :placeholder="t('polls', 'Enter a valid username with at least 3 Characters')">
+ <input v-show="isValidName && !checkingUserName" class="icon-confirm" :class="{ error: !isValidName, success: isValidName }"
+ @click="writeUserName">
+ <span v-show="checkingUserName" class="icon-loading-small" />
+ <!-- <span v-if="!isValidName" class="error"> {{ invalidUserNameMessage }} </span> -->
+ </form>
+ <div v-else>
+ <span>{{ t('polls', 'You will be redirected to your personal share.') }}</span>
+ <span>
+ {{ t('polls', 'If you are not redirected to your poll click this link:') }}
+ <router-link :to="{ name: 'publicVote', params: { token: token }}">
+ Link
+ </router-link>
+ </span>
+ </div>
+ </div>
+ <div v-if="displayLink" class="personal-link">
+ {{ t('polls', 'Your personal link to this poll: %n', 1, personalLink) }}
+ <a class="icon icon-clippy" @click="copyLink( { url: OC.generateUrl($route.path) } )" />
+ </div>
+ </div>
+</template>
+
+<script>
+import debounce from 'lodash/debounce'
+import axios from 'nextcloud-axios'
+import { mapState } from 'vuex'
+
+export default {
+ name: 'VoteHeaderPublic',
+
+ data() {
+ return {
+ userName: '',
+ token: '',
+ checkingUserName: false,
+ redirecting: false,
+ isValidName: false,
+ newName: ''
+ }
+ },
+
+ computed: {
+ ...mapState({
+ event: state => state.event,
+ acl: state => state.acl
+ }),
+
+ personalLink() {
+ return location.protocol.concat('//', window.location.hostname, OC.generateUrl(this.$route.path))
+ },
+
+ displayLink() {
+ return (this.acl.userId !== '' && this.acl.userId !== null && this.acl.foundByToken)
+ },
+
+ isValidUser() {
+ return (this.acl.userId !== '' && this.acl.userId !== null)
+ }
+
+ },
+
+ watch: {
+ userName: function() {
+ if (this.userName.length > 2) {
+ this.isValidName = this.validatePublicUsername()
+ } else {
+ this.invalidUserNameMessage = t('polls', 'Please use at least 3 characters for your user name!')
+ this.isValidName = false
+ }
+ }
+ },
+
+ methods: {
+ copyLink(payload) {
+ this.$copyText(window.location.origin + payload.url).then(
+ function(e) {
+ OC.Notification.showTemporary(t('polls', 'Link copied to clipboard'), { type: 'success' })
+ },
+ function(e) {
+ OC.Notification.showTemporary(t('polls', 'Error while copying link to clipboard'), { type: 'error' })
+ }
+ )
+ },
+ validatePublicUsername: debounce(function() {
+ if (this.userName.length > 2) {
+ this.checkingUserName = true
+ return axios.post(OC.generateUrl('apps/polls/check/username'), { pollId: this.event.id, userName: this.userName, token: this.$route.params.token })
+ .then((response) => {
+ this.checkingUserName = false
+ this.isValidName = true
+ this.invalidUserNameMessage = 'User name is OK.'
+ return true
+ })
+ .catch(() => {
+ this.checkingUserName = false
+ this.isValidName = false
+ this.invalidUserNameMessage = t('polls', 'This user name can not be choosed.')
+ return false
+ })
+ } else {
+ this.checkingUserName = false
+ this.isValidName = false
+ this.invalidUserNameMessage = t('polls', 'Please use at least 3 characters for your user name!')
+ return false
+ }
+ }, 500),
+
+ writeUserName() {
+ if (this.validatePublicUsername()) {
+ this.$store.dispatch('addShareFromUser', { token: this.$route.params.token, userName: this.userName })
+ .then((response) => {
+ this.token = response.token
+ this.redirecting = true
+ this.$router.replace({ name: 'publicVote', params: { 'token': response.token } })
+ })
+ .catch(() => {
+ OC.Notification.showTemporary(t('polls', 'Error saving user name"', 1, event.title), { type: 'error' })
+ })
+ }
+ }
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+ .voteHeader {
+ margin: 8px 24px;
+ }
+
+ .personal-link {
+ display: flex;
+ padding: 4px 12px;
+ margin: 0 12px 0 24px;
+ border: 2px solid var(--color-success);
+ background-color: #d6fdda !important;
+ border-radius: var(--border-radius);
+ font-size: 1.2em;
+ opacity: 0.8;
+ .icon {
+ margin: 0 12px;
+ }
+ }
+
+ .getUsername {
+ & > label {
+ margin-right: 12px;
+ }
+
+ margin: 0 12px 12px 24px;
+ border:2px solid var(--color-border-dark);
+ font-size: 1.2em;
+ padding: 0 12px 0 12px;
+ display: flex;
+ align-items: center;
+ border-radius: var(--border-radius);
+ background-color: var(--color-background-dark);
+ flex-wrap: wrap;
+
+ form, div {
+ flex: 1;
+ display: flex;
+
+ }
+ input {
+ flex: 1;
+ }
+
+ .icon-loading-small {
+ position: relative;
+ right: 24px;
+ top: 0px;
+ }
+
+ input[type="text"] + .icon-confirm, input[type="text"] + .icon-loading-small {
+ flex: 0;
+ margin-left: -8px !important;
+ border-left-color: transparent !important;
+ border-radius: 0 var(--border-radius) var(--border-radius) 0 !important;
+ background-clip: padding-box;
+ opacity: 1;
+ height: 34px;
+ width: 34px;
+ padding: 7px 20px;
+ cursor: pointer;
+ margin-right: 0;
+ }
+ }
+
+</style>
diff --git a/src/js/components/VoteTable/VoteTable.vue b/src/js/components/VoteTable/VoteTable.vue
index a5b534e1..c5c8a48b 100644
--- a/src/js/components/VoteTable/VoteTable.vue
+++ b/src/js/components/VoteTable/VoteTable.vue
@@ -32,8 +32,7 @@
<VoteTableHeader v-for="(option) in sortedOptions"
:key="option.id"
:option="option"
- :poll-type="event.type"
- :mode="poll.mode" />
+ :poll-type="event.type" />
</div>
<div v-for="(participant) in participants" :key="participant" :class="{currentuser: (participant === currentUser) }">
@@ -64,8 +63,8 @@ export default {
computed: {
...mapState({
- poll: state => state.poll,
- event: state => state.event
+ event: state => state.event,
+ acl: state => state.acl
}),
...mapGetters([
@@ -74,7 +73,7 @@ export default {
]),
currentUser() {
- return this.event.acl.userId
+ return this.acl.userId
},
noOptions() {
@@ -95,7 +94,7 @@ export default {
setTo: nextAnswer
})
.then(() => {
- this.$emit('voteSaved')
+ // this.$emit('voteSaved')
})
}
}
diff --git a/src/js/components/VoteTable/VoteTableHeader.vue b/src/js/components/VoteTable/VoteTableHeader.vue
index 33db5e6c..28634cb8 100644
--- a/src/js/components/VoteTable/VoteTableHeader.vue
+++ b/src/js/components/VoteTable/VoteTableHeader.vue
@@ -24,7 +24,6 @@
<div class="vote-header" :class=" { winner: isWinner }">
<div v-if="textPoll" class="text-box">
{{ option.pollOptionText }}
- <a v-if="mode === 'edit'" class="icon-delete" @click="$emit('remove')" />
</div>
<div v-if="datePoll" v-tooltip.auto="localFullDate" class="date-box">
@@ -68,10 +67,6 @@ export default {
pollType: {
type: String,
default: undefined
- },
- mode: {
- type: String,
- default: 'vote'
}
},
diff --git a/src/js/components/VoteTable/VoteTableItem.vue b/src/js/components/VoteTable/VoteTableItem.vue
index 0b83311f..1e3526c0 100644
--- a/src/js/components/VoteTable/VoteTableItem.vue
+++ b/src/js/components/VoteTable/VoteTableItem.vue
@@ -45,7 +45,8 @@ export default {
computed: {
...mapState({
- event: state => state.event
+ event: state => state.event,
+ acl: state => state.acl
}),
answer() {
@@ -64,7 +65,7 @@ export default {
},
isActive() {
- return (this.isValidUser && this.event.acl.userId === this.userId && !this.event.expired)
+ return (this.isValidUser && this.acl.userId === this.userId && !this.event.expired)
}
},
diff --git a/src/js/components/base/sideBar.vue b/src/js/components/base/sideBar.vue
deleted file mode 100644
index 0b9eb190..00000000
--- a/src/js/components/base/sideBar.vue
+++ /dev/null
@@ -1,45 +0,0 @@
-<!--
- - @copyright Copyright (c) 2018 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/>.
- -
- -->
-
-<template>
- <div class="polls-sidebar">
- <slot />
- </div>
-</template>
-
-<script>
-export default {
- name: 'SideBar'
-}
-</script>
-
-<style lang="scss">
-.polls-sidebar {
- min-width: 300px;
- border-left: 1px solid var(--color-border);
- z-index: 500;
- > ul,
- > div {
- padding: 8px;
- }
-}
-</style>
diff --git a/src/js/components/create/createDlg.vue b/src/js/components/create/createDlg.vue
index c7136373..4330abb9 100644
--- a/src/js/components/create/createDlg.vue
+++ b/src/js/components/create/createDlg.vue
@@ -23,19 +23,19 @@
<template lang="html">
<div class="create-dialog">
<h2>{{ t('polls', 'Create new poll') }}</h2>
- <input id="pollTitle" v-model="event.title" type="text"
+ <input id="pollTitle" v-model="title" type="text"
:placeholder="t('polls', 'Enter Title')">
<div class="configBox">
<label class="title icon-checkmark">
{{ t('polls', 'Poll type') }}
</label>
- <input id="datePoll" v-model="event.type" value="datePoll"
+ <input id="datePoll" v-model="type" value="datePoll"
:disabled="protect" type="radio" class="radio">
<label for="datePoll">
{{ t('polls', 'Event schedule') }}
</label>
- <input id="textPoll" v-model="event.type" value="textPoll"
+ <input id="textPoll" v-model="type" value="textPoll"
:disabled="protect" type="radio" class="radio">
<label for="textPoll">
{{ t('polls', 'Text based') }}
@@ -54,47 +54,48 @@
</template>
<script>
+import { mapState, mapMutations } from 'vuex'
export default {
name: 'CreateDlg',
data() {
return {
- event: {
- title: '',
- description: '',
- type: 'datePoll',
- allowMaybe: false,
- isAnonymous: false,
- trueAnonymous: false,
- expiration: false,
- expirationDate: '',
- access: 'hidden'
-
- }
+ id: 0,
+ type: 'datePoll',
+ title: ''
}
},
computed: {
+ ...mapState({
+ event: state => state.event
+ }),
+
titleEmpty() {
- return this.event.title === ''
+ return this.title === ''
}
},
methods: {
+ ...mapMutations([ 'setEventProperty', 'resetEvent', 'reset' ]),
+
cancel() {
- this.event.title = ''
- this.event.type = 'datePoll'
+ this.title = ''
+ this.type = 'datePoll'
this.$emit('closeCreate')
},
confirm() {
- this.$store
- .dispatch('addEventPromise', { event: this.event })
+ this.resetEvent()
+ this.reset()
+ this.setEventProperty({ 'id': 0 })
+ this.setEventProperty({ 'title': this.title })
+ this.setEventProperty({ 'type': this.type })
+ this.$store.dispatch('writeEventPromise')
.then((response) => {
- OC.Notification.showTemporary(t('polls', 'Poll "%n" added', 1, this.event.title), { type: 'success' })
- this.$store.dispatch('loadPolls')
- this.$router.push({ name: 'edit', params: { id: response.data.id } })
this.cancel()
+ OC.Notification.showTemporary(t('polls', 'Poll "%n" added', 1, this.event.title), { type: 'success' })
+ this.$router.push({ name: 'vote', params: { id: this.event.id } })
})
.catch(() => {
OC.Notification.showTemporary(t('polls', 'Error while creating Poll "%n"', 1, this.event.title), { type: 'error' })
diff --git a/src/js/components/navigation/navigation.vue b/src/js/components/navigation/navigation.vue
index 3733c5f0..5509369e 100644
--- a/src/js/components/navigation/navigation.vue
+++ b/src/js/components/navigation/navigation.vue
@@ -29,11 +29,11 @@
:title="t('polls', 'All polls')"
:allow-collapse="true"
icon="icon-folder"
- :to="{ name: 'list'}"
+ :to="{ name: 'list', params: {type: 'all'}}"
:open="true">
<ul>
<AppNavigationItem
- v-for="(poll) in pollList"
+ v-for="(poll) in allPolls"
:key="poll.id"
:title="poll.title"
:icon="eventIcon(poll.type)"
@@ -43,7 +43,8 @@
<AppNavigationItem
:title="t('polls', 'My polls')"
:allow-collapse="true"
- icon="icon-folder"
+ icon="icon-user"
+ :to="{ name: 'list', params: {type: 'my'}}"
:open="false">
<ul>
<AppNavigationItem
@@ -57,7 +58,8 @@
<AppNavigationItem
:title="t('polls', 'Public polls')"
:allow-collapse="true"
- icon="icon-folder"
+ icon="icon-link"
+ :to="{ name: 'list', params: {type: 'public'}}"
:open="false">
<ul>
<AppNavigationItem
@@ -71,7 +73,8 @@
<AppNavigationItem
:title="t('polls', 'Hidden polls')"
:allow-collapse="true"
- icon="icon-folder"
+ icon="icon-password"
+ :to="{ name: 'list', params: {type: 'hidden'}}"
:open="false">
<ul>
<AppNavigationItem
@@ -82,6 +85,21 @@
:to="{name: 'vote', params: {id: poll.id}}" />
</ul>
</AppNavigationItem>
+ <AppNavigationItem
+ :title="t('polls', 'Deleted polls')"
+ :allow-collapse="true"
+ icon="icon-delete"
+ :to="{ name: 'list', params: {type: 'deleted'}}"
+ :open="false">
+ <ul>
+ <AppNavigationItem
+ v-for="(poll) in deletedPolls"
+ :key="poll.id"
+ :title="poll.title"
+ :icon="eventIcon(poll.type)"
+ :to="{name: 'vote', params: {id: poll.id}}" />
+ </ul>
+ </AppNavigationItem>
</ul>
<AppNavigationSettings>
@@ -118,9 +136,11 @@ export default {
computed: {
...mapGetters([
+ 'allPolls',
'myPolls',
'publicPolls',
- 'hiddenPolls'
+ 'hiddenPolls',
+ 'deletedPolls'
]),
pollList() {
diff --git a/src/js/components/navigation/store/polls.js b/src/js/components/navigation/store/polls.js
index 6adf6556..f43ba652 100644
--- a/src/js/components/navigation/store/polls.js
+++ b/src/js/components/navigation/store/polls.js
@@ -37,6 +37,9 @@ const getters = {
countPolls: (state) => {
return state.list.length
},
+ allPolls: (state) => {
+ return state.list.filter(poll => (!poll.deleted))
+ },
myPolls: (state) => {
return state.list.filter(poll => (poll.owner === OC.getCurrentUser().uid))
},
@@ -45,6 +48,9 @@ const getters = {
},
hiddenPolls: (state) => {
return state.list.filter(poll => (poll.access === 'hidden'))
+ },
+ deletedPolls: (state) => {
+ return state.list.filter(poll => (poll.deleted))
}
}
diff --git a/src/js/components/sidebar/SideBar.vue b/src/js/components/sidebar/SideBar.vue
new file mode 100644
index 00000000..30931f01
--- /dev/null
+++ b/src/js/components/sidebar/SideBar.vue
@@ -0,0 +1,109 @@
+<!--
+ - @copyright Copyright (c) 2018 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/>.
+ -
+ -->
+
+<template>
+ <AppSidebar :active="initialTab" :title="t('polls', 'Details')" @close="$emit('closeSideBar')">
+ <UserDiv slot="primary-actions" :user-id="event.owner" :description="t('polls', 'Owner')" />
+
+ <AppSidebarTab :name="t('polls', 'Comments')" icon="icon-comment">
+ <SideBarTabComments />
+ </AppSidebarTab>
+
+ <AppSidebarTab v-if="acl.allowEdit && event.type === 'datePoll'" :name="t('polls', 'Date options')" icon="icon-calendar">
+ <SideBarTabDateOptions />
+ </AppSidebarTab>
+
+ <AppSidebarTab v-if="acl.allowEdit && event.type === 'textPoll'" :name="t('polls', 'Text options')" icon="icon-toggle-filelist">
+ <SideBarTabTextOptions />
+ </AppSidebarTab>
+
+ <AppSidebarTab v-if="acl.allowEdit" :name="t('polls', 'Configuration')" icon="icon-settings">
+ <SideBarTabConfiguration @deletePoll="$emit('deletePoll')" />
+ </AppSidebarTab>
+
+ <AppSidebarTab v-if="acl.allowEdit" :name="t('polls', 'Shares')" icon="icon-share">
+ <SideBarTabShare />
+ </AppSidebarTab>
+ </AppSidebar>
+</template>
+
+<script>
+import { AppSidebar, AppSidebarTab } from '@nextcloud/vue'
+
+import SideBarTabConfiguration from './SideBarTabConfiguration'
+import SideBarTabDateOptions from './SideBarTabDateOptions'
+import SideBarTabTextOptions from './SideBarTabTextOptions'
+import SideBarTabComments from './SideBarTabComments'
+import SideBarTabShare from './SideBarTabShare'
+import { mapState } from 'vuex'
+
+export default {
+ name: 'SideBar',
+ components: {
+ SideBarTabConfiguration,
+ SideBarTabComments,
+ SideBarTabDateOptions,
+ SideBarTabTextOptions,
+ SideBarTabShare,
+ AppSidebar,
+ AppSidebarTab
+ },
+
+ data() {
+ return {
+ initialTab: 'comments'
+ }
+ },
+
+ computed: {
+ ...mapState({
+ event: state => state.event,
+ acl: state => state.acl
+ })
+ }
+
+}
+
+</script>
+
+<style scoped lang="scss">
+
+ ul {
+ & > li {
+ margin-bottom: 30px;
+ & > .comment-item {
+ display: flex;
+ align-items: center;
+
+ & > .date {
+ right: 0;
+ top: 5px;
+ opacity: 0.5;
+ }
+ }
+ & > .message {
+ margin-left: 44px;
+ flex: 1 1;
+ }
+ }
+ }
+</style>
diff --git a/src/js/components/sidebar/SideBarTabConfiguration.vue b/src/js/components/sidebar/SideBarTabConfiguration.vue
index 561b1f6f..1e135af1 100644
--- a/src/js/components/sidebar/SideBarTabConfiguration.vue
+++ b/src/js/components/sidebar/SideBarTabConfiguration.vue
@@ -31,19 +31,19 @@
</label>
</div>
- <div v-if="allowEdit" class="configBox">
+ <div v-if="acl.allowEdit" class="configBox">
<label class="icon-sound title">
{{ t('polls', 'Title') }}
</label>
<input v-model="eventTitle" :class="{ error: titleEmpty }" type="text">
</div>
- <div v-if="allowEdit" class="configBox">
+ <div v-if="acl.allowEdit" class="configBox">
<label class="icon-edit title">
{{ t('polls', 'Description') }}
</label>
<textarea v-model="eventDescription" />
- <!-- <textarea v-if="allowEdit" :value="event.description" @input="updateDescription" /> -->
+ <!-- <textarea v-if="acl.allowEdit" :value="event.description" @input="updateDescription" /> -->
</div>
<div class="configBox">
@@ -53,7 +53,7 @@
<input id="allowMaybe"
v-model="eventAllowMaybe"
- :disabled="!allowEdit"
+ :disabled="!acl.allowEdit"
type="checkbox"
class="checkbox">
<label for="allowMaybe" class="title">
@@ -61,7 +61,7 @@
</label>
<input id="anonymous" v-model="eventIsAnonymous"
- :disabled="!allowEdit"
+ :disabled="!acl.allowEdit"
type="checkbox"
class="checkbox">
<label for="anonymous" class="title">
@@ -71,7 +71,7 @@
<input v-show="event.isAnonymous"
id="trueAnonymous"
v-model="eventFullAnonymous"
- :disabled="!allowEdit"
+ :disabled="!acl.allowEdit"
type="checkbox"
class="checkbox">
<label v-show="event.isAnonymous" class="title" for="trueAnonymous">
@@ -80,17 +80,17 @@
<input id="expiration"
v-model="eventExpiration"
- :disabled="!allowEdit"
+ :disabled="!acl.allowEdit"
type="checkbox"
class="checkbox">
- <label class="title" for="expiration">
+ <label class="title" for="expirtion">
{{ t('polls', 'Expires') }}
</label>
- <date-picker v-show="event.expiration"
- v-model="eventExpirationDate"
+ <date-picker v-show="event.expire"
+ v-model="eventExpiration"
v-bind="expirationDatePicker"
- :disabled="!allowEdit"
+ :disabled="!acl.allowEdit"
:time-picker-options="{ start: '00:00', step: '00:05', end: '23:55' }"
style="width:170px" />
</div>
@@ -99,47 +99,33 @@
<label class="title icon-category-auth">
{{ t('polls', 'Access') }}
</label>
- <input id="private"
- v-model="eventAccess"
- :disabled="!allowEdit"
- type="radio"
- value="registered"
- class="radio">
- <label for="private" class="title">
- <div class="title icon-group" />
- <span>{{ t('polls', 'Registered users only') }}</span>
- </label>
+
<input id="hidden"
v-model="eventAccess"
- :disabled="!allowEdit"
+ :disabled="!acl.allowEdit"
type="radio"
value="hidden"
class="radio">
<label for="hidden" class="title">
<div class="title icon-category-security" />
- <span>{{ t('polls', 'hidden') }}</span>
+ <span>{{ t('polls', 'Hidden to other users') }}</span>
</label>
+
<input id="public"
v-model="eventAccess"
- :disabled="!allowEdit"
+ :disabled="!acl.allowEdit"
type="radio"
value="public"
class="radio">
<label for="public" class="title">
<div class="title icon-link" />
- <span>{{ t('polls', 'Public access') }}</span>
- </label>
- <input id="select"
- v-model="eventAccess"
- :disabled="!allowEdit"
- type="radio"
- value="select"
- class="radio">
- <label for="select" class="title">
- <div class="title icon-shared" />
- <span>{{ t('polls', 'Only shared') }}</span>
+ <span>{{ t('polls', 'Visible to other users') }}</span>
</label>
</div>
+
+ <button class="button btn primary" @click="$emit('deletePoll')">
+ <span>{{ t('polls', 'Delete this poll') }}</span>
+ </button>
</div>
</template>
@@ -160,14 +146,12 @@ export default {
computed: {
...mapState({
- poll: state => state.poll,
- event: state => state.event
+ event: state => state.event,
+ acl: state => state.acl
}),
...mapGetters([
- 'languageCodeShort',
- 'adminMode',
- 'allowEdit'
+ 'languageCodeShort'
]),
// Add bindings
@@ -200,10 +184,10 @@ export default {
eventExpiration: {
get() {
- return this.event.expiration
+ return this.event.expire
},
set(value) {
- this.writeValue({ 'expiration': value })
+ this.writeValue({ 'expire': value })
}
},
@@ -234,14 +218,14 @@ export default {
}
},
- eventExpirationDate: {
- get() {
- return this.$store.state.event.expirationDate
- },
- set(value) {
- this.writeValue({ 'expirationDate': value })
- }
- },
+ // eventExpiration: {
+ // get() {
+ // return this.$store.state.event.expiration
+ // },
+ // set(value) {
+ // this.writeValue({ 'expiration': value })
+ // }
+ // },
expirationDatePicker() {
return {
@@ -282,7 +266,7 @@ export default {
saveButtonTitle: function() {
if (this.writingPoll) {
return t('polls', 'Writing poll')
- } else if (this.allowEdit) {
+ } else if (this.acl.allowEdit) {
return t('polls', 'Update poll')
} else {
return t('polls', 'Create new poll')
@@ -291,10 +275,8 @@ export default {
},
methods: {
- ...mapMutations([ 'setEventProperty', 'pollSetProperty' ]),
- ...mapActions([
- 'writeEventPromise'
- ]),
+ ...mapMutations([ 'setEventProperty' ]),
+ ...mapActions([ 'writeEventPromise' ]),
writeValueDebounced: debounce(function(e) {
this.writeValue(e)
@@ -317,7 +299,7 @@ export default {
},
write() {
- if (this.allowEdit) {
+ if (this.acl.allowEdit) {
this.writePoll()
}
diff --git a/src/js/router.js b/src/js/router.js
index 5c9690a4..1e6ccebc 100644
--- a/src/js/router.js
+++ b/src/js/router.js
@@ -38,11 +38,11 @@ export default new Router({
linkActiveClass: 'active',
routes: [
{
- path: '/:index(index.php/)?apps/polls/',
+ path: '/:index(index.php/)?apps/polls/:type?',
components: {
default: List
},
- props: false,
+ props: true,
name: 'list'
},
{
@@ -54,14 +54,6 @@ export default new Router({
name: 'vote'
},
{
- path: '/:index(index.php/)?apps/polls/edit/:id',
- components: {
- default: Vote
- },
- props: true,
- name: 'edit'
- },
- {
path: '/:index(index.php/)?apps/polls/vote/:id',
components: {
default: Vote
diff --git a/src/js/store/index.js b/src/js/store/index.js
index fcda4cb6..cdf31566 100644
--- a/src/js/store/index.js
+++ b/src/js/store/index.js
@@ -24,7 +24,7 @@
import Vue from 'vue'
import Vuex from 'vuex'
-import poll from './modules/currentPoll'
+import acl from './modules/acl'
import comments from './modules/comments'
import event from './modules/event'
import notification from './modules/notification'
@@ -41,7 +41,7 @@ const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
modules: {
- poll,
+ acl,
comments,
event,
notification,
diff --git a/src/js/store/modules/acl.js b/src/js/store/modules/acl.js
new file mode 100644
index 00000000..0fcc47a0
--- /dev/null
+++ b/src/js/store/modules/acl.js
@@ -0,0 +1,82 @@
+/*
+ * @copyright Copyright (c) 2019 Rene Gieling <github@dartcafe.de>
+ *
+ * @author Rene Gieling <github@dartcafe.de>
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @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/>.
+ *
+ */
+
+import axios from 'nextcloud-axios'
+
+const defaultAcl = () => {
+ return {
+ userId: null,
+ pollId: null,
+ token: null,
+ isOwner: false,
+ isAdmin: false,
+ allowView: false,
+ allowVote: false,
+ allowComment: false,
+ allowEdit: false,
+ allowSeeUsernames: false,
+ allowSeeAllVotes: false,
+ foundByToken: false,
+ accessLevel: ''
+ }
+}
+
+const state = defaultAcl()
+
+const mutations = {
+
+ setAcl(state, payload) {
+ Object.assign(state, payload.acl)
+ },
+
+ reset(state) {
+ Object.assign(state, defaultAcl())
+ }
+
+}
+
+const actions = {
+
+ loadPoll({ commit, rootState }, payload) {
+ commit('reset')
+ let endPoint = 'apps/polls/get/acl/'
+
+ if (payload.token !== undefined) {
+ endPoint = endPoint.concat('s/', payload.token)
+ } else if (payload.pollId !== undefined) {
+ endPoint = endPoint.concat(payload.pollId)
+ } else {
+ return
+ }
+
+ return axios.get(OC.generateUrl(endPoint))
+ .then((response) => {
+ commit('setAcl', { 'acl': response.data })
+ }, (error) => {
+ console.error('Error loading comments', { 'error': error.response }, { 'payload': payload })
+ throw error
+ })
+ }
+}
+
+export default { state, mutations, actions }
diff --git a/src/js/store/modules/currentPoll.js b/src/js/store/modules/currentPoll.js
deleted file mode 100644
index 2835a423..00000000
--- a/src/js/store/modules/currentPoll.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * @copyright Copyright (c) 2019 Rene Gieling <github@dartcafe.de>
- *
- * @author Rene Gieling <github@dartcafe.de>
- * @author Julius Härtl <jus@bitgrid.net>
- *
- * @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/>.
- *
- */
-
-const defaultPoll = () => {
- return {
- id: 0,
- mode: 'create'
- }
-}
-
-const state = defaultPoll()
-
-const mutations = {
- pollSet(state, payload) {
- Object.assign(state, payload.poll)
- },
-
- pollReset(state) {
- Object.assign(state, defaultPoll())
- },
-
- pollSetProperty(state, payload) {
- Object.assign(state, payload)
- }
-}
-
-const actions = {
- loadPoll({ commit, rootState }, payload) {
- commit('pollSetProperty', { 'mode': payload.mode })
- commit('pollSetProperty', { 'id': payload.pollId })
-
- }
-}
-
-export default {
- state,
- mutations,
- actions
-}
diff --git a/src/js/store/modules/event.js b/src/js/store/modules/event.js
index 71f43b16..6fefbdbd 100644
--- a/src/js/store/modules/event.js
+++ b/src/js/store/modules/event.js
@@ -30,16 +30,17 @@ const defaultEvent = () => {
type: 'datePoll',
title: '',
description: '',
+ owner: undefined,
created: '',
access: 'public',
- expiration: false,
- expirationDate: '',
- expired: false,
+ expire: null,
isAnonymous: false,
fullAnonymous: false,
allowMaybe: false,
- owner: undefined,
- acl: {}
+ voteLimit: null,
+ showResults: true,
+ deleted: false,
+ deleteDate: null
}
}
@@ -50,7 +51,7 @@ const mutations = {
Object.assign(state, payload.event)
},
- eventReset(state) {
+ resetEvent(state) {
Object.assign(state, defaultEvent())
},
@@ -66,34 +67,38 @@ const getters = {
return moment(state.created).fromNow()
},
- timeSpanExpiration: state => {
- if (state.expiration) {
- return moment(state.expirationDate).fromNow()
+ isExpirationSet: state => {
+ return Boolean(moment(state.expire).unix())
+ },
+
+ expired: (state, getters) => {
+ return (getters.isExpirationSet && moment(state.expire).diff() < 0)
+ },
+
+ timeSpanExpiration: (state, getters) => {
+ if (getters.expired) {
+ return moment(state.expire).fromNow()
} else {
return t('polls', 'never')
}
},
- accessType: state => {
- if (state.acl.accessLevel === 'public') {
+ accessType: (state, getters, rootState) => {
+ if (rootState.acl.accessLevel === 'public') {
return t('polls', 'Public access')
- } else if (state.acl.accessLevel === 'select') {
+ } else if (rootState.acl.accessLevel === 'select') {
return t('polls', 'Only shared')
- } else if (state.acl.accessLevel === 'registered') {
+ } else if (rootState.acl.accessLevel === 'registered') {
return t('polls', 'Registered users only')
- } else if (state.acl.accessLevel === 'hidden') {
+ } else if (rootState.acl.accessLevel === 'hidden') {
return t('polls', 'Hidden poll')
} else {
- return state.acl.accessLevel
+ return rootState.acl.accessLevel
}
},
- adminMode: state => {
- return (!state.acl.isOwner && state.acl.isAdmin)
- },
-
- allowEdit: (state, getters) => {
- return (state.acl.allowEdit)
+ allowEdit: (state, getters, rootState) => {
+ return (rootState.acl.allowEdit)
}
}
@@ -101,7 +106,7 @@ const getters = {
const actions = {
loadEvent({ commit }, payload) {
- commit('eventReset')
+ commit('resetEvent')
let endPoint = 'apps/polls/get/event/'
if (payload.token !== undefined) {
@@ -115,7 +120,6 @@ const actions = {
return axios.get(OC.generateUrl(endPoint))
.then((response) => {
commit('setEvent', { 'event': response.data })
- // return response
}, (error) => {
if (error.response.status !== '404') {
console.error('Error loading event', { 'error': error.response }, { 'payload': payload })
@@ -124,17 +128,6 @@ const actions = {
})
},
- addEventPromise({ commit }, payload) {
- return axios.post(OC.generateUrl('apps/polls/add/event'), { event: payload.event })
- .then((response) => {
- return response
- }, (error) => {
- console.error('Error adding event', { 'error': error.response }, { 'payload': payload })
- throw error
- })
-
- },
-
deleteEventPromise({ commit }, payload) {
return axios.post(OC.generateUrl('apps/polls/delete/event'), { event: payload.id })
.then((response) => {
@@ -147,9 +140,10 @@ const actions = {
},
writeEventPromise({ commit, rootState }) {
- return axios.post(OC.generateUrl('apps/polls/write/event'), { event: state, mode: rootState.poll.mode })
+ return axios.post(OC.generateUrl('apps/polls/write/event'), { event: state })
.then((response) => {
commit('setEvent', { 'event': response.data })
+ return response.event
}, (error) => {
console.error('Error writing event:', { 'error': error.response }, { 'state': state })
throw error
@@ -158,4 +152,4 @@ const actions = {
}
}
-export default { state, mutations, getters, actions }
+export default { state, mutations, getters, actions, defaultEvent }
diff --git a/src/js/store/modules/shares.js b/src/js/store/modules/shares.js
index d3e70c51..11d4767c 100644
--- a/src/js/store/modules/shares.js
+++ b/src/js/store/modules/shares.js
@@ -23,30 +23,30 @@
import axios from 'nextcloud-axios'
-const defaultComments = () => {
+const defaultShares = () => {
return {
list: []
}
}
-const state = defaultComments()
+const state = defaultShares()
const mutations = {
- sharesSet(state, payload) {
+ setShares(state, payload) {
Object.assign(state, payload)
},
- shareRemove(state, payload) {
+ removeShare(state, payload) {
state.list = state.list.filter(share => {
return share.id !== payload.share.id
})
},
reset(state) {
- Object.assign(state, defaultComments())
+ Object.assign(state, defaultShares())
},
- shareAdd(state, payload) {
+ addShare(state, payload) {
state.list.push(payload)
}
@@ -79,6 +79,12 @@ const getters = {
const actions = {
loadPoll({ commit, rootState }, payload) {
commit('reset')
+ // console.log(rootState.acl)
+ // if (!rootState.acl.allowEdit) {
+ // console.log('rootState.acl.allowEdit', rootState.acl.allowEdit)
+ // return
+ // }
+ // console.log('number 1')
let endPoint = 'apps/polls/get/shares/'
if (payload.token !== undefined) {
@@ -91,7 +97,7 @@ const actions = {
return axios.get(OC.generateUrl(endPoint))
.then((response) => {
- commit('sharesSet', {
+ commit('setShares', {
'list': response.data
})
}, (error) => {
@@ -125,7 +131,7 @@ const actions = {
payload.share.pollId = rootState.event.id
return axios.post(OC.generateUrl('apps/polls/write/share'), { pollId: rootState.event.id, share: payload.share })
.then((response) => {
- commit('shareAdd', response.data)
+ commit('addShare', response.data)
}, (error) => {
console.error('Error writing share', { 'error': error.response }, { 'payload': payload })
throw error
@@ -135,7 +141,7 @@ const actions = {
removeShareAsync({ commit, getters, dispatch, rootState }, payload) {
return axios.post(OC.generateUrl('apps/polls/remove/share'), { share: payload.share })
.then((response) => {
- commit('shareRemove', { 'share': payload.share })
+ commit('removeShare', { 'share': payload.share })
}, (error) => {
console.error('Error removing share', { 'error': error.response }, { 'payload': payload })
throw error
diff --git a/src/js/store/modules/votes.js b/src/js/store/modules/votes.js
index ac25162a..36ac8a1d 100644
--- a/src/js/store/modules/votes.js
+++ b/src/js/store/modules/votes.js
@@ -71,8 +71,8 @@ const getters = {
}
})
- if (!list.includes(rootState.event.acl.userId) && rootState.event.acl.userId !== null) {
- list.push(rootState.event.acl.userId)
+ if (!list.includes(rootState.acl.userId) && rootState.acl.userId !== null) {
+ list.push(rootState.acl.userId)
}
return list
@@ -143,13 +143,13 @@ const actions = {
let endPoint = 'apps/polls/set/vote/'
- if (rootState.event.acl.foundByToken) {
+ if (rootState.acl.foundByToken) {
endPoint = endPoint.concat('s/')
}
return axios.post(OC.generateUrl(endPoint), {
pollId: rootState.event.id,
- token: rootState.event.acl.token,
+ token: rootState.acl.token,
option: payload.option,
userId: payload.userId,
setTo: payload.setTo
diff --git a/src/js/views/PollList.vue b/src/js/views/PollList.vue
index da2420de..fddb5326 100644
--- a/src/js/views/PollList.vue
+++ b/src/js/views/PollList.vue
@@ -38,9 +38,9 @@
v-for="(poll, index) in pollList"
:key="poll.id"
:poll="poll"
- @deletePoll="removePoll(index, poll.event)"
- @editPoll="callPoll(index, poll.event, 'edit')"
- @clonePoll="callPoll(index, poll.event, 'clone')" />
+ @deletePoll="removePoll(index, poll)"
+ @editPoll="callPoll(index, poll, 'edit')"
+ @clonePoll="callPoll(index, poll, 'clone')" />
</transition-group>
</div>
<loading-overlay v-if="loading" />
@@ -50,7 +50,7 @@
<script>
import PollListItem from '../components/PollList/PollListItem'
-
+import { mapGetters } from 'vuex'
export default {
name: 'PollList',
@@ -61,13 +61,37 @@ export default {
data() {
return {
noPolls: false,
- loading: true
+ loading: true,
+ pollList: this.$store.state.polls.list
}
},
computed: {
- pollList() {
- return this.$store.state.polls.list
+ ...mapGetters([
+ 'myPolls',
+ 'publicPolls',
+ 'hiddenPolls',
+ 'deletedPolls'
+ ])
+
+ // pollList() {
+ // return this.$store.state.polls.list
+ // }
+ },
+
+ watch: {
+ $route(to, from) {
+ if (this.$route.params.type === 'all') {
+ this.pollList = this.$store.state.polls.list
+ } else if (this.$route.params.type === 'my') {
+ this.pollList = this.myPolls
+ } else if (this.$route.params.type === 'public') {
+ this.pollList = this.publicPolls
+ } else if (this.$route.params.type === 'hidden') {
+ this.pollList = this.hiddenPolls
+ } else if (this.$route.params.type === 'deleted') {
+ this.pollList = this.deletedPolls
+ }
}
},
diff --git a/src/js/views/PublicVote.vue b/src/js/views/PublicVote.vue
index 85c19e2a..ed5f1a1e 100644
--- a/src/js/views/PublicVote.vue
+++ b/src/js/views/PublicVote.vue
@@ -26,84 +26,33 @@
<a v-if="!sideBarOpen" href="#" class="icon icon-settings active"
:title="t('polls', 'Open Sidebar')" @click="toggleSideBar()" />
- <div>
- <h2>
- {{ event.title }}
- <span v-if="event.expired" class="label error">{{ t('polls', 'Expired') }}</span>
- <span v-if="!event.expired && event.expiration" class="label success">{{ t('polls', 'Votes are possible until %n', 1, event.expirationDate) }}</span>
- <span v-if="!event.expiration" class="label success">{{ t('polls', 'No expiration date set') }}</span>
- <transition name="fade">
- <span v-if="voteSaved" class="label success">Vote saved</span>
- </transition>
- </h2>
- <h3>
- {{ event.description }}
- </h3>
- </div>
-
- <div v-if="!isValidUser" class="get-username">
- <!-- <label>
- {{ t('polls', 'Enter a valid username, to participate in this poll.') }}
- </label> -->
-
- <form v-if="!redirecting">
- <input v-model="userName" :class="{ error: (!isValidName && userName.length > 0), success: isValidName }" type="text"
- :placeholder="t('polls', 'Enter a valid username with at least 3 Characters')">
- <input v-show="isValidName && !checkingUserName" class="icon-confirm" :class="{ error: !isValidName, success: isValidName }"
- @click="writeUserName">
- <span v-show="checkingUserName" class="icon-loading-small" />
- <!-- <span v-if="!isValidName" class="error"> {{ invalidUserNameMessage }} </span> -->
- </form>
- <div v-else>
- <span>{{ t('polls', 'You will be redirected to your personal share.') }}</span>
- <span>
- {{ t('polls', 'If you are not redirected to your poll click this link:') }}
- <router-link :to="{ name: 'publicVote', params: { token: token }}">
- Link
- </router-link>
- </span>
- </div>
- </div>
- <div v-if="displayLink" class="personal-link">
- {{ t('polls', 'Your personal link to this poll: %n', 1, personalLink) }}
- <a class="icon icon-clippy" @click="copyLink( { url: OC.generateUrl($route.path) } )" />
- </div>
-
- <VoteTable v-show="!loading" @voteSaved="indicateVoteSaved()" />
+ <VoteHeader />
+ <VoteHeaderPublic />
+ <VoteTable />
<Notification />
</div>
- <AppSidebar v-if="sideBarOpen" :active="initialTab" :title="t('polls', 'Details')"
- @close="toggleSideBar">
- <template slot="primary-actions">
- <UserDiv :user-id="event.owner" :description="t('polls', 'Owner')" />
- </template>
-
- <AppSidebarTab :name="t('polls', 'Comments')" icon="icon-comment">
- <SideBarTabComments />
- </AppSidebarTab>
- </AppSidebar>
+ <SideBar v-if="sideBarOpen" @closeSideBar="toggleSideBar" />
<LoadingOverlay v-if="loading" />
</AppContent>
</template>
<script>
-import Notification from '../components/notification/notification'
+import VoteHeader from '../components/VoteTable/VoteHeader'
+import VoteHeaderPublic from '../components/VoteTable/VoteHeaderPublic'
import VoteTable from '../components/VoteTable/VoteTable'
-import SideBarTabComments from '../components/SideBar/SideBarTabComments'
-import debounce from 'lodash/debounce'
-import axios from 'nextcloud-axios'
-import { mapState, mapGetters } from 'vuex'
-import { AppSidebar, AppSidebarTab } from '@nextcloud/vue'
+import Notification from '../components/notification/notification'
+import SideBar from '../components/SideBar/SideBar'
+import { mapState } from 'vuex'
export default {
name: 'Vote',
components: {
Notification,
- SideBarTabComments,
+ VoteHeader,
+ VoteHeaderPublic,
VoteTable,
- AppSidebar,
- AppSidebarTab
+ SideBar
},
data() {
@@ -111,42 +60,18 @@ export default {
voteSaved: false,
delay: 50,
sideBarOpen: false,
- loading: false,
- checkingUserName: false,
- token: '',
- userName: '',
- isValidName: false,
- invalidUserNameMessage: '',
- redirecting: false,
initialTab: 'comments'
}
},
computed: {
...mapState({
- poll: state => state.poll,
event: state => state.event,
- shares: state => state.poll.shares
+ acl: state => state.acl
}),
- ...mapGetters([
- 'allowEdit'
- ]),
-
- personalLink() {
- return location.protocol.concat('//', window.location.hostname, OC.generateUrl(this.$route.path))
- },
-
- displayLink() {
- return (this.event.acl.userId !== '' && this.event.acl.userId !== null && this.event.acl.foundByToken)
- },
-
windowTitle: function() {
return t('polls', 'Polls') + ' - ' + this.event.title
- },
-
- isValidUser() {
- return (this.event.acl.userId !== '' && this.event.acl.userId !== null)
}
},
@@ -154,14 +79,6 @@ export default {
watch: {
'$route'(to, from) {
this.loadPoll()
- },
- userName: function() {
- if (this.userName.length > 2) {
- this.isValidName = this.validatePublicUsername()
- } else {
- this.invalidUserNameMessage = t('polls', 'Please use at least 3 characters for your user name!')
- this.isValidName = false
- }
}
},
@@ -172,8 +89,6 @@ export default {
methods: {
loadPoll() {
this.loading = false
- // this.$store.dispatch('getShareAsync', { token: this.$route.params.token })
- // .then((response) => {
this.$store.dispatch('loadEvent', { token: this.$route.params.token })
.then((response) => {
this.$store.dispatch('loadPoll', { token: this.$route.params.token })
@@ -187,91 +102,10 @@ export default {
})
},
- copyLink(payload) {
- this.$copyText(window.location.origin + payload.url).then(
- function(e) {
- OC.Notification.showTemporary(t('polls', 'Link copied to clipboard'), { type: 'success' })
- },
- function(e) {
- OC.Notification.showTemporary(t('polls', 'Error while copying link to clipboard'), { type: 'error' })
- }
- )
- },
-
- validatePublicUsername: debounce(function() {
- if (this.userName.length > 2) {
- this.checkingUserName = true
- return axios.post(OC.generateUrl('apps/polls/check/username'), { pollId: this.event.id, userName: this.userName, token: this.$route.params.token })
- .then((response) => {
- this.checkingUserName = false
- this.isValidName = true
- this.invalidUserNameMessage = 'User name is OK.'
- return true
- })
- .catch(() => {
- this.checkingUserName = false
- this.isValidName = false
- this.invalidUserNameMessage = t('polls', 'This user name can not be choosed.')
- return false
- })
- } else {
- this.checkingUserName = false
- this.isValidName = false
- this.invalidUserNameMessage = t('polls', 'Please use at least 3 characters for your user name!')
- return false
- }
- }, 500),
-
- writeUserName() {
- if (this.validatePublicUsername()) {
- this.$store.dispatch('addShareFromUser', { token: this.$route.params.token, userName: this.userName })
- .then((response) => {
- this.token = response.token
- this.redirecting = true
- this.$router.replace({ name: 'publicVote', params: { 'token': response.token } })
- })
- .catch(() => {
- OC.Notification.showTemporary(t('polls', 'Error saving user name"', 1, event.title), { type: 'error' })
- })
- }
- },
-
toggleSideBar() {
this.sideBarOpen = !this.sideBarOpen
- },
-
- openConfigurationTab() {
- this.initialTab = 'configuration'
- this.sideBarOpen = true
- this.$store.commit('pollSetProperty', { 'mode': 'edit' })
- },
-
- openOptionsTab() {
- if (this.event.type === 'datePoll') {
- this.initialTab = 'date-options'
- } else if (this.event.type === 'textPoll') {
- this.initialTab = 'text-options'
- }
- this.sideBarOpen = true
- this.$store.commit('pollSetProperty', { 'mode': 'edit' })
- },
-
- toggleEdit() {
- if (this.poll.mode === 'vote') {
- this.$store.commit('pollSetProperty', { 'mode': 'edit' })
- } else if (this.poll.mode === 'edit') {
- this.$store.commit('pollSetProperty', { 'mode': 'vote' })
- }
- },
-
- timer() {
- this.voteSaved = false
- },
-
- indicateVoteSaved() {
- this.voteSaved = true
- window.setTimeout(this.timer, this.delay)
}
+
}
}
</script>
@@ -289,64 +123,6 @@ export default {
}
}
- .personal-link {
- display: flex;
- padding: 4px 12px;
- margin: 0 12px 0 24px;
- border: 2px solid var(--color-success);
- background-color: #d6fdda !important;
- border-radius: var(--border-radius);
- font-size: 1.2em;
- opacity: 0.8;
- .icon {
- margin: 0 12px;
- }
- }
- .get-username {
- & > label {
- margin-right: 12px;
- }
-
- margin: 0 12px 12px 24px;
- border:2px solid var(--color-border-dark);
- font-size: 1.2em;
- padding: 0 12px 0 12px;
- display: flex;
- align-items: center;
- border-radius: var(--border-radius);
- background-color: var(--color-background-dark);
- flex-wrap: wrap;
-
- form, div {
- flex: 1;
- display: flex;
-
- }
- input {
- flex: 1;
- }
-
- .icon-loading-small {
- position: relative;
- right: 24px;
- top: 0px;
- }
-
- input[type="text"] + .icon-confirm, input[type="text"] + .icon-loading-small {
- flex: 0;
- margin-left: -8px !important;
- border-left-color: transparent !important;
- border-radius: 0 var(--border-radius) var(--border-radius) 0 !important;
- background-clip: padding-box;
- opacity: 1;
- height: 34px;
- width: 34px;
- padding: 7px 20px;
- cursor: pointer;
- margin-right: 0;
- }
- }
-
.icon.icon-settings.active {
display: block;
width: 44px;
diff --git a/src/js/views/Vote.vue b/src/js/views/Vote.vue
index aeb03008..d3cfd666 100644
--- a/src/js/views/Vote.vue
+++ b/src/js/views/Vote.vue
@@ -25,79 +25,30 @@
<div v-if="event.id > 0" class="main-container">
<a v-if="!sideBarOpen" href="#" class="icon icon-settings active"
:title="t('polls', 'Open Sidebar')" @click="toggleSideBar()" />
-
- <div>
- <h2>
- {{ event.title }}
- <span v-if="event.expired" class="label error">{{ t('polls', 'Expired') }}</span>
- <span v-if="!event.expired && event.expiration" class="label success">{{ t('polls', 'Votes are possible until %n', 1, event.expirationDate) }}</span>
- <span v-if="!event.expiration" class="label success">{{ t('polls', 'No expiration date set') }}</span>
- <transition name="fade">
- <span v-if="voteSaved" class="label success">Vote saved</span>
- </transition>
- </h2>
- <h3>
- {{ event.description }}
- </h3>
- </div>
-
- <VoteTable v-show="!loading" @voteSaved="indicateVoteSaved()" />
+ <VoteHeader />
+ <VoteTable />
<Notification />
</div>
- <AppSidebar v-if="sideBarOpen" :active="initialTab" :title="t('polls', 'Details')"
- @close="toggleSideBar">
- <template slot="primary-actions">
- <UserDiv :user-id="event.owner" :description="t('polls', 'Owner')" />
- </template>
-
- <AppSidebarTab :name="t('polls', 'Comments')" icon="icon-comment">
- <SideBarTabComments />
- </AppSidebarTab>
-
- <AppSidebarTab v-if="allowEdit && event.type === 'datePoll'" :name="t('polls', 'Date options')" icon="icon-calendar">
- <SideBarTabDateOptions />
- </AppSidebarTab>
-
- <AppSidebarTab v-if="allowEdit && event.type === 'textPoll'" :name="t('polls', 'Text options')" icon="icon-toggle-filelist">
- <SideBarTabTextOptions />
- </AppSidebarTab>
-
- <AppSidebarTab v-if="allowEdit" :name="t('polls', 'Configuration')" icon="icon-settings">
- <SideBarTabConfiguration />
- </AppSidebarTab>
-
- <AppSidebarTab v-if="allowEdit" :name="t('polls', 'Shares')" icon="icon-share">
- <SideBarTabShare />
- </AppSidebarTab>
- </AppSidebar>
+ <SideBar v-if="sideBarOpen" @closeSideBar="toggleSideBar" />
<LoadingOverlay v-if="loading" />
</AppContent>
</template>
<script>
import Notification from '../components/notification/notification'
+import VoteHeader from '../components/VoteTable/VoteHeader'
import VoteTable from '../components/VoteTable/VoteTable'
-import SideBarTabConfiguration from '../components/SideBar/SideBarTabConfiguration'
-import SideBarTabDateOptions from '../components/SideBar/SideBarTabDateOptions'
-import SideBarTabTextOptions from '../components/SideBar/SideBarTabTextOptions'
-import SideBarTabComments from '../components/SideBar/SideBarTabComments'
-import SideBarTabShare from '../components/SideBar/SideBarTabShare'
+import SideBar from '../components/SideBar/SideBar'
import { mapState, mapGetters } from 'vuex'
-import { AppSidebar, AppSidebarTab } from '@nextcloud/vue'
export default {
name: 'Vote',
components: {
Notification,
- SideBarTabConfiguration,
- SideBarTabComments,
- SideBarTabDateOptions,
- SideBarTabTextOptions,
- SideBarTabShare,
+ VoteHeader,
VoteTable,
- AppSidebar,
- AppSidebarTab
+ SideBar
},
data() {
@@ -113,17 +64,23 @@ export default {
computed: {
...mapState({
- poll: state => state.poll,
event: state => state.event,
- shares: state => state.poll.shares
+ shares: state => state.shares,
+ acl: state => state.acl
}),
...mapGetters([
- 'allowEdit'
+ 'isExpirationSet',
+ 'expired',
+ 'timeSpanExpiration'
]),
windowTitle: function() {
return t('polls', 'Polls') + ' - ' + this.event.title
+ },
+
+ votePossible() {
+ return this.acl.allowVote && !this.expired
}
},
@@ -178,23 +135,6 @@ export default {
}
this.sideBarOpen = true
this.$store.commit('pollSetProperty', { 'mode': 'edit' })
- },
-
- toggleEdit() {
- if (this.poll.mode === 'vote') {
- this.$store.commit('pollSetProperty', { 'mode': 'edit' })
- } else if (this.poll.mode === 'edit') {
- this.$store.commit('pollSetProperty', { 'mode': 'vote' })
- }
- },
-
- timer() {
- this.voteSaved = false
- },
-
- indicateVoteSaved() {
- this.voteSaved = true
- window.setTimeout(this.timer, this.delay)
}
}
}