Merge pull request #394 from nextcloud/stable-0.9

Stable 0.9
This commit is contained in:
René Gieling 2018-10-21 13:35:42 +02:00 коммит произвёл GitHub
Родитель b80fb7df77 5143738063
Коммит 0c270ba507
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
81 изменённых файлов: 3338 добавлений и 3009 удалений

6
.babelrc Normal file
Просмотреть файл

@ -0,0 +1,6 @@
{
"presets": [
["env", { "modules": false }],
"stage-3"
]
}

11
.gitignore поставляемый
Просмотреть файл

@ -1,3 +1,4 @@
.DS_Store
.sass-cache/
.project/
.idea/
@ -5,8 +6,14 @@ build/
css/*.map
nbproject/
node_modules/
npm-debug.log
package-lock.json
Thumbs.db
yarn-error.log
*.cmd
*.lock
*.iml
.DS_Store
Thumbs.db
*.ntvs*
*.njsproj
*.sln
*.suo

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

@ -7,35 +7,33 @@ services:
- postgresql
php:
- 5.6
- 7.0
- 7.1
- 7.2
addons:
apt:
packages:
- libxml2-utils
env:
global:
- CORE_BRANCH=stable14
- APP_NAME=polls
matrix:
- DB=mysql;CLOUD=nextcloud;CORE_BRANCH=stable12
- DB=sqlite;CLOUD=nextcloud;CORE_BRANCH=stable12
- DB=pgsql;CLOUD=nextcloud;CORE_BRANCH=stable12
- DB=mysql;CLOUD=owncloud;CORE_BRANCH=stable10
- DB=mysql
- DB=pgsql
- DB=sqlite
cache:
directories:
- $HOME/.composer/cache
before_install:
# Install NC / oC and app
- if [ $CLOUD = 'nextcloud' ]; then wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh; fi
- if [ $CLOUD = 'owncloud' ]; then wget https://raw.githubusercontent.com/owncloud/administration/master/travis-ci/before_install.sh; fi
# Install NC and app
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh;
- bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB
- if [ $CLOUD = 'nextcloud' ]; then cd ../server; fi
- if [ $CLOUD = 'owncloud' ]; then cd ../core; fi
- cd ../server;
- ./occ app:enable $APP_NAME
before_script:
@ -57,4 +55,4 @@ after_success:
- php ocular.phar code-coverage:upload --format=php-clover clover.xml
after_failure:
- cat ../../data/${CLOUD}.log
- cat ../../data/nextcloud.log

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

@ -2,7 +2,33 @@
All notable changes to this project will be documented in this file.
## [0.9.0] - tbD
### Added
- create/edit page
- rewrite as a vue app
- improved UI
- introduced new NC date time picker from vue-nextcloud
- added option to forbid "maybe" vote
- vote page
- made polls table scrollable
- show new vote options after voting
### Changed
- Compatibility to NC 14
- Introduced vue
- Changing database theme
- Polls is a Nextcloud only app now. If you wish to proceed developing the ownCloud version, make a fork from the `stable-0.8` branch.
### Fixed
- 'Edit poll' did not work from poll's details view (#294)
- Bug which makes voting impossible after edit
- Write escapes option texts to db (#341)
- ... a lot more minor bugs
See https://github.com/nextcloud/polls/milestone/9?closed=1 for all changes and additions.
## [0.8.3] - 2018-08-30
### Added

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

@ -8,19 +8,14 @@
# * which
# * curl: used if phpunit and composer are not installed to fetch them from the web
# * tar: for building the archive
# * sass: for building css files for ownCloud
app_name=$(notdir $(CURDIR))
build_tools_directory=$(CURDIR)/build/tools
build_source_directory=$(CURDIR)/build/source
appstore_build_directory=$(CURDIR)/build/artifacts/appstore
appstore_package_name=$(appstore_build_directory)/$(app_name)
marketplace_build_directory=$(CURDIR)/build/artifacts/marketplace
marketplace_package_name=$(marketplace_build_directory)/$(app_name)
nc_cert_directory=$(HOME)/.nextcloud/certificates
oc_cert_directory=$(HOME)/.owncloud/certificates
composer=$(shell which composer 2> /dev/null)
sass=$(shell which sass 2> /dev/null)
all: composer
@ -43,9 +38,7 @@ endif
# Removes the appstore build
.PHONY: clean
clean:
rm -rf ./.sass-cache
rm -rf ./build
rm -rf ./css/*.css
# Builds the source package for the app store, ignores php and js tests
.PHONY: appstore
@ -65,7 +58,7 @@ appstore:
--exclude="ISSUE_TEMPLATE.md" \
--exclude="karma.*" \
--exclude="Makefile" \
--exclude="package.json" \
--exclude="package*" \
--exclude="phpunit*xml" \
--exclude="protractor.*" \
--exclude="build" \
@ -81,6 +74,7 @@ appstore:
--exclude="js/.*" \
--exclude="l10n/no-php" \
--exclude="node_modules" \
--exclude="screenshots" \
--exclude="src" \
--exclude="tests" \
--exclude="vendor" \
@ -93,60 +87,6 @@ appstore:
openssl dgst -sha512 -sign $(nc_cert_directory)/$(app_name).key $(appstore_build_directory)/$(app_name).tar.gz | openssl base64; \
fi
# Builds the source package for the marketplace, ignores php and js tests
.PHONY: marketplace
marketplace:
ifeq (,$(sass))
@echo "No sass command available, please install it and rerun"
else
sass --sourcemap=none --update ./src/css-oc:css
rm -rf $(marketplace_build_directory)
rm -rf $(build_source_directory)
mkdir -p $(marketplace_build_directory)
mkdir -p $(build_source_directory)
rsync -a \
--include="js/vendor" \
--include="css/vendor" \
--exclude="*.log" \
--exclude=".*" \
--exclude="bower.json" \
--exclude="composer.*" \
--exclude="ISSUE_TEMPLATE.md" \
--exclude="karma.*" \
--exclude="Makefile" \
--exclude="package.json" \
--exclude="phpunit*xml" \
--exclude="protractor.*" \
--exclude="build" \
--exclude="css/*.scss" \
--exclude="js/node_modules" \
--exclude="js/tests" \
--exclude="js/test" \
--exclude="js/*.log" \
--exclude="js/package.json" \
--exclude="js/bower.json" \
--exclude="js/karma.*" \
--exclude="js/protractor.*" \
--exclude="js/.*" \
--exclude="l10n/no-php" \
--exclude="node_modules" \
--exclude="src" \
--exclude="tests" \
--exclude="vendor" \
./ $(build_source_directory)/$(app_name)
# We need to replace Nc screenshot urls with the oC ones
sed -i -E "s~(<screenshot>)([^<]*).(png|jpg|jpeg)(</screenshot>)~\1\2-oc.\3\4~" $(build_source_directory)/$(app_name)/appinfo/info.xml
tar cvzf $(marketplace_package_name).tar.gz --directory="$(build_source_directory)" $(app_name)
@if [ -f $(oc_cert_directory)/$(app_name).key ]; then \
echo "Signing package..."; \
openssl dgst -sha512 -sign $(oc_cert_directory)/$(app_name).key $(marketplace_build_directory)/$(app_name).tar.gz | openssl base64; \
fi
endif
.PHONY: test
test: composer
$(CURDIR)/vendor/phpunit/phpunit/phpunit --coverage-clover clover.xml -c phpunit.xml

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

@ -5,7 +5,7 @@
[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/nextcloud/polls.svg?style=flat-square)](https://scrutinizer-ci.com/g/nextcloud/polls)
[![Software License](https://img.shields.io/badge/license-AGPL-brightgreen.svg?style=flat-square)](LICENSE)
This is a poll app, similar to doodle or dudle, for Nextcloud / ownCloud written in PHP and JS / jQuery.
This is a poll app, similar to doodle or dudle, for Nextcloud written in PHP and JS / jQuery.
It is a rework of the already existing [polls app](https://github.com/raduvatav/polls) written by @raduvatav.
### Features
@ -22,17 +22,17 @@ It is a rework of the already existing [polls app](https://github.com/raduvatav/
![Vote](https://github.com/nextcloud/polls/blob/master/screenshots/vote.png)
![New poll](https://github.com/nextcloud/polls/blob/master/screenshots/edit-poll.png)
![Vote mobile portrait](https://github.com/nextcloud/polls/blob/master/screenshots/vote-mobile-portrait.png)
![Vote mobile landscape](https://github.com/nextcloud/polls/blob/master/screenshots/vote-mobile-landscape.png)
![New poll](https://github.com/nextcloud/polls/blob/master/screenshots/edit-poll.png)
## Installation / Update
This app is supposed to work on Nextcloud version 12+ or ownCloud version 10.
This app is supposed to work on Nextcloud version 13+.
### Install latest release
You can download and install the latest release from the [Nextcloud app store](https://apps.nextcloud.com/apps/polls) or from the [ownCloud marketplace](https://marketplace.owncloud.com/apps/polls).
You can download and install the latest release from the [Nextcloud app store](https://apps.nextcloud.com/apps/polls) or a legacy version from the [ownCloud marketplace](https://marketplace.owncloud.com/apps/polls).
### Install from git
If you want to run the latest development version from git source, you need to clone the repo to your apps folder:

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

@ -1,230 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<database>
<name>*dbname*</name>
<create>true</create>
<overwrite>false</overwrite>
<charset>utf8</charset>
<table>
<name>*dbprefix*polls_events</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<default>0</default>
</field>
<field>
<name>hash</name>
<type>text</type>
<length>64</length>
</field>
<field>
<name>type</name>
<type>integer</type>
<notnull>false</notnull>
<length>16</length>
</field>
<field>
<name>title</name>
<type>text</type>
<notnull>true</notnull>
<length>128</length>
</field>
<field>
<name>description</name>
<type>text</type>
<notnull>true</notnull>
<length>1024</length>
</field>
<field>
<name>owner</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>created</name>
<type>timestamp</type>
</field>
<field>
<name>access</name>
<type>text</type>
<notnull>false</notnull>
<length>1024</length>
</field>
<field>
<name>expire</name>
<type>timestamp</type>
</field>
<field>
<name>is_anonymous</name>
<type>integer</type>
<default>0</default>
</field>
<field>
<name>full_anonymous</name>
<type>integer</type>
<default>0</default>
</field>
</declaration>
</table>
<table>
<name>*dbprefix*polls_dts</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
</field>
<field>
<name>poll_id</name>
<type>integer</type>
</field>
<field>
<name>dt</name>
<type>timestamp</type>
<length>32</length>
</field>
</declaration>
</table>
<table>
<name>*dbprefix*polls_txts</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
</field>
<field>
<name>poll_id</name>
<type>integer</type>
</field>
<field>
<name>text</name>
<type>text</type>
<length>256</length>
</field>
</declaration>
</table>
<table>
<name>*dbprefix*polls_particip</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
</field>
<field>
<name>poll_id</name>
<type>integer</type>
</field>
<field>
<name>dt</name>
<type>timestamp</type>
</field>
<field>
<name>type</name>
<type>integer</type>
</field>
<field>
<name>user_id</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
</declaration>
</table>
<table>
<name>*dbprefix*polls_particip_text</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
</field>
<field>
<name>poll_id</name>
<type>integer</type>
</field>
<field>
<name>text</name>
<type>text</type>
<length>256</length>
</field>
<field>
<name>user_id</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>type</name>
<type>integer</type>
</field>
</declaration>
</table>
<table>
<name>*dbprefix*polls_comments</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<default>0</default>
</field>
<field>
<name>poll_id</name>
<type>integer</type>
</field>
<field>
<name>user_id</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>dt</name>
<type>text</type>
<notnull>true</notnull>
<length>32</length>
</field>
<field>
<name>comment</name>
<type>text</type>
<notnull>false</notnull>
<length>1024</length>
</field>
</declaration>
</table>
<table>
<name>*dbprefix*polls_notif</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
</field>
<field>
<name>poll_id</name>
<type>integer</type>
</field>
<field>
<name>user_id</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
</declaration>
</table>
</database>

7
appinfo/info.xml Executable file → Normal file
Просмотреть файл

@ -5,7 +5,7 @@
<name>Polls</name>
<summary>A polls app, similar to doodle/dudle with the possibility to restrict access.</summary>
<description>A polls app, similar to doodle/dudle with the possibility to restrict access (members, certain groups/users, hidden and public).</description>
<version>0.8.3</version>
<version>0.9.0</version>
<licence>agpl</licence>
<author>Vinzenz Rosenkranz</author>
<author>René Gieling</author>
@ -14,7 +14,7 @@
<admin>https://github.com/nextcloud/polls/blob/master/README.md</admin>
</documentation>
<category>tools</category>
<category>social</category>
<category>social</category>
<category>organization</category>
<website>https://github.com/nextcloud/polls</website>
<bugs>https://github.com/nextcloud/polls/issues</bugs>
@ -23,7 +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>
<owncloud min-version="8.2" max-version="10.0" />
<nextcloud min-version="11" max-version="14" />
<nextcloud min-version="14" max-version="14" />
</dependencies>
</info>

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

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (c) 2017 Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
* @copyright Copyright (c] 2017 Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
*
* @author Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
*
@ -9,7 +9,7 @@
* 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.
* 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
@ -21,19 +21,22 @@
*
*/
$app = new \OCA\Polls\AppInfo\Application();
$app->registerRoutes($this, array(
'routes' => array(
array('name' => 'page#index', 'url' => '/', 'verb' => 'GET'),
array('name' => 'page#goto_poll', 'url' => '/poll/{hash}', 'verb' => 'GET'),
array('name' => 'page#edit_poll', 'url' => '/edit/{hash}', 'verb' => 'GET'),
array('name' => 'page#create_poll', 'url' => '/create', 'verb' => 'GET'),
array('name' => 'page#delete_poll', 'url' => '/delete', 'verb' => 'POST'),
array('name' => 'page#update_poll', 'url' => '/update', 'verb' => 'POST'),
array('name' => 'page#insert_poll', 'url' => '/insert', 'verb' => 'POST'),
array('name' => 'page#insert_vote', 'url' => '/insert/vote', 'verb' => 'POST'),
array('name' => 'page#insert_comment', 'url' => '/insert/comment', 'verb' => 'POST'),
array('name' => 'page#search', 'url' => '/search', 'verb' => 'POST'),
array('name' => 'page#get_display_name', 'url' => '/get/displayname', 'verb' => 'POST'),
)
));
return [
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'page#goto_poll', 'url' => '/poll/{hash}', 'verb' => 'GET'],
['name' => 'page#edit_poll', 'url' => '/edit/{hash}', 'verb' => 'GET'],
['name' => 'page#create_poll', 'url' => '/create', 'verb' => 'GET'],
['name' => 'page#delete_poll', 'url' => '/delete', 'verb' => 'POST'],
['name' => 'page#update_poll', 'url' => '/update', 'verb' => 'POST'],
['name' => 'page#insert_poll', 'url' => '/insert', 'verb' => 'POST'],
['name' => 'page#insert_vote', 'url' => '/insert/vote', 'verb' => 'POST'],
['name' => 'page#insert_comment', 'url' => '/insert/comment', 'verb' => 'POST'],
['name' => 'page#search', 'url' => '/search', 'verb' => 'POST'],
['name' => 'page#get_display_name', 'url' => '/get/displayname', 'verb' => 'POST'],
['name' => 'api#write_poll', 'url' => '/write', 'verb' => 'POST'],
['name' => 'api#get_poll', 'url' => '/get/poll/{hash}', 'verb' => 'GET'],
['name' => 'api#get_site_users_and_groups', 'url' => '/get/siteusers', 'verb' => 'POST'],
['name' => 'api#get_system', 'url' => '/get/system', 'verb' => 'GET']
]
];

15
css/colors.scss Normal file
Просмотреть файл

@ -0,0 +1,15 @@
$hover-color: #f7f7f7;
$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;
$bg_current_user: #f7f7f7;
$fg_current_user: #0082c9;

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

@ -1,103 +0,0 @@
@import 'polls-variables.scss';
@import 'main.scss';
.user-group-list {
display: none;
border: 1px solid #DDD;
margin: 15px;
padding: 5px;
border-radius: 3px;
max-height: 150px;
overflow-y: auto;
}
#sec_name {
display: none;
}
/* #app-content #app-content-wrapper {
flex-direction: column;
}
*/
.cl_access_item {
padding: 5px;
background-color: white;
color: #333;
cursor: pointer;
&:hover {
background-color: #EEE;
}
&.selected {
background-color: #409AE7;
color: #FFF;
&:hover {
background-color: #FF6F6F;
color: #FFF;
text-decoration: line-through;
}
}
}
textarea {
min-height: 66px;
}
.choices {
margin-top: 10px;
td {
min-width: 30px;
}
}
table{
td {
text-align: center;
}
.icon-close {
cursor: pointer;
background-color: $bg_no; /*red*/
padding: 0 5px;
background-image: url('../img/no-vote.svg');
}
.icon-checkmark {
cursor: pointer;
background-color: $bg_yes; /*green*/
padding: 0 5px;
background-image: url('../img/yes-vote.svg');
}
}
.text-row, .date-row {
font-weight: bold;
padding: 2px 5px;
background-color: #337AB7;
color: #FFF;
text-align: left;
&:hover {
background-color: #ff6f6f;
text-decoration: line-through;
}
}
#selected-dates-table tr:first-child td {
font-weight: bold;
padding: 2px 5px;
background-color: #337AB7;
color: #FFF;
}
#expiration {
max-width: 200px;
}
#pollDesc {
width: 100%;
}

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

