vote module added, minor fixes

This commit is contained in:
Dro1d.Ru 2016-06-15 18:14:22 +05:00
parent f5180f1568
commit af24d129fd
25 changed files with 1508 additions and 5 deletions

32
assets/css/vote/vote.css Normal file
View file

@ -0,0 +1,32 @@
.vote-options-wrapper p.subject,
.vote-results-wrapper p.subject {
margin-bottom: 10px;
}
.vote-options-wrapper ul,
.vote-results-wrapper ul {
margin: 0px 4px 10px;
padding: 0px;
list-style-type: none;
}
.vote-options-wrapper ul li {
margin-bottom: 4px;
}
.vote-options-wrapper ul label {
font-weight: normal;
margin-left: 7px;
}
.vote-results-line {
height: 7px;
background: #ADADAD;
border-radius: 3px;
}
ul.vote-results {
margin: 0px;
}
ul.vote-results li {
margin-bottom: 10px;
}
ul.vote-results li .vote-result {
color: #7B6D7E;
font-weight: bold;
}

File diff suppressed because one or more lines are too long

172
assets/js/vote/dash.js Normal file
View file

@ -0,0 +1,172 @@
var dash_votes_install = function() {
var selected = desk_window_selected(this);
if (selected && typeof(selected.items)!="undefined" && selected.items.length>0) {
desk_window_request(this, url('vote/dash/install'),{'votes':selected.items});
}
};
var dash_votes_select = function() {
var selected = this.getSelectedContentItems();
if (selected && selected.length>0) {
this.disableItemsByProperty('typo','install');
for (var i=0; i<selected.length; i++) {
if (typeof(selected[i].inactive)!="undefined" && selected[i].inactive) {
this.enableItemsByProperty('typo','install');
break;
}
}
}
if (selected && selected.length && selected.length==1 && (typeof(this.info_last_item)=="undefined" || this.info_last_item!=selected[0].data || $(this.element).find('.vote-infobar').html().length==0)) {
this.info_last_item = selected[0].data;
$(this.element).find('.vote-infobar').html('');
try { window.clearTimeout(this.timer); } catch(err) {};
this.timer = window.setTimeout(this.bind(this,function(){
$(this.element).find('.vote-infobar').html('');
var selected = this.getSelectedContentItems();
if (!selected || !selected.length || selected.length!=1) return;
desk_post(url('vote/dash/info'),{'item':selected[0].data, 'token':token()}, this.bind(this, function(response){
if (response && response.length>0) {
$(this.element).find('.vote-infobar').append('<div style="cursor:default;padding:0px;margin:10px 0px 0px"><span class="glyphicon glyphicon-info-sign"></span> '+t('Information')+':</div>');
$(this.element).find('.vote-infobar').append('<div style="border-top:1px solid #B3B6D1;border-bottom:1px solid #EDEDF6;height:1px;padding:0px;margin:10px 0px"></div>');
for (var i=0; i<response.length; i++) {
$(this.element).find('.vote-infobar').append('<div style="font-weight:normal;padding:2px 0px;cursor:default;text-overflow:ellipsis;overflow:hidden" title="'+response[i].split('>').slice(-1)[0]+'">'+response[i]+'</div>');
}
}
}));
}),1000);
}
};
var dash_votes_load = function() {
for (var i=0; i<this.options.bodyItems.length; i++) {
if (typeof(this.options.bodyItems[i].inactive)!="undefined" && this.options.bodyItems[i].inactive) {
$(this.options.bodyItems[i].element).addClass('inactive');
}
}
$(this.element).find('.vote-infobar').html('');
};
var dash_votes_options = function() {
var selected = this.getSelectedContentItems();
if (selected && selected.length==1) {
var data = {
'data':desk_window_selected(this,1),
'reload': this.className,
'onClose':function(){
desk_window_reload_all(this.options.reload);
}
};
desk_call(dash_votes_options_wnd, null, data);
}
};
var dash_votes_add_option = function() {
desk_prompt(t('Enter text'), this.bind(this, function(txt){
desk_window_request(this, url('vote/dash/option'),{'content':txt, 'vote': this.options.data.items[0]});
}));
};
var dash_votes_edit_option = function() {
var selected = this.getSelectedContentItems();
if (selected && selected.length==1) {
desk_prompt(t('Enter text'), this.bind(this, function(txt){
desk_window_request(this, url('vote/dash/option'),{'content':txt, 'vote': this.options.data.items[0], 'option':selected[0].data});
}));
$('#zira-prompt-dialog input[name=modal-input]').val(selected[0].tooltip);
}
};
var dash_votes_delete_options = function() {
var selected = desk_window_selected(this);
if (selected && selected.items.length>0) {
desk_window_request(this, url('vote/dash/deloptions'), {'options':selected.items, 'vote': this.options.data.items[0]});
}
};
var dash_vote_options_drag = function() {
this.isContentDragging = false;
this.dragStartY = null;
this.dragStartItem = null;
this.dragOverItem = null;
this.dragReplaced = false;
this.dragImage = new Image();
this.dragImage.src=dash_votes_blank_src;
$(this.content).bind('dragstart',this.bind(this,function(e){
if (this.isDisabled()) return;
if (typeof(e.originalEvent.target)=="undefined") return;
if ($(e.originalEvent.target).parents('li').children('a').hasClass('inactive')) return;
this.isContentDragging = true;
this.dragStartY = e.originalEvent.pageY;
this.dragStartItem = $(e.originalEvent.target).parents('li').children('a').attr('id');
e.originalEvent.dataTransfer.setDragImage(this.dragImage,-10,0);
$(this.content).find('#'+this.dragStartItem).parent('li').css('opacity',.5);
for (var i=0; i<this.options.bodyItems.length; i++) {
this.options.bodyItems[i].is_dragged = false;
}
}));
$(this.content).bind('dragover',this.bind(this,function(e){
if (this.isDisabled()) return;
if (typeof(e.originalEvent.target)=="undefined" || !this.isContentDragging) return;
var item = $(e.originalEvent.target).parents('li').children('a');
if ($(item).length==0 || $(item).parents('#'+this.getId()).length==0) return;
if ($(item).hasClass('inactive') || $(item).parent('li').hasClass('tmp-drag-options-item')) return;
if (this.dragReplaced && $(item).attr('id') == this.dragStartItem) {
var startItem = this.findBodyItemByProperty('id',this.dragStartItem);
var endItem = this.findBodyItemByProperty('id',this.dragOverItem);
if (startItem && endItem && typeof(startItem.sort_order)!="undefined" && typeof(endItem.sort_order)!="undefined") {
var start_order = startItem.sort_order;
var end_order = endItem.sort_order;
startItem.sort_order = end_order;
endItem.sort_order = start_order;
startItem.is_dragged = true;
endItem.is_dragged = true;
}
this.dragOverItem = null;
this.dragStartY = e.originalEvent.pageY;
this.dragReplaced = false;
}
if (this.dragStartItem!=$(item).attr('id') && this.dragOverItem!=$(item).attr('id')) {
this.dragOverItem=$(item).attr('id');
var tmp = '<li class="tmp-drag-options-item"></li>';
if (e.originalEvent.pageY > this.dragStartY) {
$(this.content).find('#'+this.dragOverItem).parent('li').after(tmp);
} else {
$(this.content).find('#'+this.dragOverItem).parent('li').before(tmp);
}
$(this.content).find('li.tmp-drag-options-item').replaceWith($(this.content).find('#'+this.dragStartItem).parent('li'));
this.dragReplaced = true;
}
}));
$(this.content).bind('drop',this.bind(this,function(e){
if (this.isDisabled()) return;
var dragged = [];
var orders = [];
for (var i=0; i<this.options.bodyItems.length; i++) {
if (typeof(this.options.bodyItems[i].sort_order)!="undefined" && typeof(this.options.bodyItems[i].is_dragged)!="undefined" && this.options.bodyItems[i].is_dragged) {
dragged.push(this.options.bodyItems[i].data);
orders.push(this.options.bodyItems[i].sort_order);
}
}
if (dragged.length>1 && orders.length>1) {
desk_window_request(this, url('vote/dash/drag'),{'item':this.options.data.items[0], 'options':dragged,'orders':orders});
}
}));
$(this.content).bind('dragend',this.bind(this,function(e){
$(this.content).find('#'+this.dragStartItem).parent('li').css('opacity',1);
this.isContentDragging = false;
this.dragStartY = null;
this.dragStartItem = null;
this.dragOverItem = null;
this.dragReplaced = false;
$(this.content).find('li.tmp-drag-options-item').remove();
}));
};
var dash_votes_results = function() {
var selected = this.getSelectedContentItems();
if (selected && selected.length==1) {
desk_window_request(this, url('vote/dash/results'),{'item':selected[0].data}, this.bind(this, function(result){
desk_message(t('Vote results')+':<div style="color:black;padding:10px 16px">'+result.join('<br />')+'</div>');
}));
}
};

