From 6b49958396e25f32dbbdb33f6ade0e8f14eca51c Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Tue, 19 Oct 2010 12:56:23 +0000 Subject: [PATCH] Fixed missing display of event url; Fixed XHTML validation error in calendar; Added portal block; --- .../adm/style/portal/acp_portal_calendar.html | 2 +- .../en/mods/portal/portal_poll_module.php | 56 ++ root/portal/modules/portal_calendar.php | 5 +- root/portal/modules/portal_poll.php | 892 ++++++++++++++++++ .../portal/modules/main_menu_side.html | 2 +- .../template/portal/modules/poll_center.html | 66 ++ .../template/portal/modules/poll_side.html | 65 ++ .../theme/images/portal/portal_poll.png | Bin 0 -> 3593 bytes 8 files changed, 1084 insertions(+), 4 deletions(-) create mode 100644 root/language/en/mods/portal/portal_poll_module.php create mode 100644 root/portal/modules/portal_poll.php create mode 100644 root/styles/prosilver/template/portal/modules/poll_center.html create mode 100644 root/styles/prosilver/template/portal/modules/poll_side.html create mode 100644 root/styles/prosilver/theme/images/portal/portal_poll.png diff --git a/root/adm/style/portal/acp_portal_calendar.html b/root/adm/style/portal/acp_portal_calendar.html index 8d8e9a62..ff9ea78c 100644 --- a/root/adm/style/portal/acp_portal_calendar.html +++ b/root/adm/style/portal/acp_portal_calendar.html @@ -151,7 +151,7 @@ {events.EVENT_TITLE} {events.EVENT_DESC} {L_EVENT_START}: {events.EVENT_START} | {L_EVENT_END}: {events.EVENT_END}{L_EVENT_TIME}: {events.EVENT_START} | {L_EVENT_ALL_DAY} - {events.LINK_URL} + {events.EVENT_URL} {ICON_EDIT} {ICON_DELETE} diff --git a/root/language/en/mods/portal/portal_poll_module.php b/root/language/en/mods/portal/portal_poll_module.php new file mode 100644 index 00000000..09da53e6 --- /dev/null +++ b/root/language/en/mods/portal/portal_poll_module.php @@ -0,0 +1,56 @@ + 'Poll', + 'LATEST_POLLS' => 'Latest Polls', + 'NO_OPTIONS' => 'This poll has no available options.', + 'NO_POLL' => 'No polls available', + 'RETURN_PORTAL' => '%sReturn to the portal%s', + + // ACP + 'ACP_PORTAL_POLLS_SETTINGS' => 'Poll settings', + 'ACP_PORTAL_POLLS_SETTINGS_EXP' => 'This is where you customize the poll block.', + 'PORTAL_POLL_TOPIC' => 'Display poll blocks', + 'PORTAL_POLL_TOPIC_EXP' => 'Display this block on the portal.', + 'PORTAL_POLL_TOPIC_ID' => 'Poll forum(s)', + 'PORTAL_POLL_TOPIC_ID_EXP' => 'The forum(s) from which the polls should be displayed. If "Exclude forums" is set to "Yes", select the forums you want to exclude.
If "Exclude forums" is set to "No" select the forums you want to see.
Select/Deselect multiple forums by holding CTRL and clicking.', + 'PORTAL_POLL_EXCLUDE_ID' => 'Exclude Forums', + 'PORTAL_POLL_EXCLUDE_ID_EXP' => 'Select "Yes" if you want to exlude the selected forums from the polls block, and "No" if you only want to see the polls from the selected forums in the polls block.', + 'PORTAL_POLL_LIMIT' => 'Poll display limit', + 'PORTAL_POLL_LIMIT_EXP' => 'The number of polls you would like to display on the portal page.', + 'PORTAL_POLL_ALLOW_VOTE' => 'Allow voting', + 'PORTAL_POLL_ALLOW_VOTE_EXP' => 'Allow users with the required permissions to vote from the portal page.', + 'PORTAL_POLL_HIDE' => 'Hide expired polls?', +)); + +?> \ No newline at end of file diff --git a/root/portal/modules/portal_calendar.php b/root/portal/modules/portal_calendar.php index ad97786a..8da4d9ef 100644 --- a/root/portal/modules/portal_calendar.php +++ b/root/portal/modules/portal_calendar.php @@ -184,7 +184,7 @@ class portal_calendar_module if(($cur_event['start_time'] <= $today_timestamp && $cur_event['end_time'] <= $cur_event['start_time']) || ($cur_event['start_time'] <= $today_timestamp && $cur_event['end_time'] >= $today_timestamp)) { $template->assign_block_vars('cur_events', array( - 'EVENT_URL' => (isset($cur_event['url']) && $cur_event['url'] != '') ? $this->validate_url($cur_event['url']) : '', + 'EVENT_URL' => (isset($cur_event['url']) && $cur_event['url'] != '') ? $this->validate_url(str_replace('&', '&', $cur_event['url'])) : '', 'EVENT_TITLE' => $cur_event['title'], 'START_TIME' => $user->format_date($cur_event['start_time'], 'j. M Y, H:i'), 'END_TIME' => $user->format_date($cur_event['end_time'], 'j. M Y, H:i'), @@ -195,7 +195,7 @@ class portal_calendar_module else { $template->assign_block_vars('upcoming_events', array( - 'EVENT_URL' => (isset($cur_event['url']) && $cur_event['url'] != '') ? $this->validate_url($cur_event['url']) : '', + 'EVENT_URL' => (isset($cur_event['url']) && $cur_event['url'] != '') ? $this->validate_url(str_replace('&', '&', $cur_event['url'])) : '', 'EVENT_TITLE' => $cur_event['title'], 'CUR_EVENT' => ($cur_event['start_time'] <= $today_timestamp && $cur_event['end_time'] >= $today_timestamp) ? true : false, 'START_TIME' => $user->format_date($cur_event['start_time'], 'j. M Y, H:i'), @@ -500,6 +500,7 @@ class portal_calendar_module 'EVENT_URL' => ($action != 'add') ? str_replace('&', '&', $events[$i]['url']) : '', 'EVENT_START' => ($action != 'add') ? $user->format_date($events[$i]['start_time'], 'j. M Y, H:i') : '', 'EVENT_END' => ($action != 'add' && ($events[$i]['start_time'] - $events[$i]['end_time']) != 1) ? $user->format_date($events[$i]['end_time'], 'j. M Y, H:i') : '', + 'EVENT_URL' => ($action != 'add' && isset($events[$i]['url']) && !empty($events[$i]['url'])) ? $events[$i]['url'] : '', 'U_EDIT' => $u_action . '&action=edit&id=' . $i, 'U_DELETE' => $u_action . '&action=delete&id=' . $i, 'EVENT_ALL_DAY' => ($events[$i]['end_time'] - $events[$i]['start_time']) ? true : false, diff --git a/root/portal/modules/portal_poll.php b/root/portal/modules/portal_poll.php new file mode 100644 index 00000000..a67431ca --- /dev/null +++ b/root/portal/modules/portal_poll.php @@ -0,0 +1,892 @@ +lang}/mods/portal/" + */ + var $language = 'portal_poll_module'; + + /** + * custom acp template + * file must be in "adm/style/portal/" + */ + var $custom_acp_tpl = ''; + + function get_template_center($module_id) + { + global $config, $template, $db, $user, $auth, $phpbb_root_path, $phpEx; + + + $user->add_lang('viewtopic'); + + $view = request_var('view', ''); + $update = request_var('update', false); + $poll_view = request_var('polls', ''); + + $poll_view_ar = (strpos($poll_view, ',') !== FALSE) ? explode(',', $poll_view) : (($poll_view != '') ? array($poll_view) : array()); + + if ($update && $config['board3_poll_allow_vote_' . $module_id]) + { + $up_topic_id = request_var('t', 0); + $up_forum_id = request_var('f', 0); + $voted_id = request_var('vote_id', array('' => 0)); + + $cur_voted_id = array(); + if ($user->data['is_registered']) + { + $sql = 'SELECT poll_option_id + FROM ' . POLL_VOTES_TABLE . ' + WHERE topic_id = ' . $up_topic_id . ' + AND vote_user_id = ' . $user->data['user_id']; + $result = $db->sql_query($sql); + + while ($row = $db->sql_fetchrow($result)) + { + $cur_voted_id[] = $row['poll_option_id']; + } + $db->sql_freeresult($result); + } + else + { + // Cookie based guest tracking ... I don't like this but hum ho + // it's oft requested. This relies on "nice" users who don't feel + // the need to delete cookies to mess with results. + if (isset($_COOKIE[$config['cookie_name'] . '_poll_' . $up_topic_id])) + { + $cur_voted_id = explode(',', request_var($config['cookie_name'] . '_poll_' . $up_topic_id, 0, false, true)); + $cur_voted_id = array_map('intval', $cur_voted_id); + } + } + + $sql = 'SELECT t.poll_length, t.poll_start, t.poll_vote_change, t.topic_status, f.forum_status, t.poll_max_options + FROM ' . TOPICS_TABLE . ' t, ' . FORUMS_TABLE . " f + WHERE t.forum_id = f.forum_id AND t.topic_id = " . (int) $up_topic_id . " AND t.forum_id = " . (int) $up_forum_id; + $result = $db->sql_query_limit($sql, 1); + $topic_data = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + $s_can_up_vote = (((!sizeof($cur_voted_id) && $auth->acl_get('f_vote', $up_forum_id)) || + ($auth->acl_get('f_votechg', $up_forum_id) && $topic_data['poll_vote_change'])) && + (($topic_data['poll_length'] != 0 && $topic_data['poll_start'] + $topic_data['poll_length'] > time()) || $topic_data['poll_length'] == 0) && + $topic_data['topic_status'] != ITEM_LOCKED && + $topic_data['forum_status'] != ITEM_LOCKED) ? true : false; + + if($s_can_up_vote) + { + if (!sizeof($voted_id) || sizeof($voted_id) > $topic_data['poll_max_options'] || in_array(VOTE_CONVERTED, $cur_voted_id)) + { + $redirect_url = append_sid("{$phpbb_root_path}portal.$phpEx"); + + meta_refresh(5, $redirect_url); + if (!sizeof($voted_id)) + { + $message = 'NO_VOTE_OPTION'; + } + else if (sizeof($voted_id) > $topic_data['poll_max_options']) + { + $message = 'TOO_MANY_VOTE_OPTIONS'; + } + else + { + $message = 'VOTE_CONVERTED'; + } + + $message = $user->lang[$message] . '