@ -1,5 +1,30 @@
@import 'polls-variables.scss';
@import 'main.scss';
@import 'colors.scss';
$row-padding: 15px;
$table-padding: 4px;
$date-width: 120px;
$participants-width: 95px;
$group-2-2-width: max($date-width, $participants-width);
$owner-width: 140px;
$access-width: 140px;
$group-2-1-width: max($access-width, $date-width);
$group-2-width: $owner-width + $group-2-1-width + $group-2-2-width;
$action-width: 44px;
$thumbnail-width: 44px;
$thumbnail-icon-width: 32px;
$name-width: 150px;
$description-width: 150px;
$group-1-1-width: max($name-width, $description-width);
$group-1-width: $thumbnail-width + $group-1-1-width + $action-width;
$group-master-width: max($group-1-width, $group-2-width);
$mediabreak-1: ($group-1-width + $owner-width + $access-width + $date-width + $date-width + $participants-width + $row-padding * 2);
$mediabreak-2: ($group-1-width + $group-2-width + $row-padding * 2);
$mediabreak-3: $group-1-width + $owner-width + max($group-2-1-width, $group-2-2-width) + $row-padding *2 ;
.table {
width: 100%;
@ -12,7 +37,7 @@
line-height: 2em;
transition: background-color 0.3s ease;
background-color: $color-main-background;
background-color: var(--color-main-background);
min-height: 4em;
border-bottom: 1px solid #eee;
@ -65,7 +90,7 @@
.flex-column {
/* padding: 0 $table-padding; */
padding: 0 $table-padding;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
@ -101,7 +126,7 @@
}
}
.participants {
.participants{
width: $participants-width;
div {
&.partic_voted {
@ -125,12 +150,17 @@
}
.actions {
width: $action-width;
position: relative;
overflow: initial;
}
.thumbnail {
background-size: $thumbnail-icon-width;
width: $thumbnail-width;
height: $thumbnail-width;
/* padding-right: $table-padding; */
padding-right: $table-padding;
background-repeat: no-repeat;
background-position: center;
&.progress, &.endless {

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

@ -3,6 +3,24 @@ h1 {
margin-bottom: 5px;
}
#app, #create-poll {
header {
padding-top: 44px;
}
}
/* allow horizontal scrollbar
otherwise user management is not usable on mobile */
@media only screen and (max-width: 768px) {
#app-content {
overflow-x: auto !important;
}
}
#app-content-wrapper {
flex-direction: column;
}
.icon-polls {
background-color: black;
-webkit-mask: url('../img/app.svg') no-repeat 50% 50%;
@ -11,6 +29,10 @@ h1 {
#controls {
// adopted from NC13 for compatibily with OC10 and NC11-NC12
width: 100%;
position: relative;
top: 4px;
h2 {
margin-top: 12px;
}
@ -38,16 +60,15 @@ h1 {
padding: 0px 15px;
}
.col-70 {
width: 65%;
float: left;
padding: 0px 15px;
}
.col-70 { width: 65%; float: left; padding: 0px 15px; }
.col-30 {
width: 35%;
float: left;
padding: 0px 15px;
.col-30 { width: 35%; float: left; padding: 0px 15px; }
@media(max-width: 992px) {
.col-70, .col-30, .col-50 {
float: none;
width: 100%;
}
}
@ -56,6 +77,11 @@ input[type="text"] {
width: 100%;
}
/* textarea {
min-width: 100%;
max-width: 100%;
}
*/
.form-actions {
padding: 15px;
}
@ -64,21 +90,8 @@ input[type="text"] {
width: 100%;
}
div {
.new_poll {
width: 100%;
}
.scroll_div {
width: 100%;
overflow-x: auto;
overflow-y: hidden;
}
.scroll_div_dialog {
width: 100%;
height: 250px;
overflow-y: auto;
}
div.new_poll {
width: 100%;
}
input.table_button {
@ -89,6 +102,17 @@ input.table_button {
vertical-align: middle;
}
div.scroll_div {
width: 100%;
overflow-x: auto;
overflow-y: hidden;
}
div.scroll_div_dialog {
width: 100%;
height: 250px;
overflow-y: auto;
}
.bordered {
border-left: 1px solid #FFF;
@ -137,12 +161,11 @@ td.td_shown {
font-size: 2em;
}
.input_title {
.input_title{
display: block;
font-size: 1.2em;
margin-top: 5px;
}
.padded td {
padding: 10px;
}
@ -191,30 +214,33 @@ td.td_shown {
cursor: pointer;
padding: 0px 5px 0px 5px;
&:hover {
background-color: #ffccca;
}
}
.cl_del_item:hover {
background-color: #ffccca;
}
.cl_maybe:before, .cl_maybe:after{
content: "?";
}
.cl_user_comments {
table-layout: fixed;
td {
text-align: left;
}
th {
word-wrap: break-word;
vertical-align: top;
text-align: center;
}
}
.cl_user_comments td {
text-align: left;
}
.cl_user_comments th {
word-wrap: break-word;
vertical-align: top;
text-align: center;
}
.user_comment {
font-size: 0.9em;
}
.user_comment {
padding-bottom: 10px;
}
@ -266,29 +292,29 @@ td.td_shown {
white-space: nowrap;
vertical-align: middle;
width: 1%;
input[type="checkbox"] {
margin: 0;
}
}
.input-group-addon input[type="checkbox"] {
margin: 0;
}
.input-group-btn {
vertical-align: top;
.btn {
height: 31.25px;
border: 1px solid #DDD;
margin: 0;
border-radius: 3px;
vertical-align: middle;
display: block;
width: 100%;
}
}
.input-group-btn .btn {
height: 31.25px;
border: 1px solid #DDD;
margin: 0;
border-radius: 3px;
vertical-align: middle;
display: block;
width: 100%;
}
.input-group>input {
position: relative;
/* z-index: 2; */
z-index: 2;
float: left;
width: 100%;
margin: 0;

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

@ -1,52 +0,0 @@
// colors
$hover-color: #f7f7f7;
$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;
$bg_current_user: #f7f7f7;
$fg_current_user: #0082c9;
// various structure data
$navigation-width: 300px;
$sidebar-min-width: 300px;
$sidebar-max-width: 500px;
// list.scss definition
$border_current_user: 2px solid;
$border_user: 1px solid #ddd;
$user-column-width: 265px;
$row-padding: 15px;
$table-padding: 4px;
$date-width: 120px;
$participants-width: 80px;
$group-2-2-width: max($date-width, $participants-width);
$owner-width: 140px;
$access-width: 140px;
$group-2-1-width: max($access-width, $date-width);
$group-2-width: $owner-width + $group-2-1-width + $group-2-2-width;
$action-width: 44px;
$thumbnail-width: 44px;
$thumbnail-icon-width: 32px;
$name-width: 150px;
$description-width: 150px;
$group-1-1-width: max($name-width, $description-width);
$group-1-width: $thumbnail-width + $group-1-1-width + $action-width;
$group-master-width: max($group-1-width, $group-2-width);
$mediabreak-1: ($group-1-width + $owner-width + $access-width + $date-width + $date-width + $participants-width + $row-padding * 2);
$mediabreak-2: ($group-1-width + $group-2-width + $row-padding * 2);
$mediabreak-3: $group-1-width + $owner-width + max($group-2-1-width, $group-2-2-width) + $row-padding *2 ;

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

@ -1,230 +1,225 @@
@import 'polls-variables.scss';
@import 'colors.scss';
$border_current_user: 2px solid;
$border_user: 1px solid #ddd;
$user-column-width: 265px;
#polls-sidebar {
width: 520px;
flex-grow: 0;
flex-shrink: 1;
min-width: 300px;
/* padding-top: 8px; */
/* margin-top: 3px; */
border-left: 1px solid $color-border;
/* -webkit-transition: margin-right 300ms; */
border-left: 1px solid var(--color-border);
transition: margin-right 300ms;
overflow-x: hidden;
overflow-y: auto;
/* visibility: visible; */
z-index: 500;
/* height: 100%; */
> div, > ul {
padding: 8px;
}
}
.authorRow {
align-items: center;
.author {
margin-left: 8px;
opacity: .5;
flex-grow: 1;
&.external {
margin-right: 33px;
opacity: 1;
> input {
width: 100%
.authorRow {
align-items: center;
.author {
margin-left: 8px;
opacity: .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;
}
.pollInformation {
width: 220px;
flex-grow: 1;
flex-shrink: 1;
padding-right: 15px;
.authorRow {
.leftLabel {
margin-right: 4px;
}
.detailsView {
z-index: 1000 !important;
.close.flex-row {
justify-content: flex-end;
margin: 8px 8px 0 0;
}
.cloud {
margin: 4px 0;
> span {
color: #fff;
margin: 2px;
padding: 2px 4px;
border-radius: 3px;
float: left;
text-shadow: 1px 1px #666;
background-color: #aaa;
}
.open {
background-color: $fg_yes;
}
.expired {
background-color: $fg_no;
}
.information {
background-color: $bg_information;
}
}
}
#expired_info {
margin: 0 15px;
}
.pollActions {
display: flex;
flex-direction: column;
margin-right: 15px;
.close {
margin: 15px;
background-position: right top;
height: 30px;
.header.flex-row {
flex-direction: row;
flex-grow: 0;
align-items: flex-start;
margin-left: 0;
margin-top: 0;
padding: 0 17px;
}
> ul > li {
&:focus, &:hover, &.active, a.selected {
&, > a {
opacity: 1;
box-shadow: inset 2px 0 #0082c9;
.pollInformation {
width: 220px;
flex-grow: 1;
flex-shrink: 1;
padding-right: 15px;
.authorRow {
.leftLabel {
margin-right: 4px;
}
}
> a[class*="icon-"],
> ul > li > a[class*="icon-"],
> a[style*="background-image"],
> ul > li > a[style*="background-image"] {
padding-left: 44px;
.cloud {
margin: 4px 0;
> span {
color: #fff;
margin: 2px;
padding: 2px 4px;
border-radius: 3px;
float: left;
text-shadow: 1px 1px #666;
background-color: #aaa;
}
.open {
background-color: $fg_yes;
}
.expired {
background-color: $fg_no;
}
.information {
background-color: $bg_information;
}
}
}
#expired_info {
margin: 0 15px;
}
.pollActions {
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 2px 0 #0082c9;
}
}
> 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: #000;
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 #dbdbdb;
border-radius: 3px;
padding: 7px 6px;
margin: 3px 3px 3px 40px;
cursor: text;
}
.comment {
margin-bottom: 30px;
.comment-header {
background-color: #EEE;
border-bottom: 1px solid #DDD;
border-radius: 3px 3px 0 0;
}
.comment-date {
float: right;
color: #555;
}
.date {
right: 0;
top: 5px;
opacity: .5;
}
}
.message {
margin-left: 40px;
flex-grow: 1;
flex-shrink: 1;
}
> 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: #000;
opacity: 0.57;
flex: 1 1 0;
z-index: 100;
}
a,
.app-navigation-entry-deleted {
padding-left: 44px !important;
.new-comment {
.submitComment {
align-self: last baseline;
width: 30px;
margin: 0;
padding: 7px 9px;
background-color: transparent;
border: none;
opacity: .3;
}
.icon-loading-small {
float: left;
margin-top: 10px;
display: none;
}
}
}
}
#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 #dbdbdb;
border-radius: 3px;
padding: 7px 6px;
margin: 3px 3px 3px 40px;
cursor: text;
}
.comment {
margin-bottom: 30px;
.comment-header {
background-color: #EEE;
border-bottom: 1px solid #DDD;
border-radius: 3px 3px 0 0;
}
.comment-date {
float: right;
color: #555;
}
.date {
right: 0;
top: 5px;
opacity: .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: .3;
}
.icon-loading-small {
float: left;
margin-top: 10px;
display: none;
}
}
}
}

1
css/vendor/jquery.datetimepicker.min.css поставляемый

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

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

@ -1,113 +1,117 @@
@import 'polls-variables.scss';
@import 'main.scss';
@import 'colors.scss';
$border_current_user: 2px solid;
$border_user: 1px solid #ddd;
$user-column-width: 265px;
#content {
display:flex;
}
.flex-row {
display: flex;
flex-direction: row;
flex-grow: 1;
align-items: center;
}
.flex-column {
display: flex;
flex-direction: column;
flex-grow: 0;
flex-shrink: 0;
}
#votings {
position: relative !important;
.description {
.expired-vote{
color: red;
font-weight: bold;
#app-content {
width: 100%;
overflow-x: hidden;
#votings {
position: relative !important;
padding: 12px 17px;
.table {
overflow-x: auto;
padding-bottom: 10px;
}
}
padding: 12px 17px;
}
.header {
margin-left: $user-column-width;
padding: 0 17px;
align-items: initial;
.vote.option {
.date-box {
flex-grow: 1;
.description {
.expired-vote{
color: var(--color-error);
font-weight: bold;
}
}
.header {
margin-left: $user-column-width;
padding: 0 17px;
align-items: initial;
.date-box {
padding: 0 2px;
align-items: center;
.month, .dayow {
font-size: 1.2em;
color: var(--color-text-lighter);
}
.day {
font-size: 1.8em;
margin: 5px 0 5px 0;
}
}
.counter {
.counter {
font-size: 18px;
.yes, .no {
margin: 0 2px;
.svg {
background-position: center;
background-repeat: no-repeat;
background-size: 24px;
height: 24px;
width: 24px;
}
}
.yes {
color: $fg_yes;
.svg {
background-image: url('../img/yes-vote.svg');
}
}
.no {
color: $fg_no;
.svg {
background-image: url('../img/no-vote.svg');
}
}
}
.winner {
font-style: italic;
font-weight: bold;
color: $fg_yes;
}
.vote.option {
.date-box {
flex-grow: 1;
}
.counter {
flex-grow: 0;
height: 32px;
}
}
}
.user {
border-top: $border_user;
height: 44px;
padding: 0 17px;
}
.first {
flex-grow: 0;
height: 32px;
}
}
}
flex-shrink: 0;
width: $user-column-width;
}
.user {
border-top: $border_user;
height: 44px;
padding: 0 17px;
}
.first {
flex-grow: 0;
flex-shrink: 0;
width: $user-column-width;
}
.vote, .poll-cell {
flex-grow: 1;
width: 85px;
margin: 2px;
align-items:center;
}
.date-box {
padding: 0 2px;
align-items: center;
.month, .dayow {
font-size: 1.2em;
color: #666;
}
.day {
font-size: 1.8em;
margin: 5px 0 5px 0;
}
}
.winner {
font-style: italic;
font-weight: bold;
color: $fg_yes;
}
.counter {
font-size: 18px;
.yes, .no {
margin: 0 2px;
.svg {
background-position: center;
background-repeat: no-repeat;
background-size: 24px;
height: 24px;
width: 24px;
.vote, .poll-cell {
flex-grow: 1;
width: 85px;
margin: 2px;
align-items:center;
}
}
.yes {
color: $fg_yes;
.svg {
background-image: url('../img/yes-vote.svg');
}
}
.no {
color: $fg_no;
.svg {
background-image: url('../img/no-vote.svg');
}
}
}
.name {
margin-left: 5px;
}
@ -118,13 +122,6 @@
background-size: 32px;
height: 44px;
&.active {
cursor: pointer;
border: 2px solid;
border-radius: 4px;
box-sizing: border-box;
}
&.yes {
background-color: $bg_yes;
color: $fg_yes;
@ -144,11 +141,28 @@
}
&.unvoted {
background-color: $bg_unvoted;
color: $fg_unvoted;
background-color: $bg_unvoted;
background-color: $bg_no;
color: $fg_no;
&:before {
content: attr(data-unvoted);
color: $fg_no;
font-size: 14px;
font-style: italic;
font-weight: bold;
line-height: 38px;
}
}
&.active {
cursor: pointer;
border: 2px solid;
border-radius: 4px;
box-sizing: border-box;
&.unvoted {
background-color: $bg_maybe;
color: $fg_maybe;
}
}
}
.toggle-cell {
@ -229,192 +243,6 @@
margin-bottom: 15px;
}
.authorRow {
position: relative;
.author {
margin-left: 8px;
opacity: .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;
align-items: flex-start;
margin-left: 0;
margin-top:0;
}
.pollInformation {
width: 220px;
flex-grow: 1;
flex-shrink: 1;
padding-right: 15px;
.authorRow {
.leftLabel {
margin-right: 4px;
}
}
.cloud {
margin: 4px 0;
> span {
color: #fff;
margin: 2px;
padding: 2px 4px;
border-radius: 3px;
float: left;
text-shadow: 1px 1px #666;
background-color: #aaa;
}
.open {
background-color: $fg_yes;
}
.expired {
background-color: $fg_no;
}
.information {
background-color: $bg_information;
}
}
}
#expired_info {
margin: 0 15px;
}
.pollActions {
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 2px 0 #0082c9;
}
}
> 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: #000;
opacity: 0.57;
flex: 1 1 0;
z-index: 100;
}
a,
.app-navigation-entry-deleted {
padding-left: 44px !important;
}
}
}
#commentsTabView {
.newCommentForm div.message:empty:before {
content: attr(data-placeholder);
color: grey;
}
#commentBox {
border: 1px solid #dbdbdb;
border-radius: 3px;
padding: 7px 6px;
margin: 3px 3px 3px 40px;
cursor: text;
}
.comment {
margin-bottom: 30px;
.comment-header {
background-color: #EEE;
border-bottom: 1px solid #DDD;
border-radius: 3px 3px 0 0;
}
.comment-date {
float: right;
color: #555;
}
.date {
position: absolute;
right: 0;
top: 5px;
opacity: .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: .3;
}
.icon-loading-small {
float: left;
margin-top: 10px;
display: none;
}
}
}
}
@media all and (max-width: (768px) ) {
#app-content {
position: relative !important;
@ -422,88 +250,94 @@
}
@media all and (max-width: (480px) ) {
#votings {
#app-content #votings {
padding: 0px 2px;
}
.flex-row {
flex-direction: column;
&.user-cell, &.counter, &.counter .yes, &.counter .no, &.controls, &.breadcrump, &.submitPoll, &.newCommentForm, &.close {
flex-direction: row;
}
&.header {
flex-grow: 1;
margin-left: 0;
margin-top: 44px;
width: 120px;
padding: 0 0 0 4px;
.vote {
padding-right: 10px;
&.option {
align-items: baseline;
width: 100%;
border-top: $border_user;
.flex-row {
flex-direction: column;
.first {
height: 44px;
width: unset;
}
&.time {
align-items: center;
width: 100%;
border-top: $border_user;
.counter {
flex-direction: column;
align-items: flex-end;
&.user-cell, &.counter, &.counter .yes, &.counter .no, &.controls, &.breadcrump, &.submitPoll, &.newCommentForm, &.close {
flex-direction: row;
}
&.header {
flex-grow: 1;
margin-left: 0;
margin-top: 44px;
width: 120px;
padding: 0 0 0 4px;
.vote {
padding-right: 10px;
&.option {
align-items: baseline;
width: 100%;
border-top: $border_user;
}
&.time {
align-items: center;
width: 100%;
border-top: $border_user;
.counter {
flex-direction: column;
align-items: flex-end;
}
}
}
}
&.user {
display: none;
}
&.current-user {
display: flex;
width: 44px;
padding:0;
border: none;
background-color: transparent;
.poll-cell {
border:none;
border-radius: 0;
border-top: 1px solid #ddd;
background-color: transparent;
padding: 0 2px;
&.active {
&.yes {
background-image: url('../img/yes-vote-bordered.svg');
}
&.no {
background-image: url('../img/no-vote-bordered.svg');
}
&.maybe {
background-image: url('../img/maybe-vote-bordered.svg');
}
&.unvoted {
background-image: url('../img/unvoted-vote-bordered.svg');
}
}
}
.user-cell {
position: absolute;
left: 22px;
}
.poll-cell, .toggle-cell {
width: 44px;
height: 44px;
background-color: transparent;
}
}
}
&.user {
display: none;
}
&.current-user {
display: flex;
width: 44px;
padding:0;
border: none;
background-color: transparent;
.poll-cell {
border:none;
border-radius: 0;
border-top: 1px solid #ddd;
background-color: transparent;
padding: 0 2px;
&.active {
&.yes {
background-image: url('../img/yes-vote-bordered.svg');
}
&.no {
background-image: url('../img/no-vote-bordered.svg');
}
&.maybe {
background-image: url('../img/maybe-vote-bordered.svg');
}
&.unvoted {
background-image: url('../img/unvoted-vote-bordered.svg');
}
}
}
.user-cell {
position: absolute;
left: 22px;
}
.poll-cell, .toggle-cell {
width: 44px;
height: 44px;
background-color: transparent;
}
}
}
.description {
margin: 4px;
}
@ -548,10 +382,6 @@
}
}
.first {
height: 44px;
width: unset;
}
#options.flex-row {
flex-direction: column;

35
img/save.svg Normal file
Просмотреть файл

@ -0,0 +1,35 @@
<?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>

После

Ширина:  |  Высота:  |  Размер: 1.0 KiB

86
img/toggle.svg Normal file
Просмотреть файл

@ -0,0 +1,86 @@
<?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>

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

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

@ -1,5 +1,5 @@
function deletePoll($pollEl) {
var str = t('polls', 'Do you really want to delete that poll (new)?') + '\n\n' + $($pollEl).attr('data-value');
var str = t('polls', 'Do you really want to delete this new poll?') + '\n\n' + $($pollEl).attr('data-value');
if (confirm(str)) {
var form = document.form_delete_poll;
var hiddenId = document.createElement("input");
@ -10,3 +10,5 @@ function deletePoll($pollEl) {
form.submit();
}
}

1
js/create-poll.js Normal file

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

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

@ -1,637 +0,0 @@
var g_chosen_datetimes = [];
var g_chosen_texts = [];
var g_chosen_groups = [];
var g_chosen_users = [];
var chosen_type = 'event';
var access_type = '';
var isAnonymous;
var hideNames;
function selectItem(cell) {
cell.removeClass('icon-close');
cell.addClass('icon-checkmark');
cell.removeClass('date-text-not-selected');
cell.addClass('date-text-selected');
if (cell.attr('class').indexOf('is-text') > -1) {
var id = cell.attr('id');
g_chosen_texts.push(id.substring(id.indexOf('_') + 1));
} else {
var dateId = cell.parent().attr('id'); //timestamp of date
var timeId = cell.attr('id');
g_chosen_datetimes.push(parseInt(dateId) + parseInt(timeId));
}
}
function deselectItem(cell) {
var id;
var index;
var dateId;
var timeId;
cell.removeClass('icon-checkmark');
cell.addClass('icon-close');
cell.removeClass('date-text-selected');
cell.addClass('date-text-not-selected');
if (cell.attr('class').indexOf('is-text') > -1) {
id = cell.attr('id');
index = g_chosen_texts.indexOf(id.substring(id.indexOf('_') + 1));
if (index > -1) {
g_chosen_texts.splice(index, 1);
}
} else {
dateId = cell.parent().attr('id'); //timestamp of date
timeId = cell.attr('id');
index = g_chosen_datetimes.indexOf(parseInt(dateId) + parseInt(timeId));
if (index > -1) {
g_chosen_datetimes.splice(index, 1);
}
}
}
function insertText(text, set) {
if (typeof set == 'undefined') {
set = false;
}
var table = document.getElementById('selected-texts-table');
var tr = table.insertRow(-1);
var td = tr.insertCell(-1);
td.innerHTML = text;
td.className = 'text-row';
td = tr.insertCell(-1);
if (set) {
td.className = 'icon-checkmark is-text date-text-selected';
} else {
td.className = 'icon-close is-text date-text-not-selected';
}
td.id = 'text_' + text;
}
function addRowToList(ts, text, timeTs) {
if (typeof timeTs == 'undefined') {
timeTs = -1;
}
var table = document.getElementById('selected-dates-table');
var rows = table.rows;
var td, tr, tdId;
var i, j;
var curr;
if (rows.length == 0) {
tr = table.insertRow(-1); //start new header
tr.insertCell(-1);
tr = table.insertRow(-1); //append new row
tr.id = ts;
tr.className = 'toggleable-row';
td = tr.insertCell(-1);
td.className = 'date-row';
td.innerHTML = text;
return;
}
for ( i=1; i<rows.length; i++) {
curr = rows[i];
if (curr.id == ts) {
for (j=1; j<curr.cells.length; j++) {
td = curr.cells[j];
tdId = curr.cells[j].id;
if ( timeTs == tdId) {
td.className = 'icon-checkmark date-text-selected';
}
}
return; //already in table, cancel
} else if (curr.id > ts) {
tr = table.insertRow(i); //insert row at current index
tr.id = ts;
tr.className = 'toggleable-row';
td = tr.insertCell(-1);
td.className = 'date-row';
td.innerHTML = text;
for (j=1; j<rows[0].cells.length; j++) {
tdId = rows[0].cells[j].id;
td = tr.insertCell(-1);
if (timeTs == tdId) {
td.className = 'icon-checkmark date-text-selected';
} else {
td.className = 'icon-close date-text-not-selected';
}
td.id = tdId;
td.innerHTML = '';
}
return;
}
}
tr = table.insertRow(-1); //highest value, append new row
tr.id = ts;
tr.className = 'toggleable-row';
td = tr.insertCell(-1);
td.className = 'date-row';
td.innerHTML = text;
for (j=1; j<rows[0].cells.length; j++) {
tdId = rows[0].cells[j].id;
td = tr.insertCell(-1);
if (timeTs == tdId) {
td.className = 'icon-checkmark date-text-selected';
} else {
td.className = 'icon-close date-text-not-selected';
}
td.id = tdId;
td.innerHTML = '';
}
}
function addColToList(ts, text, dateTs) {
if (typeof dateTs == 'undefined') {
dateTs = -1;
}
var table = document.getElementById('selected-dates-table');
var rows = table.rows;
var tr, row, td, cells, tmpRow;
var i, curr;
var index = -1;
if (rows.length == 0) {
tr = table.insertRow(-1);
tr.insertCell(-1);
}
rows = table.rows;
tmpRow = rows[0];
for (i=0; i<tmpRow.cells.length; i++) {
curr = tmpRow.cells[i];
if (curr.id == ts) {
return; //already in table, cancel
} else if (curr.id > ts) {
index = i;
break;
}
}
for (i=0; i<rows.length; i++) {
row = rows[i];
cells = row.cells;
td = row.insertCell(index);
//only display time in header row
if (i==0) {
td.innerHTML = text;
td.className = 'date-col';
} else {
td.innerHTML = '';
if (row.id == dateTs) {
td.className = 'icon-checkmark date-text-selected';
} else {
td.className = 'icon-close date-text-not-selected';
}
}
td.id = ts;
}
}
function debounce(f, wait, immediate) {
var timeout;
return function () {
var context = this;
var args = arguments;
var later = function () {
timeout = null;
if (!immediate) {
f.apply(context, args);
}
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) {
f.apply(context, args);
}
};
}
$(document).ready(function () {
// enable / disable date picker
var i;
$('#id_expire_set').click(function (){
$('#id_expire_date').prop("disabled", !this.checked);
if (this.checked) {
$("#id_expire_date").focus();
}
});
var anonOptions = document.getElementById('anonOptions');
$('#hideNames').click(function () {
hideNames = this.checked;
});
$('#isAnonymous').click(function () {
isAnonymous = this.checked;
if (isAnonymous) {
anonOptions.style.display = 'inline';
} else {
anonOptions.style.display = 'none';
}
});
var privateRadio = document.getElementById('private');
var hiddenRadio = document.getElementById('hidden');
var publicRadio = document.getElementById('public');
var selectRadio = document.getElementById('select');
if (privateRadio.checked) {
access_type = 'registered';
} else if (hiddenRadio.checked) {
access_type = 'hidden';
} else if (publicRadio.checked) {
access_type = 'public';
} else if (selectRadio.checked) {
access_type = 'select';
}
isAnonymous = document.getElementById('isAnonymous').checked;
hideNames = anonOptions.checked;
var accessValues = document.getElementById('accessValues');
if (accessValues.value.length > 0) {
var list = document.getElementById('selected-search-list-id');
var accessValueArr = accessValues.value.split(';');
for (i=0; i<accessValueArr.length; i++) {
var val = accessValueArr[i];
if (val == '') {
continue;
}
var li = document.createElement('li');
li.id = val;
li.className = 'cl_item cl_access_item selected';
var index = val.indexOf('group_');
if (index == 0) {
g_chosen_groups.push(val);
li.className += ' is-group';
li.appendChild(document.createTextNode(val.substring(6) + " (group)"));
list.appendChild(li);
} else {
index = val.indexOf('user_');
if (index == 0) {
g_chosen_users.push(val);
li.className = 'cl_item cl_access_item selected';
var username = val.substring(5);
$.post(OC.generateUrl('/apps/polls/get/displayname'), {username: username}, function (data) {
li.appendChild(document.createTextNode(username + " (" + data + ")"));
list.appendChild(li);
});
}
}
}
}
var chosenDates = document.getElementById('chosenDates').value;
var chosen = '';
if (chosenDates.length > 0) {
chosen = JSON.parse(chosenDates);
}
var text = document.getElementById('text');
var event = document.getElementById('event');
if (event.checked) {
chosen_type = event.value;
if (chosenDates.length > 0) {
g_chosen_datetimes = chosen;
}
for (i=0; i<chosen.length; i++) {
var date = new Date(chosen[i]*1000);
var year = date.getFullYear();
var month = date.getMonth();
var day = date.getDate();
var newDate = new Date(year, month, day).getTime(); //save timestamp without time of day
month = '0' + (month+1); //month is 0-11, so +1
day = '0' + day;
var dateStr = day.substr(-2) + '.' + month.substr(-2) + '.' + year;
var hours = date.getHours();
var minutes = date.getMinutes();
var ms = (hours * 60 * 60 * 1000) + (minutes * 60 * 1000); //time of day in milliseconds
hours = '0' + hours;
minutes = '0' + minutes;
var timeStr = hours.substr(-2) + ':' + minutes.substr(-2);
addRowToList(newDate/1000, dateStr, ms/1000);
addColToList(ms/1000, timeStr, newDate/1000);
}
} else {
chosen_type = text.value;
if (chosenDates.length > 0) {
g_chosen_texts = chosen;
}
for (i=0; i<chosen.length; i++) {
insertText(chosen[i], true);
}
}
var expirepicker = jQuery('#id_expire_date').datetimepicker({
inline: false,
onSelectDate: function (date) {
var year = date.getFullYear();
var month = date.getMonth();
var day = date.getDate();
// set expiry date to the last second before midnight of the choosen date (local time)
var newDate = new Date(year, month, day, 23, 59, 59).getTime()/1000;
document.getElementById('expireTs').value = newDate;
},
timepicker: false,
format: 'd.m.Y'
});
var datepicker = jQuery('#datetimepicker').datetimepicker({
inline:true,
step: 15,
todayButton: true,
onSelectDate: function (date) {
var year = date.getFullYear();
var month = date.getMonth();
var day = date.getDate();
var newDate = new Date(year, month, day).getTime(); //save timestamp without time of day
month = '0' + (month+1); //month is 0-11, so +1
day = '0' + day;
var dateStr = day.substr(-2) + '.' + month.substr(-2) + '.' + year;
addRowToList(newDate/1000, dateStr);
},
onSelectTime: function (date) {
var hours = date.getHours();
var minutes = date.getMinutes();
var ms = (hours * 60 * 60 * 1000) + (minutes * 60 * 1000); //time of day in milliseconds
hours = '0' + hours;
minutes = '0' + minutes;
var timeStr = hours.substr(-2) + ':' + minutes.substr(-2);
addColToList(ms/1000, timeStr);
}
});
$(document).on('click', '.date-row', function () {
var tr = $(this).parent();
var dateId = parseInt(tr.attr('id'));
var index = tr.index();
var cells = tr[0].cells; //convert jQuery object to DOM
for (var i=1; i<cells.length; i++) {
var cell = cells[i];
var delIndex = g_chosen_datetimes.indexOf(dateId + parseInt(cell.id));
if (delIndex > -1) {
g_chosen_datetimes.splice(delIndex, 1);
}
}
var table = document.getElementById('selected-dates-table');
table.deleteRow(index);
});
$(document).on('click', '.date-col', function () {
var cellIndex = $(this).index();
var timeId = parseInt($(this).attr('id'));
var table = document.getElementById('selected-dates-table');
var rows = table.rows;
rows[0].deleteCell(cellIndex);
for (var i=1; i<rows.length; i++) {
var row = rows[i];
var delIndex = g_chosen_datetimes.indexOf(parseInt(row.id) + timeId);
if (delIndex > -1) {
g_chosen_datetimes.splice(delIndex, 1);
}
row.deleteCell(cellIndex);
}
});
$(document).on('click', '.text-row', function () {
var tr = $(this).parent();
var rowIndex = tr.index();
var name = $(this).html();
var delIndex = g_chosen_texts.indexOf(name);
if (delIndex > -1) {
g_chosen_texts.splice(delIndex, 1);
}
var table = document.getElementById('selected-texts-table');
table.deleteRow(rowIndex);
});
$(document).on('click', '.icon-close', function () {
selectItem($(this));
});
$(document).on('click', '.icon-checkmark', function () {
deselectItem($(this));
});
$(document).on('click', '#text-submit', function () {
var text = document.getElementById('text-title');
if (text.value.length == 0) {
alert('Please enter a text!');
return false;
}
insertText(text.value);
text.value = '';
});
$(document).on('click', '.cl_item', function () {
var index;
var list = document.getElementById('selected-search-list-id');
var isGroup = $(this).hasClass('is-group');
if ($(this).hasClass('selected')) {
if (isGroup) {
index = g_chosen_groups.indexOf(this.id);
}
else {
index = g_chosen_users.indexOf(this.id);
}
if (index > -1) {
if (isGroup) {
g_chosen_groups.splice(index, 1);
}
else {
g_chosen_users.splice(index, 1);
}
$(this).remove();
}
} else {
if (!isGroup) {
var text = this.id.replace('user_', '');
g_chosen_users.push(this.id);
} else {
g_chosen_groups.push(this.id);
}
document.getElementById('user-group-search-box').value = '';
var li = document.createElement('li');
li.id = this.id;
li.className = 'cl_item cl_access_item selected' + (isGroup ? ' is-group' : '');
if (!isGroup) {
$.post(OC.generateUrl('/apps/polls/get/displayname'), {username: text}, function (data) {
li.appendChild(document.createTextNode(text + " (" + data + ")"));
list.appendChild(li);
});
} else {
li.appendChild(document.createTextNode($(this).html()));
list.appendChild(li);
}
$(this).remove();
}
});
$('.toggleable-row').hover(
function () {
var td = this.insertCell(-1);
td.className = 'toggle-all selected-all';
}, function () {
$(this).find('td:last-child').remove();
}
);
$(document).on('click', '.toggle-all', function () {
var children;
var i;
if ($(this).attr('class').indexOf('selected-all') > -1) {
children = $(this).parent().children('.icon-checkmark');
for (i=0; i<children.length; i++) {
deselectItem($(children[i]));
}
$(this).removeClass('selected-all');
$(this).addClass('selected-none');
} else {
children = $(this).parent().children('.icon-close');
for (i=0; i<children.length; i++) {
selectItem($(children[i]));
}
$(this).removeClass('selected-none');
$(this).addClass('selected-all');
}
});
$('input[type=radio][name=pollType]').change(function () {
if (this.value == 'event') {
chosen_type = 'event';
document.getElementById('text-select-container').style.display = 'none';
document.getElementById('date-select-container').style.display = 'inline';
} else {
chosen_type = 'text';
document.getElementById('text-select-container').style.display = 'inline';
document.getElementById('date-select-container').style.display = 'none';
}
});
$('input[type=radio][name=accessType]').click(function () {
access_type = this.value;
if (access_type == 'select') {
$("#access_rights").show();
$("#selected_access").show();
} else {
$("#access_rights").hide();
$("#selected_access").hide();
}
});
$('input[type=checkbox][name=check_expire]').change(function () {
if (!$(this).is(':checked')) {
document.getElementById('expireTs').value = '';
}
});
$('#user-group-search-box').on('input', debounce(function () {
var ul = document.getElementById('live-search-list-id');
while(ul.firstChild) {
ul.removeChild(ul.firstChild);
}
var val = $(this).val();
if (val.length < 3) {
return;
}
var formData = {
searchTerm: val,
groups: JSON.stringify(g_chosen_groups),
users: JSON.stringify(g_chosen_users)
};
$.post(OC.generateUrl('/apps/polls/search'), formData, function (data) {
for (var i=0; i<data.length; i++) {
var ug = data[i];
var li = document.createElement('li');
li.className = 'cl_item cl_access_item';
if (ug.isGroup) {
li.id = 'group_' + ug.gid;
li.className += ' is-group';
li.appendChild(document.createTextNode(ug.gid + " (group)"));
ul.appendChild(li);
} else {
li.id = 'user_' + ug.uid;
li.appendChild(document.createTextNode(ug.uid + " (" + ug.displayName + ")"));
var span = document.createElement('span');
span.id = 'sec_name';
span.appendChild(document.createTextNode(ug.uid));
li.appendChild(span);
ul.appendChild(li);
}
}
});
}, 250));
$('.live-search-list-user li').each(function (){
$(this).attr('data-search-term', $(this).text().toLowerCase());
});
$('.live-search-box-user').on('keyup', function (){
var searchTerm = $(this).val().toLowerCase();
$('.live-search-list-user li').each(function (){
if ( $(this).filter('[data-search-term *= ' + searchTerm + ']').length > 0
|| searchTerm.length < 1
) {
$(this).show();
} else {
$(this).hide();
}
});
});
$('.live-search-list-group li').each(function (){
$(this).attr('data-search-term', $(this).text().toLowerCase());
});
$('.live-search-box-group').on('keyup', function (){
var searchTerm = $(this).val().toLowerCase();
$('.live-search-list-group li').each(function (){
if (
$(this).filter('[data-search-term *= ' + searchTerm + ']').length > 0 ||
searchTerm.length < 1
) {
$(this).show();
} else {
$(this).hide();
}
});
});
var form = document.finish_poll;
var submit_finish_poll = document.getElementById('submit_finish_poll');
if (submit_finish_poll !== null) {
submit_finish_poll.onclick = function () {
if (
g_chosen_datetimes.length == 0 &&
g_chosen_texts.length == 0
) {
alert(t('polls', 'Nothing selected!\nClick on cells to turn them green.'));
return false;
}
if (chosen_type == 'event') {
form.elements.chosenDates.value = JSON.stringify(g_chosen_datetimes);
}
else {
form.elements.chosenDates.value = JSON.stringify(g_chosen_texts);
}
var title = document.getElementById('pollTitle');
if (
title == null ||
title.value.length == 0
) {
alert(t('polls', 'You must enter at least a title for the new poll.'));
return false;
}
if (access_type == 'select') {
if (
g_chosen_groups.length == 0 &&
g_chosen_users == 0
) {
alert(t('polls', 'Please select at least one user or group!'));
return false;
}
form.elements.accessValues.value = JSON.stringify({
groups: g_chosen_groups,
users: g_chosen_users
});
}
form.elements.isAnonymous.value = isAnonymous;
form.elements.hideNames.value = hideNames;
form.submit();
};
}
});

7
js/start.js Executable file → Normal file
Просмотреть файл

@ -1,4 +1,4 @@
/** global: Clipboard */
/* global clipboard, navigator */
$(document).ready(function () {
var clipboard = new Clipboard('.copy-link');
clipboard.on('success', function (e) {
@ -18,13 +18,14 @@ $(document).ready(function () {
}
}, 3000);
});
clipboard.on('error', function (e) {
var $input = $(e.trigger);
var actionMsg = '';
if (/iPhone|iPad/i.test(navigator.userAgent)) {
actionMsg = t('core', 'Not supported!');
} else if (/Mac/i.test(navigator.userAgent)) {
actionMsg = t('core', 'Press -C to copy.');
actionMsg = t('core', 'Press ?-C to copy.');
} else {
actionMsg = t('core', 'Press Ctrl-C to copy.');
}
@ -36,7 +37,7 @@ $(document).ready(function () {
.tooltip('show');
_.delay(function () {
$input.tooltip('hide');
if (OC.Share.Social.Collection.size() == 0) {
if (OC.Share.Social.Collection.size() === 0) {
$input.attr('data-original-title', t('core', 'Copy'))
.tooltip('fixTitle');
} else {

1
js/vendor/jquery.datetimepicker.full.min.js поставляемый

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

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

@ -1,12 +1,23 @@
/** global: Clipboard */
var newUserDates = [];
var newUserTypes = [];
/* global Clipboard, Handlebars, navigator */
var newUserOptions = [];
var newUserAnswers = [];
var maxVotes = 0;
var valuesChanged = false;
var maybeAllowed = true;
var tzOffset = new Date().getTimezoneOffset();
// HTML template for new comment (handlebars.js)
var tmpl_comment = Handlebars.compile('<li class="comment flex-column"> ' +
'<div class="authorRow user-cell flex-row"> ' +
'<div class="avatar missing" title="{{userId}}"></div> ' +
'<div class="author">{{displayName}}</div>' +
'<div class="date has-tooltip live-relative-timestamp datespan" data-timestamp="{{timeStamp}}" title="{{date}}">{{relativeNow}}</div>' +
'</div>' +
'<div class="message wordwrap comment-content">{{comment}}</div>' +
'</li>');
$.fn.switchClass = function (a, b) {
this.removeClass(a);
this.addClass(b);
@ -16,7 +27,6 @@ $.fn.switchClass = function (a, b) {
function updateCommentsCount() {
$('#comment-counter').removeClass('no-comments');
$('#comment-counter').text(parseInt($('#comment-counter').text()) +1);
}
function updateBest() {
@ -64,7 +74,9 @@ function switchSidebar() {
}
$(document).ready(function () {
var clipboard = new Clipboard('.copy-link');
clipboard.on('success', function(e) {
var $input = $(e.trigger);
$input.tooltip('hide')
@ -82,6 +94,7 @@ $(document).ready(function () {
}
}, 3000);
});
clipboard.on('error', function (e) {
var $input = $(e.trigger);
var actionMsg = '';
@ -100,7 +113,7 @@ $(document).ready(function () {
.tooltip('show');
_.delay(function () {
$input.tooltip('hide');
if (OC.Share.Social.Collection.size() == 0) {
if (OC.Share.Social.Collection.size() === 0) {
$input.attr('data-original-title', t('core', 'Copy'))
.tooltip('fixTitle');
} else {
@ -111,6 +124,10 @@ $(document).ready(function () {
// count how many times in each date
updateBest();
if ($('#app-content').hasClass('maybedisallowed')) {
maybeAllowed = false;
}
// Temporary hack - Check if we have Nextcloud or ownCloud with an anonymous user
var hideAvatars = false;
if (!document.getElementById('nextcloud')) {
@ -158,25 +175,25 @@ $(document).ready(function () {
}
}
var check_notif = document.getElementById('check_notif');
var newUserDates = [], newUserTypes = [];
var newUserOptions = [], newUserAnswers = [];
$('.poll-cell.active').each(function () {
if($(this).hasClass('no')) {
newUserTypes.push(0);
newUserAnswers.push('no');
} else if ($(this).hasClass('yes')) {
newUserTypes.push(1);
newUserAnswers.push('yes');
} else if($(this).hasClass('maybe')) {
newUserTypes.push(2);
newUserAnswers.push('maybe');
} else {
newUserTypes.push(-1);
newUserAnswers.push('no');
}
if (isNaN($(this).attr('data-value'))) {
newUserDates.push($(this).attr('data-value'));
newUserOptions.push($(this).attr('data-value'));
} else {
newUserDates.push(parseInt($(this).attr('data-value')));
newUserOptions.push(parseInt($(this).attr('data-value')));
}
});
form.elements.dates.value = JSON.stringify(newUserDates);
form.elements.types.value = JSON.stringify(newUserTypes);
form.elements.options.value = JSON.stringify(newUserOptions);
form.elements.answers.value = JSON.stringify(newUserAnswers);
form.elements.receiveNotifications.value = (check_notif && check_notif.checked) ? 'true' : 'false';
form.elements.changed.value = valuesChanged ? 'true' : 'false';
form.submit();
@ -206,17 +223,7 @@ $(document).ready(function () {
};
$('.new-comment .icon-loading-small').show();
$.post(form.action, data, function (data) {
var newCommentElement = '<li class="comment flex-column"> ' +
'<div class="authorRow user-cell flex-row"> ' +
'<div class="avatar missing" title="' + data.userId + '"></div> ' +
'<div class="author">' + data.displayName + '</div>' +
'<div class="date has-tooltip live-relative-timestamp datespan" data-timestamp="' + Date.now() + '" title="' + data.date + '">' + t('polls', 'just now') + '</div>' +
'</div>' +
'<div class="message wordwrap comment-content">' + data.comment + '</div>' +
'</li>';
$('#no-comments').after(newCommentElement);
$('#no-comments').after(tmpl_comment(data));
if (!$('#no-comments').hasClass('hidden')) {
$('#no-comments').addClass('hidden');
@ -268,7 +275,11 @@ $(document).on('click', '.toggle-cell, .poll-cell.active', function () {
$nextClass = 'no';
$toggleAllClasses= 'yes';
} else if($(this).hasClass('no')) {
$nextClass = 'maybe';
if (maybeAllowed) {
$nextClass = 'maybe';
} else {
$nextClass = 'yes';
}
$toggleAllClasses= 'no';
} else if($(this).hasClass('maybe')) {
$nextClass = 'yes';

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

@ -68,4 +68,4 @@
"You are not allowed to edit this poll or the poll does not exist." : "Sinulla ei ole oikeutta muokata tätä kyselyä tai kyselyä ei ole olemassa.",
"You are not allowed to delete this poll or the poll does not exist." : "Sinulla ei ole oikeutta poistaa tätä kyselyä tai kyselyä ei ole olemassa."
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
}

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

@ -25,12 +25,10 @@ namespace OCA\Polls\AppInfo;
use OCA\Polls\Controller\PageController;
use OCA\Polls\Db\CommentMapper;
use OCA\Polls\Db\DateMapper;
use OCA\Polls\Db\OptionsMapper;
use OCA\Polls\Db\EventMapper;
use OCA\Polls\Db\NotificationMapper;
use OCA\Polls\Db\ParticipationMapper;
use OCA\Polls\Db\ParticipationTextMapper;
use OCA\Polls\Db\TextMapper;
use OCA\Polls\Db\VotesMapper;
use OCP\AppFramework\App;
use OCP\IContainer;
@ -49,7 +47,7 @@ class Application extends App {
/**
* Controllers
*/
$container->registerService('PageController', function (IContainer $c) {
$container->registerService('PageController', function(IContainer $c) {
return new PageController(
$c->query('AppName'),
$c->query('Request'),
@ -61,84 +59,70 @@ class Application extends App {
$c->query('ServerContainer')->getURLGenerator(),
$c->query('UserId'),
$c->query('CommentMapper'),
$c->query('DateMapper'),
$c->query('OptionsMapper'),
$c->query('EventMapper'),
$c->query('NotificationMapper'),
$c->query('ParticipationMapper'),
$c->query('ParticipationTextMapper'),
$c->query('TextMapper')
$c->query('VotesMapper')
);
});
$container->registerService('UserManager', function (IContainer $c) {
$container->registerService('UserManager', function(IContainer $c) {
return $c->query('ServerContainer')->getUserManager();
});
$container->registerService('GroupManager', function (IContainer $c) {
$container->registerService('GroupManager', function(IContainer $c) {
return $c->query('ServerContainer')->getGroupManager();
});
$container->registerService('AvatarManager', function (IContainer $c) {
$container->registerService('AvatarManager', function(IContainer $c) {
return $c->query('ServerContainer')->getAvatarManager();
});
$container->registerService('Logger', function (IContainer $c) {
$container->registerService('Logger', function(IContainer $c) {
return $c->query('ServerContainer')->getLogger();
});
$container->registerService('L10N', function (IContainer $c) {
$container->registerService('L10N', function(IContainer $c) {
return $c->query('ServerContainer')->getL10N($c->query('AppName'));
});
$container->registerService('CommentMapper', function (IContainer $c) use ($server) {
$container->registerService('CommentMapper', function(IContainer $c) use ($server) {
return new CommentMapper(
$server->getDatabaseConnection()
);
});
$container->registerService('DateMapper', function (IContainer $c) use ($server) {
return new DateMapper(
$container->registerService('OptionsMapper', function(IContainer $c) use ($server) {
return new OptionsMapper(
$server->getDatabaseConnection()
);
});
$container->registerService('EventMapper', function (IContainer $c) use ($server) {
$container->registerService('EventMapper', function(IContainer $c) use ($server) {
return new EventMapper(
$server->getDatabaseConnection()
);
});
$container->registerService('NotificationMapper', function (IContainer $c) use ($server) {
$container->registerService('NotificationMapper', function(IContainer $c) use ($server) {
return new NotificationMapper(
$server->getDatabaseConnection()
);
});
$container->registerService('ParticipationMapper', function (IContainer $c) use ($server) {
return new ParticipationMapper(
$container->registerService('VotesMapper', function(IContainer $c) use ($server) {
return new VotesMapper(
$server->getDatabaseConnection()
);
});
$container->registerService('ParticipationTextMapper', function (IContainer $c) use ($server) {
return new ParticipationTextMapper(
$server->getDatabaseConnection()
);
});
$container->registerService('TextMapper', function (IContainer $c) use ($server) {
return new TextMapper(
$server->getDatabaseConnection()
);
});
}
/**
* Register navigation entry for main navigation.
*/
public function registerNavigationEntry() {
$container = $this->getContainer();
$container->query('OCP\INavigationManager')->add(function () use ($container) {
$container->query('OCP\INavigationManager')->add(function() use ($container) {
$urlGenerator = $container->query('OCP\IURLGenerator');
$l10n = $container->query('OCP\IL10N');
return [

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

@ -0,0 +1,419 @@
<?php
/**
* @copyright Copyright (c) 2017 Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
*
* @author René Gieling <github@dartcafe.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Polls\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\IGroupManager;
use OCP\IRequest;
use OCP\IUser;
use OCP\IConfig;
use OCP\IUserManager;
use OCP\Security\ISecureRandom;
use OCA\Polls\Db\Event;
use OCA\Polls\Db\EventMapper;
use OCA\Polls\Db\Options;
use OCA\Polls\Db\OptionsMapper;
use OCA\Polls\Db\Votes;
use OCA\Polls\Db\VotesMapper;
use OCA\Polls\Db\Comment;
use OCA\Polls\Db\CommentMapper;
class ApiController extends Controller {
private $eventMapper;
private $optionsMapper;
private $votesMapper;
private $commentMapper;
private $systemConfig;
/**
* PageController constructor.
* @param string $appName
* @param IRequest $request
* @param string $userId
* @param EventMapper $eventMapper
* @param OptionsMapper $optionsMapper
* @param VotesMapper $VotesMapper
* @param CommentMapper $CommentMapper
*/
public function __construct(
$appName,
IConfig $systemConfig,
IGroupManager $groupManager,
IRequest $request,
IUserManager $userManager,
$userId,
EventMapper $eventMapper,
OptionsMapper $optionsMapper,
VotesMapper $VotesMapper,
CommentMapper $CommentMapper
) {
parent::__construct($appName, $request);
$this->userId = $userId;
$this->groupManager = $groupManager;
$this->systemConfig = $systemConfig;
$this->userManager = $userManager;
$this->eventMapper = $eventMapper;
$this->optionsMapper = $optionsMapper;
$this->votesMapper = $VotesMapper;
$this->commentMapper = $CommentMapper;
}
/**
* @NoAdminRequired
* @NoCSRFRequired
* @return DataResponse
*/
public function getSiteUsersAndGroups($query = '', $getGroups = true, $getUsers = true, $skipGroups = array(), $skipUsers = array()) {
$list = array();
$data = array();
if ($getGroups) {
$groups = $this->groupManager->search($query);
foreach ($groups as $group) {
if (!in_array($group->getGID(), $skipGroups)) {
$list[] = [
'id' => $group->getGID(),
'type' => 'group',
'displayName' => $group->getGID(),
'avatarURL' => ''
];
}
}
}
if ($getUsers) {
$users = $this->userManager->searchDisplayName($query);
foreach ($users as $user) {
if (!in_array($user->getUID(), $skipUsers)) {
$list[] = [
'id' => $user->getUID(),
'type' => 'user',
'displayName' => $user->getDisplayName(),
'avatarURL' => '',
'lastLogin' => $user->getLastLogin(),
'cloudId' => $user->getCloudId()
];
}
}
}
$data['siteusers'] = $list;
return new DataResponse($data, Http::STATUS_OK);
}
/**
* @NoAdminRequired
* @NoCSRFRequired
* @return Array
*/
function convertAccessList($item) {
$split = Array();
if (strpos($item, 'user_') === 0) {
$user = $this->userManager->get(substr($item, 5));
$split = [
'id' => $user->getUID(),
'type' => 'user',
'displayName' => $user->getDisplayName(),
'avatarURL' => ''
];
} elseif (strpos($item, 'group_') === 0) {
$group = substr($item, 6);
$group = $this->groupManager->get($group);
$split = [
'id' => $group->getGID(),
'type' => 'group',
'displayName' => $group->getDisplayName(),
'avatarURL' => '',
];
}
return($split);
}
/**
* @NoAdminRequired
* @NoCSRFRequired
* @PublicPage
* @param string $hash
* @return DataResponse
*/
public function getPoll($hash) {
if (!\OC::$server->getUserSession()->getUser() instanceof IUser) {
return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
}
try {
$poll = $this->eventMapper->findByHash($hash);
if ($poll->getExpire() === null) {
$expired = false;
$expiration = false;
} else {
$expired = time() > strtotime($poll->getExpire());
$expiration = true;
}
if ($poll->getType() == 0) {
$pollType = 'datePoll';
} else {
$pollType = 'textPoll';
};
if ($poll->getOwner() !== \OC::$server->getUserSession()->getUser()->getUID()) {
$mode = 'create';
} else {
$mode = 'edit';
}
$accessList = Array();
$accessType = $poll->getAccess();
if (!strpos('|public|hidden|registered', $accessType)) {
$accessList = explode(';',$accessType);
$accessList = array_filter($accessList);
$accessList = array_map(Array($this,'convertAccessList'), $accessList);
$accessType = 'select';
}
$data = array();
$commentsList = array();
$optionList = array();
$votesList = array();
} catch (DoesNotExistException $e) {
return new DataResponse($e, Http::STATUS_NOT_FOUND);
};
try {
$options = $this->optionsMapper->findByPoll($poll->getId());
foreach ($options as $optionElement) {
$optionList[] = [
'id' => $optionElement->getId(),
'text' => $optionElement->getPollOptionText(),
'timestamp' => $optionElement->getTimestamp()
];
};
} catch (DoesNotExistException $e) {
// ignore
};
try {
$votes = $this->votesMapper->findByPoll($poll->getId());
foreach ($votes as $voteElement) {
$votesList[] = [
'id' => $voteElement->getId(),
'userId' => $voteElement->getUserId(),
'voteOptionId' => $voteElement->getVoteOptionId(),
'voteOptionText' => $voteElement->getVoteOptionText(),
'voteAnswer' => $voteElement->getVoteAnswer()
];
};
} catch (DoesNotExistException $e) {
// ignore
};
try {
$comments = $this->commentMapper->findByPoll($poll->getId());
foreach ($comments as $commentElement) {
$commentsList[] = [
'id' => $commentElement->getId(),
'userId' => $commentElement->getUserId(),
'date' => $commentElement->getDt() . ' UTC',
'comment' => $commentElement->getComment()
];
};
} catch (DoesNotExistException $e) {
// ignore
};
$data['poll'] = [
'result' => 'found',
'mode' => $mode,
'comments' => $commentsList,
'votes' => $votesList,
'shares' => $accessList,
'event' => [
'id' => $poll->getId(),
'hash' => $hash,
'type' => $pollType,
'title' => $poll->getTitle(),
'description' => $poll->getDescription(),
'owner' => $poll->getOwner(),
'created' => $poll->getCreated(),
'access' => $accessType,
'expiration' => $expiration,
'expired' => $expired,
'expirationDate' => $poll->getExpire(),
'isAnonymous' => $poll->getIsAnonymous(),
'fullAnonymous' => $poll->getFullAnonymous(),
'disallowMaybe' => $poll->getDisallowMaybe()
],
'options' => [
'pollDates' => [],
'pollTexts' => $optionList
]
];
return new DataResponse($data, Http::STATUS_OK);
}
/**
* @NoAdminRequired
* @NoCSRFRequired
* @param string $poll
* @return DataResponse
*/
public function writePoll($event, $options, $shares, $mode) {
$user = \OC::$server->getUserSession()->getUser();
if (!$user instanceof IUser) {
return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
}
$userId = \OC::$server->getUserSession()->getUser()->getUID();
$newEvent = new Event();
// Set the configuration options entered by the user
$newEvent->setTitle($event['title']);
$newEvent->setDescription($event['description']);
$newEvent->setType($event['type']);
$newEvent->setIsAnonymous($event['isAnonymous']);
$newEvent->setFullAnonymous($event['fullAnonymous']);
$newEvent->setDisallowMaybe($event['disallowMaybe']);
if ($event['access'] === 'select') {
$shareAccess = '';
foreach ($shares as $shareElement) {
if ($shareElement['type'] === 'user') {
$shareAccess = $shareAccess . 'user_' . $shareElement['id'] . ';';
} elseif ($shareElement['type'] === 'group') {
$shareAccess = $shareAccess . 'group_' . $shareElement['id'] . ';';
}
}
$newEvent->setAccess(rtrim($shareAccess, ';'));
} else {
$newEvent->setAccess($event['access']);
}
if ($event['expiration']) {
$newEvent->setExpire($event['expirationDate']);
} else {
$newEvent->setExpire(null);
}
if ($event['type'] === 'datePoll') {
$newEvent->setType(0);
} elseif ($event['type'] === 'textPoll') {
$newEvent->setType(1);
}
if ($mode === 'edit') {
// Edit existing poll
$oldPoll = $this->eventMapper->findByHash($event['hash']);
// Check if current user is allowed to edit existing poll
if ($oldPoll->getOwner() !== $userId) {
// If current user is not owner of existing poll deny access
return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
}
// else take owner, hash and id of existing poll
$newEvent->setOwner($oldPoll->getOwner());
$newEvent->setHash($oldPoll->getHash());
$newEvent->setId($oldPoll->getId());
$this->eventMapper->update($newEvent);
$this->optionsMapper->deleteByPoll($newEvent->getId());
} elseif ($mode === 'create') {
// Create new poll
// Define current user as owner, set new creation date and create a new hash
$newEvent->setOwner($userId);
$newEvent->setCreated(date('Y-m-d H:i:s'));
$newEvent->setHash(\OC::$server->getSecureRandom()->generate(
16,
ISecureRandom::CHAR_DIGITS .
ISecureRandom::CHAR_LOWER .
ISecureRandom::CHAR_UPPER
));
$newEvent = $this->eventMapper->insert($newEvent);
}
// Update options
if ($event['type'] === 'datePoll') {
foreach ($options['pollDates'] as $optionElement) {
$newOption = new Options();
$newOption->setPollId($newEvent->getId());
$newOption->setPollOptionText(date('Y-m-d H:i:s', $optionElement['timestamp']));
$newOption->setTimestamp($optionElement['timestamp']);
$this->optionsMapper->insert($newOption);
}
} elseif ($event['type'] === "textPoll") {
foreach ($options['pollTexts'] as $optionElement) {
$newOption = new Options();
$newOption->setPollId($newEvent->getId());
$newOption->setpollOptionText(htmlspecialchars($optionElement['text']));
$this->optionsMapper->insert($newOption);
}
}
return new DataResponse(array(
'id' => $newEvent->getId(),
'hash' => $newEvent->getHash()
), Http::STATUS_OK);
}
private function getVendor() {
// this should really be a JSON file
require \OC::$SERVERROOT . '/version.php';
/** @var string $vendor */
return (string) $vendor;
}
/**
* @NoAdminRequired
* @NoCSRFRequired
* @return DataResponse
*/
public function getSystem() {
$userId = \OC::$server->getUserSession()->getUser()->getUID();
$data['system'] = [
'versionArray' => \OCP\Util::getVersion(),
'version' => implode('.', \OCP\Util::getVersion()),
'vendor' => $this->getVendor(),
'language' => $this->systemConfig->getUserValue($userId, 'core', 'lang')
];
return new DataResponse($data, Http::STATUS_OK);
}
}

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

@ -25,18 +25,14 @@ namespace OCA\Polls\Controller;
use OCA\Polls\Db\Comment;
use OCA\Polls\Db\CommentMapper;
use OCA\Polls\Db\Date;
use OCA\Polls\Db\DateMapper;
use OCA\Polls\Db\Event;
use OCA\Polls\Db\EventMapper;
use OCA\Polls\Db\Notification;
use OCA\Polls\Db\NotificationMapper;
use OCA\Polls\Db\Participation;
use OCA\Polls\Db\ParticipationMapper;
use OCA\Polls\Db\ParticipationText;
use OCA\Polls\Db\ParticipationTextMapper;
use OCA\Polls\Db\Text;
use OCA\Polls\Db\TextMapper;
use OCA\Polls\Db\Options;
use OCA\Polls\Db\OptionsMapper;
use OCA\Polls\Db\Votes;
use OCA\Polls\Db\VotesMapper;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http\ContentSecurityPolicy;
@ -52,19 +48,17 @@ use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Mail\IMailer;
use OCP\Security\ISecureRandom;
use OCP\User;
use OCP\User; //To do: replace according to API
use OCP\Util;
class PageController extends Controller {
private $userId;
private $commentMapper;
private $dateMapper;
private $eventMapper;
private $notificationMapper;
private $participationMapper;
private $participationTextMapper;
private $textMapper;
private $optionsMapper;
private $votesMapper;
private $urlGenerator;
private $userMgr;
private $avatarManager;
@ -84,12 +78,10 @@ class PageController extends Controller {
* @param IURLGenerator $urlGenerator
* @param string $userId
* @param CommentMapper $commentMapper
* @param DateMapper $dateMapper
* @param EventMapper $eventMapper
* @param NotificationMapper $notificationMapper
* @param ParticipationMapper $ParticipationMapper
* @param ParticipationTextMapper $ParticipationTextMapper
* @param TextMapper $textMapper
* @param OptionsMapper $optionsMapper
* @param VotesMapper $VotesMapper
*/
public function __construct(
$appName,
@ -102,12 +94,10 @@ class PageController extends Controller {
IURLGenerator $urlGenerator,
$userId,
CommentMapper $commentMapper,
DateMapper $dateMapper,
OptionsMapper $optionsMapper,
EventMapper $eventMapper,
NotificationMapper $notificationMapper,
ParticipationMapper $ParticipationMapper,
ParticipationTextMapper $ParticipationTextMapper,
TextMapper $textMapper
VotesMapper $VotesMapper
) {
parent::__construct($appName, $request);
$this->userMgr = $userMgr;
@ -118,12 +108,10 @@ class PageController extends Controller {
$this->urlGenerator = $urlGenerator;
$this->userId = $userId;
$this->commentMapper = $commentMapper;
$this->dateMapper = $dateMapper;
$this->eventMapper = $eventMapper;
$this->notificationMapper = $notificationMapper;
$this->participationMapper = $ParticipationMapper;
$this->participationTextMapper = $ParticipationTextMapper;
$this->textMapper = $textMapper;
$this->optionsMapper = $optionsMapper;
$this->votesMapper = $VotesMapper;
}
/**
@ -133,13 +121,11 @@ class PageController extends Controller {
public function index() {
$polls = $this->eventMapper->findAllForUserWithInfo($this->userId);
$comments = $this->commentMapper->findDistinctByUser($this->userId);
$partic = $this->participationMapper->findDistinctByUser($this->userId);
$particText = $this->participationTextMapper->findDistinctByUser($this->userId);
$votes = $this->votesMapper->findDistinctByUser($this->userId);
$response = new TemplateResponse('polls', 'main.tmpl', [
'polls' => $polls,
'comments' => $comments,
'participations' => $partic,
'participations_text' => $particText,
'votes' => $votes,
'userId' => $this->userId,
'userMgr' => $this->userMgr,
'urlGenerator' => $this->urlGenerator
@ -224,16 +210,11 @@ class PageController extends Controller {
} catch (DoesNotExistException $e) {
return new TemplateResponse('polls', 'no.acc.tmpl', []);
}
if ($poll->getType() === 0) {
$dates = $this->dateMapper->findByPoll($poll->getId());
$votes = $this->participationMapper->findByPoll($poll->getId());
$participants = $this->participationMapper->findParticipantsByPoll($poll->getId());
} else {
$dates = $this->textMapper->findByPoll($poll->getId());
$votes = $this->participationTextMapper->findByPoll($poll->getId());
$participants = $this->participationTextMapper->findParticipantsByPoll($poll->getId());
}
$options = $this->optionsMapper->findByPoll($poll->getId());
$votes = $this->votesMapper->findByPoll($poll->getId());
$participants = $this->votesMapper->findParticipantsByPoll($poll->getId());
$comments = $this->commentMapper->findByPoll($poll->getId());
try {
$notification = $this->notificationMapper->findByUserAndPoll($poll->getId(), $this->userId);
} catch (DoesNotExistException $e) {
@ -242,10 +223,10 @@ class PageController extends Controller {
if ($this->hasUserAccess($poll)) {
return new TemplateResponse('polls', 'goto.tmpl', [
'poll' => $poll,
'dates' => $dates,
'options' => $options,
'comments' => $comments,
'votes' => $votes,
'participants' => $participants,
'participant' => $participants,
'notification' => $notification,
'userId' => $this->userId,
'userMgr' => $this->userMgr,
@ -272,10 +253,8 @@ class PageController extends Controller {
$poll = new Event();
$poll->setId($pollId);
$this->eventMapper->delete($poll);
$this->textMapper->deleteByPoll($pollId);
$this->dateMapper->deleteByPoll($pollId);
$this->participationMapper->deleteByPoll($pollId);
$this->participationTextMapper->deleteByPoll($pollId);
$this->optionsMapper->deleteByPoll($pollId);
$this->votesMapper->deleteByPoll($pollId);
$this->commentMapper->deleteByPoll($pollId);
$url = $this->urlGenerator->linkToRoute('polls.page.index');
return new RedirectResponse($url);
@ -288,227 +267,19 @@ class PageController extends Controller {
* @return TemplateResponse
*/
public function editPoll($hash) {
$poll = $this->eventMapper->findByHash($hash);
if ($this->userId !== $poll->getOwner()) {
return new TemplateResponse('polls', 'no.create.tmpl');
}
if ($poll->getType() === 0) {
$dates = $this->dateMapper->findByPoll($poll->getId());
} else {
$dates = $this->textMapper->findByPoll($poll->getId());
}
return new TemplateResponse('polls', 'create.tmpl', [
'poll' => $poll,
'dates' => $dates,
'userId' => $this->userId,
'userMgr' => $this->userMgr,
'urlGenerator' => $this->urlGenerator
'urlGenerator' => $this->urlGenerator,
'hash' => $hash
]);
}
/**
* @NoAdminRequired
* @NoCSRFRequired
* @param int $pollId
* @param string $pollType
* @param string $pollTitle
* @param string $pollDesc
* @param string $userId
* @param string $chosenDates
* @param int $expireTs
* @param string $accessType
* @param string $accessValues
* @param bool $isAnonymous
* @param bool $hideNames
* @return RedirectResponse
*/
public function updatePoll(
$pollId,
$pollType,
$pollTitle,
$pollDesc,
$userId,
$chosenDates,
$expireTs,
$accessType,
$accessValues,
$isAnonymous,
$hideNames
) {
$event = $this->eventMapper->find($pollId);
$event->setTitle($pollTitle);
$event->setDescription($pollDesc);
$event->setIsAnonymous($isAnonymous ? 1 : 0);
$event->setFullAnonymous($isAnonymous && $hideNames ? 1 : 0);
if ($accessType === 'select') {
if (isset($accessValues)) {
$accessValues = json_decode($accessValues);
if ($accessValues !== null) {
$groups = array();
$users = array();
if ($accessValues->groups !== null) {
$groups = $accessValues->groups;
}
if ($accessValues->users !== null) {
$users = $accessValues->users;
}
$accessType = '';
foreach ($groups as $gid) {
$accessType .= $gid . ';';
}
foreach ($users as $uid) {
$accessType .= $uid . ';';
}
}
}
}
$event->setAccess($accessType);
/** @var string[] $chosenDates */
$chosenDates = json_decode($chosenDates);
$expire = null;
if ($expireTs !== 0 && $expireTs !== '') {
$expire = date('Y-m-d H:i:s', $expireTs);
}
$event->setExpire($expire);
$this->dateMapper->deleteByPoll($pollId);
$this->textMapper->deleteByPoll($pollId);
if ($pollType === 'event') {
$event->setType(0);
$this->eventMapper->update($event);
sort($chosenDates);
foreach ($chosenDates as $el) {
$date = new Date();
$date->setPollId($pollId);
$date->setDt(date('Y-m-d H:i:s', $el));
$this->dateMapper->insert($date);
}
} else {
$event->setType(1);
$this->eventMapper->update($event);
foreach ($chosenDates as $el) {
$text = new Text();
$text->setPollId($pollId);
$text->setText($el);
$this->textMapper->insert($text);
}
}
$url = $this->urlGenerator->linkToRoute('polls.page.index');
return new RedirectResponse($url);
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*/
public function createPoll() {
return new TemplateResponse('polls', 'create.tmpl',
['userId' => $this->userId, 'userMgr' => $this->userMgr, 'urlGenerator' => $this->urlGenerator]);
}
/**
* @NoAdminRequired
* @NoCSRFRequired
* @param string $pollType
* @param string $pollTitle
* @param string $pollDesc
* @param string $userId
* @param string $chosenDates
* @param int $expireTs
* @param string $accessType
* @param string $accessValues
* @param bool $isAnonymous
* @param bool $hideNames
* @return RedirectResponse
*/
public function insertPoll(
$pollType,
$pollTitle,
$pollDesc,
$userId,
$chosenDates,
$expireTs,
$accessType,
$accessValues,
$isAnonymous,
$hideNames
) {
$event = new Event();
$event->setTitle($pollTitle);
$event->setDescription($pollDesc);
$event->setOwner($userId);
$event->setCreated(date('Y-m-d H:i:s'));
$event->setHash(\OC::$server->getSecureRandom()->generate(
16,
ISecureRandom::CHAR_DIGITS .
ISecureRandom::CHAR_LOWER .
ISecureRandom::CHAR_UPPER
));
$event->setIsAnonymous($isAnonymous ? 1 : 0);
$event->setFullAnonymous($isAnonymous && $hideNames ? 1 : 0);
if ($accessType === 'select') {
if (isset($accessValues)) {
$accessValues = json_decode($accessValues);
if ($accessValues !== null) {
$groups = array();
$users = array();
if ($accessValues->groups !== null) {
$groups = $accessValues->groups;
}
if ($accessValues->users !== null) {
$users = $accessValues->users;
}
$accessType = '';
foreach ($groups as $gid) {
$accessType .= $gid . ';';
}
foreach ($users as $uid) {
$accessType .= $uid . ';';
}
}
}
}
$event->setAccess($accessType);
/** @var string[] $chosenDates */
$chosenDates = json_decode($chosenDates);
$expire = null;
if ($expireTs !== 0 && $expireTs !== '') {
$expire = date('Y-m-d H:i:s', $expireTs);
}
$event->setExpire($expire);
if ($pollType === 'event') {
$event->setType(0);
$ins = $this->eventMapper->insert($event);
$pollId = $ins->getId();
sort($chosenDates);
foreach ($chosenDates as $el) {
$date = new Date();
$date->setPollId($pollId);
$date->setDt(date('Y-m-d H:i:s', $el));
$this->dateMapper->insert($date);
}
} else {
$event->setType(1);
$ins = $this->eventMapper->insert($event);
$pollId = $ins->getId();
$cnt = 1;
foreach ($chosenDates as $el) {
$text = new Text();
$text->setPollId($pollId);
$text->setText($el . '_' . $cnt);
$this->textMapper->insert($text);
$cnt++;
}
}
$url = $this->urlGenerator->linkToRoute('polls.page.index');
return new RedirectResponse($url);
['urlGenerator' => $this->urlGenerator]);
}
/**
@ -517,13 +288,13 @@ class PageController extends Controller {
* @PublicPage
* @param int $pollId
* @param string $userId
* @param string $types
* @param string $dates
* @param string $answers
* @param string $options
* @param bool $receiveNotifications
* @param bool $changed
* @return RedirectResponse
*/
public function insertVote($pollId, $userId, $types, $dates, $receiveNotifications, $changed) {
public function insertVote($pollId, $userId, $answers, $options, $receiveNotifications, $changed) {
if ($this->userId !== null) {
if ($receiveNotifications) {
try {
@ -547,31 +318,20 @@ class PageController extends Controller {
}
}
$poll = $this->eventMapper->find($pollId);
if ($changed) {
$dates = json_decode($dates);
$types = json_decode($types);
$count_dates = count($dates);
if ($poll->getType() === 0) {
$this->participationMapper->deleteByPollAndUser($pollId, $userId);
} else {
$this->participationTextMapper->deleteByPollAndUser($pollId, $userId);
}
for ($i = 0; $i < $count_dates; $i++) {
if ($poll->getType() === 0) {
$part = new Participation();
$part->setPollId($pollId);
$part->setUserId($userId);
$part->setDt(date('Y-m-d H:i:s', $dates[$i]));
$part->setType($types[$i]);
$this->participationMapper->insert($part);
} else {
$part = new ParticipationText();
$part->setPollId($pollId);
$part->setUserId($userId);
$part->setText($dates[$i]);
$part->setType($types[$i]);
$this->participationTextMapper->insert($part);
}
$options = json_decode($options);
$answers = json_decode($answers);
$count_options = count($options);
$this->votesMapper->deleteByPollAndUser($pollId, $userId);
for ($i = 0; $i < $count_options; $i++) {
$vote = new Votes();
$vote->setPollId($pollId);
$vote->setUserId($userId);
$vote->setVoteOptionText(htmlspecialchars($options[$i]));
$vote->setVoteAnswer($answers[$i]);
$this->votesMapper->insert($vote);
}
$this->sendNotifications($pollId, $userId);
@ -598,16 +358,19 @@ class PageController extends Controller {
$comment->setDt(date('Y-m-d H:i:s'));
$this->commentMapper->insert($comment);
$this->sendNotifications($pollId, $userId);
$timeStamp = time();
$displayName = $userId;
$user = $this->userMgr->get($userId);
if ($user !== null) {
$displayName = $user->getDisplayName();
}
return new JSONResponse(array(
'comment' => $commentBox,
'date' => date('Y-m-d H:i:s'),
'userId' => $userId,
'displayName' => $displayName
'displayName' => $displayName,
'timeStamp' => $timeStamp * 100,
'date' => date('Y-m-d H:i:s', $timeStamp),
'relativeNow' => $this->trans->t('just now'),
'comment' => $commentBox
));
}
@ -666,15 +429,15 @@ class PageController extends Controller {
$sUsers[] = str_replace('user_', '', $su);
}
foreach ($userNames as $u) {
$alreadyAdded = false;
$allreadyAdded = false;
foreach ($sUsers as &$su) {
if ($su === $u->getUID()) {
unset($su);
$alreadyAdded = true;
$allreadyAdded = true;
break;
}
}
if (!$alreadyAdded) {
if (!$allreadyAdded) {
$users[] = array('uid' => $u->getUID(), 'displayName' => $u->getDisplayName(), 'isGroup' => false);
} else {
continue;
@ -703,12 +466,14 @@ class PageController extends Controller {
}
// Nextcloud >= 12
$groups = $this->groupManager->getUserGroups(\OC::$server->getUserSession()->getUser());
return array_map(function ($group) {
return array_map(function($group) {
return $group->getGID();
}, $groups);
}
/**
* Check if user has access to this poll
*
* @param Event $poll
* @return bool
*/
@ -749,4 +514,20 @@ class PageController extends Controller {
}
return false;
}
/**
* Check if user is owner of this poll
*
* @param Event $poll
* @return bool
*/
private function userIsOwner($poll) {
$owner = $poll->getOwner();
if ($owner === $this->userId) {
return true;
}
Util::writeLog('polls', $this->userId, Util::ERROR);
return false;
}
}

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

@ -24,6 +24,8 @@
namespace OCA\Polls\Db;
use OCP\AppFramework\Db\Entity;
/**
* @method string getUserId()
* @method void setUserId(string $value)

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

@ -1,57 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2017 Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
*
* @author Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.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/>.
*
*/
namespace OCA\Polls\Db;
use OCP\AppFramework\Db\Mapper;
use OCP\IDBConnection;
class DateMapper extends Mapper {
/**
* DateMapper constructor.
* @param IDBConnection $db
*/
public function __construct(IDBConnection $db) {
parent::__construct($db, 'polls_dts', '\OCA\Polls\Db\Date');
}
/**
* @param int $pollId
* @param int $limit
* @param int $offset
* @return Date[]
*/
public function findByPoll($pollId, $limit = null, $offset = null) {
$sql = 'SELECT * FROM ' . $this->getTableName() . ' WHERE poll_id = ?';
return $this->findEntities($sql, [$pollId], $limit, $offset);
}
/**
* @param int $pollId
*/
public function deleteByPoll($pollId) {
$sql = 'DELETE FROM ' . $this->getTableName() . ' WHERE poll_id = ?';
$this->execute($sql, [$pollId]);
}
}

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

@ -24,6 +24,8 @@
namespace OCA\Polls\Db;
use OCP\AppFramework\Db\Entity;
/**
* @method integer getType()
* @method void setType(integer $value)
@ -45,6 +47,8 @@ namespace OCA\Polls\Db;
* @method void setIsAnonymous(integer $value)
* @method integer getFullAnonymous()
* @method void setFullAnonymous(integer $value)
* @method integer getDisallowMaybe()
* @method void setDisallowMaybe(integer $value)
*/
class Event extends Model {
protected $type;
@ -57,6 +61,7 @@ class Event extends Model {
protected $hash;
protected $isAnonymous;
protected $fullAnonymous;
protected $disallowMaybe;
/**
* Event constructor.
@ -65,5 +70,6 @@ class Event extends Model {
$this->addType('type', 'integer');
$this->addType('isAnonymous', 'integer');
$this->addType('fullAnonymous', 'integer');
$this->addType('disallowMaybe', 'integer');
}
}

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

@ -90,8 +90,8 @@ class EventMapper extends Mapper {
*PREFIX*polls_events.is_anonymous,
*PREFIX*polls_events.full_anonymous
FROM *PREFIX*polls_events
LEFT JOIN *PREFIX*polls_particip
ON *PREFIX*polls_events.id = *PREFIX*polls_particip.id
LEFT JOIN *PREFIX*polls_votes
ON *PREFIX*polls_events.id = *PREFIX*polls_votes.id
LEFT JOIN *PREFIX*polls_comments
ON *PREFIX*polls_events.id = *PREFIX*polls_comments.id
WHERE
@ -99,7 +99,7 @@ class EventMapper extends Mapper {
OR
*PREFIX*polls_events.access != ?
OR
*PREFIX*polls_particip.user_id = ?
*PREFIX*polls_votes.user_id = ?
OR
*PREFIX*polls_comments.user_id = ?
ORDER BY created';

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

@ -24,6 +24,8 @@
namespace OCA\Polls\Db;
use OCP\AppFramework\Db\Entity;
/**
* @method string getUserId()
* @method void setUserId(string $value)

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

@ -24,20 +24,27 @@
namespace OCA\Polls\Db;
use OCP\AppFramework\Db\Entity;
/**
* @method string getDt()
* @method void setDt(string $value)
* @method integer getPollId()
* @method void setPollId(integer $value)
* @method string getPollOptionText()
* @method void setPollOptionText(string $value)
* @method string getTimestamp()
* @method void setTimestamp(Integer $value)
*/
class Date extends Model {
protected $dt;
class Options extends Model {
protected $pollId;
protected $pollOptionText;
protected $timestamp;
/**
* Date constructor.
* Options constructor.
*/
public function __construct() {
$this->addType('pollId', 'integer');
$this->addType('pollOptionText', 'string');
$this->addType('timestamp', 'integer');
}
}

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

@ -26,14 +26,14 @@ namespace OCA\Polls\Db;
use OCP\AppFramework\Db\Mapper;
use OCP\IDBConnection;
class TextMapper extends Mapper {
class OptionsMapper extends Mapper {
/**
* TextMapper constructor.
* @param IDBConnection $db
*/
public function __construct(IDBConnection $db) {
parent::__construct($db, 'polls_txts', '\OCA\Polls\Db\Text');
parent::__construct($db, 'polls_options', '\OCA\Polls\Db\Options');
}
/**

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

@ -1,50 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2017 Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
*
* @author Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
* @author Kai Schröer <git@schroeer.co>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Polls\Db;
/**
* @method string getDt()
* @method void setDt(string $value)
* @method string getUserId()
* @method void setUserId(string $value)
* @method integer getPollId()
* @method void setPollId(integer $value)
* @method integer getType()
* @method void setType(integer $value)
*/
class Participation extends Model {
protected $dt;
protected $userId;
protected $pollId;
protected $type;
/**
* Participation constructor.
*/
public function __construct() {
$this->addType('pollId', 'integer');
$this->addType('type', 'integer');
}
}

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

@ -1,88 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2017 Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
*
* @author Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.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/>.
*
*/
namespace OCA\Polls\Db;
use OCP\AppFramework\Db\Mapper;
use OCP\IDBConnection;
class ParticipationTextMapper extends Mapper {
/**
* ParticipationTextMapper constructor.
* @param IDBConnection $db
*/
public function __construct(IDBConnection $db) {
parent::__construct($db, 'polls_particip_text', '\OCA\Polls\Db\ParticipationText');
}
/**
* @param int $pollId
* @param int $limit
* @param int $offset
* @return ParticipationText[]
*/
public function findByPoll($pollId, $limit = null, $offset = null) {
$sql = 'SELECT * FROM ' . $this->getTableName() . ' WHERE poll_id = ?';
return $this->findEntities($sql, [$pollId], $limit, $offset);
}
/**
* @param string $userId
* @param int $limit
* @param int $offset
* @return ParticipationText[]
*/
public function findDistinctByUser($userId, $limit = null, $offset = null) {
$sql = 'SELECT DISTINCT * FROM ' . $this->getTableName() . ' WHERE user_id = ?';
return $this->findEntities($sql, [$userId], $limit, $offset);
}
/**
* @param int $pollId
* @param int $limit
* @param int $offset
* @return ParticipationText[]
*/
public function findParticipantsByPoll($pollId, $limit = null, $offset = null) {
$sql = 'SELECT DISTINCT user_id FROM ' . $this->getTableName() . ' WHERE poll_id = ?';
return $this->findEntities($sql, [$pollId], $limit, $offset);
}
/**
* @param int $pollId
*/
public function deleteByPoll($pollId) {
$sql = 'DELETE FROM ' . $this->getTableName() . ' WHERE poll_id = ?';
$this->execute($sql, [$pollId]);
}
/**
* @param int $pollId
* @param string $userId
*/
public function deleteByPollAndUser($pollId, $userId) {
$sql = 'DELETE FROM ' . $this->getTableName() . ' WHERE poll_id = ? AND user_id = ?';
$this->execute($sql, [$pollId, $userId]);
}
}

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

@ -1,43 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2017 Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
*
* @author Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
* @author Kai Schröer <git@schroeer.co>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Polls\Db;
/**
* @method string getText()
* @method void setText(string $value)
* @method integer getPollId()
* @method void setPollId(integer $value)
*/
class Text extends Model {
protected $text;
protected $pollId;
/**
* Text constructor.
*/
public function __construct() {
$this->addType('pollId', 'integer');
}
}

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

@ -24,27 +24,32 @@
namespace OCA\Polls\Db;
use OCP\AppFramework\Db\Entity;
/**
* @method text getText()
* @method void setText(text $value)
* @method string getUserId()
* @method void setUserId(string $value)
* @method integer getPollId()
* @method void setPollId(integer $value)
* @method integer getType()
* @method void setType(integer $value)
* @method string getUserId()
* @method void setUserId(string $value)
* @method integer getVoteOptionId()
* @method void setVoteOptionId(integer $value)
* @method integer getVoteOptionText()
* @method void setVoteOptionText(string $value)
* @method integer getVoteAnswer()
* @method void setVoteAnswer(string $value)
*/
class ParticipationText extends Model {
protected $text;
protected $userId;
class Votes extends Model {
protected $pollId;
protected $type;
protected $userId;
protected $voteOptionId;
protected $voteOptionText;
protected $voteAnswer;
/**
* ParticipationText constructor.
* Options constructor.
*/
public function __construct() {
$this->addType('pollId', 'integer');
$this->addType('type', 'integer');
$this->addType('vote_type', 'integer');
}
}

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

@ -26,21 +26,21 @@ namespace OCA\Polls\Db;
use OCP\AppFramework\Db\Mapper;
use OCP\IDBConnection;
class ParticipationMapper extends Mapper {
class VotesMapper extends Mapper {
/**
* ParticipationMapper constructor.
* VotesMapper constructor.
* @param IDBConnection $db
*/
public function __construct(IDBConnection $db) {
parent::__construct($db, 'polls_particip', '\OCA\Polls\Db\Participation');
parent::__construct($db, 'polls_votes', '\OCA\Polls\Db\Votes');
}
/**
* @param string $userId
* @param int $limit
* @param int $offset
* @return Participation[]
* @return Votes[]
*/
public function findDistinctByUser($userId, $limit = null, $offset = null) {
$sql = 'SELECT DISTINCT * FROM ' . $this->getTableName() . ' WHERE user_id = ?';
@ -51,7 +51,7 @@ class ParticipationMapper extends Mapper {
* @param int $pollId
* @param int $limit
* @param int $offset
* @return Participation[]
* @return Votes[]
*/
public function findByPoll($pollId, $limit = null, $offset = null) {
$sql = 'SELECT * FROM ' . $this->getTableName() . ' WHERE poll_id = ?';
@ -62,7 +62,7 @@ class ParticipationMapper extends Mapper {
* @param int $pollId
* @param int $limit
* @param int $offset
* @return Participation[]
* @return Votes[]
*/
public function findParticipantsByPoll($pollId, $limit = null, $offset = null) {
$sql = 'SELECT DISTINCT user_id FROM ' . $this->getTableName() . ' WHERE poll_id = ?';

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

@ -0,0 +1,380 @@
<?php
/**
* @copyright Copyright (c) 2017 René Gieling <github@dartcafe.de>
*
* @author René Gieling <github@dartcafe.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Polls\Migration;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
use Doctrine\DBAL\Types\Type;
use OCP\DB\ISchemaWrapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Migration\SimpleMigrationStep;
use OCP\Migration\IOutput;
/**
* Installation class for the polls app.
* Initial db creation
*/
class Version009000Date20171202105141 extends SimpleMigrationStep {
/** @var IDBConnection */
protected $connection;
/** @var IConfig */
protected $config;
/**
* @param IDBConnection $connection
* @param IConfig $config
*/
public function __construct(IDBConnection $connection, IConfig $config) {
$this->connection = $connection;
$this->config = $config;
}
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
* @since 13.0.0
*/
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if (!$schema->hasTable('polls_events')) {
$table = $schema->createTable('polls_events');
$table->addColumn('id', Type::INTEGER, [
'autoincrement' => true,
'notnull' => true,
]);
$table->addColumn('hash', Type::STRING, [
'notnull' => false,
'length' => 64,
]);
$table->addColumn('type', Type::BIGINT, [
'notnull' => false,
'length' => 16,
]);
$table->addColumn('title', Type::STRING, [
'notnull' => true,
'length' => 128,
]);
$table->addColumn('description', Type::STRING, [
'notnull' => true,
'length' => 1024,
]);
$table->addColumn('owner', Type::STRING, [
'notnull' => true,
'length' => 64,
]);
$table->addColumn('created', Type::DATETIME, [
'notnull' => false,
]);
$table->addColumn('access', Type::STRING, [
'notnull' => false,
'length' => 1024,
]);
$table->addColumn('expire', Type::DATETIME, [
'notnull' => false,
]);
$table->addColumn('is_anonymous', Type::INTEGER, [
'notnull' => false,
'default' => 0,
]);
$table->addColumn('full_anonymous', Type::INTEGER, [
'notnull' => false,
'default' => 0,
]);
$table->setPrimaryKey(['id']);
}
if (!$schema->hasTable('polls_options')) {
$table = $schema->createTable('polls_options');
$table->addColumn('id', Type::INTEGER, [
'autoincrement' => true,
'notnull' => true,
]);
$table->addColumn('poll_id', Type::INTEGER, [
'notnull' => false,
]);
$table->addColumn('poll_option_text', Type::STRING, [
'notnull' => false, // maybe true?
'length' => 256,
]);
$table->setPrimaryKey(['id']);
}
if (!$schema->hasTable('polls_votes')) {
$table = $schema->createTable('polls_votes');
$table->addColumn('id', Type::INTEGER, [
'autoincrement' => true,
'notnull' => true,
]);
$table->addColumn('poll_id', Type::INTEGER, [
'notnull' => false,
]);
$table->addColumn('user_id', Type::STRING, [
'notnull' => true,
'length' => 64,
]);
$table->addColumn('vote_option_id', Type::INTEGER, [
'notnull' => true,
'length' => 64,
]);
$table->addColumn('vote_option_text', Type::STRING, [
'notnull' => false, // maybe true?
'length' => 256,
]);
$table->addColumn('vote_answer', Type::STRING, [
'notnull' => false,
'length' => 64,
]);
$table->setPrimaryKey(['id']);
}
if (!$schema->hasTable('polls_comments')) {
$table = $schema->createTable('polls_comments');
$table->addColumn('id', Type::INTEGER, [
'autoincrement' => true,
'notnull' => true,
]);
$table->addColumn('poll_id', Type::INTEGER, [
'notnull' => false,
]);
$table->addColumn('user_id', Type::STRING, [
'notnull' => true,
'length' => 64,
]);
$table->addColumn('dt', Type::STRING, [
'notnull' => true,
'length' => 32,
]);
$table->addColumn('comment', Type::STRING, [
'notnull' => false,
'length' => 1024,
]);
$table->setPrimaryKey(['id']);
}
if (!$schema->hasTable('polls_notif')) {
$table = $schema->createTable('polls_notif');
$table->addColumn('id', Type::INTEGER, [
'autoincrement' => true,
'notnull' => true,
]);
$table->addColumn('poll_id', Type::INTEGER, [
'notnull' => false,
]);
$table->addColumn('user_id', Type::STRING, [
'notnull' => true,
'length' => 64,
]);
$table->setPrimaryKey(['id']);
}
return $schema;
}
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @since 13.0.0
*/
public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if ($schema->hasTable('polls_dts')) {
$this->copyDateOptions();
$this->copyDateVotes();
}
if ($schema->hasTable('polls_txts')) {
$this->copyTextOptions();
$this->copyTextVotes();
}
}
/**
* Copy date options
*/
protected function copyDateOptions() {
$insert = $this->connection->getQueryBuilder();
$insert->insert('polls_options')
->values([
'poll_id' => $insert->createParameter('poll_id'),
// Decide between one of both
// 'poll_date' => $insert->createParameter('poll_date'),
'poll_option_text' => $insert->createParameter('poll_option_text'),
]);
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('polls_dts');
$result = $query->execute();
while ($row = $result->fetch()) {
$insert
->setParameter('poll_id', $row['poll_id'])
// Decide between one of both
// ->setParameter('poll_date', $row['dt'])
->setParameter('poll_option_text', date($row['dt']));
$insert->execute();
}
$result->closeCursor();
}
/**
* Copy text options
*/
protected function copyTextOptions() {
$insert = $this->connection->getQueryBuilder();
$insert->insert('polls_options')
->values([
'poll_id' => $insert->createParameter('poll_id'),
// Decide between one of both
// 'poll_text' => $insert->createParameter('poll_text'),
'poll_option_text' => $insert->createParameter('poll_option_text'),
]);
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('polls_txts');
$result = $query->execute();
while ($row = $result->fetch()) {
$insert
->setParameter('poll_id', $row['poll_id'])
// Decide between one of both
// ->setParameter('poll_text', $row['text'])
->setParameter('poll_option_text', preg_replace("/_\d*$/", "$1", $row['text']));
$insert->execute();
}
$result->closeCursor();
}
/**
* Copy date votes
*/
protected function copyDateVotes() {
$insert = $this->connection->getQueryBuilder();
$insert->insert('polls_votes')
->values([
'poll_id' => $insert->createParameter('poll_id'),
'user_id' => $insert->createParameter('user_id'),
// 'vote_date' => $insert->createParameter('vote_date'),
'vote_option_id' => $insert->createParameter('vote_option_id'),
'vote_option_text' => $insert->createParameter('vote_option_text'),
// 'vote_type' => $insert->createParameter('vote_type'),
'vote_answer' => $insert->createParameter('vote_answer'),
]);
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('polls_particip');
$result = $query->execute();
while ($row = $result->fetch()) {
$insert
->setParameter('poll_id', $row['poll_id'])
->setParameter('user_id', $row['user_id'])
// ->setParameter('vote_date', $row['dt'])
->setParameter('vote_option_id', $this->findOptionId($row['poll_id'], $row['dt']))
->setParameter('vote_option_text', date($row['dt']))
// ->setParameter('vote_type', $row['type'])
->setParameter('vote_answer', $this->translateVoteTypeToAnswer($row['type']));
$insert->execute();
}
$result->closeCursor();
}
/**
* Copy text votes
*/
protected function copyTextVotes() {
$insert = $this->connection->getQueryBuilder();
$insert->insert('polls_votes')
->values([
'poll_id' => $insert->createParameter('poll_id'),
'user_id' => $insert->createParameter('user_id'),
// 'vote_text' => $insert->createParameter('vote_text'),
'vote_option_id' => $insert->createParameter('vote_option_id'),
'vote_option_text' => $insert->createParameter('vote_option_text'),
// 'vote_type' => $insert->createParameter('vote_type'),
'vote_answer' => $insert->createParameter('vote_answer'),
]);
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('polls_particip_text');
$result = $query->execute();
while ($row = $result->fetch()) {
$insert
->setParameter('poll_id', $row['poll_id'])
->setParameter('user_id', $row['user_id'])
// ->setParameter('vote_text', $row['text'])
->setParameter('vote_option_id', $this->findOptionId($row['poll_id'], preg_replace("/_\d*$/", "$1", $row['text'])))
->setParameter('vote_option_text', preg_replace("/_\d*$/", "$1", $row['text']))
// ->setParameter('vote_type', $row['type'])
->setParameter('vote_answer', $this->translateVoteTypeToAnswer($row['type']));
$insert->execute();
}
$result->closeCursor();
}
/**
* @param int $voteType
* @return string
*/
protected function findOptionId($pollId, $text) {
$queryFind = $this->connection->getQueryBuilder();
$queryFind->select(['id'])
->from('polls_options')
// ->where($queryFind->expr()->eq('poll_id', $pollId))
// ->andWhere($queryFind->expr()->eq('poll_option', $text));
->where('poll_id = "' . $pollId . '"')
->andWhere('poll_option_text ="' . $text . '"');
$resultFind = $queryFind->execute();
$row = $resultFind->fetch();
return $row['id'];
}
protected function translateVoteTypeToAnswer($voteType) {
switch ($voteType) {
case 0:
$answer = "no";
break;
case 1:
$answer = "yes";
break;
case 2:
$answer = "maybe";
break;
default:
$answer = "no";
}
return $answer;
}
}

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

@ -0,0 +1,59 @@
<?php
/**
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.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/>.
*
*/
namespace OCA\Polls\Migration;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\SimpleMigrationStep;
use OCP\Migration\IOutput;
/**
* Deleting unused tables 'polls_dts', 'polls_txts', 'polls_particip' and 'polls_particip_text'
* after migration in 'Version009000Date20171202105141.php'
*/
class Version009000Date20180202213017 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
* @since 13.0.0
*/
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if ($schema->hasTable('polls_dts')) {
$schema->dropTable('polls_dts');
}
if ($schema->hasTable('polls_txts')) {
$schema->dropTable('polls_txts');
}
if ($schema->hasTable('polls_particip')) {
$schema->dropTable('polls_particip');
}
if ($schema->hasTable('polls_particip_text')) {
$schema->dropTable('polls_particip_text');
}
return $schema;
}
}

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

@ -0,0 +1,90 @@
<?php
/**
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.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/>.
*
*/
namespace OCA\Polls\Migration;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
use Doctrine\DBAL\Types\Type;
use OCP\DB\ISchemaWrapper;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Migration\SimpleMigrationStep;
use OCP\Migration\IOutput;
/**
* Auto-generated migration step: Please modify to your needs!
* Adding column 'disallow_maybe' to table 'polls_events'
*/
class Version009000Date20180421050115 extends SimpleMigrationStep {
/** @var IDBConnection */
protected $connection;
/** @var IConfig */
protected $config;
/**
* @param IDBConnection $connection
* @param IConfig $config
*/
public function __construct(IDBConnection $connection, IConfig $config) {
$this->connection = $connection;
$this->config = $config;
}
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @since 13.0.0
*/
public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
}
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
* @since 13.0.0
*/
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('polls_events');
$table->addColumn('disallow_maybe', Type::INTEGER, [
'notnull' => false,
'default' => 0
]);
return $schema;
}
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @since 13.0.0
*/
public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
}
}

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

@ -0,0 +1,68 @@
<?php
namespace OCA\Polls\Migration;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
use Doctrine\DBAL\Types\Type;
use OCP\DB\ISchemaWrapper;
use OCP\IDBConnection;
use OCP\IConfig;
use OCP\Migration\SimpleMigrationStep;
use OCP\Migration\IOutput;
/**
* Adding timestamp to options table
*/
class Version009000Date20180501201949 extends SimpleMigrationStep {
/** @var IDBConnection */
protected $connection;
/** @var IConfig */
protected $config;
/**
* @param IDBConnection $connection
* @param IConfig $config
*/
public function __construct(IDBConnection $connection, IConfig $config) {
$this->connection = $connection;
$this->config = $config;
}
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @since 13.0.0
*/
public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
}
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
* @since 13.0.0
*/
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('polls_options');
$table->addColumn('timestamp', Type::INTEGER, [
'notnull' => false,
'default' => 0
]);
return $schema;
}
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @since 13.0.0
*/
public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
}
}

50
package.json Normal file
Просмотреть файл

@ -0,0 +1,50 @@
{
"name": "polls",
"description": "Polls app for nextcloud",
"version": "0.9.0",
"author": "Rene Gieling",
"license": "AGPL-3.0",
"private": true,
"scripts": {
"live": "cross-env NODE_ENV=development webpack --progress --hot --config src/js/webpack.config.js --watch",
"dev": "cross-env NODE_ENV=development webpack --progress --hide-modules --config src/js/webpack.config.js --watch",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules --config src/js/webpack.config.js",
"test": "cross-env NODE_ENV=development webpack --progress --hide-modules --config src/js/webpack.config.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nextcloud/polls.git"
},
"bugs": {
"url": "https://github.com/nextcloud/polls/issues"
},
"homepage": "https://github.com/nextcloud/polls#readme",
"dependencies": {
"axios": "^0.17.1",
"lodash": "^4.17.11",
"moment": "^2.22.1",
"nextcloud-vue": "^0.2.0",
"velocity-animate": "^1.5.1",
"vue": "^2.5.16",
"vue-js-modal": "^1.3.26"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.5",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.2.0",
"css-loader": "^0.28.8",
"file-loader": "^1.1.6",
"node-sass": "^4.9.3",
"sass-loader": "^7.1.0",
"vue-loader": "^13.7.3",
"vue-template-compiler": "^2.5.17",
"webpack": "^3.12.0"
}
}

Двоичные данные
screenshots/edit-poll-oc.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 80 KiB

Двоичные данные
screenshots/edit-poll.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 81 KiB

После

Ширина:  |  Высота:  |  Размер: 57 KiB

Двоичные данные
screenshots/overview-oc.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 130 KiB

Двоичные данные
screenshots/overview.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 128 KiB

После

Ширина:  |  Высота:  |  Размер: 82 KiB

Двоичные данные
screenshots/vote-mobile-landscape.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 36 KiB

После

Ширина:  |  Высота:  |  Размер: 38 KiB

Двоичные данные
screenshots/vote-mobile-portrait.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 41 KiB

После

Ширина:  |  Высота:  |  Размер: 132 KiB

Двоичные данные
screenshots/vote-oc.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 132 KiB

Двоичные данные
screenshots/vote.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 136 KiB

После

Ширина:  |  Высота:  |  Размер: 46 KiB

580
src/js/Create.vue Normal file
Просмотреть файл

@ -0,0 +1,580 @@
<template>
<div id="create-poll">
<controls :index-page="indexPage" :intitle="title">
<button @click="writePoll(poll.mode)" class="button btn primary" :disabled="writingPoll">
<span>{{ saveButtonTitle }}</span>
<span v-if="writingPoll" class="icon-loading-small"></span>
</button>
<button @click="switchSidebar" class="button">
<span class="symbol icon-settings"></span>
</button>
</controls>
<div class="workbench">
<div>
<h2>{{ t('polls', 'Poll description') }}</h2>
<label>{{ t('polls', 'Title') }}</label>
<input type="text" id="pollTitle" :class="{ error: titleEmpty }" v-model="poll.event.title">
<label>{{ t('polls', 'Description') }}</label>
<textarea id="pollDesc" v-model="poll.event.description" style="resize: vertical; width: 100%;"></textarea>
</div>
<div>
<h2>{{ t('polls', 'Vote options') }}</h2>
<div v-if="poll.mode == 'create'">
<input id="datePoll" v-model="poll.event.type" value="datePoll" type="radio" class="radio" :disabled="protect"/>
<label for="datePoll">{{ t('polls', 'Event schedule') }}</label>
<input id="textPoll" v-model="poll.event.type" value="textPoll" type="radio" class="radio" :disabled="protect"/>
<label for="textPoll">{{ t('polls', 'Text based') }}</label>
</div>
<date-picker @change="addNewPollDate"
v-bind="optionDatePicker"
v-model="newPollDate"
style="width:100%"
v-show="poll.event.type === 'datePoll'"
confirm />
<transition-group
id="date-poll-list"
name="list"
tag="ul"
class="poll-table"
v-show="poll.event.type === 'datePoll'">
<li
is="date-poll-item"
v-for="(pollDate, index) in poll.options.pollDates"
:option="pollDate"
:key="pollDate.id"
@remove="poll.options.pollDates.splice(index, 1)">
</li>
</transition-group>
<div id="poll-item-selector-text" v-show="poll.event.type === 'textPoll'" >
<input v-model="newPollText" @keyup.enter="addNewPollText()" :placeholder=" t('polls', 'Add option') ">
</div>
<transition-group
id="text-poll-list"
name="list"
tag="ul"
class="poll-table"
v-show="poll.event.type === 'textPoll'">
<li
is="text-poll-item"
v-for="(pollText, index) in poll.options.pollTexts"
:option="pollText"
:key="pollText.id"
@remove="poll.options.pollTexts.splice(index, 1)">
</li>
</transition-group>
</div>
</div>
<side-bar v-if="sidebar">
<user-div :description="t('polls', 'Owner')" :user-id="poll.event.owner"></user-div>
<ul class="tabHeaders">
<li class="tabHeader selected" data-tabid="configurationsTabView" data-tabindex="0">
<a href="#">{{ t('polls', 'Configuration') }}</a>
</li>
</ul>
<div v-if="protect">
<span>{{ t('polls', 'Configuration is locked. Changing options may result in unwanted behaviour, but you can unlock it anyway.') }}</span>
<button @click="protect=false" > {{ t('polls', 'Unlock configuration ') }} </button>
</div>
<div id="configurationsTabView" class="tab">
<div class="configBox" v-if="poll.mode =='edit'">
<label class="title">{{ t('polls', 'Poll type') }}</label>
<input id="datePoll" v-model="poll.event.type" value="datePoll" type="radio" class="radio" :disabled="protect"/>
<label for="datePoll">{{ t('polls', 'Event schedule') }}</label>
<input id="textPoll" v-model="poll.event.type" value="textPoll" type="radio" class="radio" :disabled="protect"/>
<label for="textPoll">{{ t('polls', 'Text based') }}</label>
</div>
<div class="configBox">
<label class="title">{{ t('polls', 'Poll configurations') }}</label>
<input :disabled="protect" id="disallowMaybe" v-model="poll.event.disallowMaybe"type="checkbox" class="checkbox" />
<label for="disallowMaybe">{{ t('polls', 'Disallow maybe vote') }}</label>
<input :disabled="protect" id="anonymous" v-model="poll.event.isAnonymous"type="checkbox" class="checkbox" />
<label for="anonymous">{{ t('polls', 'Anonymous poll') }}</label>
<input :disabled="protect" id="trueAnonymous" v-model="poll.event.fullAnonymous" v-show="poll.event.isAnonymous" type="checkbox" class="checkbox"/>
<label for="trueAnonymous" v-show="poll.event.isAnonymous">{{ t('polls', 'Hide user names for admin') }} </label>
<input :disabled="protect" id="expiration" v-model="poll.event.expiration" type="checkbox" class="checkbox" />
<label for="expiration">{{ t('polls', 'Expires') }}</label>
<date-picker v-bind="expirationDatePicker"
:disabled="protect"
v-model="poll.event.expirationDate"
v-show="poll.event.expiration"
style="width:170px"
:time-picker-options="{ start: '00:00', step: '00:05', end: '23:55' }" />
</div>
<div class="configBox">
<label class="title">{{ t('polls', 'Access') }}</label>
<input :disabled="protect" type="radio" v-model="poll.event.access" value="registered" id="private" class="radio"/>
<label for="private">{{ t('polls', 'Registered users only') }}</label>
<input :disabled="protect" type="radio" v-model="poll.event.access" value="hidden" id="hidden" class="radio"/>
<label for="hidden">{{ t('polls', 'hidden') }}</label>
<input :disabled="protect" type="radio" v-model="poll.event.access" value="public" id="public" class="radio"/>
<label for="public">{{ t('polls', 'Public access') }}</label>
<input :disabled="protect" type="radio" v-model="poll.event.access" value="select" id="select" class="radio"/>
<label for="select">{{ t('polls', 'Only shared') }}</label>
</div>
</div>
<share-div :active-shares="poll.shares"
@update-shares="updateShares"
@remove-share="removeShare"
hide-names="true"
:placeholder="t('polls', 'Name of user or group')"
v-show="poll.event.access === 'select'"/>
</side-bar>
<div class="loading-overlay" v-if="loadingPoll">
<span class="icon-loading"></span>
</div>
</div>
</template>
<script>
import axios from 'axios';
import moment from 'moment';
import lodash from 'lodash';
import DatePollItem from './components/datePollItem.vue';
import TextPollItem from './components/textPollItem.vue';
export default {
name: 'create-poll',
components: {
'DatePollItem': DatePollItem,
'TextPollItem': TextPollItem,
},
data: function () {
return {
poll: {
mode: 'create',
comments: [],
votes: [],
shares: [],
event: {
id: 0,
hash: '',
type: 'datePoll',
title: '',
description: '',
owner:'',
created: '',
access: 'public',
expiration: false,
expirationDate: '',
expired: false,
isAnonymous: false,
fullAnonymous: false,
disallowMaybe: false,
},
options: {
pollDates: [],
pollTexts: []
}
},
system:[],
lang: '',
locale: '',
placeholder: '',
newPollDate: '',
newPollTime: '',
newPollText: '',
nextPollDateId: 1,
nextPollTextId: 1,
protect: false,
writingPoll: false,
loadingPoll: true,
sidebar: false,
titleEmpty: false,
indexPage: '',
longDateFormat: '',
dateTimeFormat: '',
}
},
created: function() {
this.indexPage = OC.generateUrl('apps/polls/');
this.getSystemValues();
this.lang = OC.getLanguage();
try {
this.locale = OC.getLocale();
} catch (e) {
if (e instanceof TypeError) {
this.locale = this.lang;
} else {
console.log(e)
}
}
moment.locale(this.locale);
this.longDateFormat = moment.localeData().longDateFormat('L');
this.dateTimeFormat = moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT');
var urlArray = window.location.pathname.split( '/' );
if (urlArray[urlArray.length - 1] === 'create') {
this.poll.event.owner = OC.getCurrentUser().uid;
this.loadingPoll = false
} else {
this.loadPoll(urlArray[urlArray.length - 1])
this.protect = true;
this.poll.mode = 'edit';
};
if (window.innerWidth >1024) {
this.sidebar = true;
}
},
computed: {
langShort: function () {
return this.lang.split("-")[0]
},
title: function() {
if (this.poll.event.title === '') {
return t('polls','Create new poll');
} else {
return this.poll.event.title;
}
},
saveButtonTitle: function() {
if (this.writingPoll) {
return t('polls', 'Writing poll')
} else if (this.poll.mode === 'edit') {
return t('polls', 'Update poll')
} else {
return t('polls', 'Create new poll')
}
},
localeData: function () {
return moment.localeData(moment.locale(this.locale))
},
expirationDatePicker: function () {
return {
editable: true,
minuteStep: 1,
type: 'datetime',
lang: this.lang.split("-")[0],
format: moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT'),
placeholder: t('polls', 'Expiration date')
}
},
optionDatePicker: function () {
return {
editable: false,
minuteStep: 1,
type: 'datetime',
format: moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT'),
lang: this.lang.split("-")[0],
placeholder: t('polls', 'Click to add a date'),
timePickerOptions: {
start: '00:00',
step: '00:05',
end: '23:55'
}
}
}
},
watch: {
title () {
// only used when the title changes after page load
document.title = t('polls','Polls') + ' - ' + this.title;
}
},
methods: {
switchSidebar () {
this.sidebar = !this.sidebar;
},
getSystemValues: function() {
axios.get(OC.generateUrl('apps/polls/get/system'))
.then((response) => {
this.system = response.data.system;
}, (error) => {
this.poll.event.hash = '';
console.log(error.response);
});
},
addShare: function (item){
this.poll.shares.push(item);
},
updateShares: function (share){
this.poll.shares= share.slice(0);
},
removeShare: function (item){
this.poll.shares.splice(this.poll.shares.indexOf(item), 1);
},
addNewPollDate: function (newPollDate) {
if (newPollDate != null) {
this.newPollDate = moment(newPollDate);
this.poll.options.pollDates.push({
id: this.nextPollDateId++,
timestamp: moment(newPollDate).unix(),
});
this.poll.options.pollDates = _.sortBy(this.poll.options.pollDates, 'timestamp');
}
},
addNewPollText: function () {
if (this.newPollText !== null & this.newPollText !== '') {
this.poll.options.pollTexts.push({
id: this.nextPollTextId++,
text: this.newPollText
});
}
this.newPollText = '';
},
writePoll: function (mode) {
this.writingPoll = true
if (mode !== '') {
this.poll.mode = mode;
}
if (this.poll.event.title.length === 0) {
this.titleEmpty = true;
} else {
this.titleEmpty = false;
axios.post(OC.generateUrl('apps/polls/write'), this.poll)
.then((response) => {
this.poll.mode = 'edit';
this.poll.event.hash = response.data.hash;
this.poll.event.id = response.data.id;
this.writingPoll = false;
// window.location.href = OC.generateUrl('apps/polls/edit/' + this.poll.event.hash);
}, (error) => {
this.poll.event.hash = '';
console.log(this.poll.event.hash);
console.log(error.response);
});
}
},
loadPoll: function (hash) {
this.loadingPoll = true
axios.get(OC.generateUrl('apps/polls/get/poll/' + hash))
.then((response) => {
this.poll = response.data.poll;
if (this.poll.event.expirationDate !== null) {
this.poll.event.expirationDate = new Date(moment.utc(this.poll.event.expirationDate))
} else {
this.poll.event.expirationDate = ''
}
if (this.poll.event.type === 'datePoll') {
var i;
for (i = 0; i < this.poll.options.pollTexts.length; i++) {
this.addNewPollDate(new Date(moment.utc(this.poll.options.pollTexts[i].text)))
}
this.poll.options.pollTexts = [];
}
this.loadingPoll = false
this.newPollDate = ''
this.newPollText = ''
}, (error) => {
this.poll.event.hash = '';
console.log(error.response);
this.loadingPoll = false
});
}
}
}
</script>
<style lang="scss">
#create-poll {
width: 100%;
display: flex;
input.hasTimepicker {
width: 75px;
}
}
.polls-content {
display: flex;
padding-top: 45px;
flex-grow: 1;
}
input[type="text"] {
display: block;
width: 100%;
}
.workbench {
margin-top: 45px;
display: flex;
flex-grow: 1;
flex-wrap: wrap;
overflow-x: hidden;
> div {
min-width: 245px;
max-width: 540px;
display: flex;
flex-direction: column;
flex-grow: 1;
padding: 8px;
}
}
.loading-overlay {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff;
opacity: 0.9;
z-index: 1001;
.icon-loading {
position: fixed;
left: 50%;
top: 50%;
margin-left: -35px;
margin-top: -10px;
&::after {
border: 10px solid var(--color-loading-light);
border-top-color: var(--color-primary-element);
height: 70px;
width: 70px;
}
}
}
.polls-sidebar {
margin-top: 45px;
width: 40%;
.configBox {
display: flex;
flex-direction: column;
padding: 8px 8px;
&> .title {
font-weight: bold;
margin-bottom: 4px;
}
}
}
input, textarea {
&.error {
border: 2px solid var(--color-error);
box-shadow: 1px 0 var(--border-radius) var(--color-box-shadow);
}
}
/* Transitions for inserting and removing list items */
.list-enter-active, .list-leave-active {
transition: all 0.5s ease;
}
.list-enter, .list-leave-to {
opacity: 0;
}
.list-move {
transition: transform 0.5s;
}
/* */
#poll-item-selector-text {
> input {
width: 100%
}
}
.poll-table {
>li {
display: flex;
align-items: center;
padding-left: 8px;
padding-right: 8px;
line-height: 2em;
min-height: 4em;
border-bottom: 1px solid var(--color-border);
overflow: hidden;
white-space: nowrap;
&:hover, &:active {
transition: var(--background-dark) 0.3s ease;
background-color: var(--color-loading-light); //$hover-color;
}
> div {
display: flex;
flex-grow: 1;
font-size: 1.2em;
opacity: 0.7;
white-space: normal;
padding-right: 4px;
&.avatar {
flex-grow: 0;
}
}
> div:nth-last-child(1) {
justify-content: center;
flex-grow: 0;
flex-shrink: 0;
}
}
}
button {
&.button-inline{
border: 0;
background-color: transparent;
}
}
.tab {
display: flex;
flex-wrap: wrap;
}
</style>

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

@ -0,0 +1,29 @@
/* global Vue, oc_userconfig */
<template>
<div class="cloud">
<span class="expired" v-if="options.expired">{{ t('polls', 'Expired')}} </span>
<span class="open" v-if="options.expiration">{{ t('polls', 'Expires %n', 1, expirationdate) }}</span>
<span class="open" v-else>{{ t('polls', 'Expires never') }}</span>
<span class="information">{{ options.access }}</span>
<span class="information" v-if="options.isAnonymous"> {{ t('polls', 'Anonymous poll') }}</span>
<span class="information" v-if="options.fullAnonymous"> {{ t('polls', 'Usernames hidden to Owner') }}</span>
<span class="information" v-if="options.isAnonymous & !options.fullAnonymous"> {{ t('polls', 'Usernames visible to Owner') }}</span>
</div>
</template>
<script>
export default {
props: ['options', ],
computed: {
expirationdate: function() {
var date = moment(this.options.expirationDate, moment.localeData().longDateFormat('L')).fromNow();
return date
}
}
}
</script>
<style scoped>
</style>

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

@ -0,0 +1,108 @@
/* global Vue, oc_userconfig */
<template>
<div class="user-row" :class="type">
<div v-show="description" class="description">{{description}}</div>
<div class="avatar"><img :src="avatarURL" :width="size" :height="size" :title="computedDisplayName"></div>
<div v-show="!hideNames" class="user-name">{{ computedDisplayName }}</div>
</div>
</template>
<script>
export default {
props: {
hideNames: {
default: false
},
userId: {
type: String,
default: OC.getCurrentUser().uid
},
displayName: {
type: String
},
size: {
type: Number,
default: 32
},
type: {
type: String,
default: 'user'
},
description: String
},
data: function () {
return {
nothidden: false,
}
},
computed: {
computedDisplayName: function () {
var value = this.displayName;
if (this.userId === OC.getCurrentUser().uid) {
value = OC.getCurrentUser().displayName;
} else {
if (!this.displayName) {
value = this.userId;
}
}
if (this.type === 'group') {
value = value + ' (' + t('polls','Group') +')';
}
return value;
},
avatarURL: function() {
if (this.userId === OC.getCurrentUser().uid) {
return OC.generateUrl(
'/avatar/{user}/{size}?v={version}',
{
user: OC.getCurrentUser().uid,
size: Math.ceil(this.size * window.devicePixelRatio),
version: oc_userconfig.avatar.version
})
} else {
return OC.generateUrl(
'/avatar/{user}/{size}',
{
user: this.userId,
size: Math.ceil(this.size * window.devicePixelRatio),
})
}
}
}
}
</script>
<style lang="scss">
.user-row {
display: flex;
flex-grow: 0;
align-items: center;
margin-left: 0;
margin-top: 0;
> div {
margin: 2px 4px 2px 4px;
}
.description {
opacity: 0.7;
flex-grow: 0;
}
.avatar {
height: 32px;
width: 32px;
flex-grow: 0;
}
.user-name {
opacity: 0.5;
flex-grow: 1;
}
}
</style>

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

@ -0,0 +1,29 @@
<template>
<div class="breadcrump">
<div class="crumb svg">
<a :href="indexPage">
<img class="svg" :src="imagePath" alt="Home">
</a>
</div>
<div class="crumb svg last">
<span v-text="intitle" />
</div>
</div>
</template>
<script>
export default {
props: ['intitle','indexPage'],
data: function () {
return {
imagePath: OC.imagePath('core', 'places/home.svg'),
};
}
}
</script>
<style lang="scss">
.breadcrump {
flex-grow: 1;
}
</style>

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

@ -0,0 +1,54 @@
<template>
<div class="controls">
<breadcrump :index-page="indexPage" :intitle="intitle"></breadcrump>
<slot></slot>
</div>
</template>
<script>
import Breadcrump from './_base-breadcrump.vue';
export default {
components: {
Breadcrump
},
props: ['intitle','indexPage'],
}
</script>
<style lang="scss">
.controls {
display: flex;
border-bottom: 1px solid var(--color-border);
position: fixed;
background: var(--color-main-background);
width: 100%;
height: 45px;
z-index: 1001;
.button, button {
flex-shrink: 0;
height: 36px;
padding: 7px 10px;
border: 0;
background: transparent;
color: var(--color-text-lighter);
&.primary {
background: var(--color-primary);
color: var(--color-primary-text);
}
}
.breadcrump {
overflow: hidden;
min-width: 35px;
div.crumb:last-child {
flex-shrink: 1;
overflow: hidden;
> span {
flex-shrink: 1;
text-overflow: ellipsis;
}
}
}
}
</style>

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

@ -0,0 +1,16 @@
<template>
<div class="polls-sidebar" >
<slot></slot>
</div>
</template>
<style lang="scss">
.polls-sidebar {
min-width: 300px;
border-left: 1px solid var(--color-border);
z-index: 500;
> div, > ul {
padding: 8px;
}
}
</style>

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

@ -0,0 +1,22 @@
<template>
<li>
<div>{{option.timestamp | localFullDate}}</div>
<div>
<a @click="$emit('remove')" class="icon-delete"></a>
</div>
</li>
</template>
<script>
export default {
props: ['option'],
filters: {
localFullDate: function (timestamp) {
if (!timestamp) return '';
if (!moment(timestamp).isValid()) return 'Invalid Date';
if (timestamp < 999999999999) timestamp = timestamp *1000;
return moment(timestamp).format('llll');
}
}
}
</script>

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

@ -0,0 +1,184 @@
<template>
<div>
<h2> {{ t('polls', 'Share with') }}</h2>
<multiselect
v-model="shares"
:options="users"
:option-height=32
:multiple="true"
:close-on-select="false"
:clear-on-select="false"
:preserve-search="true"
label="displayName"
track-by="id"
:options-limit="20"
id="ajax"
@search-change="loadUsersAsync"
@close="updateShares"
:loading="isLoading"
:internal-search="false"
:hide-selected="true"
:searchable="true"
:preselect-first="true"
:placeholder="placeholder">
<template slot="selection" slot-scope="{ values, search, isOpen }">
<span class="multiselect__single" v-if="values.length &amp;&amp; !isOpen">
{{ values.length }} users selected
</span>
</template>
<template slot="option" slot-scope="props">
<div class="option__desc">
<user-div :user-id="props.option.id" :display-name="props.option.displayName" :type="props.option.type"></user-div>
</div>
</template>
</multiselect>
<transition-group tag="ul" v-bind:css="false" class="shared-list">
<li v-for="(item, index) in sortedShares"
v-bind:key="item.displayName"
v-bind:data-index="index">
<div class="options">
<a @click="removeShare(index, item)" class="icon icon-delete svg delete-poll"></a>
</div>
<user-div :user-id="item.id" :display-name="item.displayName" :type="item.type" :hide-names="hideNames"></user-div>
</li>
</transition-group>
</div>
</template>
<script>
import axios from 'axios';
import { Multiselect } from 'nextcloud-vue';
export default {
components: {
Multiselect
},
props: ['placeholder', 'activeShares','hideNames'],
data: function () {
return {
shares: [],
users: [],
isLoading: false,
siteUsersListOptions: {
getUsers: true,
getGroups: true,
query: ''
}
}
},
computed: {
sortedShares: function() {
return this.shares.sort(this.sortByDisplayname)
}
},
methods: {
removeShare: function (index, item){
this.$emit('remove-share', item);
},
updateShares: function (){
this.$emit('update-shares', this.shares);
},
loadUsersAsync: function (query) {
this.isLoading = false
this.siteUsersListOptions.query = query
axios.post(OC.generateUrl('apps/polls/get/siteusers'), this.siteUsersListOptions)
.then((response) => {
this.users = response.data.siteusers
this.isLoading = false
}, (error) => {
console.log(error.response)
});
},
sortByDisplayname: function (a, b) {
if (a.displayName.toLowerCase() < b.displayName.toLowerCase()) return -1;
if (a.displayName.toLowerCase() > b.displayName.toLowerCase()) return 1;
return 0;
}
},
watch: {
activeShares(value) {
this.shares = value.slice(0);
}
}
}
</script>
<style lang="scss">
.shared-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
padding-top: 8px;
> li {
display: flex;
}
}
.options {
display: flex;
position: relative;
top: 13px;
left: 43px;
}
div, select {
&.multiselect:not(.multiselect-vue), &.multiselect:not(.multiselect-vue) {
max-width: unset;
}
}
.multiselect {
width: 100%;
.multiselect__content-wrapper li > span {
height: unset;
}
.option__desc {
flex-grow: 1;
}
.multiselect__option--highlight {
background: #41b883;
outline: none;
color: #fff;
&::after {
content: attr(data-select);
background: #41b883;
color: #fff;
border-radius: 4px;
padding: 2px;
}
}
.multiselect__option--selected {
&::after {
content: attr(data-selected);
color: silver;
}
&.multiselect__option--highlight {
background: #ff6a6a;
color: #fff;
&::after {
background: #ff6a6a;
content: attr(data-deselect);
color: #fff;
border-radius: 4px;
padding: 2px;
}
}
}
}
</style>

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

@ -0,0 +1,22 @@
<template>
<div class="close flex-row">
<a id="closeDetails" @:click="hideSidebar" class="close icon-close has-tooltip-bottom" :title="closeDetailLabel" href="#" :alt="closeDetailLabelAlt"></a>
</div>
</template>
<script>
export default {
data: function () {
return {
closeDetailLabel: t('Close details'),
closeDetailLabelAlt: t('Close')
};
},
methods: {
hideSidebar: function () {
OC.Apps.hideAppSidebar();
}
}
}
</script>

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

@ -0,0 +1,14 @@
<template>
<li>
<div>{{ option.text }}</div>
<div>
<a @click="$emit('remove')" class="icon icon-delete svg delete-poll"></a>
</div>
</li>
</template>
<script>
export default {
props: ['option'],
}
</script>

34
src/js/main.js Normal file
Просмотреть файл

@ -0,0 +1,34 @@
/*jshint esversion: 6 */
import Vue from 'vue';
import Create from './Create.vue';
import { DatetimePicker } from 'nextcloud-vue';
import Controls from './components/_base-controls.vue';
import SideBarClose from './components/sideBarClose.vue';
import UserDiv from './components/_base-UserDiv.vue';
import SideBar from './components/_base-SideBar.vue';
import ShareDiv from './components/shareDiv.vue';
Vue.config.debug = true
Vue.config.devTools = true
Vue.component('Controls', Controls);
Vue.component('DatePicker', DatetimePicker);
Vue.component('SideBarClose', SideBarClose);
Vue.component('UserDiv', UserDiv);
Vue.component('SideBar', SideBar);
Vue.component('ShareDiv', ShareDiv);
Vue.mixin({
methods: {
t: function(app, text, vars, count, options) {
return OC.L10N.translate(app, text, vars, count, options)
},
n: function(app, textSingular, textPlural, count, vars, options) {
return OC.L10N.translatePlural(app, textSingular, textPlural, count, vars, options)
}
}
});
new Vue({
el: '#create-poll',
render: h => h(Create)
});

80
src/js/webpack.config.js Normal file
Просмотреть файл

@ -0,0 +1,80 @@
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: './src/js/main.js',
output: {
path: path.resolve(__dirname, '../../js'),
publicPath: '/js/',
filename: 'create-poll.js'
},
module: {
rules: [
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
],
}, {
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['*', '.js', '.vue', '.json']
},
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true
},
performance: {
hints: false
},
devtool: '#eval-source-map'
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: false,
comments: false,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}

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

@ -1,8 +1,9 @@
<?php
<?php
/**
* @copyright Copyright (c) 2017 Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
*
* @author Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
* @author René Gieling <github@dartcafe.de>
*
* @license GNU AGPL version 3 or any later version
*
@ -20,179 +21,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
use OCP\Util;
use OCP\Template;
Util::addStyle('polls', 'createpoll');
Util::addStyle('polls', 'vendor/jquery.datetimepicker.min');
Util::addScript('polls', 'create_edit');
Util::addScript('polls', 'vendor/jquery.datetimepicker.full.min');
$userId = $_['userId'];
/** @var \OCP\IUserManager $userMgr */
$userMgr = $_['userMgr'];
/** @var \OCP\IURLGenerator $urlGenerator */
$urlGenerator = $_['urlGenerator'];
$isUpdate = isset($_['poll']) && $_['poll'] !== null;
$isAnonymous = false;
$hideNames = false;
if ($isUpdate) {
/** @var OCA\Polls\Db\Event $poll */
$poll = $_['poll'];
$isAnonymous = $poll->getIsAnonymous();
$hideNames = $isAnonymous && $poll->getFullAnonymous();
/** @var OCA\Polls\Db\Date[]|OCA\Polls\Db\Text[] $dates */
$dates = $_['dates'];
$chosen = '[';
foreach ($dates as $d) {
if ($poll->getType() === 0) {
$chosen .= strtotime($d->getDt());
} else {
$chosen .= '"' . $d->getText() . '"';
}
$chosen .= ',';
}
$chosen = trim($chosen, ',');
$chosen .= ']';
$title = $poll->getTitle();
$desc = $poll->getDescription();
if ($poll->getExpire() !== null) {
$expireTs = strtotime($poll->getExpire());
$expireStr = date('d.m.Y', $expireTs);
}
$access = $poll->getAccess();
$accessTypes = $access;
if (
$access !== 'registered'
&& $access !== 'hidden' && $access !== 'public'
) {
$access = 'select';
}
}
?>
<div id="app-content">
<div id="controls">
<div id="breadcrump">
<div class="crumb svg" data-dir="/">
<a href="<?php p($urlGenerator->linkToRoute('polls.page.index')); ?>">
<img class="svg" src="<?php print_unescaped(Template::image_path('core', 'places/home.svg')); ?>" alt="Home">
</a>
</div>
<div class="crumb svg last">
<span>
<?php if ($isUpdate): ?>
<?php p($l->t('Edit poll') . ' ' . $poll->getTitle()); ?>
<?php else: ?>
<?php p($l->t('Create new poll')); ?>
<?php endif; ?>
</span>
</div>
</div>
</div>
<?php if ($isUpdate): ?>
<form name="finish_poll" action="<?php p($urlGenerator->linkToRoute('polls.page.update_poll')); ?>" method="POST">
<input type="hidden" name="pollId" value="<?php p($poll->getId()); ?>" />
<?php else: ?>
<form name="finish_poll" action="<?php p($urlGenerator->linkToRoute('polls.page.insert_poll')); ?>" method="POST">
<?php endif; ?>
<input type="hidden" name="chosenDates" id="chosenDates" value="<?php if (isset($chosen)) p($chosen); ?>" />
<input type="hidden" name="expireTs" id="expireTs" value="<?php if (isset($expireTs)) p($expireTs); ?>" />
<input type="hidden" name="userId" id="userId" value="<?php p($userId); ?>" />
<header class="row">
</header>
<div class="new_poll row">
<div class="col-50">
<label for="pollTitle" class="input_title"><?php p($l->t('Title')); ?></label>
<input type="text" class="input_field" id="pollTitle" name="pollTitle" value="<?php if (isset($title)) p($title); ?>" />
<label for="pollDesc" class="input_title"><?php p($l->t('Description')); ?></label>
<textarea class="input_field" id="pollDesc" name="pollDesc"><?php if (isset($desc)) p($desc); ?></textarea>
<label class="input_title"><?php p($l->t('Access')); ?></label>
<input type="radio" name="accessType" id="private" value="registered" class="radio" <?php if (!$isUpdate || $access === 'registered') print_unescaped('checked'); ?> />
<label for="private"><?php p($l->t('Registered users only')); ?></label>
<input type="radio" name="accessType" id="hidden" value="hidden" class="radio" <?php if ($isUpdate && $access === 'hidden') print_unescaped('checked'); ?> />
<label for="hidden"><?php p($l->t('hidden')); ?></label>
<input type="radio" name="accessType" id="public" value="public" class="radio" <?php if ($isUpdate && $access === 'public') print_unescaped('checked'); ?> />
<label for="public"><?php p($l->t('Public access')); ?></label>
<input type="radio" name="accessType" id="select" value="select" class="radio" <?php if ($isUpdate && $access === 'select') print_unescaped('checked'); ?>>
<label for="select"><?php p($l->t('Select')); ?></label>
<span id="id_label_select">...</span>
<div id="selected_access" class="row user-group-list">
<ul id="selected-search-list-id">
</ul>
</div>
<div id="access_rights" class="row user-group-list">
<div class="col-50">
<input type="text" class="live-search-box" id="user-group-search-box" placeholder="<?php p($l->t('User/Group search')); ?>" />
<ul class="live-search-list" id="live-search-list-id">
</ul>
</div>
</div>
<input type="hidden" name="accessValues" id="accessValues" value="<?php if ($isUpdate && $access === 'select') p($accessTypes) ?>" />
<input id="isAnonymous" name="isAnonymous" type="checkbox" class="checkbox" <?php $isAnonymous ? print_unescaped('value="true" checked') : print_unescaped('value="false"'); ?> />
<label for="isAnonymous" class="input_title"><?php p($l->t('Anonymous')) ?></label>
<div id="anonOptions" style="display:none;">
<input id="hideNames" name="hideNames" type="checkbox" class="checkbox" <?php $hideNames ? print_unescaped('value="true" checked') : print_unescaped('value="false"'); ?> />
<label for="hideNames" class="input_title"><?php p($l->t('Hide user names for admin')) ?></label>
</div>
<input id="id_expire_set" name="check_expire" type="checkbox" class="checkbox" <?php ($isUpdate && $poll->getExpire() !== null) ? print_unescaped('value="true" checked') : print_unescaped('value="false"'); ?> />
<label for="id_expire_set" class="input_title"><?php p($l->t('Expires')); ?></label>
<div class="input-group" id="expiration">
<input id="id_expire_date" type="text" required="" <?php (!$isUpdate || $poll->getExpire() === null) ? print_unescaped('disabled="true"') : print_unescaped('value="' . $expireStr . '"'); ?> name="expire_date_input" />
</div>
</div>
<div class="col-50">
<input type="radio" name="pollType" id="event" value="event" class="radio" <?php if (!$isUpdate || $poll->getType() === 0) print_unescaped('checked'); ?> />
<label for="event"><?php p($l->t('Event schedule')); ?></label>
<!-- TODO texts to db -->
<input type="radio" name="pollType" id="text" value="text" class="radio" <?php if ($isUpdate && $poll->getType() === 1) print_unescaped('checked'); ?>>
<label for="text"><?php p($l->t('Text based')); ?></label>
<div id="date-select-container" <?php if ($isUpdate && $poll->getType() === 1) print_unescaped('style="display:none;"'); ?> >
<label for="datetimepicker" class="input_title"><?php p($l->t('Dates')); ?></label>
<input id="datetimepicker" type="text" />
<table id="selected-dates-table" class="choices">
</table>
</div>
<div id="text-select-container" <?php if(!$isUpdate || $poll->getType() === 0) print_unescaped('style="display:none;"'); ?> >
<label for="text-title" class="input_title"><?php p($l->t('Text item')); ?></label>
<div class="input-group">
<input type="text" id="text-title" placeholder="<?php print_unescaped('Insert text...'); ?>" />
<div class="input-group-btn">
<input type="button" id="text-submit" class="button btn" value="<?php p($l->t('Add')); ?>" class="btn"/>
</div>
</div>
<table id="selected-texts-table" class="choices">
</table>
</div>
</div>
</div>
<div class="form-actions">
<?php if ($isUpdate): ?>
<input type="submit" id="submit_finish_poll" class="button btn primary" value="<?php p($l->t('Update poll')); ?>" />
<?php else: ?>
<input type="submit" id="submit_finish_poll" class="button btn primary" value="<?php p($l->t('Create poll')); ?>" />
<?php endif; ?>
<a href="<?php p($urlGenerator->linkToRoute('polls.page.index')); ?>" id="submit_cancel_poll" class="button"><?php p($l->t('Cancel')); ?></a>
</div>
</form>
</div>
<div id="create-poll"></div>
<?php \OCP\Util::addScript('polls', 'create-poll'); ?>

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

@ -21,14 +21,18 @@
*
*/
use OCP\User;
use OCP\User; //To do: replace according to API
use OCP\Util;
use OCP\Template;
Util::addStyle('polls', 'main');
Util::addStyle('polls', 'flex');
Util::addStyle('polls', 'vote');
Util::addStyle('polls', 'sidebar');
if (!User::isLoggedIn()) {
Util::addStyle('polls', 'public');
}
Util::addScript('polls', 'app');
Util::addScript('polls', 'vote');
@ -41,9 +45,9 @@
$avaMgr = $_['avatarManager'];
/** @var \OCA\Polls\Db\Event $poll */
$poll = $_['poll'];
/** @var OCA\Polls\Db\Date[]|OCA\Polls\Db\Text[] $dates */
$dates = $_['dates'];
/** @var OCA\Polls\Db\Participation[]|OCA\Polls\Db\ParticipationText[] $votes */
/** @var OCA\Polls\Db\Options[] $options */
$options = $_['options'];
/** @var OCA\Polls\Db\Votes[] $votes */
$votes = $_['votes'];
/** @var \OCA\Polls\Db\Comment[] $comments */
$comments = $_['comments'];
@ -52,7 +56,15 @@
$isAnonymous = $poll->getIsAnonymous() && $userId !== $poll->getOwner();
$hideNames = $poll->getIsAnonymous() && $poll->getFullAnonymous();
if ($poll->getDisallowMaybe()) {
$maybe = 'maybedisallowed';
} else {
$maybe = 'maybeallowed';
}
$access = $poll->getAccess();
$updatedPoll = false;
$dataUnvoted = '';
if ($poll->getExpire() === null) {
$expired = false;
} else {
@ -88,24 +100,28 @@
// init array for counting 'yes'-votes for each date
$total = array();
for ($i = 0; $i < count($dates); $i++) {
for ($i = 0; $i < count($votes); $i++) {
$total['yes'][$i] = 0;
$total['no'][$i] = 0;
$total['maybe'][$i] = 0;
}
$userVoted = array();
$pollUrl = $urlGenerator->linkToRouteAbsolute('polls.page.goto_poll', ['hash' => $poll->getHash()]);
?>
<div id="app-content" class="<?php p($statusClass . ' ' . $pollTypeClass); ?>">
<div id="app-content" class="<?php p($statusClass . ' ' . $pollTypeClass . ' ' . $maybe); ?>">
<div id="controls" class="controls">
<div id="breadcrump" class="breadcrump">
<?php if (User::isLoggedIn()) : ?>
<div class="crumb svg" data-dir="/">
<?php if (User::isLoggedIn()) : ?>
<div class="crumb svg">
<a href="<?php p($urlGenerator->linkToRoute('polls.page.index')); ?>">
<img class="svg" src="<?php print_unescaped(Template::image_path('core', 'places/home.svg')); ?>" alt="Home">
</a>
</div>
<?php endif; ?>
<?php endif; ?>
<div class="crumb svg last">
<span><?php p($poll->getTitle()); ?></span>
</div>
@ -124,44 +140,45 @@
</div>
<div id="votings" class="main-container">
<div class="wordwrap description"><span><?php print_unescaped($description); ?></span>
<?php
if ($expired) {
print_unescaped('<span class="' . $statusClass . '">' . $l->t('The poll expired on %s. Voting is disabled, but you can still comment.', array(date('d.m.Y H:i', strtotime($poll->getExpire())))) . '</span>');
}?>
<div class="wordwrap description">
<span>
<?php print_unescaped($description); ?>
</span>
<?php if ($expired) { print_unescaped('<span class="' . $statusClass . '">' . $l->t('The poll expired on %s. Voting is disabled, but you can still comment.', array(date('d.m.Y H:i', strtotime($poll->getExpire())))) . '</span>'); }?>
</div>
<div class="table">
<ul class="flex-row header" >
<?php
foreach ($dates as $dateElement) {
if ($poll->getType() === 0) {
$timestamp = strtotime($dateElement->getDt());
print_unescaped('<li id="slot_' . $dateElement->getId() . '" title="' . $dateElement->getDt() . ' ' . date_default_timezone_get() . '" class="flex-column vote time has-tooltip" data-timestamp="' . $timestamp . '"data-value-utc="' . $dateElement->getDt() . '">');
print_unescaped(' <div class="date-box flex-column">');
print_unescaped(' <div class="month">' . $l->t(date('M', $timestamp)) . '</div>');
print_unescaped(' <div class="day">' . date('j', $timestamp) . '</div>');
print_unescaped(' <div class="dayow">' . $l->t(date('D', $timestamp)) . '</div>');
print_unescaped(' <div class="time">' . date('G:i', $timestamp) . ' UTC</div>');
print_unescaped(' </div>');
} else {
print_unescaped('<li id="slot_' . $dateElement->getId() . '" title="' . preg_replace('/_\d+$/', '', $dateElement->getText()) . '" class="flex-column vote option">');
print_unescaped(' <div class="date-box flex-column">' . preg_replace('/_\d+$/', '', $dateElement->getText()) . '</div>');
}
print_unescaped('<div class="counter flex-row">');
print_unescaped(' <div class="yes flex-row">');
print_unescaped(' <div class="svg"></div>');
print_unescaped(' <div id="counter_yes_voteid_' . $dateElement->getId() . '" class ="result-cell yes" data-voteId="' . $dateElement->getId() . '">0</div>');
<ul class="flex-row header" >
<?php
foreach ($options as $optionElement) {
if ($poll->getType() === 0) {
$timestamp = strtotime($optionElement->getPollOptionText());
print_unescaped('<li id="slot_' . $optionElement->getId() . '" title="' . $optionElement->getPollOptionText() . ' ' . date_default_timezone_get() . '" class="flex-column vote time has-tooltip" data-timestamp="' . $timestamp . '"data-value-utc="' . $optionElement->getPollOptionText() . '">');
print_unescaped(' <div class="date-box flex-column">');
print_unescaped(' <div class="month">' . $l->t(date('M', $timestamp)) . '</div>');
print_unescaped(' <div class="day">' . date('j', $timestamp) . '</div>');
print_unescaped(' <div class="dayow">' . $l->t(date('D', $timestamp)) . '</div>');
print_unescaped(' <div class="time">' . date('G:i', $timestamp) . ' UTC</div>');
print_unescaped(' </div>');
print_unescaped(' <div class="no flex-row">');
print_unescaped(' <div class="svg"></div>');
print_unescaped(' <div id="counter_no_voteid_' . $dateElement->getId() . '" class ="result-cell no" data-voteId="' . $dateElement->getId() . '">0</div>');
print_unescaped(' </div>');
print_unescaped('</div>');
} else {
print_unescaped('<li id="slot_' . $optionElement->getId() . '" title="' . $optionElement->getPollOptionText() . '" class="flex-column vote option">');
print_unescaped(' <div class="date-box flex-column">' . $optionElement->getPollOptionText() . '</div>');
}
?>
</li>
</ul>
print_unescaped('<div class="counter flex-row">');
print_unescaped(' <div class="yes flex-row">');
print_unescaped(' <div class="svg"></div>');
print_unescaped(' <div id="counter_yes_voteid_' . $optionElement->getId() . '" class ="result-cell yes" data-voteId="' . $optionElement->getId() . '">0</div>');
print_unescaped(' </div>');
print_unescaped(' <div class="no flex-row">');
print_unescaped(' <div class="svg"></div>');
print_unescaped(' <div id="counter_no_voteid_' . $optionElement->getId() . '" class ="result-cell no" data-voteId="' . $optionElement->getId() . '">0</div>');
print_unescaped(' </div>');
print_unescaped('</div>');
}
?>
</li>
</ul>
<ul class="flex-column table-body">
<?php
if ($votes !== null) {
@ -169,7 +186,6 @@
$others = array();
$displayName = '';
$avatarName = '';
$activeClass = '';
foreach ($votes as $vote) {
if (!isset($others[$vote->getUserId()])) {
$others[$vote->getUserId()] = array();
@ -192,7 +208,7 @@
!$isAnonymous &&
!$hideNames
) {
$displayName = $userMgr->get($usr)->getDisplayName();
$displayName = $usr;
$avatarName = $usr;
} else {
if ($isAnonymous || $hideNames) {
@ -216,37 +232,16 @@
// loop over dts
$i_tot = 0;
foreach ($dates as $dateElement) {
if ($poll->getType() === 0) {
$dateId = strtotime($dateElement->getDt());
$pollId = 'voteid_' . $dateElement->getId();
} else {
$dateId = $dateElement->getText();
$pollId = 'voteid_' . $dateElement->getId();
}
foreach ($options as $optionElement) {
// look what user voted for this dts
$class = 'flex-column poll-cell no';
foreach ($others[$usr] as $vote) {
$voteVal = null;
if ($poll->getType() === 0) {
$voteVal = strtotime($vote->getDt());
} else {
$voteVal = $vote->getText();
}
if ($dateId === $voteVal) {
if ($vote->getType() === 1) {
$class = 'flex-column poll-cell yes';
$total['yes'][$i_tot]++;
} else if ($vote->getType() === 0) {
$class = 'flex-column poll-cell no';
$total['no'][$i_tot]++;
} else if ($vote->getType() === 2) {
$class = 'flex-column poll-cell maybe';
}
if ($optionElement->getPollOptionText() === $vote->getVoteOptionText()) {
$class = $vote->getVoteAnswer();
break;
}
$class = 'no';
}
print_unescaped('<li id="' . $pollId . '" class="' . $class . '"></li>');
print_unescaped('<li id="voteid_' . $optionElement->getId() . '" class="flex-column poll-cell ' . $class . '"></li>');
$i_tot++;
}
@ -254,8 +249,6 @@
print_unescaped('</li>');
}
}
$totalYesOthers = array_merge(array(), $total['yes']);
$totalNoOthers = array_merge(array(), $total['no']);
$toggleTooltip = $l->t('Switch all options at once');
if (!$expired) {
print_unescaped('<li class="flex-row user current-user">');
@ -264,51 +257,45 @@
if (User::isLoggedIn()) {
print_unescaped(' <div class="avatar has-tooltip" title="' . ($userId) . '"></div>');
print_unescaped(' <div class="name">');
p($userMgr->get($userId)->getDisplayName());
p($userId);
} else {
print_unescaped(' <div class="avatar has-tooltip" title="?"></div>');
print_unescaped(' <div id="id_ac_detected" class="name external current-user"><input type="text" name="user_name" id="user_name" placeholder="' . $l->t('Your name here') . '" />');
}
print_unescaped(' </div>');
print_unescaped(' </div>');
if ($maybe === 'maybeallowed') {
print_unescaped(' <div id="toggle-cell" class="toggle-cell has-tooltip maybe" title="' . $toggleTooltip . '">');
} else {
print_unescaped(' <div id="toggle-cell" class="toggle-cell has-tooltip yes" title="' . $toggleTooltip . '">');
}
print_unescaped(' <div class="toggle"></div>');
print_unescaped(' </div>');
print_unescaped('</div>');
print_unescaped('<ul class="flex-row">');
$i_tot = 0;
foreach ($dates as $dateElement) {
if ($poll->getType() === 0) {
$dateId = strtotime($dateElement->getDt());
$pollId = 'voteid_' . $dateElement->getId();
} else {
$dateId = $dateElement->getText();
$pollId = 'voteid_' . $dateElement->getId();
}
foreach ($options as $optionElement) {
// see if user already has data for this event
$class = 'no';
$activeClass = 'flex-column active poll-cell';
$dataUnvoted = '';
if (isset($userVoted)) {
foreach ($userVoted as $obj) {
$voteVal = null;
if ($poll->getType() === 0) {
$voteVal = strtotime($obj->getDt());
} else {
$voteVal = $obj->getText();
}
if ($voteVal === $dateId) {
if ($obj->getType() === 1) {
$class = 'yes';
$total['yes'][$i_tot]++;
} else if ($obj->getType() === 2) {
$class = 'maybe';
}
foreach ($userVoted as $vote) {
if ($optionElement->getPollOptionText() === $vote->getVoteOptionText()) {
$class = $vote->getVoteAnswer();
break;
} else {
$class = 'unvoted';
}
}
}
print_unescaped('<li id="' . $pollId . '" class="' . $activeClass . ' ' . $class . '" data-value="' . $dateId . '"></li>');
if ($class === 'unvoted') {
$dataUnvoted = $l->t('New');
$updatedPoll = true;
}
print_unescaped('<li id="voteid_' . $optionElement->getId() . '" class="flex-column active poll-cell ' . $class . '" data-value="' . $optionElement->getPollOptionText() . '" data-unvoted="' . $dataUnvoted . '"></li>');
$i_tot++;
}
@ -318,24 +305,33 @@
?>
</ul>
</div>
<?php if ($updatedPoll) : ?>
<div class="updated-poll alert">
<p> <?php p($l->t('This poll was updated since your last visit. Please check your votes.')); ?></p>
</div>
<?php endif; ?>
<div class="submitPoll flex-row">
<div>
<form class="finish_vote" name="finish_vote" action="<?php p($urlGenerator->linkToRoute('polls.page.insert_vote')); ?>" method="POST">
<input type="hidden" name="pollId" value="<?php p($poll->getId()); ?>" />
<input type="hidden" name="userId" value="<?php p($userId); ?>" />
<input type="hidden" name="dates" value="<?php p($poll->getId()); ?>" />
<input type="hidden" name="types" value="<?php p($poll->getId()); ?>" />
<input type="hidden" name="options" value="<?php p($poll->getId()); ?>" />
<input type="hidden" name="answers" value="<?php p($poll->getId()); ?>" />
<input type="hidden" name="receiveNotifications" />
<input type="hidden" name="changed" />
<input type="button" id="submit_finish_vote" class="button btn" value="<?php p($l->t('Vote!')); ?>" />
<input type="button" id="submit_finish_vote" class="button btn primary" value="<?php p($l->t('Vote!')); ?>" />
</form>
</div>
<?php if (User::isLoggedIn()) : ?>
<?php if (User::isLoggedIn()) : ?>
<div class="notification">
<input type="checkbox" id="check_notif" class="checkbox" <?php if ($notification !== null) print_unescaped(' checked'); ?> />
<label for="check_notif"><?php p($l->t('Receive notification email on activity')); ?></label>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
@ -351,7 +347,7 @@
<div class="authorRow user-cell flex-row">
<div class="description leftLabel"><?php p($l->t('Owner')); ?></div>
<div class="avatar has-tooltip-bottom" title="<?php p($poll->getOwner())?>"></div>
<div class="author"><?php p($userMgr->get($poll->getOwner())->getDisplayName()); ?></div>
<div class="author"><?php p($poll->getOwner()); ?></div>
</div>
<div class="cloud">
@ -407,30 +403,31 @@
</div>
</div>
<?php if ($expired) : ?>
<div id="expired_info">
<h2><?php p($l->t('Poll expired')); ?></h2>
<p>
<?php p($l->t('The poll expired on %s. Voting is disabled, but you can still comment.', array(date('d.m.Y H:i', strtotime($poll->getExpire()))))); ?>
</p>
</div>
<?php endif; ?>
<?php if ($expired) : ?>
<div id="expired_info">
<h2><?php p($l->t('Poll expired')); ?></h2>
<p>
<?php p($l->t('The poll expired on %s. Voting is disabled, but you can still comment.', array(date('d.m.Y H:i', strtotime($poll->getExpire()))))); ?>
</p>
</div>
<?php endif; ?>
<ul class="tabHeaders">
<li class="tabHeader selected" data-tabid="commentsTabView" data-tabindex="0">
<a href="#"><?php p($l->t('Comments')); ?></a>
</li>
</ul>
<div class="tabsContainer">
<div id="commentsTabView" class="tab commentsTabView">
<div class="newCommentRow comment new-comment">
<?php if (User::isLoggedIn()) : ?>
<?php if (User::isLoggedIn()) : ?>
<div class="authorRow user-cell flex-row">
<div class="avatar has-tooltip" title="<?php p($userId)?>"></div>
<div class="author"><?php p($userMgr->get($userId)->getDisplayName()) ?></div>
<div class="author"><?php p($userId) ?></div>
</div>
<?php else: ?>
<?php else: ?>
<a href="<?php p($urlGenerator->linkToRouteAbsolute('core.login.showLoginForm')); ?>"><?php p($l->t('Login or ...')); ?></a>
<div class="authorRow user-cell flex-row">
<div class="avatar has-tooltip" title="?"></div>
@ -438,7 +435,8 @@
<input type="text" name="user_name_comm" id="user_name_comm" placeholder="<?php p($l->t('Your name here')); ?>" />
</div>
</div>
<?php endif; ?>
<?php endif; ?>
<form class="newCommentForm flex-row" name="send_comment" action="<?php p($urlGenerator->linkToRoute('polls.page.insert_comment')); ?>" method="POST">
<input type="hidden" name="pollId" value="<?php p($poll->getId()); ?>" />
<input type="hidden" name="userId" value="<?php p($userId); ?>" />
@ -467,7 +465,7 @@
// Comment is from current user
// -> display user
$avatarName = $userId;
$displayName = $userMgr->get($userId)->getDisplayName();
$displayName = $userId;
} else if (!$isAnonymous && !$hideNames) {
// comment is from another user,
@ -476,9 +474,6 @@
// -> display user
$avatarName = $comment->getUserId();
$displayName = $avatarName;
if ($userMgr->get($comment->getUserId()) !== null) {
$displayName = $userMgr->get($avatarName)->getDisplayName();
}
} else {
// in all other cases
// -> make user anonymous
@ -501,6 +496,6 @@
</ul>
</div>
</div>
<form id="form_delete_poll" name="form_delete_poll" action="<?php p($urlGenerator->linkToRoute('polls.page.delete_poll')); ?>" method="POST"></form>
</div>
<form id="form_delete_poll" name="form_delete_poll" action="<?php p($urlGenerator->linkToRoute('polls.page.delete_poll')); ?>" method="POST"></form>

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

@ -37,6 +37,7 @@
/** @var \OCA\Polls\Db\Event[] $polls */
$polls = $_['polls'];
?>
<div id="app-content">
<div id="controls">
<div class="breadcrump">
@ -97,11 +98,7 @@
$owner = $poll->getOwner();
$expiry_style = '';
if ($poll->getType() === 0) {
$participated = $_['participations'];
} else {
$participated = $_['participations_text'];
}
$participated = $_['votes'];
$participated_class = 'partic_no';
$participated_title = 'You did not vote';
$participated_count = count($participated);
@ -115,6 +112,7 @@
$owner = $l->t('Yourself');
}
$timestamp_style = '';
$expiry_style = ' endless';
$expiry_date = $l->t('Never');

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

@ -33,7 +33,7 @@
<?php p($l->t('Access denied')); ?>
</h1>
<h2>
<?php p($l->t('You are not allowed to delete this poll or the poll does not exist.')); ?>
<?php p($l->t('You are either not allowed to delete this poll or it doesn\'t exist.')); ?>
</h2>
</div>
</div>

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

@ -62,7 +62,7 @@ class PageControllerTest extends UnitTestCase {
$commentMapper = $this->getMockBuilder('OCA\Polls\Db\CommentMapper')
->disableOriginalConstructor()
->getMock();
$dateMapper = $this->getMockBuilder('OCA\Polls\Db\DateMapper')
$optionsMapper = $this->getMockBuilder('OCA\Polls\Db\OptionsMapper')
->disableOriginalConstructor()
->getMock();
$eventMapper = $this->getMockBuilder('OCA\Polls\Db\EventMapper')
@ -71,13 +71,7 @@ class PageControllerTest extends UnitTestCase {
$notificationMapper = $this->getMockBuilder('OCA\Polls\Db\NotificationMapper')
->disableOriginalConstructor()
->getMock();
$participationMapper = $this->getMockBuilder('OCA\Polls\Db\ParticipationMapper')
->disableOriginalConstructor()
->getMock();
$participationTextMapper = $this->getMockBuilder('OCA\Polls\Db\ParticipationTextMapper')
->disableOriginalConstructor()
->getMock();
$textMapper = $this->getMockBuilder('OCA\Polls\Db\TextMapper')
$votesMapper = $this->getMockBuilder('OCA\Polls\Db\VotesMapper')
->disableOriginalConstructor()
->getMock();
@ -92,12 +86,10 @@ class PageControllerTest extends UnitTestCase {
$urlGenerator,
$this->userId,
$commentMapper,
$dateMapper,
$optionsMapper,
$eventMapper,
$notificationMapper,
$participationMapper,
$participationTextMapper,
$textMapper
$votesMapper
);
}

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

@ -25,18 +25,18 @@ namespace OCA\Polls\Tests\Unit\Db;
use OCA\Polls\Db\Event;
use OCA\Polls\Db\EventMapper;
use OCA\Polls\Db\Text;
use OCA\Polls\Db\TextMapper;
use OCA\Polls\Db\Options;
use OCA\Polls\Db\OptionsMapper;
use OCA\Polls\Tests\Unit\UnitTestCase;
use OCP\IDBConnection;
use League\FactoryMuffin\Faker\Facade as Faker;
class TextMapperTest extends UnitTestCase {
class OptionsMapperTest extends UnitTestCase {
/** @var IDBConnection */
private $con;
/** @var TextMapper */
private $textMapper;
/** @var OptionsMapper */
private $optionsMapper;
/** @var EventMapper */
private $eventMapper;
@ -46,52 +46,52 @@ class TextMapperTest extends UnitTestCase {
public function setUp() {
parent::setUp();
$this->con = \OC::$server->getDatabaseConnection();
$this->textMapper = new TextMapper($this->con);
$this->optionsMapper = new OptionsMapper($this->con);
$this->eventMapper = new EventMapper($this->con);
}
/**
* Create some fake data and persist them to the database.
*
* @return Text
* @return Options
*/
public function testCreate() {
/** @var Event $event */
$event = $this->fm->instance('OCA\Polls\Db\Event');
$this->assertInstanceOf(Event::class, $this->eventMapper->insert($event));
/** @var Text $text */
$text = $this->fm->instance('OCA\Polls\Db\Text');
$text->setPollId($event->getId());
$this->assertInstanceOf(Text::class, $this->textMapper->insert($text));
/** @var Options $options */
$options = $this->fm->instance('OCA\Polls\Db\Options');
$options->setPollId($event->getId());
$this->assertInstanceOf(Options::class, $this->optionsMapper->insert($options));
return $text;
return $options;
}
/**
* Update the previously created entry and persist the changes.
*
* @depends testCreate
* @param Text $text
* @return Text
* @param Options $options
* @return Options
*/
public function testUpdate(Text $text) {
$newText = Faker::paragraph();
$text->setText($newText());
$this->textMapper->update($text);
public function testUpdate(Options $options) {
$newPollOptionText = Faker::paragraph();
$options->setPollOptionText($newPollOptionText());
$this->optionsMapper->update($options);
return $text;
return $options;
}
/**
* Delete the previously created entries from the database.
*
* @depends testUpdate
* @param Text $text
* @param Options $options
*/
public function testDelete(Text $text) {
$event = $this->eventMapper->find($text->getPollId());
$this->textMapper->delete($text);
public function testDelete(Options $options) {
$event = $this->eventMapper->find($options->getPollId());
$this->optionsMapper->delete($options);
$this->eventMapper->delete($event);
}
}

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

@ -1,97 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2017 Kai Schröer <git@schroeer.co>
*
* @author Kai Schröer <git@schroeer.co>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Polls\Tests\Unit\Db;
use OCA\Polls\Db\Event;
use OCA\Polls\Db\EventMapper;
use OCA\Polls\Db\Participation;
use OCA\Polls\Db\ParticipationMapper;
use OCA\Polls\Tests\Unit\UnitTestCase;
use OCP\IDBConnection;
use League\FactoryMuffin\Faker\Facade as Faker;
class ParticipationMapperTest extends UnitTestCase {
/** @var IDBConnection */
private $con;
/** @var ParticipationMapper */
private $participationMapper;
/** @var EventMapper */
private $eventMapper;
/**
* {@inheritDoc}
*/
public function setUp() {
parent::setUp();
$this->con = \OC::$server->getDatabaseConnection();
$this->participationMapper = new ParticipationMapper($this->con);
$this->eventMapper = new EventMapper($this->con);
}
/**
* Create some fake data and persist them to the database.
*
* @return Participation
*/
public function testCreate() {
/** @var Event $event */
$event = $this->fm->instance('OCA\Polls\Db\Event');
$this->assertInstanceOf(Event::class, $this->eventMapper->insert($event));
/** @var Participation $participation */
$participation = $this->fm->instance('OCA\Polls\Db\Participation');
$participation->setPollId($event->getId());
$this->assertInstanceOf(Participation::class, $this->participationMapper->insert($participation));
return $participation;
}
/**
* Update the previously created entry and persist the changes.
*
* @depends testCreate
* @param Participation $participation
* @return Participation
*/
public function testUpdate(Participation $participation) {
$newDt = Faker::date('Y-m-d H:i:s');
$participation->setDt($newDt());
$this->participationMapper->update($participation);
return $participation;
}
/**
* Delete the previously created entries from the database.
*
* @depends testUpdate
* @param Participation $participation
*/
public function testDelete(Participation $participation) {
$event = $this->eventMapper->find($participation->getPollId());
$this->participationMapper->delete($participation);
$this->eventMapper->delete($event);
}
}

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

@ -1,97 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2017 Kai Schröer <git@schroeer.co>
*
* @author Kai Schröer <git@schroeer.co>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Polls\Tests\Unit\Db;
use OCA\Polls\Db\Event;
use OCA\Polls\Db\EventMapper;
use OCA\Polls\Db\ParticipationText;
use OCA\Polls\Db\ParticipationTextMapper;
use OCA\Polls\Tests\Unit\UnitTestCase;
use OCP\IDBConnection;
use League\FactoryMuffin\Faker\Facade as Faker;
class ParticipationTextMapperTest extends UnitTestCase {
/** @var IDBConnection */
private $con;
/** @var ParticipationTextMapper */
private $participationTextMapper;
/** @var EventMapper */
private $eventMapper;
/**
* {@inheritDoc}
*/
public function setUp() {
parent::setUp();
$this->con = \OC::$server->getDatabaseConnection();
$this->participationTextMapper = new ParticipationTextMapper($this->con);
$this->eventMapper = new EventMapper($this->con);
}
/**
* Create some fake data and persist them to the database.
*
* @return ParticipationText
*/
public function testCreate() {
/** @var Event $event */
$event = $this->fm->instance('OCA\Polls\Db\Event');
$this->assertInstanceOf(Event::class, $this->eventMapper->insert($event));
/** @var ParticipationText $participationText */
$participationText = $this->fm->instance('OCA\Polls\Db\ParticipationText');
$participationText->setPollId($event->getId());
$this->assertInstanceOf(ParticipationText::class, $this->participationTextMapper->insert($participationText));
return $participationText;
}
/**
* Update the previously created entry and persist the changes.
*
* @depends testCreate
* @param ParticipationText $participationText
* @return ParticipationText
*/
public function testUpdate(ParticipationText $participationText) {
$newText = Faker::paragraph();
$participationText->setText($newText());
$this->participationTextMapper->update($participationText);
return $participationText;
}
/**
* Delete the previously created entries from the database.
*
* @depends testUpdate
* @param ParticipationText $participationText
*/
public function testDelete(ParticipationText $participationText) {
$event = $this->eventMapper->find($participationText->getPollId());
$this->participationTextMapper->delete($participationText);
$this->eventMapper->delete($event);
}
}

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

@ -23,20 +23,20 @@
namespace OCA\Polls\Tests\Unit\Db;
use OCA\Polls\Db\Date;
use OCA\Polls\Db\DateMapper;
use OCA\Polls\Db\Event;
use OCA\Polls\Db\EventMapper;
use OCA\Polls\Db\Votes;
use OCA\Polls\Db\VotesMapper;
use OCA\Polls\Tests\Unit\UnitTestCase;
use OCP\IDBConnection;
use League\FactoryMuffin\Faker\Facade as Faker;
class DateMapperTest extends UnitTestCase {
class VotesMapperTest extends UnitTestCase {
/** @var IDBConnection */
private $con;
/** @var DateMapper */
private $dateMapper;
/** @var VotesMapper */
private $votesMapper;
/** @var EventMapper */
private $eventMapper;
@ -46,52 +46,54 @@ class DateMapperTest extends UnitTestCase {
public function setUp() {
parent::setUp();
$this->con = \OC::$server->getDatabaseConnection();
$this->dateMapper = new DateMapper($this->con);
$this->votesMapper = new VotesMapper($this->con);
$this->eventMapper = new EventMapper($this->con);
}
/**
* Create some fake data and persist them to the database.
*
* @return Date
* @return Votes
*/
public function testCreate() {
/** @var Event $event */
$event = $this->fm->instance('OCA\Polls\Db\Event');
$this->assertInstanceOf(Event::class, $this->eventMapper->insert($event));
/** @var Date $date */
$date = $this->fm->instance('OCA\Polls\Db\Date');
$date->setPollId($event->getId());
$this->assertInstanceOf(Date::class, $this->dateMapper->insert($date));
/** @var Votes $votes */
$votes = $this->fm->instance('OCA\Polls\Db\Votes');
$votes->setPollId($event->getId());
$votes->setVoteOptionId(1);
$this->assertInstanceOf(Votes::class, $this->votesMapper->insert($votes));
return $date;
return $votes;
}
/**
* Update the previously created entry and persist the changes.
*
* @depends testCreate
* @param Date $date
* @return Date
* @param Votes $votes
* @return Votes
*/
public function testUpdate(Date $date) {
$newDt = Faker::date('Y-m-d H:i:s');
$date->setDt($newDt());
$this->dateMapper->update($date);
public function testUpdate(Votes $votes) {
$newVoteOptionText = Faker::date('Y-m-d H:i:s');
$votes->setVoteOptionText($newVoteOptionText());
$this->votesMapper->update($votes);
return $date;
return $votes;
}
/**
* Delete the previously created entries from the database.
*
* @depends testUpdate
* @param Date $date
* @param Votes $votes
*/
public function testDelete(Date $date) {
$event = $this->eventMapper->find($date->getPollId());
$this->dateMapper->delete($date);
public function testDelete(Votes $votes) {
$event = $this->eventMapper->find($votes->getPollId());
$this->votesMapper->delete($votes);
$this->eventMapper->delete($event);
}
}

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

@ -28,9 +28,9 @@ use League\FactoryMuffin\Faker\Facade as Faker;
*/
$fm->define('OCA\Polls\Db\Comment')->setDefinitions([
'userId' => Faker::firstNameMale(),
'dt' => function () {
'dt' => function() {
$date = new DateTime('today');
return $date->format('Y-m-d H:i:s');
},
'comment' => Faker::text(256)
'comment' => Faker::text(255)
]);

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

@ -1,32 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2017 Kai Schröer <git@schroeer.co>
*
* @author Kai Schröer <git@schroeer.co>
*
* @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/>.
*
*/
/**
* General factory for the date model.
*/
$fm->define('OCA\Polls\Db\Date')->setDefinitions([
'dt' => function () {
$date = new DateTime('today');
return $date->format('Y-m-d H:i:s');
}
]);

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

@ -29,14 +29,14 @@ use League\FactoryMuffin\Faker\Facade as Faker;
$fm->define('OCA\Polls\Db\Event')->setDefinitions([
'type' => 0,
'title' => Faker::sentence(10),
'description' => Faker::text(256),
'description' => Faker::text(255),
'owner' => Faker::firstNameMale(),
'created' => function () {
'created' => function() {
$date = new DateTime('today');
return $date->format('Y-m-d H:i:s');
},
'access' => 'registered',
'expire' => function () {
'expire' => function() {
$date = new DateTime('tomorrow');
return $date->format('Y-m-d H:i:s');
},

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

@ -26,6 +26,6 @@ use League\FactoryMuffin\Faker\Facade as Faker;
/**
* General factory for the text model.
*/
$fm->define('OCA\Polls\Db\Text')->setDefinitions([
'text' => Faker::text(256)
$fm->define('OCA\Polls\Db\Options')->setDefinitions([
'pollOptionText' => Faker::text(255)
]);

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

@ -1,36 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2017 Kai Schröer <git@schroeer.co>
*
* @author Kai Schröer <git@schroeer.co>
*
* @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/>.
*
*/
use League\FactoryMuffin\Faker\Facade as Faker;
/**
* General factory for the participation model.
*/
$fm->define('OCA\Polls\Db\Participation')->setDefinitions([
'userId' => Faker::firstNameMale(),
'dt' => function () {
$date = new DateTime('today');
return $date->format('Y-m-d H:i:s');
},
'type' => 0
]);

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

@ -24,10 +24,10 @@
use League\FactoryMuffin\Faker\Facade as Faker;
/**
* General factory for the participation text model.
* General factory for the votes model.
*/
$fm->define('OCA\Polls\Db\ParticipationText')->setDefinitions([
'text' => Faker::text(256),
$fm->define('OCA\Polls\Db\Votes')->setDefinitions([
'voteOptionText' => Faker::text(255),
'userId' => Faker::firstNameMale(),
'type' => 0
'voteAnswer' => 'yes'
]);