Cleanup and change routes

Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
John Molakvoæ (skjnldsv) 2020-03-20 12:22:07 +01:00
parent d6ec29ea24
commit 37ee5fcea6
No known key found for this signature in database
GPG key ID: 60C25B8C072916CF
32 changed files with 650 additions and 430 deletions

View file

@ -24,12 +24,14 @@
return [
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'page#goto_form', 'url' => '/form/{hash}', 'verb' => 'GET'],
['name' => 'page#create_form', 'url' => '/new', 'verb' => 'GET'],
['name' => 'page#edit_form', 'url' => '/edit/{hash}', 'verb' => 'GET'],
['name' => 'page#clone_form', 'url' => '/clone/{hash}', 'verb' => 'GET'],
['name' => 'page#getResult', 'url' => '/results/{id}', 'verb' => 'GET'],
// Before /{hash} to avoid conflict
['name' => 'page#createForm', 'url' => '/new', 'verb' => 'GET'],
['name' => 'page#editForm', 'url' => '/{hash}/edit/', 'verb' => 'GET'],
['name' => 'page#cloneForm', 'url' => '/{hash}/clone/', 'verb' => 'GET'],
['name' => 'page#getResult', 'url' => '/{hash}/results/', 'verb' => 'GET'],
['name' => 'page#goto_form', 'url' => '/{hash}', 'verb' => 'GET'],
['name' => 'page#delete_form', 'url' => '/delete', 'verb' => 'POST'],
['name' => 'page#insert_vote', 'url' => '/insert/vote', 'verb' => 'POST'],

View file

@ -1,33 +0,0 @@
$bg-no: #ffede9;
$bg-maybe: #fcf7e1;
$bg-unvoted: #fff4c8;
$bg-yes: #ebf5d6;
$bg-information: #b19c3e;
$fg-no: #f45573;
$fg-maybe: #f0db98;
$fg-unvoted: #f0db98;
$fg-yes: #49bc49;
// Icon definitions
@include icon-black-white('app', 'forms', 2);
.icon-yes {
@include icon-color('checkmark', 'actions', $fg-yes, 1, true);
}
.icon-comment-yes {
@include icon-color('comment', 'actions', $fg-yes, 1, true);
}
.icon-comment-no {
@include icon-color('comment', 'actions', $fg-no, 1, true);
}
.icon-no {
@include icon-color('close', 'actions', $fg-no, 1, true);
}
.icon-maybe {
@include icon-color('maybe-vote-variant', 'forms', $fg-maybe);
}

23
css/forms.scss Normal file
View file