31
assets/js/vote/vote.js Normal file
View file

@ -0,0 +1,31 @@
(function($){
$(document).ready(function(){
$('.vote-options-wrapper .vote-submit').click(function(){
var vote_id = $(this).data('vote_id');
var url = $(this).data('url');
var token = $(this).data('token');
if (typeof(vote_id)=="undefined" || typeof(url)=="undefined" || typeof(token)=="undefined") return;
var opts = $(this).parent('.vote-options-wrapper').find('input:checked');
if ($(opts).length==0) return;
var o = [];
$(opts).each(function(){
o.push($(this).val());
});
$(this).attr('disabled','disabled');
$(this).parent('.vote-options-wrapper').append('<div class="zira-loader-wrapper"><span class="zira-loader"></span></div>');
$.post(url, {
'vote_id': vote_id,
'options': o,
'token': token
}, zira_bind(this, function(response){
$(this).parent('.vote-options-wrapper').children('ul').replaceWith(response);
$(this).parent('.vote-options-wrapper').children('.zira-loader-wrapper').remove();
$(this).remove();
}),'html');
});
});
})(jQuery);

View file

@ -26,6 +26,7 @@ class Emoji {
}
public function bootstrap() {
Zira\View::addDefaultAssets();
Zira\View::addStyle('emoji/emoji.css');
Zira\View::addScript('emoji/emoji.js');

5
languages/en/vote.php Normal file
View file

@ -0,0 +1,5 @@
<?php
return array(
'Activated %s widgets' => array('Activated %s widget', 'Activated %s widgets')
);

20
languages/ru/vote.php Normal file
View file

@ -0,0 +1,20 @@
<?php
return array(
'Vote' => 'Голосование',
'Votes' => 'Голосование',
'Let your website visitors to vote' => 'Дайте вашим пользователям возможность голосовать',
'Vote subject' => 'Тема голосования',
'New vote subject' => 'Новая тема для голосования',
'Subject' => 'Тема',
'Multiple selection' => 'Множественный выбор',
'Widget placeholder' => 'Расположение виджета',
'Activated %s widgets' => array('Активирован %s виджет', 'Активировано %s виджета', 'Активировано %s виджетов'),
'Vote options' => 'Опции для выбора',
'Add vote option' => 'Добавить опцию выбора',
'Enter text' => 'Введите текст',
'Votes: %s' => 'Голосов: %s',
'Vote!' => 'Голосовать!',
'Vote results' => 'Результаты голосования',
'Results' => 'Результаты'
);

View file

@ -0,0 +1,26 @@
<?php if (count($options)>0): ?>
<div class="vote-options-wrapper block">
<?php if ($is_sidebar): ?>
<h2><?php echo tm('Vote','vote') ?></h2>
<?php else: ?>
<div class="page-header">
<h2><?php echo tm('Vote','vote') ?></h2>
</div>
<?php endif; ?>
<p class="subject"><?php echo t(Zira\Helper::html($subject)) ?></p>
<ul>
<?php foreach($options as $option): ?>
<?php $id = 'vote-'.$vote_id.'-option-'.$option->id; ?>
<li>
<?php if ($multiple): ?>
<?php echo Zira\Form\Form::checkbox($token, 'vote_options', $option->id, array('id'=>$id)) ?>
<?php else: ?>
<?php echo Zira\Form\Form::radio($token, 'vote_options', $option->id, array('id'=>$id)) ?>
<?php endif; ?>
<?php echo '<label for="'.Zira\Helper::html($id).'">'.t(Zira\Helper::html($option->content)).'</label>' ?>
</li>
<?php endforeach; ?>
</ul>
<button class="btn btn-primary vote-submit" data-token="<?php echo Zira\Helper::html($token); ?>" data-vote_id="<?php echo Zira\Helper::html($vote_id); ?>" data-url="<?php echo Zira\Helper::url('vote/index/index'); ?>"><?php echo tm('Vote!','vote') ?></button>
</div>
<?php endif; ?>

View file

@ -0,0 +1,24 @@
<?php if (count($options)>0): ?>
<?php if (!$is_ajax): ?>
<div class="vote-results-wrapper block">
<?php if ($is_sidebar): ?>
<h2><?php echo tm('Vote','vote') ?></h2>
<?php else: ?>
<div class="page-header">
<h2><?php echo tm('Vote','vote') ?></h2>
</div>
<?php endif; ?>
<p class="subject"><?php echo t(Zira\Helper::html($subject)) ?></p>
<?php endif; ?>
<ul class="vote-results">
<?php foreach($options as $option): ?>
<li>
<?php echo '<div>'.t(Zira\Helper::html($option->content)).' &mdash; <span class="vote-result">'.Zira\Helper::html($option->count).'</span></div>' ?>
<div class="vote-results-line" style="width:<?php echo round(($option->count/$total)*100) ?>%"></div>
</li>
<?php endforeach; ?>
</ul>
<?php if (!$is_ajax): ?>
</div>
<?php endif; ?>
<?php endif; ?>

76
vote/controllers/dash.php Normal file
View file

@ -0,0 +1,76 @@
<?php
/**
* Zira project.
* index.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Controllers;
use Zira;
use Vote;
class Dash extends \Dash\Controller {
public function _before() {
parent::_before();
Zira\View::setAjax(true);
}
protected function getWindowModel() {
$window = new Vote\Windows\Votes();
return new Vote\Models\Votes($window);
}
public function install() {
if (Zira\Request::isPost()) {
$items = Zira\Request::post('votes');
$response = $this->getWindowModel()->install($items);
Zira\Page::render($response);
}
}
public function option() {
if (Zira\Request::isPost()) {
$vote = Zira\Request::post('vote');
$content = Zira\Request::post('content');
$option = Zira\Request::post('option');
$response = $this->getWindowModel()->option($vote, $content, $option);
Zira\Page::render($response);
}
}
public function deloptions() {
if (Zira\Request::isPost()) {
$vote = Zira\Request::post('vote');
$options = Zira\Request::post('options');
$response = $this->getWindowModel()->deleteOptions($vote, $options);
Zira\Page::render($response);
}
}
public function drag() {
if (Zira\Request::isPost()) {
$vote = Zira\Request::post('item');
$options = Zira\Request::post('options');
$orders = Zira\Request::post('orders');
$response = $this->getWindowModel()->drag($vote, $options, $orders);
Zira\Page::render($response);
}
}
public function info() {
if (Zira\Request::isPost()) {
$vote = Zira\Request::post('item');
$response = $this->getWindowModel()->info($vote);
Zira\Page::render($response);
}
}
public function results() {
if (Zira\Request::isPost()) {
$vote = Zira\Request::post('item');
$response = $this->getWindowModel()->results($vote);
Zira\Page::render($response);
}
}
}

102
vote/controllers/index.php Normal file
View file

@ -0,0 +1,102 @@
<?php
/**
* Zira project.
* index.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Controllers;
use Zira;
use Vote;
class Index extends Zira\Controller {
public function index() {
if (Zira\Request::isPost()) {
$vote_id = Zira\Request::post('vote_id');
$options = Zira\Request::post('options');
$token = Zira\Request::post('token');
if (empty($vote_id) || empty($options) || !is_array($options) || empty($token)) return;
if (!Zira\User::checkToken($token)) return;
$user_id = Zira\User::isAuthorized() ? Zira\User::getCurrent()->id : 0;
$anonymous_id = Zira\User::getAnonymousUserId();
if (empty($user_id) && empty($anonymous_id)) return;
$vote = new Vote\Models\Vote($vote_id);
if (!$vote->loaded()) return;
if (!$vote->multiple && count($options)>1) {
$options = array_slice($options, 0, 1);
}
$query = Vote\Models\Voteresult::getCollection()
->count()
->where('vote_id','=',$vote->id);
if (!empty($user_id)) {
$query->and_where();
$query->open_where();
$query->where('user_id','=',$user_id);
$query->or_where('anonymous_id','=',$anonymous_id);
$query->close_where();
} else if (!empty($anonymous_id)) {
$query->and_where('anonymous_id','=',$anonymous_id);
}
$co = $query->get('co');
if ($co==0) {
foreach($options as $option_id) {
$res = new Vote\Models\Voteresult();
$res->vote_id = $vote->id;
$res->option_id = $option_id;
$res->user_id = $user_id;
$res->anonymous_id = $anonymous_id;
$res->creation_date = date('Y-m-d H:i:s');
$res->save();
}
$vote->votes++;
$vote->save();
}
// results
$options = \Vote\Models\Voteoption::getCollection()
->select(array('id','content'))
->where('vote_id','=',$vote->id)
->order_by('sort_order', 'asc')
->get();
$results = Vote\Models\Voteresult::getCollection()
->select('option_id')
->count()
->where('vote_id','=',$vote->id)
->group_by('option_id')
->get();
$vals = array();
foreach($results as $result) {
$vals[$result->option_id] = $result->co;
}
$total = 0;
for($i=0; $i<count($options); $i++) {
if (array_key_exists($options[$i]->id, $vals)) {
$options[$i]->count = $vals[$options[$i]->id];
} else {
$options[$i]->count = 0;
}
$total += $options[$i]->count;
}
Zira\View::renderView(array(
'vote_id' => $vote->id,
'subject' => $vote->subject,
'options' => $options,
'total' => $total,
'is_ajax' => true
), 'vote/results');
}
}
}

70
vote/forms/vote.php Normal file
View file

@ -0,0 +1,70 @@
<?php
/**
* Zira project.
* vote.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Forms;
use Zira;
use Zira\Form;
use Zira\Locale;
class Vote extends Form
{
protected $_id = 'vote-vote-form';
protected $_label_class = 'col-sm-4 control-label';
protected $_input_wrap_class = 'col-sm-8';
protected $_input_offset_wrap_class = 'col-sm-offset-4 col-sm-8';
protected $_select_wrapper_class = 'col-sm-8';
protected $_checkbox_inline_label = false;
public function __construct()
{
parent::__construct($this->_id);
}
protected function _init()
{
$this->setRenderPanel(false);
$this->setFormClass('form-horizontal dash-window-form');
}
protected function _render()
{
$html = $this->open();
$id = $this->getValue('id');
if (empty($id)) {
$placeholders = Zira\Models\Widget::getPlaceholders();
$html .= $this->selectDropdown(Locale::t('Widget placeholder').'*','placeholder',$placeholders);
}
$html .= $this->input(Locale::tm('Subject','vote'), 'subject');
$html .= $this->checkbox(Locale::tm('Multiple selection','vote'), 'multiple', null, false);
$html .= $this->hidden('id');
$html .= $this->close();
return $html;
}
protected function _validate() {
$validator = $this->getValidator();
$validator->registerString('subject',null,255,false,Locale::t('Invalid value "%s"',Locale::tm('Subject','vote')));
$validator->registerNoTags('subject',Locale::t('Invalid value "%s"',Locale::tm('Subject','vote')));
$validator->registerUtf8('subject',Locale::t('Invalid value "%s"',Locale::tm('Subject','vote')));
$id = $this->getValue('id');
if (empty($id)) {
$validator->registerString('placeholder', null, 255, true, Locale::t('Invalid value "%s"',Locale::tm('Widget placeholder','vote')));
$validator->registerCustom(array(get_class(), 'checkPlaceholder'), 'placeholder', Locale::t('Invalid value "%s"',Locale::tm('Widget placeholder','vote')));
}
}
public static function checkPlaceholder($placeholder) {
$placeholders = Zira\Models\Widget::getPlaceholders();
return array_key_exists($placeholder, $placeholders);
}
}

49
vote/install/vote.php Normal file
View file

@ -0,0 +1,49 @@
<?php
/**
* Zira project.
* vote.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Install;
use Zira\Db\Table;
use Zira\Db\Field;
class Vote extends Table {
protected $_table = 'votes';
public function __construct() {
parent::__construct($this->_table);
}
public function getFields() {
return array(
'id' => Field::primary(),
'creator_id' => Field::int(true, true),
'subject' => Field::string(true),
'placeholder' => Field::string(true),
'multiple' => Field::tinyint(true, true, 0),
'votes' => Field::int(true, true, 0),
'date_created' => Field::datetime(true)
);
}
public function getKeys() {
return array(
);
}
public function getUnique() {
return array(
);
}
public function getDefaults() {
return array(
);
}
}

View file

@ -0,0 +1,46 @@
<?php
/**
* Zira project.
* voteoption.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Install;
use Zira\Db\Table;
use Zira\Db\Field;
class Voteoption extends Table {
protected $_table = 'vote_options';
public function __construct() {
parent::__construct($this->_table);
}
public function getFields() {
return array(
'id' => Field::primary(),
'vote_id' => Field::int(true, true),
'content' => Field::string(true),
'sort_order' => Field::int(true, false, 0)
);
}
public function getKeys() {
return array(
'vote' => array('vote_id', 'sort_order')
);
}
public function getUnique() {
return array(
);
}
public function getDefaults() {
return array(
);
}
}

View file

@ -0,0 +1,48 @@
<?php
/**
* Zira project.
* voteresult.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Install;
use Zira\Db\Table;
use Zira\Db\Field;
class Voteresult extends Table {
protected $_table = 'vote_results';
public function __construct() {
parent::__construct($this->_table);
}
public function getFields() {
return array(
'id' => Field::primary(),
'vote_id' => Field::int(true, true),
'option_id' => Field::int(true, true),
'user_id' => Field::int(true, true),
'anonymous_id' => Field::string(true),
'creation_date' => Field::datetime(true)
);
}
public function getKeys() {
return array(
'vote' => array('vote_id', 'option_id', 'user_id', 'anonymous_id')
);
}
public function getUnique() {
return array(
);
}
public function getDefaults() {
return array(
);
}
}

37
vote/models/vote.php Normal file
View file

@ -0,0 +1,37 @@
<?php
/**
* Zira project.
* vote.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Models;
use Zira;
use Zira\Orm;
class Vote extends Orm {
const WIDGET_CLASS = '\Vote\Widgets\Vote';
public static $table = 'votes';
public static $pk = 'id';
public static $alias = 'vot';
public static function getTable() {
return self::$table;
}
public static function getPk() {
return self::$pk;
}
public static function getAlias() {
return self::$alias;
}
public static function getReferences() {
return array(
);
}
}

View file

@ -0,0 +1,35 @@
<?php
/**
* Zira project.
* voteoption.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Models;
use Zira;
use Zira\Orm;
class Voteoption extends Orm {
public static $table = 'vote_options';
public static $pk = 'id';
public static $alias = 'vot_opt';
public static function getTable() {
return self::$table;
}
public static function getPk() {
return self::$pk;
}
public static function getAlias() {
return self::$alias;
}
public static function getReferences() {
return array(
Vote::getClass() => 'vote_id'
);
}
}

View file

@ -0,0 +1,35 @@
<?php
/**
* Zira project.
* voteresult.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Models;
use Zira;
use Zira\Orm;
class Voteresult extends Orm {
public static $table = 'vote_results';
public static $pk = 'id';
public static $alias = 'vot_rst';
public static function getTable() {
return self::$table;
}
public static function getPk() {
return self::$pk;
}
public static function getAlias() {
return self::$alias;
}
public static function getReferences() {
return array(
);
}
}

261
vote/models/votes.php Normal file
View file

@ -0,0 +1,261 @@
<?php
/**
* Zira project.
* votes.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Models;
use Zira;
use Dash;
use Vote;
use Zira\Permission;
class Votes extends Dash\Models\Model {
public function save($data) {
if (!Permission::check(Permission::TO_CHANGE_OPTIONS)) {
return array('error' => Zira\Locale::t('Permission denied'));
}
$form = new Vote\Forms\Vote();
if ($form->isValid()) {
$id = (int)$form->getValue('id');
if ($id) {
$vote = new Vote\Models\Vote($id);
if (!$vote->loaded()) return array('error' => Zira\Locale::t('An error occurred'));
} else {
$vote = new Vote\Models\Vote();
$vote->placeholder = $form->getValue('placeholder');
$vote->creator_id = Zira\User::getCurrent()->id;
$vote->date_created = date('Y-m-d H:i:s');
$vote->votes = 0;
}
$vote->subject = $form->getValue('subject');
$vote->multiple = (int)$form->getValue('multiple');
$vote->save();
if (empty($id)) {
$max_order = Zira\Models\Widget::getCollection()->max('sort_order')->get('mx');
$widget = new Zira\Models\Widget();
$widget->name = Vote\Models\Vote::WIDGET_CLASS;
$widget->module = 'vote';
$widget->placeholder = $form->getValue('placeholder');
$widget->params = $vote->id;
$widget->category_id = null;
$widget->sort_order = ++$max_order;
$widget->active = Zira\Models\Widget::STATUS_ACTIVE;
$widget->save();
}
Zira\Cache::clear();
return array('message'=>Zira\Locale::t('Successfully saved'), 'close'=>true);
} else {
return array('error'=>$form->getError());
}
}
public function install($votes) {
if (empty($votes) || !is_array($votes)) return array('error' => Zira\Locale::t('An error occurred'));
if (!Permission::check(Permission::TO_CHANGE_LAYOUT)) {
return array('error'=>Zira\Locale::t('Permission denied'));
}
$widgets = array();
$rows = Zira\Models\Widget::getCollection()
->where('name','=',Vote\Models\Vote::WIDGET_CLASS)
->get()
;
foreach($rows as $row) {
if (!is_numeric($row->params)) continue;
$widgets[] = $row->params;
}
$max_order = Zira\Models\Widget::getCollection()->max('sort_order')->get('mx');
$co=0;
foreach($votes as $vote_id) {
$vote = new Vote\Models\Vote(intval($vote_id));
if (!$vote->loaded()) continue;
if (in_array($vote->id,$widgets)) continue;
$widget = new Zira\Models\Widget();
$widget->name = Vote\Models\Vote::WIDGET_CLASS;
$widget->module = 'vote';
$widget->placeholder = $vote->placeholder;
$widget->params = $vote->id;
$widget->category_id = null;
$widget->sort_order = ++$max_order;
$widget->active = Zira\Models\Widget::STATUS_ACTIVE;
$widget->save();
$co++;
}
Zira\Cache::clear();
return array('message' => Zira\Locale::t('Activated %s widgets', $co), 'reload'=>$this->getJSClassName());
}
public function delete($data) {
if (empty($data) || !is_array($data)) return array('error' => Zira\Locale::t('An error occurred'));
if (!Permission::check(Permission::TO_CHANGE_LAYOUT)) {
return array('error'=>Zira\Locale::t('Permission denied'));
}
foreach($data as $vote_id) {
$vote = new Vote\Models\Vote($vote_id);
if (!$vote->loaded()) {
return array('error' => Zira\Locale::t('An error occurred'));
};
$vote->delete();
Zira\Models\Widget::getCollection()
->where('name','=',Vote\Models\Vote::WIDGET_CLASS)
->and_where('params','=',$vote_id)
->delete()
->execute();
Vote\Models\Voteoption::getCollection()
->where('vote_id','=',$vote_id)
->delete()
->execute();
Vote\Models\Voteresult::getCollection()
->where('vote_id','=',$vote_id)
->delete()
->execute();
}
Zira\Cache::clear();
return array('reload' => $this->getJSClassName());
}
public function option($vote_id, $content, $option_id = null) {
if (empty($vote_id) || empty($content)) return array('error' => Zira\Locale::t('An error occurred'));
if (!Permission::check(Permission::TO_CHANGE_LAYOUT)) {
return array('error'=>Zira\Locale::t('Permission denied'));
}
$vote = new Vote\Models\Vote(intval($vote_id));
if (!$vote->loaded()) return array('error' => Zira\Locale::t('An error occurred'));
if ($option_id === null) {
$max_order = Vote\Models\Voteoption::getCollection()->max('sort_order')->get('mx');
$option = new Vote\Models\Voteoption();
$option->vote_id = $vote->id;
$option->sort_order = ++$max_order;
} else {
$option = new Vote\Models\Voteoption($option_id);
if (!$option->loaded()) return array('error' => Zira\Locale::t('An error occurred'));
}
$option->content = strip_tags($content);
$option->save();
return array('reload' => Dash\Dash::getInstance()->getWindowJSName(Vote\Windows\Options::getClass()));
}
public function drag($vote_id, $options, $orders) {
if (empty($vote_id)) return array('error' => Zira\Locale::t('An error occurred'));
if (empty($options) || !is_array($options) || count($options)<2 || empty($orders) || !is_array($orders) || count($orders)<2 || count($options)!=count($orders)) {
return array('error' => Zira\Locale::t('An error occurred'));
}
if (!Permission::check(Permission::TO_CHANGE_LAYOUT)) {
return array('error'=>Zira\Locale::t('Permission denied'));
}
$_options = array();
$_orders = array();
foreach($options as $id) {
$_option = new Vote\Models\Voteoption($id);
if (!$_option->loaded() || $_option->vote_id != $vote_id) {
return array('error' => Zira\Locale::t('An error occurred'));
}
$_options []= $_option;
$_orders []= $_option->sort_order;
}
foreach($orders as $order) {
if (!in_array($order, $_orders)) {
return array('error' => Zira\Locale::t('An error occurred'));
}
}
foreach($_options as $index=>$option) {
$option->sort_order = intval($orders[$index]);
$option->save();
}
return array('reload'=>Dash\Dash::getInstance()->getWindowJSName(Vote\Windows\Options::getClass()));
}
public function deleteOptions($vote_id, $options) {
if (empty($vote_id)) return array('error' => Zira\Locale::t('An error occurred'));
if (empty($options) || !is_array($options)) return array('error' => Zira\Locale::t('An error occurred'));
if (!Permission::check(Permission::TO_CHANGE_LAYOUT)) {
return array('error'=>Zira\Locale::t('Permission denied'));
}
foreach($options as $option_id) {
$option = new Vote\Models\Voteoption($option_id);
if (!$option->loaded() || $option->vote_id != $vote_id) {
return array('error' => Zira\Locale::t('An error occurred'));
};
$option->delete();
}
return array('reload' => Dash\Dash::getInstance()->getWindowJSName(Vote\Windows\Options::getClass()));
}
public function info($id) {
if (!Permission::check(Permission::TO_CHANGE_LAYOUT)) {
return array();
}
$info = array();
$vote = new Vote\Models\Vote($id);
if (!$vote->loaded()) return array();
$info[] = '<span class="glyphicon glyphicon-tag"></span> ' . Zira\Helper::html($vote->subject);
$info[] = '<span class="glyphicon glyphicon-thumbs-up"></span> ' . Zira\Locale::tm('Votes: %s', 'vote', Zira\Helper::html($vote->votes));
$info[] = '<span class="glyphicon glyphicon-time"></span> ' . date(Zira\Config::get('date_format'), strtotime($vote->date_created));
return $info;
}
public function results($vote_id) {
$options = \Vote\Models\Voteoption::getCollection()
->select(array('id','content'))
->where('vote_id','=',$vote_id)
->order_by('sort_order', 'asc')
->get();
$results = Vote\Models\Voteresult::getCollection()
->select('option_id')
->count()
->where('vote_id','=',$vote_id)
->group_by('option_id')
->get();
$vals = array();
foreach($results as $result) {
$vals[$result->option_id] = $result->co;
}
$return = array();
foreach($options as $option) {
if (array_key_exists($option->id, $vals)) {
$count = $vals[$option->id];
} else {
$count = 0;
}
$return []= '&bull;&nbsp;'.$option->content.' &mdash; '.$count;
}
return $return;
}
}

5
vote/module.meta Normal file
View file

@ -0,0 +1,5 @@
[meta]
name = Vote
description = Let your website visitors to vote
author = Zira
version = 1.0

41
vote/vote.php Normal file
View file

@ -0,0 +1,41 @@
<?php
/**
* Zira project.
* vote.php
* (c)2016 http://dro1d.ru
*/
namespace Vote;
use Zira;
use Dash;
class Vote {
private static $_instance;
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new self();
}
return self::$_instance;
}
public function beforeDispatch() {
Zira\Assets::registerCSSAsset('vote/vote.css');
Zira\Assets::registerJSAsset('vote/vote.js');
}
public function bootstrap() {
Zira\View::addDefaultAssets();
Zira\View::addStyle('vote/vote.css');
Zira\View::addScript('vote/vote.js');
if (ENABLE_CONFIG_DATABASE && Dash\Dash::getInstance()->isPanelEnabled() && Zira\Permission::check(Zira\Permission::TO_ACCESS_DASHBOARD) && Zira\Permission::check(Zira\Permission::TO_CHANGE_OPTIONS)) {
Dash\Dash::getInstance()->addPanelModulesGroupItem('glyphicon glyphicon-stats', Zira\Locale::tm('Votes', 'vote'), null, 'votesWindow()');
Dash\Dash::getInstance()->registerModuleWindowClass('votesWindow', 'Vote\Windows\Votes', 'Vote\Models\Votes');
Dash\Dash::getInstance()->registerModuleWindowClass('voteWindow', 'Vote\Windows\Vote', 'Vote\Models\Votes');
Dash\Dash::getInstance()->registerModuleWindowClass('voteOptionsWindow', 'Vote\Windows\Options', 'Vote\Models\Votes');
}
}
}