' . sprintf($user->lang['RETURN_PORTAL'], '', ''); + trigger_error($message); + } + + foreach ($voted_id as $option) + { + if (in_array($option, $cur_voted_id)) + { + continue; + } + + $sql = 'UPDATE ' . POLL_OPTIONS_TABLE . ' + SET poll_option_total = poll_option_total + 1 + WHERE poll_option_id = ' . (int) $option . ' + AND topic_id = ' . (int) $up_topic_id; + $db->sql_query($sql); + + if ($user->data['is_registered']) + { + $sql_ary = array( + 'topic_id' => (int) $up_topic_id, + 'poll_option_id' => (int) $option, + 'vote_user_id' => (int) $user->data['user_id'], + 'vote_user_ip' => (string) $user->ip, + ); + + $sql = 'INSERT INTO ' . POLL_VOTES_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary); + $db->sql_query($sql); + } + } + + foreach ($cur_voted_id as $option) + { + if (!in_array($option, $voted_id)) + { + $sql = 'UPDATE ' . POLL_OPTIONS_TABLE . ' + SET poll_option_total = poll_option_total - 1 + WHERE poll_option_id = ' . (int) $option . ' + AND topic_id = ' . (int) $up_topic_id; + $db->sql_query($sql); + + if ($user->data['is_registered']) + { + $sql = 'DELETE FROM ' . POLL_VOTES_TABLE . ' + WHERE topic_id = ' . (int) $up_topic_id . ' + AND poll_option_id = ' . (int) $option . ' + AND vote_user_id = ' . (int) $user->data['user_id']; + $db->sql_query($sql); + } + } + } + + if ($user->data['user_id'] == ANONYMOUS && !$user->data['is_bot']) + { + $user->set_cookie('poll_' . $up_topic_id, implode(',', $voted_id), time() + 31536000); + } + + $sql = 'UPDATE ' . TOPICS_TABLE . ' + SET poll_last_vote = ' . time() . " + WHERE topic_id = $up_topic_id"; + //, topic_last_post_time = ' . time() . " -- for bumping topics with new votes, ignore for now + $db->sql_query($sql); + + $redirect_url = append_sid("{$phpbb_root_path}portal.$phpEx"); + + meta_refresh(5, $redirect_url); + trigger_error($user->lang['VOTE_SUBMITTED'] . '