@ -0,0 +1,23 @@
/**
* @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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 'icons'

24
css/icons.scss Normal file
View file

@ -0,0 +1,24 @@
// Icon definitions
@include icon-black-white('forms', 'forms', 3);
@include icon-black-white('clone', 'forms', 1);
.icon-yes {
@include icon-color('checkmark', 'actions', $color-success, 1, true);
}
.icon-comment-yes {
@include icon-color('comment', 'actions', $color-success, 1, true);
}
.icon-comment-no {
@include icon-color('comment', 'actions', $color-error, 1, true);
}
.icon-no {
@include icon-color('close', 'actions', $color-error, 1, true);
}
.icon-maybe {
@include icon-color('maybe-vote-variant', 'forms', $color-warning);
}

View file

@ -1,214 +0,0 @@
@import 'colors.scss';
$border_current_user: 2px solid;
$border_user: 1px solid var(--color-border-dark);
$user-column-width: 265px;
#forms-sidebar {
width: 520px;
flex-grow: 0;
flex-shrink: 1;
min-width: 300px;
border-left: 1px solid var(--color-border);
transition: margin-right 300ms;
z-index: 500;
> div,
> ul {
padding: 8px;
}
}
.authorRow {
align-items: center;
.author {
margin-left: 8px;
opacity: 0.5;
flex-grow: 1;
&.external {
margin-right: 33px;
opacity: 1;
> input {
width: 100%;
}
}
}
}
.detailsView {
z-index: 1000 !important;
.close.flex-row {
justify-content: flex-end;
margin: 8px 8px 0 0;
}
.header.flex-row {
flex-direction: row;
flex-grow: 0;
align-items: flex-start;
margin-left: 0;
margin-top: 0;
padding: 0 17px;
}
.formInformation {
width: 220px;
flex-grow: 1;
flex-shrink: 1;
padding-right: 15px;
.authorRow {
.leftLabel {
margin-right: 4px;
}
}
.cloud {
margin: 4px 0;
> span {
color: var(--color-primary-text);
margin: 2px;
padding: 2px 4px;
border-radius: var(--border-radius);
float: left;
text-shadow: 1px 1px var(--color-box-shadow);
background-color: var(--color-loading-light);
}
.open {
background-color: $fg-yes;
}
.expired {
background-color: $fg-no;
}
.information {
background-color: $bg-information;
}
}
}
#expired_info {
margin: 0 15px;
}
.formActions {
display: flex;
flex-direction: column;
margin-right: 15px;
.close {
margin: 15px;
background-position: right top;
height: 30px;
}
> ul > li {
&:focus,
&:hover,
&.active,
a.selected {
&,
> a {
opacity: 1;
box-shadow: inset 4px 0 var(--color-primary);
}
}
> a[class*='icon-'],
> ul > li > a[class*='icon-'],
> a[style*='background-image'],
> ul > li > a[style*='background-image'] {
padding-left: 44px;
}
> a,
> ul > li > a {
background-size: 16px 16px;
background-position: 14px center;
background-repeat: no-repeat;
display: block;
justify-content: space-between;
line-height: 44px;
min-height: 44px;
padding: 0 12px;
overflow: hidden;
box-sizing: border-box;
white-space: nowrap;
text-overflow: ellipsis;
color: var(--color-main-text);
opacity: 0.57;
flex: 1 1 0;
z-index: 100;
}
a,
.app-navigation-entry-deleted {
padding-left: 44px !important;
}
}
}
#configurationsTabView {
.configBox {
padding: 8px 8px;
> .title {
font-weight: bold;
margin-bottom: 4px;
}
> div {
padding-left: 4px;
}
input.hasDatepicker {
margin-left: 17px;
}
&.oneline {
width: 100%;
}
}
}
#commentsTabView {
.newCommentForm div.message:empty:before {
content: attr(data-placeholder);
color: grey;
}
#commentBox {
border: 1px solid var(--color-border-dark);
border-radius: var(--border-radius);
padding: 7px 6px;
margin: 3px 3px 3px 40px;
cursor: text;
}
.comment {
margin-bottom: 30px;
.date {
right: 0;
top: 5px;
opacity: 0.5;
}
}
.message {
margin-left: 40px;
flex-grow: 1;
flex-shrink: 1;
}
.new-comment {
.submitComment {
align-self: last baseline;
width: 30px;
margin: 0;
padding: 7px 9px;
background-color: transparent;
border: none;
opacity: 0.3;
}
.icon-loading-small {
float: left;
margin-top: 10px;
display: none;
}
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 B

View file

@ -1 +1,8 @@
<svg width="32" height="32" version="1.1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="m5 3a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3zm7 1c-0.554 0-1 0.446-1 1v2c0 0.554 0.446 1 1 1h16c0.554 0 1-0.446 1-1v-2c0-0.554-0.446-1-1-1h-16zm-7 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3zm7 1c-0.554 0-1 0.446-1 1v2c0 0.554 0.446 1 1 1h16c0.554 0 1-0.446 1-1v-2c0-0.554-0.446-1-1-1h-16zm-7 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3zm7 1c-0.554 0-1 0.446-1 1v2c0 0.554 0.446 1 1 1h16c0.554 0 1-0.446 1-1v-2c0-0.554-0.446-1-1-1h-16z" fill="#fff" style="paint-order:markers fill stroke"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32">
<circle cx="6" cy="6" r="3" fill="#fff"/>
<circle cx="6" cy="16" r="3" fill="#fff"/>
<circle cx="6" cy="26" r="3" fill="#fff"/>
<rect width="18" height="4" x="11" y="4" rx="1" ry="1" fill="#fff"/>
<rect width="18" height="4" x="11" y="14" rx="1" ry="1" fill="#fff"/>
<rect width="18" height="4" x="11" y="24" rx="1" ry="1" fill="#fff"/>
</svg>

Before

Width:  |  Height:  |  Size: 647 B

After

Width:  |  Height:  |  Size: 420 B

1
img/clone.svg Normal file
View file

@ -0,0 +1 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M11.8 13.8H2.2V4.2h9.6m1.2 0c0-.67-.53-1.2-1.2-1.2H2.2C1.53 3 1 3.53 1 4.2v9.6c0 .67.53 1.2 1.2 1.2h9.6c.67 0 1.2-.53 1.2-1.2"/><path d="m4.2 1c-0.67 0-1.2 0.54-1.2 1.2h10.8v10.8c0.67 0 1.2-0.53 1.2-1.2v-9.6c0-0.67-0.53-1.2-1.2-1.2z"/></svg>

After

Width:  |  Height:  |  Size: 327 B

View file

@ -1,45 +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="svg10"
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"
sodipodi:docname="favicon-mask.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><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="1046"
id="namedview15"
showgrid="false"
inkscape:zoom="3.6875"
inkscape:cx="11.825118"
inkscape:cy="3.2468217"
inkscape:window-x="0"
inkscape:window-y="34"
inkscape:window-maximized="1"
inkscape:current-layer="svg10" /><metadata
id="metadata16"><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 /></cc:Work></rdf:RDF></metadata><defs
id="defs14" /><path
d="M 5 0 C 2.23 0 0 2.23 0 5 L 0 27 C 0 29.77 2.23 32 5 32 L 27 32 C 29.77 32 32 29.77 32 27 L 32 5 C 32 2.23 29.77 0 27 0 L 5 0 z M 5 3 A 3 3 0 0 1 8 6 A 3 3 0 0 1 5 9 A 3 3 0 0 1 2 6 A 3 3 0 0 1 5 3 z M 12 4 L 28 4 C 28.554 4 29 4.446 29 5 L 29 7 C 29 7.554 28.554 8 28 8 L 12 8 C 11.446 8 11 7.554 11 7 L 11 5 C 11 4.446 11.446 4 12 4 z M 5 13 A 3 3 0 0 1 8 16 A 3 3 0 0 1 5 19 A 3 3 0 0 1 2 16 A 3 3 0 0 1 5 13 z M 12 14 L 28 14 C 28.554 14 29 14.446 29 15 L 29 17 C 29 17.554 28.554 18 28 18 L 12 18 C 11.446 18 11 17.554 11 17 L 11 15 C 11 14.446 11.446 14 12 14 z M 5 23 A 3 3 0 0 1 8 26 A 3 3 0 0 1 5 29 A 3 3 0 0 1 2 26 A 3 3 0 0 1 5 23 z M 12 24 L 28 24 C 28.554 24 29 24.446 29 25 L 29 27 C 29 27.554 28.554 28 28 28 L 12 28 C 11.446 28 11 27.554 11 27 L 11 25 C 11 24.446 11.446 24 12 24 z "
id="rect2" /></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View file

@ -1 +0,0 @@
<svg width="128" height="128" enable-background="new 0 0 595.275 311.111" version="1.1" viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="128" height="128" rx="20" ry="20" fill="#0082c9"/><path d="m20 12a12 12 0 0 0-12 12 12 12 0 0 0 12 12 12 12 0 0 0 12-12 12 12 0 0 0-12-12zm28 4c-2.216 0-4 1.784-4 4v8c0 2.216 1.784 4 4 4h64c2.216 0 4-1.784 4-4v-8c0-2.216-1.784-4-4-4zm-28 36a12 12 0 0 0-12 12 12 12 0 0 0 12 12 12 12 0 0 0 12-12 12 12 0 0 0-12-12zm28 4c-2.216 0-4 1.784-4 4v8c0 2.216 1.784 4 4 4h64c2.216 0 4-1.784 4-4v-8c0-2.216-1.784-4-4-4zm-28 36a12 12 0 0 0-12 12 12 12 0 0 0 12 12 12 12 0 0 0 12-12 12 12 0 0 0-12-12zm28 4c-2.216 0-4 1.784-4 4v8c0 2.216 1.784 4 4 4h64c2.216 0 4-1.784 4-4v-8c0-2.216-1.784-4-4-4z" fill="#fff" style="paint-order:markers fill stroke"/></svg>

Before

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 B

View file

@ -1 +0,0 @@
<svg width="32" height="32" enable-background="new 0 0 595.275 311.111" version="1.1" viewBox="0 0 32 32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="32" height="32" rx="5" ry="5" fill="#0082c9"/><path d="m5 3a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3zm7 1c-0.554 0-1 0.446-1 1v2c0 0.554 0.446 1 1 1h16c0.554 0 1-0.446 1-1v-2c0-0.554-0.446-1-1-1zm-7 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3zm7 1c-0.554 0-1 0.446-1 1v2c0 0.554 0.446 1 1 1h16c0.554 0 1-0.446 1-1v-2c0-0.554-0.446-1-1-1zm-7 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3zm7 1c-0.554 0-1 0.446-1 1v2c0 0.554 0.446 1 1 1h16c0.554 0 1-0.446 1-1v-2c0-0.554-0.446-1-1-1z" fill="#fff" style="paint-order:markers fill stroke"/></svg>

Before

Width:  |  Height:  |  Size: 759 B

8
img/forms.svg Normal file
View file

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32">
<circle cx="6" cy="6" r="3" fill="#fff"/>
<circle cx="6" cy="16" r="3" fill="#fff"/>
<circle cx="6" cy="26" r="3" fill="#fff"/>
<rect width="18" height="4" x="11" y="4" rx="1" ry="1" fill="#fff"/>
<rect width="18" height="4" x="11" y="14" rx="1" ry="1" fill="#fff"/>
<rect width="18" height="4" x="11" y="24" rx="1" ry="1" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 420 B

View file

@ -7,6 +7,7 @@
* @author Inigo Jiron <ijiron@terpmail.umd.edu>
* @author Natalie Gilbert
* @author Affan Hussain
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license GNU AGPL version 3 or any later version
*
@ -86,40 +87,63 @@ class PageController extends Controller {
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*/
* @NoAdminRequired
* @NoCSRFRequired
*
* @return TemplateResponse
*/
public function index(): TemplateResponse {
return new TemplateResponse('forms', 'forms.tmpl',
['urlGenerator' => $this->urlGenerator]);
}
/**
* @NoAdminRequired
*/
public function createForm(): TemplateResponse {
return new TemplateResponse('forms', 'forms.tmpl',
['urlGenerator' => $this->urlGenerator]);
}
/**
* @NoAdminRequired
*/
public function cloneForm(): TemplateResponse {
return new TemplateResponse('forms', 'forms.tmpl',
['urlGenerator' => $this->urlGenerator]);
Util::addScript($this->appName, 'forms');
Util::addStyle($this->appName, 'icons');
return new TemplateResponse($this->appName, 'main');
}
/**
* @NoAdminRequired
* @param string $hash
* @NoCSRFRequired
*
* @return TemplateResponse
*/
public function editForm($hash): TemplateResponse {
return new TemplateResponse('forms', 'forms.tmpl', [
'urlGenerator' => $this->urlGenerator,
'hash' => $hash
]);
public function createForm(): TemplateResponse {
Util::addScript($this->appName, 'forms');
Util::addStyle($this->appName, 'icons');
return new TemplateResponse($this->appName, 'main');
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @return TemplateResponse
*/
public function cloneForm(): TemplateResponse {
Util::addScript($this->appName, 'forms');
Util::addStyle($this->appName, 'icons');
return new TemplateResponse($this->appName, 'main');
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @return TemplateResponse
*/
public function editForm(): TemplateResponse {
Util::addScript($this->appName, 'forms');
Util::addStyle($this->appName, 'icons');
return new TemplateResponse($this->appName, 'main');
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @return TemplateResponse
*/
public function getResult(): TemplateResponse {
Util::addScript($this->appName, 'forms');
Util::addStyle($this->appName, 'icons');
return new TemplateResponse($this->appName, 'main');
}
/**
@ -402,13 +426,4 @@ class PageController extends Controller {
}
return false;
}
/**
* @NoAdminRequired
* @param int $id
* @return TemplateResponse
*/
public function getResult(int $id): TemplateResponse {
return new TemplateResponse('forms', 'forms.tmpl');
}
}

21
package-lock.json generated
View file

@ -1668,6 +1668,22 @@
"integrity": "sha512-f+sKpdLZXkODV+OY39K1M+Spmd4RgxmtEXmNn4Bviv4R7uBFHXuw+JX9ZdfDeOryfHjJ/TRQxQEp0GMpBwZFUw==",
"dev": true
},
"@nextcloud/dialogs": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@nextcloud/dialogs/-/dialogs-1.2.2.tgz",
"integrity": "sha512-N8A8J8UKSvz/hqNcm7gwpm70uAAsx0wurjhdYZ989jaMho+H/Hinjd2jkbV8UnsYYw0x/vWvEX5t6Lwbv08K0g==",
"requires": {
"core-js": "3.6.4",
"toastify-js": "^1.7.0"
},
"dependencies": {
"core-js": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
"integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw=="
}
}
},
"@nextcloud/eslint-config": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@nextcloud/eslint-config/-/eslint-config-1.0.0.tgz",
@ -10977,6 +10993,11 @@
}
}
},
"toastify-js": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.7.0.tgz",
"integrity": "sha512-GmPy4zJ/ulCfmCHlfCtgcB+K2xhx2AXW3T/ZZOSjyjaIGevhz+uvR8HSCTay/wBq4tt2mUnBqlObP1sSWGlsnQ=="
},
"tough-cookie": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",