127
vote/widgets/vote.php Normal file
View file

@ -0,0 +1,127 @@
<?php
/**
* Zira project.
* vote.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Widgets;
use Zira;
class Vote extends Zira\Widget {
protected $_title = 'Vote';
protected static $_titles;
protected function _init() {
$this->setDynamic(true);
$this->setCaching(false);
$this->setOrder(0);
$this->setPlaceholder(Zira\View::VAR_CONTENT_BOTTOM);
}
protected function getTitles() {
if (self::$_titles===null) {
self::$_titles = array();
$rows = \Vote\Models\Vote::getCollection()->get();
foreach($rows as $row) {
self::$_titles[$row->id] = $row->subject;
}
}
return self::$_titles;
}
public function getTitle() {
$id = $this->getData();
if (is_numeric($id)) {
$titles = $this->getTitles();
if (empty($titles) || !array_key_exists($this->getData(), $titles)) return parent::getTitle();
return Zira\Locale::t('Vote') . ' - ' . $titles[$id];
} else {
return parent::getTitle();
}
}
protected function _render() {
$id = $this->getData();
$user_id = Zira\User::isAuthorized() ? Zira\User::getCurrent()->id : 0;
$anonymous_id = Zira\User::getAnonymousUserId();
if (empty($user_id) && empty($anonymous_id)) return;
$options = \Vote\Models\Voteoption::getCollection()
->select(array('id'=>'id','content'=>'content'))
->join(\Vote\Models\Vote::getClass(), array('vote_id'=>'id','subject'=>'subject', 'multiple'=>'multiple'))
->where('vote_id','=',$id)
->order_by('sort_order', 'asc')
->get();
if (!$options || count($options)==0) return;
$vote_id = $options[0]->vote_id;
$subject = $options[0]->subject;
$multiple = $options[0]->multiple;
$is_sidebar = $this->getPlaceholder() == Zira\View::VAR_SIDEBAR_LEFT || $this->getPlaceholder() == Zira\View::VAR_SIDEBAR_RIGHT;
$query = \Vote\Models\Voteresult::getCollection()
->count()
->where('vote_id','=',$id);
if (!empty($user_id)) {
$query->and_where();
$query->open_where();
$query->where('user_id','=',$user_id);
$query->or_where('anonymous_id','=',$anonymous_id);
$query->close_where();
} else if (!empty($anonymous_id)) {
$query->and_where('anonymous_id','=',$anonymous_id);
}
$co = $query->get('co');
if ($co==0) {
Zira\View::renderView(array(
'token' => Zira\User::getToken(),
'vote_id' => $vote_id,
'subject' => $subject,
'multiple' => $multiple,
'options' => $options,
'is_sidebar' => $is_sidebar
), 'vote/options');
} else {
// results
$results = \Vote\Models\Voteresult::getCollection()
->select('option_id')
->count()
->where('vote_id', '=', $id)
->group_by('option_id')
->get();
$vals = array();
foreach ($results as $result) {
$vals[$result->option_id] = $result->co;
}
$total = 0;
for ($i = 0; $i < count($options); $i++) {
if (array_key_exists($options[$i]->id, $vals)) {
$options[$i]->count = $vals[$options[$i]->id];
} else {
$options[$i]->count = 0;
}
$total += $options[$i]->count;
}
Zira\View::renderView(array(
'vote_id' => $vote_id,
'subject' => $subject,
'options' => $options,
'total' => $total,
'is_ajax' => false,
'is_sidebar' => $is_sidebar
), 'vote/results');
}
}
}

86
vote/windows/options.php Normal file
View file

@ -0,0 +1,86 @@
<?php
/**
* Zira project.
* options.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Windows;
use Dash;
use Zira;
use Zira\Permission;
class Options extends Dash\WIndows\Window {
protected static $_icon_class = 'glyphicon glyphicon-th-list';
protected static $_title = 'Vote options';
public $item;
public function init() {
$this->setIconClass(self::$_icon_class);
$this->setTitle(Zira\Locale::t(self::$_title));
$this->setSidebarEnabled(false);
$this->setBodyViewListVertical(true);
$this->addDefaultMenuDropdownItem(
$this->createMenuDropdownItem(Zira\Locale::t($this->_create_action_text), 'glyphicon glyphicon-file', 'desk_call(dash_votes_add_option, this);', 'create', false)
);
$this->addDefaultContextMenuItem(
$this->createContextMenuItem(Zira\Locale::t($this->_create_action_text), 'glyphicon glyphicon-file', 'desk_call(dash_votes_add_option, this);', 'create', false)
);
$this->setOnEditItemJSCallback(
$this->createJSCallback('desk_call(dash_votes_edit_option, this)')
);
$this->setOnDeleteItemsJSCallback(
$this->createJSCallback('desk_call(dash_votes_delete_options, this)')
);
}
public function create() {
$this->addDefaultToolbarItem(
$this->createToolbarButton(Zira\Locale::tm('Add vote option', 'vote'), Zira\Locale::tm('Add option', 'vote'), 'glyphicon glyphicon-plus-sign', 'desk_call(dash_votes_add_option, this)', 'create')
);
$this->setOnOpenJSCallback(
$this->createJSCallback(
'desk_call(dash_vote_options_drag, this);'
)
);
$this->addStrings(array(
'Enter text'
));
$this->addVariables(array(
'dash_votes_blank_src' => Zira\Helper::imgUrl('blank.png')
));
$this->setData(array(
'items' => array($this->item)
));
}
public function load() {
if (!empty($this->item)) $this->item=intval($this->item);
else return array('error' => Zira\Locale::t('An error occurred'));
if (!Permission::check(Permission::TO_CHANGE_LAYOUT)) {
return array('error' => Zira\Locale::t('Permission denied'));
}
$vote = new \Vote\Models\Vote($this->item);
if (!$vote->loaded()) return array('error' => Zira\Locale::t('An error occurred'));
$options = \Vote\Models\Voteoption::getCollection()
->where('vote_id','=',$vote->id)
->order_by('sort_order', 'asc')
->get();
$items = array();
foreach ($options as $option) {
$items[]=$this->createBodyItem($option->content, $option->content, Zira\Helper::imgUrl('drag.png'), $option->id, null, false, array('sort_order'=>$option->sort_order));
}
$this->setBodyItems($items);
$this->setTitle(Zira\Locale::t(self::$_title).' - '.$vote->subject);
}
}

57
vote/windows/vote.php Normal file
View file

@ -0,0 +1,57 @@
<?php
/**
* Zira project.
* vote.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Windows;
use Zira;
use Dash;
use Zira\Permission;
class Vote extends Dash\Windows\Window {
protected static $_icon_class = 'glyphicon glyphicon-list-alt';
protected static $_title = 'Vote subject';
public $item;
public function init() {
$this->setIconClass(self::$_icon_class);
$this->setTitle(Zira\Locale::t(self::$_title));
$this->setSidebarEnabled(false);
$this->setSaveActionEnabled(true);
}
public function create() {
$this->setOnLoadJSCallback(
$this->createJSCallback(
'desk_window_form_init(this);'
)
);
}
public function load() {
if (!empty($this->item)) $this->item=intval($this->item);
if (!Permission::check(Permission::TO_CHANGE_OPTIONS)) {
return array('error' => Zira\Locale::t('Permission denied'));
}
$form = new \Vote\Forms\Vote();
if (!empty($this->item)) {
$vote = new \Vote\Models\Vote($this->item);
if (!$vote->loaded()) return array('error' => Zira\Locale::t('An error occurred'));
$form->setValues($vote->toArray());
$this->setTitle(Zira\Locale::tm(self::$_title,'vote').' - '.$vote->subject);
} else {
$form->setValues(array(
'placeholder' => Zira\View::VAR_CONTENT_BOTTOM
));
$this->setTitle(Zira\Locale::tm('New vote subject','vote'));
}
$this->setBodyContent($form);
}
}

114
vote/windows/votes.php Normal file
View file

@ -0,0 +1,114 @@
<?php
/**
* Zira project.
* votes.php
* (c)2016 http://dro1d.ru
*/
namespace Vote\Windows;
use Dash;
use Zira;
use Vote;
use Zira\Permission;
class Votes extends Dash\WIndows\Window {
protected static $_icon_class = 'glyphicon glyphicon-stats';
protected static $_title = 'Votes';
public function init() {
$this->setIconClass(self::$_icon_class);
$this->setTitle(Zira\Locale::t(self::$_title));
$this->setViewSwitcherEnabled(true);
$this->setSelectionLinksEnabled(true);
$this->setBodyViewListVertical(true);
$this->setCreateActionWindowClass(Vote\Windows\Vote::getClass());
$this->setEditActionWindowClass(Vote\Windows\Vote::getClass());
$this->setDeleteActionEnabled(true);
}
public function create() {
$this->addDefaultSidebarItem(
$this->createSidebarItem(Zira\Locale::tm('Results','vote'), 'glyphicon glyphicon-stats', 'desk_call(dash_votes_results, this);', 'edit', true, array('typo'=>'results'))
);
$this->addDefaultMenuDropdownItem(
$this->createMenuDropdownSeparator()
);
$this->addDefaultMenuDropdownItem(
$this->createMenuDropdownItem(Zira\Locale::tm('Vote options','vote'), 'glyphicon glyphicon-th-list', 'desk_call(dash_votes_options, this);', 'edit', true, array('typo'=>'options'))
);
$this->addDefaultMenuDropdownItem(
$this->createMenuDropdownItem(Zira\Locale::tm('Vote results','vote'), 'glyphicon glyphicon-stats', 'desk_call(dash_votes_results, this);', 'edit', true, array('typo'=>'results'))
);
$this->addDefaultMenuDropdownItem(
$this->createMenuDropdownSeparator()
);
$this->addDefaultMenuDropdownItem(
$this->createMenuDropdownItem(Zira\Locale::t('Create widget'), 'glyphicon glyphicon-modal-window', 'desk_call(dash_votes_install, this);', 'delete', true, array('typo'=>'install'))
);
$this->addDefaultContextMenuItem(
$this->createContextMenuSeparator()
);
$this->addDefaultContextMenuItem(
$this->createContextMenuItem(Zira\Locale::tm('Vote options','vote'), 'glyphicon glyphicon-th-list', 'desk_call(dash_votes_options, this);', 'edit', true, array('typo'=>'options'))
);
$this->addDefaultContextMenuItem(
$this->createContextMenuItem(Zira\Locale::tm('Vote results','vote'), 'glyphicon glyphicon-stats', 'desk_call(dash_votes_results, this);', 'edit', true, array('typo'=>'results'))
);
$this->addDefaultContextMenuItem(
$this->createContextMenuSeparator()
);
$this->addDefaultContextMenuItem(
$this->createContextMenuItem(Zira\Locale::t('Create widget'), 'glyphicon glyphicon-modal-window', 'desk_call(dash_votes_install, this);', 'delete', true, array('typo'=>'install'))
);
$this->setSidebarContent('<div class="vote-infobar" style="white-space:nowrap;text-overflow: ellipsis;width:100%;overflow:hidden"></div>');
$this->setOnSelectJSCallback(
$this->createJSCallback(
'desk_call(dash_votes_select, this);'
)
);
$this->addDefaultOnLoadScript('desk_call(dash_votes_load, this);');
$this->addStrings(array(
'Vote results'
));
$this->addVariables(array(
'dash_votes_options_wnd' => Dash\Dash::getInstance()->getWindowJSName(Options::getClass())
));
$this->includeJS('vote/dash');
}
public function load() {
if (!Permission::check(Permission::TO_CHANGE_LAYOUT)) {
$this->setBodyItems(array());
return array('error'=>Zira\Locale::t('Permission denied'));
}
$widgets = array();
$rows = Zira\Models\Widget::getCollection()
->where('name','=',Vote\Models\Vote::WIDGET_CLASS)
->get()
;
foreach($rows as $row) {
if (!is_numeric($row->params)) continue;
$widgets[] = $row->params;
}
$votes = Vote\Models\Vote::getCollection()->get();
$items = array();
foreach($votes as $vote) {
$items[]=$this->createBodyFileItem($vote->subject, $vote->subject, $vote->id, 'desk_call(dash_votes_options,this);', false, array('type'=>'txt','inactive'=>in_array($vote->id,$widgets) ? 0 : 1));
}
$this->setBodyItems($items);
}
}