' . sprintf($user->lang['RETURN_PORTAL'], '', '')); + } + } + + $where = ''; + $poll_forums = false; + + // Get readable forums + $forum_list = array(); + + $forum_list = array_unique(array_keys($auth->acl_getf('f_read', true))); + + if($config['board3_poll_topic_id_' . $module_id] !== '') + { + $poll_forums_config = explode(',' ,$config['board3_poll_topic_id_' . $module_id]); + + if($config['board3_poll_exclude_id_' . $module_id]) + { + $forum_list = array_unique(array_diff($forum_list, $poll_forums_config)); + } + else + { + $forum_list = array_unique(array_intersect($poll_forums_config, $forum_list)); + } + } + + $where = ''; + + if(sizeof($forum_list)) + { + $poll_forums = true; + $where = 'AND ' . $db->sql_in_set('t.forum_id', $forum_list); + } + + if ($config['board3_poll_hide_' . $module_id]) + { + $portal_poll_hide = "AND (t.poll_start + t.poll_length > ". time() ." OR t.poll_length = 0)"; + } + else + { + $portal_poll_hide = ''; + } + + if ($poll_forums === true) + { + + $sql = 'SELECT t.poll_title, t.poll_start, t.topic_id, t.topic_first_post_id, t.forum_id, t.poll_length, t.poll_vote_change, t.poll_max_options, t.topic_status, f.forum_status, p.bbcode_bitfield, p.bbcode_uid + FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . ' p, ' . FORUMS_TABLE . " f + WHERE t.forum_id = f.forum_id AND t.topic_approved = 1 AND t.poll_start > 0 + {$where} + AND t.topic_moved_id = 0 + AND p.post_id = t.topic_first_post_id + {$portal_poll_hide} + ORDER BY t.poll_start DESC"; + $limit = (isset($config['board3_poll_limit_' . $module_id])) ? $config['board3_poll_limit_' . $module_id] : 3; + $result = $db->sql_query_limit($sql, $limit); + $has_poll = false; + + if ($result) + { + while($data = $db->sql_fetchrow($result)) + { + $has_poll = true; + $poll_has_options = false; + + $topic_id = (int) $data['topic_id']; + $forum_id = (int) $data['forum_id']; + + $cur_voted_id = array(); + if($config['board3_poll_allow_vote_' . $module_id]) + { + if ($user->data['is_registered']) + { + $vote_sql = 'SELECT poll_option_id + FROM ' . POLL_VOTES_TABLE . ' + WHERE topic_id = ' . $topic_id . ' + AND vote_user_id = ' . $user->data['user_id']; + $vote_result = $db->sql_query($vote_sql); + + while ($row = $db->sql_fetchrow($vote_result)) + { + $cur_voted_id[] = $row['poll_option_id']; + } + $db->sql_freeresult($vote_result); + } + else + { + // Cookie based guest tracking ... I don't like this but hum ho + // it's oft requested. This relies on "nice" users who don't feel + // the need to delete cookies to mess with results. + if (isset($_COOKIE[$config['cookie_name'] . '_poll_' . $topic_id])) + { + $cur_voted_id = explode(',', request_var($config['cookie_name'] . '_poll_' . $topic_id, 0, false, true)); + $cur_voted_id = array_map('intval', $cur_voted_id); + } + } + + $s_can_vote = (((!sizeof($cur_voted_id) && $auth->acl_get('f_vote', $forum_id)) || + ($auth->acl_get('f_votechg', $forum_id) && $data['poll_vote_change'])) && + (($data['poll_length'] != 0 && $data['poll_start'] + $data['poll_length'] > time()) || $data['poll_length'] == 0) && + $data['topic_status'] != ITEM_LOCKED && + $data['forum_status'] != ITEM_LOCKED) ? true : false; + } + else + { + $s_can_vote = false; + } + + $s_display_results = (!$s_can_vote || ($s_can_vote && sizeof($cur_voted_id)) || ($view == 'viewpoll' && in_array($topic_id, $poll_view_ar))) ? true : false; + + $poll_sql = 'SELECT po.poll_option_id, po.poll_option_text, po.poll_option_total + FROM ' . POLL_OPTIONS_TABLE . " po + WHERE po.topic_id = {$topic_id} + ORDER BY po.poll_option_id"; + + $poll_result = $db->sql_query($poll_sql); + $poll_total_votes = 0; + $poll_data = array(); + + if ($poll_result) + { + while($polls_data = $db->sql_fetchrow($poll_result)) + { + $poll_has_options = true; + $poll_data[] = $polls_data; + $poll_total_votes += $polls_data['poll_option_total']; + } + } + $db->sql_freeresult($poll_result); + + $make_poll_view = array(); + + if(in_array($topic_id, $poll_view_ar) === FALSE) + { + $make_poll_view[] = $topic_id; + $make_poll_view = array_merge($poll_view_ar, $make_poll_view); + } + + $poll_view_str = urlencode(implode(',', $make_poll_view)); + $portalpoll_url= append_sid("{$phpbb_root_path}portal.$phpEx", "polls=$poll_view_str"); + $portalvote_url= append_sid("{$phpbb_root_path}portal.$phpEx", "f=$forum_id&t=$topic_id"); + $viewtopic_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&t=$topic_id"); + $poll_end = $data['poll_length'] + $data['poll_start']; + + // Parse BBCode title + if ($data['bbcode_bitfield']) + { + $poll_bbcode = new bbcode(); + } + else + { + $poll_bbcode = false; + } + + $data['poll_title'] = censor_text($data['poll_title']); + + if ($poll_bbcode !== false) + { + $poll_bbcode->bbcode_second_pass($data['poll_title'], $data['bbcode_uid'], $data['bbcode_bitfield']); + } + + $data['poll_title'] = bbcode_nl2br($data['poll_title']); + $data['poll_title'] = smiley_text($data['poll_title']); + unset($poll_bbcode); + + $template->assign_block_vars('poll', array( + 'S_POLL_HAS_OPTIONS' => $poll_has_options, + 'POLL_QUESTION' => $data['poll_title'], + 'U_POLL_TOPIC' => append_sid($phpbb_root_path . 'viewtopic.' . $phpEx . '?t=' . $topic_id . '&f=' . $forum_id), + 'POLL_LENGTH' => $data['poll_length'], + 'TOPIC_ID' => $topic_id, + 'TOTAL_VOTES' => $poll_total_votes, + 'L_MAX_VOTES' => ($data['poll_max_options'] == 1) ? $user->lang['MAX_OPTION_SELECT'] : sprintf($user->lang['MAX_OPTIONS_SELECT'], $data['poll_max_options']), + 'L_POLL_LENGTH' => ($data['poll_length']) ? sprintf($user->lang[($poll_end > time()) ? 'POLL_RUN_TILL' : 'POLL_ENDED_AT'], $user->format_date($poll_end)) : '', + 'S_CAN_VOTE' => $s_can_vote, + 'S_DISPLAY_RESULTS' => $s_display_results, + 'S_IS_MULTI_CHOICE' => ($data['poll_max_options'] > 1) ? true : false, + 'S_POLL_ACTION' => $portalvote_url, + 'U_VIEW_RESULTS' => $portalpoll_url . '&view=viewpoll#viewpoll', + 'U_VIEW_TOPIC' => $viewtopic_url, + )); + + foreach($poll_data as $pd) + { + $option_pct = ($poll_total_votes > 0) ? $pd['poll_option_total'] / $poll_total_votes : 0; + $option_pct_txt = sprintf("%.1d%%", round($option_pct * 100)); + + // Parse BBCode option text + if ($data['bbcode_bitfield']) + { + $poll_bbcode = new bbcode(); + } + else + { + $poll_bbcode = false; + } + + $pd['poll_option_text'] = censor_text($pd['poll_option_text']); + + if ($poll_bbcode !== false) + { + $poll_bbcode->bbcode_second_pass($pd['poll_option_text'], $data['bbcode_uid'], $data['bbcode_bitfield']); + } + + $pd['poll_option_text'] = bbcode_nl2br($pd['poll_option_text']); + $pd['poll_option_text'] = smiley_text($pd['poll_option_text']); + unset($poll_bbcode); + + $template->assign_block_vars('poll.poll_option', array( + 'POLL_OPTION_ID' => $pd['poll_option_id'], + 'POLL_OPTION_CAPTION' => $pd['poll_option_text'], + 'POLL_OPTION_RESULT' => $pd['poll_option_total'], + 'POLL_OPTION_PERCENT' => $option_pct_txt, + 'POLL_OPTION_PCT' => round($option_pct * 100), + 'POLL_OPTION_IMG' => $user->img('poll_center', $option_pct_txt, round($option_pct * 250)), + 'POLL_OPTION_VOTED' => (in_array($pd['poll_option_id'], $cur_voted_id)) ? true : false + )); + } + } + } + $db->sql_freeresult($result); + + $template->assign_vars(array( + 'S_DISPLAY_POLL' => true, + 'S_HAS_POLL' => $has_poll, + 'POLL_LEFT_CAP_IMG' => $user->img('poll_left'), + 'POLL_RIGHT_CAP_IMG' => $user->img('poll_right'), + )); + } + return 'poll_center.html'; + } + + function get_template_side($module_id) + { + global $config, $template, $db, $user, $auth, $phpbb_root_path, $phpEx; + + + $user->add_lang('viewtopic'); + + $view = request_var('view', ''); + $update = request_var('update', false); + $poll_view = request_var('polls', ''); + + $poll_view_ar = (strpos($poll_view, ',') !== FALSE) ? explode(',', $poll_view) : (($poll_view != '') ? array($poll_view) : array()); + + if ($update && $config['board3_poll_allow_vote_' . $module_id]) + { + $up_topic_id = request_var('t', 0); + $up_forum_id = request_var('f', 0); + $voted_id = request_var('vote_id', array('' => 0)); + + $cur_voted_id = array(); + if ($user->data['is_registered']) + { + $sql = 'SELECT poll_option_id + FROM ' . POLL_VOTES_TABLE . ' + WHERE topic_id = ' . $up_topic_id . ' + AND vote_user_id = ' . $user->data['user_id']; + $result = $db->sql_query($sql); + + while ($row = $db->sql_fetchrow($result)) + { + $cur_voted_id[] = $row['poll_option_id']; + } + $db->sql_freeresult($result); + } + else + { + // Cookie based guest tracking ... I don't like this but hum ho + // it's oft requested. This relies on "nice" users who don't feel + // the need to delete cookies to mess with results. + if (isset($_COOKIE[$config['cookie_name'] . '_poll_' . $up_topic_id])) + { + $cur_voted_id = explode(',', request_var($config['cookie_name'] . '_poll_' . $up_topic_id, 0, false, true)); + $cur_voted_id = array_map('intval', $cur_voted_id); + } + } + + $sql = 'SELECT t.poll_length, t.poll_start, t.poll_vote_change, t.topic_status, f.forum_status, t.poll_max_options + FROM ' . TOPICS_TABLE . ' t, ' . FORUMS_TABLE . " f + WHERE t.forum_id = f.forum_id AND t.topic_id = " . (int) $up_topic_id . " AND t.forum_id = " . (int) $up_forum_id; + $result = $db->sql_query_limit($sql, 1); + $topic_data = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + $s_can_up_vote = (((!sizeof($cur_voted_id) && $auth->acl_get('f_vote', $up_forum_id)) || + ($auth->acl_get('f_votechg', $up_forum_id) && $topic_data['poll_vote_change'])) && + (($topic_data['poll_length'] != 0 && $topic_data['poll_start'] + $topic_data['poll_length'] > time()) || $topic_data['poll_length'] == 0) && + $topic_data['topic_status'] != ITEM_LOCKED && + $topic_data['forum_status'] != ITEM_LOCKED) ? true : false; + + if($s_can_up_vote) + { + if (!sizeof($voted_id) || sizeof($voted_id) > $topic_data['poll_max_options'] || in_array(VOTE_CONVERTED, $cur_voted_id)) + { + $redirect_url = append_sid("{$phpbb_root_path}portal.$phpEx"); + + meta_refresh(5, $redirect_url); + if (!sizeof($voted_id)) + { + $message = 'NO_VOTE_OPTION'; + } + else if (sizeof($voted_id) > $topic_data['poll_max_options']) + { + $message = 'TOO_MANY_VOTE_OPTIONS'; + } + else + { + $message = 'VOTE_CONVERTED'; + } + + $message = $user->lang[$message] . '

' . sprintf($user->lang['RETURN_PORTAL'], '', ''); + trigger_error($message); + } + + foreach ($voted_id as $option) + { + if (in_array($option, $cur_voted_id)) + { + continue; + } + + $sql = 'UPDATE ' . POLL_OPTIONS_TABLE . ' + SET poll_option_total = poll_option_total + 1 + WHERE poll_option_id = ' . (int) $option . ' + AND topic_id = ' . (int) $up_topic_id; + $db->sql_query($sql); + + if ($user->data['is_registered']) + { + $sql_ary = array( + 'topic_id' => (int) $up_topic_id, + 'poll_option_id' => (int) $option, + 'vote_user_id' => (int) $user->data['user_id'], + 'vote_user_ip' => (string) $user->ip, + ); + + $sql = 'INSERT INTO ' . POLL_VOTES_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary); + $db->sql_query($sql); + } + } + + foreach ($cur_voted_id as $option) + { + if (!in_array($option, $voted_id)) + { + $sql = 'UPDATE ' . POLL_OPTIONS_TABLE . ' + SET poll_option_total = poll_option_total - 1 + WHERE poll_option_id = ' . (int) $option . ' + AND topic_id = ' . (int) $up_topic_id; + $db->sql_query($sql); + + if ($user->data['is_registered']) + { + $sql = 'DELETE FROM ' . POLL_VOTES_TABLE . ' + WHERE topic_id = ' . (int) $up_topic_id . ' + AND poll_option_id = ' . (int) $option . ' + AND vote_user_id = ' . (int) $user->data['user_id']; + $db->sql_query($sql); + } + } + } + + if ($user->data['user_id'] == ANONYMOUS && !$user->data['is_bot']) + { + $user->set_cookie('poll_' . $up_topic_id, implode(',', $voted_id), time() + 31536000); + } + + $sql = 'UPDATE ' . TOPICS_TABLE . ' + SET poll_last_vote = ' . time() . " + WHERE topic_id = $up_topic_id"; + //, topic_last_post_time = ' . time() . " -- for bumping topics with new votes, ignore for now + $db->sql_query($sql); + + $redirect_url = append_sid("{$phpbb_root_path}portal.$phpEx"); + + meta_refresh(5, $redirect_url); + trigger_error($user->lang['VOTE_SUBMITTED'] . '

' . sprintf($user->lang['RETURN_PORTAL'], '', '')); + } + } + + $where = ''; + $poll_forums = false; + + // Get readable forums + $forum_list = array(); + + $forum_list = array_unique(array_keys($auth->acl_getf('f_read', true))); + + if($config['board3_poll_topic_id_' . $module_id] !== '') + { + $poll_forums_config = explode(',' ,$config['board3_poll_topic_id_' . $module_id]); + + if($config['board3_poll_exclude_id_' . $module_id]) + { + $forum_list = array_unique(array_diff($forum_list, $poll_forums_config)); + } + else + { + $forum_list = array_unique(array_intersect($poll_forums_config, $forum_list)); + } + } + + $where = ''; + + if(sizeof($forum_list)) + { + $poll_forums = true; + $where = 'AND ' . $db->sql_in_set('t.forum_id', $forum_list); + } + + if ($config['board3_poll_hide_' . $module_id]) + { + $portal_poll_hide = "AND (t.poll_start + t.poll_length > ". time() ." OR t.poll_length = 0)"; + } + else + { + $portal_poll_hide = ''; + } + + if ($poll_forums === true) + { + + $sql = 'SELECT t.poll_title, t.poll_start, t.topic_id, t.topic_first_post_id, t.forum_id, t.poll_length, t.poll_vote_change, t.poll_max_options, t.topic_status, f.forum_status, p.bbcode_bitfield, p.bbcode_uid + FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . ' p, ' . FORUMS_TABLE . " f + WHERE t.forum_id = f.forum_id AND t.topic_approved = 1 AND t.poll_start > 0 + {$where} + AND t.topic_moved_id = 0 + AND p.post_id = t.topic_first_post_id + {$portal_poll_hide} + ORDER BY t.poll_start DESC"; + $limit = (isset($config['board3_poll_limit_' . $module_id])) ? $config['board3_poll_limit_' . $module_id] : 3; + $result = $db->sql_query_limit($sql, $limit); + $has_poll = false; + + if ($result) + { + while($data = $db->sql_fetchrow($result)) + { + $has_poll = true; + $poll_has_options = false; + + $topic_id = (int) $data['topic_id']; + $forum_id = (int) $data['forum_id']; + + $cur_voted_id = array(); + if($config['board3_poll_allow_vote_' . $module_id]) + { + if ($user->data['is_registered']) + { + $vote_sql = 'SELECT poll_option_id + FROM ' . POLL_VOTES_TABLE . ' + WHERE topic_id = ' . $topic_id . ' + AND vote_user_id = ' . $user->data['user_id']; + $vote_result = $db->sql_query($vote_sql); + + while ($row = $db->sql_fetchrow($vote_result)) + { + $cur_voted_id[] = $row['poll_option_id']; + } + $db->sql_freeresult($vote_result); + } + else + { + // Cookie based guest tracking ... I don't like this but hum ho + // it's oft requested. This relies on "nice" users who don't feel + // the need to delete cookies to mess with results. + if (isset($_COOKIE[$config['cookie_name'] . '_poll_' . $topic_id])) + { + $cur_voted_id = explode(',', request_var($config['cookie_name'] . '_poll_' . $topic_id, 0, false, true)); + $cur_voted_id = array_map('intval', $cur_voted_id); + } + } + + $s_can_vote = (((!sizeof($cur_voted_id) && $auth->acl_get('f_vote', $forum_id)) || + ($auth->acl_get('f_votechg', $forum_id) && $data['poll_vote_change'])) && + (($data['poll_length'] != 0 && $data['poll_start'] + $data['poll_length'] > time()) || $data['poll_length'] == 0) && + $data['topic_status'] != ITEM_LOCKED && + $data['forum_status'] != ITEM_LOCKED) ? true : false; + } + else + { + $s_can_vote = false; + } + + $s_display_results = (!$s_can_vote || ($s_can_vote && sizeof($cur_voted_id)) || ($view == 'viewpoll' && in_array($topic_id, $poll_view_ar))) ? true : false; + + $poll_sql = 'SELECT po.poll_option_id, po.poll_option_text, po.poll_option_total + FROM ' . POLL_OPTIONS_TABLE . " po + WHERE po.topic_id = {$topic_id} + ORDER BY po.poll_option_id"; + + $poll_result = $db->sql_query($poll_sql); + $poll_total_votes = 0; + $poll_data = array(); + + if ($poll_result) + { + while($polls_data = $db->sql_fetchrow($poll_result)) + { + $poll_has_options = true; + $poll_data[] = $polls_data; + $poll_total_votes += $polls_data['poll_option_total']; + } + } + $db->sql_freeresult($poll_result); + + $make_poll_view = array(); + + if(in_array($topic_id, $poll_view_ar) === FALSE) + { + $make_poll_view[] = $topic_id; + $make_poll_view = array_merge($poll_view_ar, $make_poll_view); + } + + $poll_view_str = urlencode(implode(',', $make_poll_view)); + $portalpoll_url= append_sid("{$phpbb_root_path}portal.$phpEx", "polls=$poll_view_str"); + $portalvote_url= append_sid("{$phpbb_root_path}portal.$phpEx", "f=$forum_id&t=$topic_id"); + $viewtopic_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&t=$topic_id"); + $poll_end = $data['poll_length'] + $data['poll_start']; + + // Parse BBCode title + if ($data['bbcode_bitfield']) + { + $poll_bbcode = new bbcode(); + } + else + { + $poll_bbcode = false; + } + + $data['poll_title'] = censor_text($data['poll_title']); + + if ($poll_bbcode !== false) + { + $poll_bbcode->bbcode_second_pass($data['poll_title'], $data['bbcode_uid'], $data['bbcode_bitfield']); + } + + $data['poll_title'] = bbcode_nl2br($data['poll_title']); + $data['poll_title'] = smiley_text($data['poll_title']); + unset($poll_bbcode); + + $template->assign_block_vars('poll_side', array( + 'S_POLL_HAS_OPTIONS' => $poll_has_options, + 'POLL_QUESTION' => $data['poll_title'], + 'U_POLL_TOPIC' => append_sid($phpbb_root_path . 'viewtopic.' . $phpEx . '?t=' . $topic_id . '&f=' . $forum_id), + 'POLL_LENGTH' => $data['poll_length'], + 'TOPIC_ID' => $topic_id, + 'TOTAL_VOTES' => $poll_total_votes, + 'L_MAX_VOTES' => ($data['poll_max_options'] == 1) ? $user->lang['MAX_OPTION_SELECT'] : sprintf($user->lang['MAX_OPTIONS_SELECT'], $data['poll_max_options']), + 'L_POLL_LENGTH' => ($data['poll_length']) ? sprintf($user->lang[($poll_end > time()) ? 'POLL_RUN_TILL' : 'POLL_ENDED_AT'], $user->format_date($poll_end)) : '', + 'S_CAN_VOTE' => $s_can_vote, + 'S_DISPLAY_RESULTS' => $s_display_results, + 'S_IS_MULTI_CHOICE' => ($data['poll_max_options'] > 1) ? true : false, + 'S_POLL_ACTION' => $portalvote_url, + 'U_VIEW_RESULTS' => $portalpoll_url . '&view=viewpoll#viewpoll', + 'U_VIEW_TOPIC' => $viewtopic_url, + )); + + foreach($poll_data as $pd) + { + $option_pct = ($poll_total_votes > 0) ? $pd['poll_option_total'] / $poll_total_votes : 0; + $option_pct_txt = sprintf("%.1d%%", round($option_pct * 100)); + + // Parse BBCode option text + if ($data['bbcode_bitfield']) + { + $poll_bbcode = new bbcode(); + } + else + { + $poll_bbcode = false; + } + + $pd['poll_option_text'] = censor_text($pd['poll_option_text']); + + if ($poll_bbcode !== false) + { + $poll_bbcode->bbcode_second_pass($pd['poll_option_text'], $data['bbcode_uid'], $data['bbcode_bitfield']); + } + + $pd['poll_option_text'] = bbcode_nl2br($pd['poll_option_text']); + $pd['poll_option_text'] = smiley_text($pd['poll_option_text']); + unset($poll_bbcode); + + $template->assign_block_vars('poll_side.poll_option', array( + 'POLL_OPTION_ID' => $pd['poll_option_id'], + 'POLL_OPTION_CAPTION' => $pd['poll_option_text'], + 'POLL_OPTION_RESULT' => $pd['poll_option_total'], + 'POLL_OPTION_PERCENT' => $option_pct_txt, + 'POLL_OPTION_PCT' => round($option_pct * 100), + 'POLL_OPTION_IMG' => $user->img('poll_center', $option_pct_txt, round($option_pct * 250)), + 'POLL_OPTION_VOTED' => (in_array($pd['poll_option_id'], $cur_voted_id)) ? true : false + )); + } + } + } + $db->sql_freeresult($result); + + $template->assign_vars(array( + 'S_DISPLAY_POLL' => true, + 'S_HAS_POLL' => $has_poll, + 'POLL_LEFT_CAP_IMG' => $user->img('poll_left'), + 'POLL_RIGHT_CAP_IMG' => $user->img('poll_right'), + )); + } + return 'poll_side.html'; + } + + function get_template_acp($module_id) + { + return array( + 'title' => 'ACP_PORTAL_POLLS_SETTINGS', + 'vars' => array( + 'legend1' => 'ACP_PORTAL_POLLS_SETTINGS', + 'board3_poll_topic_id_' . $module_id => array('lang' => 'PORTAL_POLL_TOPIC_ID' , 'validate' => 'string', 'type' => 'custom', 'explain' => true, 'method' => 'select_forums'), + 'board3_poll_exclude_id_' . $module_id => array('lang' => 'PORTAL_POLL_EXCLUDE_ID' , 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), + 'board3_poll_limit_' . $module_id => array('lang' => 'PORTAL_POLL_LIMIT' , 'validate' => 'int', 'type' => 'text:3:3', 'explain' => true), + 'board3_poll_allow_vote_' . $module_id => array('lang' => 'PORTAL_POLL_ALLOW_VOTE' , 'validate' => 'ibool', 'type' => 'radio:yes_no', 'explain' => true), + 'board3_poll_hide_' . $module_id => array('lang' => 'PORTAL_POLL_HIDE' , 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), + ) + ); + } + + /** + * API functions + */ + function install($module_id) + { + set_config('board3_poll_allow_vote_' . $module_id, 1); + set_config('board3_poll_topic_id_' . $module_id, ''); + set_config('board3_poll_exclude_id_' . $module_id, 0); + set_config('board3_poll_hide_' . $module_id, 0); + set_config('board3_poll_limit_' . $module_id, 3); + return true; + } + + function uninstall($module_id) + { + global $db; + + $del_config = array( + 'board3_poll_allow_vote_' . $module_id, + 'board3_poll_topic_id_' . $module_id, + 'board3_poll_exclude_id_' . $module_id, + 'board3_poll_hide_' . $module_id, + 'board3_poll_limit_' . $module_id, + ); + $sql = 'DELETE FROM ' . CONFIG_TABLE . ' + WHERE ' . $db->sql_in_set('config_name', $del_config); + return $db->sql_query($sql); + } + + // Create forum select box + function select_forums($value, $key, $module_id) + { + global $user, $config; + + $forum_list = make_forum_select(false, false, true, true, true, false, true); + + $selected = array(); + if(isset($config[$key]) && strlen($config[$key]) > 0) + { + $selected = explode(',', $config[$key]); + } + // Build forum options + $s_forum_options = ''; + + return $s_forum_options; + + } + + // Store selected forums + function store_selected_forums($key, $module_id) + { + global $db, $cache; + + // Get selected forums + $values = request_var($key, array(0 => '')); + + $news = implode(',', $values); + + set_config($key, $news); + + } +} + +?> \ No newline at end of file diff --git a/root/styles/prosilver/template/portal/modules/main_menu_side.html b/root/styles/prosilver/template/portal/modules/main_menu_side.html index 827b548c..4817d2fa 100644 --- a/root/styles/prosilver/template/portal/modules/main_menu_side.html +++ b/root/styles/prosilver/template/portal/modules/main_menu_side.html @@ -1,5 +1,5 @@ -{$LR_BLOCK_H_L} {L_M_MENU}{$LR_BLOCK_H_R} +{$LR_BLOCK_H_L} {L_M_MENU}{$LR_BLOCK_H_R}
diff --git a/root/styles/prosilver/template/portal/modules/poll_center.html b/root/styles/prosilver/template/portal/modules/poll_center.html new file mode 100644 index 00000000..69d7cbdc --- /dev/null +++ b/root/styles/prosilver/template/portal/modules/poll_center.html @@ -0,0 +1,66 @@ + + +{$C_BLOCK_H_L}
{L_PORTAL_POLL}
{$C_BLOCK_H_R} + +
+
+ +
+
+

{poll.POLL_QUESTION}

+

{poll.L_POLL_LENGTH}
{poll.L_MAX_VOTES}

+ +
+ + +
title="{L_POLL_VOTED_OPTION}"> +
{poll.poll_option.POLL_OPTION_CAPTION}
+
checked="checked" /> checked="checked" />
+
{poll.poll_option.POLL_OPTION_RESULT}
+
{L_NO_VOTES}{poll.poll_option.POLL_OPTION_PERCENT}
+
+ + + {L_NO_OPTIONS} + + + +
+
 
+

{L_TOTAL_VOTES} : {poll.TOTAL_VOTES}

+
+ + + +
+
 
+
+
+ + + +
+
 
+

{L_VIEW_RESULTS}

+
+ + +
+
 
+

{L_VIEW_TOPIC}

+
+
+
+ {poll.S_HIDDEN_FIELDS} +
+ +
+
+ +
+
+ {L_NO_POLL} +
+
+ +{$C_BLOCK_F_L}{$C_BLOCK_F_R} \ No newline at end of file diff --git a/root/styles/prosilver/template/portal/modules/poll_side.html b/root/styles/prosilver/template/portal/modules/poll_side.html new file mode 100644 index 00000000..5cdfd0e0 --- /dev/null +++ b/root/styles/prosilver/template/portal/modules/poll_side.html @@ -0,0 +1,65 @@ + + +{$LR_BLOCK_H_L} {L_PORTAL_POLL}{$LR_BLOCK_H_R} + + +
+ +
+
+

{poll_side.POLL_QUESTION}

+

{poll_side.L_POLL_LENGTH}
{poll_side.L_MAX_VOTES}

+ +
+ + +
title="{L_POLL_VOTED_OPTION}"> +
{poll_side.poll_option.POLL_OPTION_CAPTION}
+
checked="checked" /> checked="checked" />
+
{poll_side.poll_option.POLL_OPTION_RESULT}
+
{L_NO_VOTES}{poll_side.poll_option.POLL_OPTION_PERCENT}
+
+ + + {L_NO_OPTIONS} + + + +
+
 
+

{L_TOTAL_VOTES} : {poll_side.TOTAL_VOTES}

+
+ + + +
+
 
+
+
+ + + +
+
 
+

{L_VIEW_RESULTS}

+
+ + +
+
 
+

{L_VIEW_TOPIC}

+
+
+
+ {poll_side.S_HIDDEN_FIELDS} +
+ +
+ +
+
+ {L_NO_POLL} +
+
+ +{$LR_BLOCK_F_L}{$LR_BLOCK_F_R} \ No newline at end of file diff --git a/root/styles/prosilver/theme/images/portal/portal_poll.png b/root/styles/prosilver/theme/images/portal/portal_poll.png new file mode 100644 index 0000000000000000000000000000000000000000..7bf530de03ac4ed13417b5a9001526f9fd492601 GIT binary patch literal 3593 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