View file

@ -70,7 +70,9 @@
},
"dependencies": {
"@nextcloud/axios": "^1.3.2",
"@nextcloud/dialogs": "^1.2.2",
"@nextcloud/moment": "^1.1.0",
"@nextcloud/router": "^1.0.2",
"@nextcloud/vue": "^1.4.1",
"json2csv": "5.0.0",
"vue": "^2.6.11",

131
src/Forms.vue Normal file
View file

@ -0,0 +1,131 @@
<!--
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
-
- @author René Gieling <github@dartcafe.de>
- @author John Molakvoæ <skjnldsv@protonmail.com>
-
- @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>
<Content app-name="forms">
<AppNavigation>
<AppNavigationNew button-class="icon-add" :text="t('forms', 'New form')" @click="onNewForm" />
<AppNavigationForm v-for="form in formattedForms" :key="form.id" :form="form" />
</AppNavigation>
<!-- No forms & loading emptycontents -->
<AppContent v-if="loading || noForms || !hash">
<EmptyContent v-if="loading" icon="icon-loading">
{{ t('forms', 'Loading forms …') }}
</EmptyContent>
<EmptyContent v-else-if="noForms">
{{ t('forms', 'No forms in here') }}
<template #desc>
<button class="primary" @click="onNewForm">
{{ t('forms', 'Create a new one') }}
</button>
</template>
</EmptyContent>
<EmptyContent v-else>
{{ t('forms', 'Please select a form') }}
</EmptyContent>
</AppContent>
<!-- No errors show router content -->
<router-view v-else />
</Content>
</template>
<script>
import { showError } from '@nextcloud/dialogs'
import axios from '@nextcloud/axios'
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
import AppNavigation from '@nextcloud/vue/dist/Components/AppNavigation'
import AppNavigationNew from '@nextcloud/vue/dist/Components/AppNavigationNew'
import Content from '@nextcloud/vue/dist/Components/Content'
import AppNavigationForm from './components/AppNavigationForm'
import EmptyContent from './components/EmptyContent'
import { formatForm } from './utils/FormsUtils'
export default {
name: 'Forms',
components: {
AppNavigationForm,
AppContent,
AppNavigation,
AppNavigationNew,
Content,
EmptyContent,
},
data() {
return {
loading: true,
forms: [],
}
},
computed: {
noForms() {
return this.forms && this.forms.length === 0
},
formattedForms() {
return this.forms.map(formatForm)
},
hash() {
return this.$route.params.hash
},
},
beforeMount() {
this.loadForms()
},
methods: {
/**
* Initial forms load
*/
async loadForms() {
this.loading = true
try {
const response = await axios.get(OC.generateUrl('apps/forms/get/forms'))
this.forms = response.data
} catch (error) {
showError(t('forms', 'An error occured while loading the forms list'))
console.error(error)
} finally {
this.loading = false
}
},
/**
*
*/
onNewForm() {
this.$router.push({ name: 'create' })
},
},
}
</script>

View file

@ -0,0 +1,79 @@
<!--
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
-
- @author John Molakvoæ <skjnldsv@protonmail.com>
-
- @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>
<AppNavigationItem
:exact="true"
:title="form.title"
:to="{ name: 'edit', params: { hash: form.hash } }">
<template #actions>
<ActionRouter :close-after-click="true"
:exact="true"
icon="icon-checkmark"
:to="{ name: 'results', params: { hash: form.hash } }">
{{ t('forms', 'Show results') }}
</ActionRouter>
<ActionRouter :close-after-click="true"
:exact="true"
icon="icon-clone"
:to="{ name: 'clone', params: { hash: form.hash } }">
{{ t('forms', 'Clone form') }}
</ActionRouter>
<ActionSeparator />
<ActionButton :close-after-click="true" icon="icon-delete" @click="deleteForm">
{{ t('forms', 'Delete form') }}
</ActionButton>
</template>
</AppNavigationItem>
</template>
<script>
import AppNavigationItem from '@nextcloud/vue/dist/Components/AppNavigationItem'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import ActionRouter from '@nextcloud/vue/dist/Components/ActionRouter'
import ActionSeparator from '@nextcloud/vue/dist/Components/ActionSeparator'
export default {
name: 'AppNavigationForm',
components: {
AppNavigationItem,
ActionButton,
ActionRouter,
ActionSeparator,
},
props: {
form: {
type: Object,
required: true,
},
},
methods: {
async deleteForm() {
},
},
}
</script>

View file

@ -1,7 +1,7 @@
<!--
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
-
- @author René Gieling <github@dartcafe.de>
- @author John Molakvoæ <skjnldsv@protonmail.com>
-
- @license GNU AGPL version 3 or any later version
-
@ -12,34 +12,40 @@
-
- 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
- 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/>.
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-->
<template>
<div id="app-forms">
<router-view />
<div class="emptycontent" role="note">
<div :class="icon" role="img" />
<h2><slot /></h2>
<p v-show="$slots.desc">
<slot name="desc" />
</p>
</div>
</template>
<script>
export default {
name: 'App',
name: 'EmptyContent',
props: {
icon: {
type: String,
default: 'icon-forms',
},
},
}
</script>
<style lang="scss">
#app-forms {
width: 100%;
display: flex;
.emptycontent {
margin-top: 20vh;
}
#app-content {
width: 100%;
display: flex;
}
</style>

