+
{{ reaction.S_HIDDEN_FIELDS }}
--- adm/style/event/acp_overall_footer_after.html
@@ -1,3 +1,3 @@
-{% if REACTIONS_IN_ADMIN and CANIDEV_CORE_STARTED %}
+{% if IN_EXT_REACTIONS and CANIDEV_CORE_STARTED %}
{% include '@canidev_core/event/acp_overall_footer_after.html' %}
{% endif %}
\ No hay ningún carácter de nueva línea al final del fichero
--- adm/style/event/acp_overall_header_head_append.html
@@ -1,3 +1,3 @@
-{% if REACTIONS_IN_ADMIN and CANIDEV_CORE_STARTED %}
+{% if IN_EXT_REACTIONS and CANIDEV_CORE_STARTED %}
{% include '@canidev_core/event/overall_header_head_append.html' %}
{% endif %}
\ No hay ningún carácter de nueva línea al final del fichero
--- adm/style/reactions-acp.css
@@ -1,7 +1,7 @@
/* cBB Reactions StyleSheet
--------------------------------------------------------------
Style: ACP
- Copyright (c) 2024 CaniDev ( https://www.canidev.com )
+ Copyright (c) 2025 CaniDev ( https://www.canidev.com )
--------------------------------------------------------------
*/
@@ -26,6 +26,10 @@
white-space: nowrap;
}
+.acp-reactions-table .column-actions a.active {
+ color: #BC2A4D;
+}
+
.reaction-score-selector {
list-style-type: none;
}
--- composer.json
@@ -3,7 +3,8 @@
"type": "phpbb-extension",
"description": "",
"homepage": "https://www.canidev.com",
- "version": "1.0.3",
+ "version": "1.0.4",
+ "time": "2025-04-01",
"keywords": ["phpbb", "extension", "canidev", "reactions"],
"license": "CreativeCommons Attribution-NonCommercial v4.0",
"authors": [
--- config/services.yml
@@ -36,7 +36,9 @@
canidev.reactions.manager:
class: canidev\reactions\libraries\reaction_manager
arguments:
+ - '@auth'
- '@cache.driver'
+ - '@config'
- '@service_container'
- '@dbal.conn'
- '@dispatcher'
--- controller/admin_config.php
@@ -1,9 +1,9 @@
'',
'reactions_allow_myself' => ['lang' => 'REACTIONS_ALLOW_MYSELF', 'validate' => 'bool', 'type' => 'radio:yes_no'],
'reactions_allow_change' => ['lang' => 'REACTIONS_ALLOW_CHANGE', 'validate' => 'bool', 'type' => 'radio:yes_no'],
+ 'reactions_anonymous' => ['lang' => 'REACTIONS_ANONYMOUS', 'validate' => 'bool', 'type' => 'radio:yes_no'],
'reactions_force_reply' => ['lang' => 'REACTIONS_FORCE_REPLY', 'validate' => 'bool', 'type' => 'radio:yes_no'],
'reactions_force_attach' => ['lang' => 'REACTIONS_FORCE_ATTACH', 'validate' => 'bool', 'type' => 'radio:yes_no'],
'reactions_score_on_profile' => ['lang' => 'REACTIONS_SCORE_ON_PROFILE', 'validate' => 'bool', 'type' => 'radio:yes_no'],
- 'reactions_zones' => ['lang' => 'REACTIONS_ZONES', 'type' => 'custom', 'function' => ['tools', 'make_select'], 'params' => [$zones_options, '{KEY}', '{CONFIG_VALUE}']],
- 'reactions_button_position' => ['lang' => 'REACTIONS_BUTTON_POSITION', 'type' => 'custom', 'function' => ['tools', 'make_select'], 'params' => [$position_options, '{KEY}', '{CONFIG_VALUE}']],
- 'reactions_list_order' => ['lang' => 'REACTIONS_LIST_ORDER', 'type' => 'custom', 'function' => ['tools', 'make_select'], 'params' => [$order_options, '{KEY}', '{CONFIG_VALUE}']],
+ 'reactions_zones' => ['lang' => 'REACTIONS_ZONES', 'type' => 'select', 'function' => ['tools', 'make_select'], 'params' => [$zones_options, false, '{CONFIG_VALUE}']],
+ 'reactions_forums' => ['lang' => 'REACTIONS_FORUMS', 'type' => 'custom', 'function' => ['tools', 'forums_select'], 'params' => ['forums', '{CONFIG_VALUE}', 1]],
+ 'reactions_button_position' => ['lang' => 'REACTIONS_BUTTON_POSITION', 'type' => 'select', 'function' => ['tools', 'make_select'], 'params' => [$position_options, false, '{CONFIG_VALUE}']],
+ 'reactions_list_order' => ['lang' => 'REACTIONS_LIST_ORDER', 'type' => 'select', 'function' => ['tools', 'make_select'], 'params' => [$order_options, false, '{CONFIG_VALUE}']],
];
$cfg_array = $this->config;
@@ -78,6 +80,7 @@
if($submit)
{
$cfg_array = $this->request->variable('config', ['' => ''], true);
+ $cfg_array['reactions_forums'] = implode(',', $this->request->variable('forums', [0]));
if($cfg_array['reactions_zones'] == constants::ZONE_ONLY_REPLIES)
{
@@ -93,7 +96,7 @@
/**
* @event reactions.acp_config_before
* @var array display_vars Array of config values to display and process
- * @var boolean submit Do we display the form or process the submission
+ * @var bool submit Do we display the form or process the submission
* @var array cfg_array Array with data
* @var array error Array with data errors
* @since 1.0.0
@@ -105,7 +108,7 @@
validate_config_vars($display_vars, $cfg_array, $error);
// Do not write values if there is an error
- if(sizeof($error))
+ if(count($error))
{
$submit = false;
}
@@ -141,11 +144,10 @@
trigger_error($this->language->lang('CONFIG_UPDATED') . adm_back_link($this->form_action));
}
- $this->template->assign_vars(array(
- 'REACTIONS_IN_ADMIN' => true,
- 'S_ERROR' => (sizeof($error)) ? true : false,
- 'ERROR_MSG' => implode('
', $error),
- ));
+ $this->template->assign_vars([
+ 'S_ERROR' => count($error) > 0,
+ 'ERROR_MSG' => implode('
', $error),
+ ]);
$this->display_vars($display_vars);
}
--- controller/admin_manage.php
@@ -1,9 +1,9 @@
dispatcher->trigger_event('reactions.acp_manage_edit_before', compact($vars)));
// Do not write values if there is an error
- if(sizeof($error))
+ if(count($error))
{
$submit = false;
}
@@ -177,7 +177,7 @@
$this->template->assign_vars([
'IN_REACTION' => true,
- 'S_ERROR' => sizeof($error) ? implode('
', $error) : '',
+ 'S_ERROR' => count($error) ? implode('
', $error) : '',
'S_HIDDEN_FIELDS' => build_hidden_fields([
'action' => $action,
'id' => $reaction_id,
@@ -274,6 +274,18 @@
$this->json->send();
break;
+
+ case 'set_default':
+ if($this->config['reactions_default'] == $reaction_id)
+ {
+ $reaction_id = 0;
+ }
+
+ $this->config->set('reactions_default', $reaction_id);
+ $this->json->send([
+ 'reactionID' => $reaction_id
+ ]);
+ break;
}
$sql = 'SELECT *
@@ -302,6 +314,7 @@
$this->template->assign_block_vars('reactions', [
'ID' => $row['reaction_id'],
+ 'IS_DEFAULT' => ($row['reaction_id'] == $this->config['reactions_default']),
'IS_ENABLED' => (bool)$row['reaction_enabled'],
'S_HIDDEN_FIELDS' => build_hidden_fields([
'item[]' => $row['reaction_id'],
@@ -310,6 +323,7 @@
'S_SCORE' => $score_label,
'S_TITLE' => $title,
'S_TITLE_RAW' => $this->language->lang($row['reaction_title']),
+ 'U_DEFAULT' => $this->form_action . '&action=set_default&id=' . $row['reaction_id'],
'U_DELETE' => $this->form_action . '&action=delete&id=' . $row['reaction_id'],
'U_EDIT' => $this->form_action . '&action=edit&id=' . $row['reaction_id'],
'U_STATUS' => $this->form_action . '&action=change_status&id=' . $row['reaction_id'],
@@ -318,8 +332,7 @@
$this->db->sql_freeresult($result);
$this->template->assign_vars([
- 'REACTIONS_IN_ADMIN' => true,
- 'U_REACTION_ADD' => $this->form_action . '&action=add',
+ 'U_REACTION_ADD' => $this->form_action . '&action=add',
]);
// Assets
--- controller/main.php
@@ -1,14 +1,15 @@
request->variable('post', 0);
$user_id = $this->request->variable('user', 0);
$score_amount = 0;
+ $score_row = [];
if(!(($user_id == $this->user->data['user_id'] && $this->auth->acl_get('u_reactions'))
|| $this->auth->acl_get('m_reactions')))
@@ -116,9 +118,10 @@
// Get post data
$sql = 'SELECT p.post_id, p.topic_id, p.poster_id, p.post_text, p.bbcode_uid, p.bbcode_bitfield,
- t.topic_first_post_id
+ t.topic_first_post_id, u.user_reaction_score AS current_score
FROM ' . POSTS_TABLE . ' p
LEFT JOIN ' . TOPICS_TABLE . ' t ON(t.topic_id = p.topic_id)
+ LEFT JOIN ' . USERS_TABLE . ' u on(u.user_id = p.poster_id)
WHERE p.post_id = ' . $post_id;
$result = $this->db->sql_query($sql);
$post_data = $this->db->sql_fetchrow($result);
@@ -204,10 +207,10 @@
}
}
- $this->template->assign_vars([
+ $score_row += [
'REACTION_HAS_MINE' => true,
'REACTION_LAUNCHER_ICON' => $reaction->get_launcher_html(),
- ]);
+ ];
// Add the new score to post/topic
$score_amount += $reaction->get_score();
@@ -224,9 +227,9 @@
AND user_id = ' . $user_id;
$this->db->sql_query($sql);
- $this->template->assign_vars([
- 'REACTION_LAUNCHER_ICON' => $this->reactions->get(false)->get_launcher_html(),
- ]);
+ $score_row += [
+ 'REACTION_LAUNCHER_ICON' => $this->reactions->get_launcher_html(false),
+ ];
$this->response->add([
'reactionID' => $reaction_data['reaction_id'],
@@ -238,6 +241,15 @@
$this->notifications->delete_notification('canidev.reactions.notification.post', $post_id, $post_data['topic_id'], $user_id);
}
+ // Update user score
+ if(!is_null($post_data['current_score']))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_reaction_score = user_reaction_score + ' . $score_amount . '
+ WHERE user_id = ' . (int)$post_data['poster_id'];
+ $this->db->sql_query($sql);
+ }
+
// Update post score
$sql = 'UPDATE ' . POSTS_TABLE . '
SET post_reaction_score = post_reaction_score + ' . $score_amount . '
@@ -249,7 +261,7 @@
{
$sql = 'UPDATE ' . TOPICS_TABLE . '
SET topic_reaction_score = topic_reaction_score + ' . $score_amount . '
- WHERE topic_id = ' . $post_data['topic_id'];
+ WHERE topic_id = ' . (int)$post_data['topic_id'];
$this->db->sql_query($sql);
}
@@ -257,18 +269,22 @@
* @event reactions.add_remove_after
* @var string mode add_reaction / remove_reaction
* @var int post_id Post ID
- * @var int user_id User ID
- * @var int reaction_id Reaction ID (only for "add_reaction" mode)
+ * @var int user_id User ID
+ * @var int reaction_id Reaction ID (only for "add_reaction" mode)
* @since 1.0.0
*/
$vars = ['mode', 'post_id', 'user_id', 'reaction_id'];
extract($this->dispatcher->trigger_event('reactions.add_remove_after', compact($vars)));
- $this->template->assign_vars([
- 'CAN_USE_REACTIONS' => $this->auth->acl_get('u_reactions'),
- 'POST_ID' => $post_id,
- 'U_REACTION' => $this->reactions->get_user_link($post_id, $user_id),
- ]);
+ $score_row += [
+ 'CAN_USE_REACTIONS' => $this->auth->acl_get('u_reactions'),
+ 'POST_ID' => $post_id,
+ 'REACTION_LIST_ATTR' => Dom::arrayToAttr([
+ 'data-post-id' => $post_id,
+ 'data-title' => $this->language->lang('REACTIONS'),
+ ]),
+ 'U_REACTION' => $this->reactions->get_user_link($post_id, $user_id),
+ ];
// Put reactions on template
foreach($this->reactions->get_all() as $reaction)
@@ -284,13 +300,16 @@
if($this->auth->acl_get('u_reactions_view'))
{
$reaction_list = $this->reactions->get_posts_reactions($post_id);
- $this->template->assign_vars($this->reactions->parse_post_score_list($reaction_list[$post_id]));
+
+ $score_row = array_merge($score_row, $this->reactions->parse_post_score_list($reaction_list[$post_id]));
}
- $this->template->set_filenames(array(
+ $this->template->assign_block_vars('score_rows', $score_row);
+
+ $this->template->set_filenames([
'launcher' => '@canidev_reactions/launcher_button.html',
'score_list' => '@canidev_reactions/score_list.html',
- ));
+ ]);
$this->response->add([
'postID' => $post_id,
@@ -399,7 +418,7 @@
foreach($tabs as $reaction_id => $data)
{
- $item_count = sizeof($data['items']);
+ $item_count = count($data['items']);
if(!$item_count)
{
@@ -418,7 +437,7 @@
}
$this->template->set_filenames([
- 'dialog' => '@canidev_reactions/reactions_list.html'
+ 'dialog' => $this->reactions->can_view_usernames() ? '@canidev_reactions/reactions_list.html' : '@canidev_reactions/reactions_list_simple.html'
]);
$this->response->addHtml($this->template->assign_display('dialog'));
--- event/listener.php
@@ -1,14 +1,15 @@
'add_permissions',
'core.memberlist_view_profile' => 'memberlist_view_profile',
'core.modify_posting_auth' => 'posting_auth',
+ 'core.viewtopic_cache_user_data' => 'viewtopic_user_data',
'core.viewforum_modify_topic_ordering' => 'viewforum_gen_sort_selects', // phpBB >= 3.2.5
'core.viewtopic_gen_sort_selects_before' => 'viewtopic_gen_sort_selects', // phpBB >= 3.2.8
'core.viewtopic_modify_post_data' => 'viewtopic_modify_data',
@@ -96,9 +99,9 @@
/**
* Add Permissions to ACP
*
- * @param array $event Event data
+ * @param \phpbb\event\data $event Event data
*/
- public function add_permissions($event)
+ public function add_permissions(\phpbb\event\data $event)
{
$event['permissions'] = array_merge($event['permissions'], [
'm_reactions' => ['lang' => 'ACL_M_REACTIONS', 'cat' => 'post_actions'],
@@ -110,9 +113,9 @@
/**
* Delete reaction's data on post delete
*
- * @param array $event Event data
+ * @param \phpbb\event\data $event Event data
*/
- public function delete_posts($event)
+ public function delete_posts(\phpbb\event\data $event)
{
$table_ary = $event['table_ary'];
$delete_notifications_types = $event['delete_notifications_types'];
@@ -127,9 +130,9 @@
/**
* Delete reaction's data on user delete
*
- * @param array $event Event data
+ * @param \phpbb\event\data $event Event data
*/
- public function delete_user($event)
+ public function delete_user(\phpbb\event\data $event)
{
$post_ids = [];
@@ -162,9 +165,9 @@
/**
* Load Reactions language
*
- * @param array $event Event data
+ * @param \phpbb\event\data $event Event data
*/
- public function load_language($event)
+ public function load_language(\phpbb\event\data $event)
{
$lang_set_ext = $event['lang_set_ext'];
$lang_set_ext[] = [
@@ -177,9 +180,9 @@
/**
* Load profile stats
*
- * @param array $event Event data
+ * @param \phpbb\event\data $event Event data
*/
- public function memberlist_view_profile($event)
+ public function memberlist_view_profile(\phpbb\event\data $event)
{
$user_id = (int)$event['member']['user_id'];
@@ -194,19 +197,39 @@
if($this->config['reactions_score_on_profile'])
{
- $score_ary = $this->reactions->get_users_score([$user_id]);
- $this->template->assign_var('REACTIONS_USER_SCORE', $score_ary[$user_id]);
+ $score = $event['member']['user_reaction_score'];
+
+ /**
+ * Adjust score count for user.
+ * Correction of the error with the creation of the index in phpbb posts in large forums.
+ */
+ if(is_null($score))
+ {
+ $score_ary = $this->reactions->get_users_score([$user_id]);
+
+ $score = $score_ary[$user_id];
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_reaction_score = ' . $score . '
+ WHERE user_id = ' . $user_id;
+ $this->db->sql_query($sql);
+ }
+
+ $this->template->assign_var('REACTIONS_USER_SCORE', $score);
}
}
/**
* Determine if user can posting a reply
*
- * @param array $event Event data
+ * @param \phpbb\event\data $event Event data
*/
- public function posting_auth($event)
+ public function posting_auth(\phpbb\event\data $event)
{
- if($event['mode'] == 'reply' && $this->config['reactions_force_reply'] && $event['post_data']['topic_poster'] != $this->user->data['user_id'])
+ if($event['mode'] == 'reply' &&
+ $this->config['reactions_force_reply'] &&
+ $event['post_data']['topic_poster'] != $this->user->data['user_id'] &&
+ $this->is_valid_forum($event['forum_id']))
{
$sql = 'SELECT reaction_id
FROM ' . $this->reactions_data_table . '
@@ -226,9 +249,9 @@
/**
* Add sort option to Viewforum
*
- * @param array $event Event data
+ * @param \phpbb\event\data $event Event data
*/
- public function viewforum_gen_sort_selects($event)
+ public function viewforum_gen_sort_selects(\phpbb\event\data $event)
{
$sort_by_text = $event['sort_by_text'];
$sort_by_sql = $event['sort_by_sql'];
@@ -243,9 +266,9 @@
/**
* Add sort option to Viewtopic
*
- * @param array $event Event data
+ * @param \phpbb\event\data $event Event data
*/
- public function viewtopic_gen_sort_selects($event)
+ public function viewtopic_gen_sort_selects(\phpbb\event\data $event)
{
$sort_by_text = $event['sort_by_text'];
$sort_by_sql = $event['sort_by_sql'];
@@ -263,11 +286,11 @@
/**
* Assign variables on viewtopic page
*
- * @param array $event Event data
+ * @param \phpbb\event\data $event Event data
*/
- public function viewtopic_modify_data($event)
+ public function viewtopic_modify_data(\phpbb\event\data $event)
{
- if(!sizeof($event['post_list']))
+ if(!count($event['post_list']) || !$this->is_valid_forum($event['forum_id']))
{
return;
}
@@ -286,34 +309,71 @@
// Load post's reactions
$this->cache_ary = $this->reactions->get_posts_reactions($event['post_list']);
- // Load user's scores
+ /**
+ * Adjust score count for users.
+ * Correction of the error with the creation of the index in phpbb posts in large forums.
+ */
if($this->config['reactions_score_on_profile'])
{
$user_cache = $event['user_cache'];
- $score_ary = $this->reactions->get_users_score(array_keys($user_cache));
+ $user_ary = [];
- foreach($score_ary as $user_id => $user_score)
+ foreach($user_cache as $user_id => $row)
{
- $user_cache[$user_id]['reactions_score'] = $user_score;
+ if(array_key_exists('reactions_score', $row) && is_null($row['reactions_score']))
+ {
+ $user_ary[$user_id] = $user_id;
+ }
}
- $event['user_cache'] = $user_cache;
+ if(count($user_ary))
+ {
+ $score_ary = $this->reactions->get_users_score($user_ary);
+ $user_ary = [];
+
+ foreach($score_ary as $user_id => $user_score)
+ {
+ $user_cache[$user_id]['reactions_score'] = $user_score;
+
+ $user_ary[$user_id] = [
+ 'user_reaction_score' => $user_score,
+ ];
+ }
+
+ foreach($user_ary as $user_id => $update_ary)
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $update_ary) . '
+ WHERE user_id = ' . $user_id;
+ $this->db->sql_query($sql);
+ }
+
+ $event['user_cache'] = $user_cache;
+ }
}
- $this->template->assign_var('REACTIONS_BTN_POSITION', $this->config['reactions_button_position']);
+ // Template variables and assets
+ $service_options = [
+ 'allowChange' => (bool)$this->config['reactions_allow_change'],
+ 'defaultID' => (int)$this->config['reactions_default'],
+ 'simpleList' => !$this->reactions->can_view_usernames(),
+ ];
+
+ $this->template->assign_vars([
+ 'REACTIONS_BTN_POSITION' => $this->config['reactions_button_position'],
+ 'REACTIONS_OPTIONS' => json_encode($service_options),
+ ]);
- // Assets
$this->template->append_asset('css', '@canidev_reactions/reactions.css');
- $this->template->append_asset('javascript', 'window.reactionsAllowChange=' . (int)$this->config['reactions_allow_change'] . ';');
$this->template->append_asset('js', '@canidev_reactions/reactions.min.js');
}
/**
* Add reaction variables to post rows in viewtopic
*
- * @param array $event Event data
+ * @param \phpbb\event\data $event Event data
*/
- public function viewtopic_modify_row($event)
+ public function viewtopic_modify_row(\phpbb\event\data $event)
{
$postrow = $event['post_row'];
$row = $event['row'];
@@ -321,6 +381,11 @@
$post_id = (int)$row['post_id'];
$url_params = [];
+ if(!$this->is_valid_forum($event['row']['forum_id']))
+ {
+ return;
+ }
+
$postrow['REACTION_HAS_MINE'] = false;
if($this->reactions->get($this->cache_ary[$post_id]['mine'])->is_enabled())
@@ -328,7 +393,7 @@
$postrow['REACTION_HAS_MINE'] = true;
}
- $postrow['REACTION_LAUNCHER_ICON'] = $this->reactions->get($this->cache_ary[$post_id]['mine'])->get_launcher_html();
+ $postrow['REACTION_LAUNCHER_ICON'] = $this->reactions->get_launcher_html($this->cache_ary[$post_id]['mine']);
$postrow['CAN_USE_REACTIONS'] = $this->auth->acl_get('u_reactions');
// Current user is the author and can't react to their own post
@@ -389,6 +454,11 @@
$postrow['U_REACTION'] = $this->reactions->get_user_link($post_id, $this->user->data['user_id'], $url_params);
+ $postrow['REACTION_LIST_ATTR'] = Dom::arrayToAttr([
+ 'data-post-id' => $post_id,
+ 'data-title' => $this->language->lang('REACTIONS'),
+ ]);
+
if($this->cache_ary[$post_id]['length'] && $this->auth->acl_get('u_reactions_view'))
{
$postrow = array_merge($postrow, $this->reactions->parse_post_score_list($this->cache_ary[$post_id]));
@@ -398,15 +468,46 @@
}
/**
+ * Assign data to user profile in viewtopic
+ *
+ * @param \phpbb\event\data $event Event data
+ */
+ public function viewtopic_user_data(\phpbb\event\data $event)
+ {
+ $user_cache_data = $event['user_cache_data'];
+ $user_cache_data['reactions_score'] = $event['row']['user_reaction_score'];
+ $event['user_cache_data'] = $user_cache_data;
+ }
+
+ /**
* Update reactions username when some username is changed
*
- * @param array $event Event data
+ * @param \phpbb\event\data $event Event data
*/
- public function update_username($event)
+ public function update_username(\phpbb\event\data $event)
{
$sql = 'UPDATE ' . $this->reactions_data_table . "
SET username = '" . $this->db->sql_escape($event['new_name']) . "'
WHERE username = '" . $this->db->sql_escape($event['old_name']) . "'";
$this->db->sql_query($sql);
}
+
+ /**
+ * Check if reactions can be used in selected forum
+ *
+ * @param int $forum_id
+ * @return bool
+ * @access private
+ */
+ protected function is_valid_forum($forum_id)
+ {
+ if(!$this->config['reactions_forums'])
+ {
+ return true;
+ }
+
+ $forum_ary = explode(',', $this->config['reactions_forums']);
+
+ return in_array($forum_id, $forum_ary);
+ }
}
--- event/media.php
@@ -1,9 +1,9 @@
container->get('notification_manager');
- $phpbb_notifications->enable_notifications('canidev.reactions.notification.post');
-
- return 'notifications';
-
- default:
- // Create upload folder
- \canidev\core\tools::mkdir($this->container->getParameter('core.root_path') . 'files/reactions/', true);
-
- // Run parent enable step method
- return parent::enable_step($old_state);
+ /** @var \phpbb\config\db */
+ $config = $this->container->get('config');
+
+ // Fresh install
+ if(!$config->offsetExists('reactions_allow_change'))
+ {
+ define('REACTIONS_FRESH_INSTALL', true);
+ }
+
+ // Enable notifications
+ /** @var \phpbb\notification\manager */
+ $phpbb_notifications = $this->container->get('notification_manager');
+ $phpbb_notifications->enable_notifications('canidev.reactions.notification.post');
+
+ // Create upload folder
+ \canidev\core\tools::mkdir($this->container->getParameter('core.root_path') . 'files/reactions/', true);
+
+ return true;
}
+
+ // Run parent enable step method
+ return parent::enable_step($old_state);
}
/**
--- language/en/acp.php
@@ -2,9 +2,9 @@
/**
* [English [En]]
* @package cBB Reactions
- * @version 1.0.3 26/02/2024
+ * @version 1.0.4 01/04/2025
*
- * @copyright (c) 2024 CaniDev
+ * @copyright (c) 2025 CaniDev
* @license https://creativecommons.org/licenses/by-nc/4.0/
*/
@@ -44,12 +44,19 @@
'REACTIONS_ALLOW_CHANGE_EXPLAIN' => 'Defines whether users will be able to modify their reaction on messages.',
'REACTIONS_ALLOW_MYSELF' => 'Allow self reactions',
'REACTIONS_ALLOW_MYSELF_EXPLAIN' => 'If enabled, users will be able to react to their own messages.',
+ 'REACTIONS_ANONYMOUS' => 'Anonymous reactions',
+ 'REACTIONS_ANONYMOUS_EXPLAIN' => 'Enable this option if you want user names not to appear in reaction listings.
+
This option does not affect users with permission to moderate reactions.',
'REACTIONS_BUTTON_POSITION' => 'Button position',
'REACTIONS_BUTTON_POSITION_EXPLAIN' => 'Defines where the button to react will appear in the message.',
'REACTIONS_FORCE_ATTACH' => 'Force reaction to view attachments',
'REACTIONS_FORCE_ATTACH_EXPLAIN' => 'If enabled, users will have to react to a message in order to see its attachments.',
'REACTIONS_FORCE_REPLY' => 'Force reaction to reply',
'REACTIONS_FORCE_REPLY_EXPLAIN' => 'If enabled, users will have to react to the first post in a topic in order to post replies to it.',
+ 'REACTIONS_FORUMS' => 'Forums where users can react',
+ 'REACTIONS_FORUMS_EXPLAIN' => 'Defines the forums in which reactions will be displayed.
+ If you don\'t select any, reactions will be displayed on all forums.
+ You can select as many as you like using the
Ctrl key on your keyboard.',
'REACTIONS_LIST_ORDER' => 'List order',
'REACTIONS_LIST_ORDER_EXPLAIN' => 'Defines the criteria that will be used to sort the users in the reaction list.',
'REACTIONS_ORDER_TIME' => 'Reaction date',
@@ -66,6 +73,7 @@
'SCORE_CUSTOM' => 'Custom',
'SCORE_CUSTOM_VALUE' => 'Custom (%s)',
'SELECT_COLOUR' => 'Select color',
+ 'SET_AS_DEFAULT' => 'Set as default',
'scores' => [
1 => 'Positive (+1)',
--- language/en/info_acp_reactions.php
@@ -2,9 +2,9 @@
/**
* [English [En]]
* @package cBB Reactions
- * @version 1.0.3 26/02/2024
+ * @version 1.0.4 01/04/2025
*
- * @copyright (c) 2024 CaniDev
+ * @copyright (c) 2025 CaniDev
* @license https://creativecommons.org/licenses/by-nc/4.0/
*/
--- language/en/main.php
@@ -2,9 +2,9 @@
/**
* [English [En]]
* @package cBB Reactions
- * @version 1.0.3 26/02/2024
+ * @version 1.0.4 01/04/2025
*
- * @copyright (c) 2024 CaniDev
+ * @copyright (c) 2025 CaniDev
* @license https://creativecommons.org/licenses/by-nc/4.0/
*/
@@ -30,9 +30,17 @@
'REACTIONS_NOTIFICATION_TYPE_POST' => 'Someone reacted to a message you posted',
- 'REACTION_SCORE_LABEL_SIMPLE' => '%1$s',
- 'REACTION_SCORE_LABEL_COUNT_ONE' => '%1$s and another user',
- 'REACTION_SCORE_LABEL_COUNT_MULTIPLE' => '%1$s and another %2$d users',
+ 'REACTION_SCORE_LABEL_ANONYMOUS' => [
+ 1 => '1 user has reacted',
+ 2 => '%d users have reacted',
+ ],
+
+ 'REACTION_SCORE_LABEL_SIMPLE' => '%1$s',
+
+ 'REACTION_SCORE_LABEL_COUNT' => [
+ 1 => '%1$s and another user',
+ 2 => '%1$s and another %2$d users',
+ ],
'REACTIONS' => 'Reactions',
'REACTIONS_ALL' => 'All',
--- language/en/permissions_reactions.php
@@ -2,9 +2,9 @@
/**
* [English [En]]
* @package cBB Reactions
- * @version 1.0.3 26/02/2024
+ * @version 1.0.4 01/04/2025
*
- * @copyright (c) 2024 CaniDev
+ * @copyright (c) 2025 CaniDev
* @license https://creativecommons.org/licenses/by-nc/4.0/
*/
--- language/es/acp.php
@@ -2,9 +2,9 @@
/**
* [Spanish [Es]]
* @package cBB Reactions
- * @version 1.0.3 26/02/2024
+ * @version 1.0.4 01/04/2025
*
- * @copyright (c) 2024 CaniDev
+ * @copyright (c) 2025 CaniDev
* @license https://creativecommons.org/licenses/by-nc/4.0/
*/
@@ -44,12 +44,19 @@
'REACTIONS_ALLOW_CHANGE_EXPLAIN' => 'Define si los usuarios podrán modificar su reacción en los mensajes.',
'REACTIONS_ALLOW_MYSELF' => 'Permitir reacciones de uno mismo',
'REACTIONS_ALLOW_MYSELF_EXPLAIN' => 'Si se habilita, los usuarios podrán reaccionar a sus propios mensajes.',
+ 'REACTIONS_ANONYMOUS' => 'Reacciones anónimas',
+ 'REACTIONS_ANONYMOUS_EXPLAIN' => 'Habilite esta opción si desea que los nombres de los usuarios no aparezcan en los listados de reacciones.
+
Esta opción no afecta a los usuarios con permiso para moderar las reacciones.',
'REACTIONS_BUTTON_POSITION' => 'Posición del botón',
'REACTIONS_BUTTON_POSITION_EXPLAIN' => 'Define en que lugar del mensaje aparecerá el botón para reaccionar.',
'REACTIONS_FORCE_ATTACH' => 'Forzar reacción para ver adjuntos',
'REACTIONS_FORCE_ATTACH_EXPLAIN' => 'Si se habilita, los usuarios tendrán que reaccionar a un mensaje para poder ver sus archivos adjuntos.',
'REACTIONS_FORCE_REPLY' => 'Forzar reacción para responder',
'REACTIONS_FORCE_REPLY_EXPLAIN' => 'Si se habilita, los usuarios tendrán que reaccionar al primer mensaje de un tema para poder publicar respuestas en el mismo.',
+ 'REACTIONS_FORUMS' => 'Foros en los que se puede reaccionar',
+ 'REACTIONS_FORUMS_EXPLAIN' => 'Define los foros en los que se mostrarán las reacciones.
+ Si no selecciona ninguno, las reacciones se mostrarán en todos los foros.
+ Puede seleccionar tantos como quiera usando la tecla
Ctrl de su teclado.',
'REACTIONS_LIST_ORDER' => 'Orden de la lista',
'REACTIONS_LIST_ORDER_EXPLAIN' => 'Define el criterio que se usará para ordenar los usuarios en la lista de reacciones.',
'REACTIONS_ORDER_TIME' => 'Fecha de reacción',
@@ -66,6 +73,7 @@
'SCORE_CUSTOM' => 'Personalizado',
'SCORE_CUSTOM_VALUE' => 'Personalizado (%s)',
'SELECT_COLOUR' => 'Seleccionar color',
+ 'SET_AS_DEFAULT' => 'Definir como predeterminada',
'scores' => [
1 => 'Positivo (+1)',
--- language/es/info_acp_reactions.php
@@ -2,9 +2,9 @@
/**
* [Spanish [Es]]
* @package cBB Reactions
- * @version 1.0.3 26/02/2024
+ * @version 1.0.4 01/04/2025
*
- * @copyright (c) 2024 CaniDev
+ * @copyright (c) 2025 CaniDev
* @license https://creativecommons.org/licenses/by-nc/4.0/
*/
--- language/es/main.php
@@ -2,9 +2,9 @@
/**
* [Spanish [Es]]
* @package cBB Reactions
- * @version 1.0.3 26/02/2024
+ * @version 1.0.4 01/04/2025
*
- * @copyright (c) 2024 CaniDev
+ * @copyright (c) 2025 CaniDev
* @license https://creativecommons.org/licenses/by-nc/4.0/
*/
@@ -30,9 +30,17 @@
'REACTIONS_NOTIFICATION_TYPE_POST' => 'Alguien reaccionó a un mensaje que ha publicado',
- 'REACTION_SCORE_LABEL_SIMPLE' => '%1$s',
- 'REACTION_SCORE_LABEL_COUNT_ONE' => '%1$s y otro usuario',
- 'REACTION_SCORE_LABEL_COUNT_MULTIPLE' => '%1$s y otros %2$d usuarios',
+ 'REACTION_SCORE_LABEL_ANONYMOUS' => [
+ 1 => '1 usuario ha reaccionado',
+ 2 => '%d usuarios han reaccionado',
+ ],
+
+ 'REACTION_SCORE_LABEL_SIMPLE' => '%1$s',
+
+ 'REACTION_SCORE_LABEL_COUNT' => [
+ 1 => '%1$s y otro usuario',
+ 2 => '%1$s y otros %2$d usuarios',
+ ],
'REACTIONS' => 'Reacciones',
'REACTIONS_ALL' => 'Todas',
--- language/es/permissions_reactions.php
@@ -2,9 +2,9 @@
/**
* [Spanish [Es]]
* @package cBB Reactions
- * @version 1.0.3 26/02/2024
+ * @version 1.0.4 01/04/2025
*
- * @copyright (c) 2024 CaniDev
+ * @copyright (c) 2025 CaniDev
* @license https://creativecommons.org/licenses/by-nc/4.0/
*/
--- libraries/constants.php
@@ -1,9 +1,9 @@
manager->get_notification_type_id($notification_type_name);
- $notification = $this->manager->get_item_type_class($notification_type_name);
$item_parent_id = isset($notification_data['item_parent_id']) ? $notification_data['item_parent_id'] : 0;
+
+ /** @var \canidev\reactions\notification\post */
+ $notification = $this->manager->get_item_type_class($notification_type_name);
$time = $this->user->create_datetime();
$time->setTime(0, 0, 0);
@@ -58,9 +60,9 @@
$sql = 'SELECT *
FROM ' . $this->notifications_table . '
WHERE notification_type_id = ' . (int)$notification_type_id . '
- AND item_id = ' . (int)$notification_data['item_id'] . '
- AND item_parent_id = ' . $item_parent_id . '
- AND notification_time >= ' . $time->getTimestamp();
+ AND item_id = ' . (int)$notification_data['item_id'] . '
+ AND item_parent_id = ' . $item_parent_id . '
+ AND notification_time >= ' . $time->getTimestamp();
$result = $this->db->sql_query($sql);
$notification_row = $this->db->sql_fetchrow($result);
$this->db->sql_freeresult($result);
@@ -100,9 +102,9 @@
$sql = 'SELECT *
FROM ' . $this->notifications_table . '
WHERE notification_type_id = ' . (int)$notification_type_id . '
- AND item_id = ' . (int)$item_id . '
- AND item_parent_id = ' . (int)$item_parent_id . '
- ORDER BY notification_time DESC';
+ AND item_id = ' . (int)$item_id . '
+ AND item_parent_id = ' . (int)$item_parent_id . '
+ ORDER BY notification_time DESC';
$result = $this->db->sql_query($sql);
$notification_row = $this->db->sql_fetchrow($result);
$this->db->sql_freeresult($result);
@@ -127,7 +129,7 @@
unset($user_ary[$key]);
// If there are still users, update the notification
- if(sizeof($user_ary))
+ if(count($user_ary))
{
$data['user_ids'] = array_values($user_ary);
@@ -202,4 +204,4 @@
return $message;
}
-}
\ No hay ningún carácter de nueva línea al final del fichero
+}
--- libraries/reaction_manager.php
@@ -1,19 +1,22 @@
auth = $auth;
$this->cache = $cache;
+ $this->config = $config;
$this->container = $container;
$this->db = $db;
$this->dispatcher = $dispatcher;
@@ -69,6 +78,16 @@
}
/**
+ * Check if user can view usernames
+ *
+ * @return bool
+ */
+ public function can_view_usernames()
+ {
+ return !$this->config['reactions_anonymous'] || $this->auth->acl_get('m_reactions');
+ }
+
+ /**
* Get singular reaction
*
* @param int $reaction_id Reaction ID
@@ -76,11 +95,6 @@
*/
public function get($reaction_id = null)
{
- if($reaction_id === null)
- {
- return $this->data;
- }
-
if(is_numeric($reaction_id) && isset($this->data[$reaction_id]))
{
return $this->data[$reaction_id];
@@ -99,6 +113,22 @@
}
/**
+ * Get default launcher icon
+ *
+ * @param int|false $reaction_id
+ * @return string
+ */
+ public function get_launcher_html($reaction_id)
+ {
+ if(!$reaction_id && $this->config['reactions_default'])
+ {
+ return '
->get_image_url() . ')
';
+ }
+
+ return $this->get($reaction_id)->get_launcher_html();
+ }
+
+ /**
* Get Reactions of posts
*
* @param array $post_ary Array with post IDs
@@ -258,7 +288,7 @@
$output['reaction_scores'][] = [
'S_REACTION_IMAGE' => $reaction->get_image_url(),
'S_REACTION_TITLE' => $reaction->get_title(),
- 'S_TITLE' => $reaction->get_title() . ' (' . sizeof($reactions) . ')',
+ 'S_TITLE' => $reaction->get_title() . ' (' . count($reactions) . ')',
'U_VIEW' => $this->routing->route('canidev_reactions_controller', [
'mode' => 'view',
'post' => $reactions_data['post_id'],
@@ -266,11 +296,11 @@
]),
];
- if(sizeof($scores_usernames) < 2)
+ if(count($scores_usernames) < 2)
{
foreach($reactions as $row)
{
- if(sizeof($scores_usernames) >= 2)
+ if(count($scores_usernames) >= 2)
{
break;
}
@@ -284,25 +314,32 @@
}
}
- if($reactions_data['length'] < 4)
+ if(!$this->can_view_usernames())
{
- $key = ($reactions_data['length'] == 3) ? 'REACTION_SCORE_LABEL_COUNT_ONE' : 'REACTION_SCORE_LABEL_SIMPLE';
-
- $output['REACTION_SCORE_LABEL'] = $this->language->lang($key, implode(', ', $scores_usernames));
+ $output['REACTION_SCORE_LABEL'] = $this->language->lang('REACTION_SCORE_LABEL_ANONYMOUS', $reactions_data['length']);
}
else
{
- $output['REACTION_SCORE_LABEL'] = $this->language->lang(
- 'REACTION_SCORE_LABEL_COUNT_MULTIPLE',
- implode(', ', $scores_usernames),
- $reactions_data['length'] - sizeof($scores_usernames)
- );
- }
+ if($reactions_data['length'] < 4)
+ {
+ $key = ($reactions_data['length'] == 3) ? 'REACTION_SCORE_LABEL_COUNT' : 'REACTION_SCORE_LABEL_SIMPLE';
+
+ $output['REACTION_SCORE_LABEL'] = $this->language->lang($key, implode(', ', $scores_usernames), 1);
+ }
+ else
+ {
+ $output['REACTION_SCORE_LABEL'] = $this->language->lang(
+ 'REACTION_SCORE_LABEL_COUNT',
+ implode(', ', $scores_usernames),
+ $reactions_data['length'] - count($scores_usernames)
+ );
+ }
- $output['U_REACTION_SCORE_ALL'] = $this->routing->route('canidev_reactions_controller', [
- 'mode' => 'view',
- 'post' => $reactions_data['post_id'],
- ]);
+ $output['U_REACTION_SCORE_ALL'] = $this->routing->route('canidev_reactions_controller', [
+ 'mode' => 'view',
+ 'post' => $reactions_data['post_id'],
+ ]);
+ }
return $output;
}
@@ -342,7 +379,7 @@
$post_ids = [$post_ids];
}
- if(!sizeof($post_ids))
+ if(!count($post_ids))
{
return;
}
--- libraries/reaction.php
@@ -1,9 +1,9 @@
db_tools->sql_index_exists($this->table_prefix . 'posts', 'reaction_score');
+ return true;
}
/**
@@ -28,32 +30,4 @@
'\canidev\reactions\migrations\v102',
];
}
-
- /**
- * {@inheritDoc}
- */
- public function update_schema()
- {
- return [
- 'add_index' => [
- $this->table_prefix . 'posts' => [
- 'reaction_score' => ['poster_id', 'post_reaction_score'],
- ],
- ],
- ];
- }
-
- /**
- * {@inheritDoc}
- */
- public function revert_schema()
- {
- return [
- 'drop_keys' => [
- $this->table_prefix . 'posts' => [
- 'reaction_score',
- ],
- ],
- ];
- }
}
--- migrations/v104.php
@@ -0,0 +1,88 @@
+db_tools->sql_column_exists($this->table_prefix . 'users', 'user_reaction_score');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ static public function depends_on()
+ {
+ return [
+ '\canidev\reactions\migrations\v102',
+ ];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function update_schema()
+ {
+ return [
+ 'add_columns' => [
+ $this->table_prefix . 'users' => [
+ 'user_reaction_score' => ['INT:4', null],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function revert_schema()
+ {
+ return [
+ 'drop_columns' => [
+ $this->table_prefix . 'users' => [
+ 'user_reaction_score',
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function update_data()
+ {
+ return [
+ ['config.add', ['reactions_forums', '']],
+ ['config.add', ['reactions_default', 0]],
+ ['config.add', ['reactions_anonymous', 0]],
+
+ ['custom', [[$this, 'adjust_first_score']]],
+ ];
+ }
+
+ /**
+ * Set first user score. 0 on fresh install, null on update. (Fix phpbb_posts index problem)
+ */
+ public function adjust_first_score()
+ {
+ if(defined('REACTIONS_FRESH_INSTALL'))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_reaction_score = 0
+ WHERE user_reaction_score IS NULL';
+ $this->db->sql_query($sql);
+ }
+ }
+}
--- notification/post.php
@@ -1,9 +1,9 @@
+ (function($) {
+ cbbCore.initService('reactions', {{ REACTIONS_OPTIONS }});
+ })(jQuery);
+
+{% endif %}
--- styles/all/template/event/viewtopic_body_postrow_post_notices_after.html
@@ -1,6 +1,8 @@
-{% if REACTIONS_BTN_POSITION == 'below' %}
- {% set launcher_need_wrapper = true %}
- {% include "@canidev_reactions/launcher_button.html" %}
-{% endif %}
+
+ {% include "@canidev_reactions/score_list.html" %}
-{% include "@canidev_reactions/score_list.html" %}
\ No hay ningún carácter de nueva línea al final del fichero
+ {% if REACTIONS_BTN_POSITION == 'below' %}
+ {% set launcher_need_wrapper = true %}
+ {% include "@canidev_reactions/launcher_button.html" %}
+ {% endif %}
+
--- styles/all/template/launcher_button.html
@@ -1,37 +1,27 @@
{% if launcher_need_wrapper %}
{% endif %}
-{% if postrow is defined %}
- {% if postrow.CAN_USE_REACTIONS %}
+{% if not postrow is defined %}
+ {% set postrow = score_rows|first %}
+{% endif %}
+
+{% if postrow.CAN_USE_REACTIONS %}
-
-
+
{{ postrow.REACTION_LAUNCHER_ICON }}
{% for reaction in reactions %}
- 
- {% endfor %}
-
-
-
- {% endif %}
-{% else %}
- {% if CAN_USE_REACTIONS %}
- -
-
- {{ REACTION_LAUNCHER_ICON }}
-
-
-
-
- {% for reaction in reactions %}
- 
+ -
+
+
+
+
{% endfor %}
- {% endif %}
{% endif %}
{% if launcher_need_wrapper %}
{% endif %}
\ No hay ningún carácter de nueva línea al final del fichero
--- styles/all/template/reactions_list.html
@@ -30,7 +30,7 @@
--- styles/all/template/reactions_list_simple.html
@@ -0,0 +1,11 @@
+
--- styles/all/template/score_list.html
@@ -1,19 +1,18 @@
-{% if postrow is defined %}
-