View file

@ -1,4 +1,3 @@
/* jshint esversion: 6 */
/**
* @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
*
@ -23,7 +22,7 @@
import Vue from 'vue'
import router from './router'
import App from './App'
import Forms from './Forms'
import VueClipboard from 'vue-clipboard2'
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip'
@ -51,7 +50,7 @@ __webpack_public_path__ = OC.linkTo('forms', 'js/')
/* eslint-disable-next-line no-new */
new Vue({
el: '#app-forms',
el: '#content',
router: router,
render: h => h(App),
render: h => h(Forms),
})

29
src/mixins/ViewsMixin.js Normal file
View file

@ -0,0 +1,29 @@
/**
* @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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/>.
*/
export default {
props: {
hash: {
type: String,
default: null,
},
},
}

View file

@ -1,4 +1,3 @@
/* jshint esversion: 6 */
// we need our modal component
import ModalDialog from './ModalDialog'

View file

@ -1,4 +1,3 @@
/* jshint esversion: 6 */
/**
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
@ -22,59 +21,56 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import Vue from 'vue'
import Router from 'vue-router'
import { generateUrl } from '@nextcloud/router'
import Create from './views/Create'
import Results from './views/Results'
// Dynamic loading
const Create = () => import('./views/Create')
const List = () => import('./views/List')
const Results = () => import('./views/Results')
Vue.use(Router)
export default new Router({
mode: 'history',
base: OC.generateUrl(''),
// if index.php is in the url AND we got this far, then it's working:
// let's keep using index.php in the url
base: generateUrl('/apps/forms', ''),
linkActiveClass: 'active',
routes: [
{
path: '/:index(index.php/)?apps/forms/',
components: {
default: List,
},
props: false,
name: 'list',
path: '/',
name: 'root',
},
{
path: '/:index(index.php/)?apps/forms/edit/:hash',
components: {
default: Create,
},
props: true,
name: 'edit',
},
{
path: '/:index(index.php/)?apps/forms/results/:hash',
components: {
default: Results,
},
props: false,
name: 'results',
},
{
path: '/:index(index.php/)?apps/forms/clone/:hash',
components: {
default: Create,
},
props: true,
name: 'clone',
},
{
path: '/:index(index.php/)?apps/forms/new',
components: {
default: Create,
},
props: false,
path: '/new',
component: Create,
name: 'create',
},
{
path: '/:hash',
name: 'fill',
props: true,
},
{
path: '/:hash/edit',
component: Create,
name: 'edit',
props: true,
},
{
path: '/:hash/results',
component: Results,
name: 'results',
props: true,
},
{
path: '/:hash/clone',
component: Create,
name: 'clone',
props: true,
},
],
})

View file

@ -0,0 +1,53 @@
/**
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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'
/**
* Get the forms list
*
* @returns {Array}
*/
const getForms = async function() {
try {
const response = await axios.get(OC.generateUrl('apps/forms/get/forms'))
return response.data
} catch (error) {
console.error(error)
throw Error(t('forms', 'Unable to fetch the forms list'))
}
}
/**
* Delete a form
*
* @param {int} id the form id to delete
*/
const deleteForm = async function(id) {
try {
axios.delete(OC.generateUrl('apps/forms/forms/{id}', { id }))
} catch (error) {
console.error(error)
throw Error(t('forms', 'Unable to delete the form'))
}
}
export { deleteForm, getForms }

44
src/utils/FormsUtils.js Normal file
View file

@ -0,0 +1,44 @@
/**
* @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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/>.
*/
/**
* Format a form object prior to forms v2.0
*
* @param {Object} form the form raw object
* @returns {Object} properly formatted form object
*/
const formatForm = function(form) {
// clone form
const newForm = Object.assign({}, form, form.event)
// migrate object architecture
Object.assign(newForm, {
questions: form.options.formQuizQuestions,
})
// cleanup
delete newForm.options
delete newForm.event
return newForm
}
export { formatForm }

View file

@ -26,7 +26,7 @@
-->
<template>
<div id="app-content">
<AppContent>
<Controls :intitle="title">
<template slot="after">
<button :disabled="writingForm" class="button btn primary" @click="writeForm(form.mode)">
@ -202,14 +202,16 @@
@remove-share="removeShare" />
</SideBar>
<LoadingOverlay v-if="loadingForm" />
</div>
</AppContent>
</template>
<script>
import axios from '@nextcloud/axios'
import DatetimePicker from '@nextcloud/vue/dist/Components/DatetimePicker'
import moment from '@nextcloud/moment'
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
import DatetimePicker from '@nextcloud/vue/dist/Components/DatetimePicker'
import Controls from '../components/_base-Controls'
import LoadingOverlay from '../components/_base-LoadingOverlay'
import QuizFormItem from '../components/quizFormItem'
@ -217,9 +219,12 @@ import ShareDiv from '../components/shareDiv'
import SideBar from '../components/_base-SideBar'
import UserDiv from '../components/_base-UserDiv'
import ViewsMixin from '../mixins/ViewsMixin'
export default {
name: 'Create',
components: {
AppContent,
Controls,
DatetimePicker,
LoadingOverlay,
@ -229,6 +234,8 @@ export default {
UserDiv,
},
mixins: [ViewsMixin],
data() {
return {
move: {

31
src/views/Fill.vue Normal file
View file

@ -0,0 +1,31 @@
<!--
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
-
- @author John Molakvoæ <skjnldsv@protonmail.com>
-
- @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>
<span>TODO</span>
</template>
<script>
export default {
name: 'Fill',
}
</script>

View file

@ -22,15 +22,7 @@
-->
<template>
<div id="app-content">
<Controls>
<router-link :to="{ name: 'create'}" class="button">
<span class="symbol icon-add" />
<span class="hidden-visually">
{{ t('forms', 'New') }}
</span>
</router-link>
</Controls>
<AppContent>
<div v-if="noForms" class="">
<div class="icon-forms" />
<h2> {{ t('No existing forms.') }} </h2>
@ -56,54 +48,69 @@
</transition-group>
<LoadingOverlay v-if="loading" />
<modal-dialog />
</div>
</AppContent>
</template>
<script>
import { showError } from '@nextcloud/dialogs'
import axios from '@nextcloud/axios'
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
import FormListItem from '../components/formListItem'
import Controls from '../components/_base-Controls'
import axios from '@nextcloud/axios'
import LoadingOverlay from '../components/_base-LoadingOverlay'
import ViewsMixin from '../mixins/ViewsMixin'
export default {
name: 'List',
components: {
Controls,
AppContent,
FormListItem,
LoadingOverlay,
},
mixins: [ViewsMixin],
data() {
return {
noForms: false,
loading: true,
forms: [],
}
},
created() {
this.indexPage = OC.generateUrl('apps/forms/')
computed: {
noForms() {
return this.forms && this.forms.length > 0
},
},
beforeMount() {
this.loadForms()
},
methods: {
loadForms() {
/**
* Initial forms load
*/
async loadForms() {
this.loading = true
axios.get(OC.generateUrl('apps/forms/get/forms'))
.then((response) => {
this.forms = response.data
this.loading = false
}, (error) => {
/* eslint-disable-next-line no-console */
console.log(error.response)
this.loading = false
})
try {
const response = await axios.get(OC.generateUrl('apps/forms/get/forms'))
this.forms = response.data
} catch (error) {
showError(t('forms', 'An error occured while loading the forms list'))
console.error(error)
} finally {
this.loading = false
}
},
/**
* Open the help page
*/
helpPage() {
window.open('https://github.com/affan98/forms/blob/master/Forms_Support.md')
window.open('https://github.com/nextcloud/forms/blob/master/Forms_Support.md')
},
viewFormResults(index, event, name) {
this.$router.push({
name: name,

View file

@ -23,7 +23,7 @@
-->
<template>
<div>
<AppContent>
<div>
<button class="button btn primary" @click="download">
<span>{{ "Export to CSV" }}</span>
@ -51,7 +51,7 @@
<LoadingOverlay v-if="loading" />
<modal-dialog />
</div>
</div>
</AppContent>
</template>
<script>
@ -59,15 +59,20 @@ import ResultItem from '../components/resultItem'
import json2csvParser from 'json2csv'
import axios from '@nextcloud/axios'
import LoadingOverlay from '../components/_base-LoadingOverlay'
import ViewsMixin from '../mixins/ViewsMixin'
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
export default {
name: 'Results',
components: {
AppContent,
ResultItem,
LoadingOverlay,
},
mixins: [ViewsMixin],
data() {
return {
loading: true,

25
templates/main.php Normal file
View file

@ -0,0 +1,25 @@
<?php
/**
* @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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/>.
*
*/
?>
<div id="content"></div>