This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ require_once ( AK_CLASSES . '/abstract/component.php' ); /** * Class aocActivity. * Records and displays all about the user activity. * * @since 0.9 * @author Jordi Canals * @package AOC * @subpackage Activity * @link http://alkivia.org */ class aocActivity extends akComponentAbstract { /** * Gallery component Statup. * Sets all needed filters and actions to the WordPress API. * * @return void */ protected function moduleLoad () { // Global activity content add_filter('the_content', array($this, '_globalActivityPage')); // User Page content add_filter('aoc_alkivia_page', array($this, '_userWallContent')); // Setup table name on database object. global $wpdb; $wpdb->aoc_activity = $wpdb->prefix . 'aoc_activity'; // Post addition and update add_action('wp_insert_post', array($this, '_postHook'), 10, 2); // Comments: additions and approvals add_action('wp_insert_comment', array($this, '_commentHook'), 10, 2); add_action('transition_comment_status', array($this, '_commentApprovedHook'), 10, 3); // User profile update add_action('personal_options_update', array($this, '_profileHook')); // Generic activity hook. add_action('aoc_generic_event', array($this, '_genericHook')); // Rewrite Rules add_filter('query_vars', array($this, '_rewriteVars')); add_filter('generate_rewrite_rules', array($this, '_rewriteRules')); } /** * Sets default component settings. * * @return void */ protected function defaultOptions () { return array( 'timeout' => 5, // 5 minutes to save a new object/action on the same object_id 'list_items' => 50, // Show 50 items on activity list 'avatar_size' => 32, // Avatar size for the activity wall 'global_wall' => 0, // Page ID that holds the global site activity 'wall_template' => 'default', // Template to be used on global wall pages. 'user_template' => 'default' // Template to be used for a user wall. ); } /** * Component activation. * * @return void */ protected function componentActivate () { $this->createTable(); } /** * Component update. * * @return void */ protected function componentUpdate ( $version ) { $this->createTable(); } /** * Create the database table to save activity items. * * @return void */ private function createTable () { global $wpdb; $query = "CREATE TABLE IF NOT EXISTS `{$wpdb->aoc_activity}` ( `even_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `owner_id` bigint(20) NOT NULL, `object_type` varchar(20) NOT NULL, `object_action` varchar(20) NOT NULL, `object_id` bigint(20) NOT NULL DEFAULT '0', `event_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `event_hook` varchar(100) NOT NULL, `event_params` longtext NOT NULL, PRIMARY KEY (`even_id`), KEY `objects` (`owner_id`,`object_type`,`object_action`), KEY `date_owner` (`event_date`,`owner_id`) ) AUTO_INCREMENT=1 ;"; $wpdb->query($query); } /** * Adds the Activity settings menu. * * @return void */ public function adminMenus () { add_submenu_page( $this->slug, __('Activity Wall', $this->PID), __('Activity Wall', $this->PID), 'aoc_manage_settings', $this->slug . '-activity', array($this, '_activitySettings')); } /** * Loads settings page for Activity Wall. * * @return void */ public function _activitySettings () { if ( ! current_user_can('aoc_manage_settings') ) { // Verify user permissions. wp_die('' .__('What do you think you\'re doing?!?', $this->PID) . ''); } global $wp_rewrite; $wp_rewrite->flush_rules(); // Force save rules. if ( 'POST' == $_SERVER['REQUEST_METHOD'] ) { $this->saveAdminSettings(); } require ( dirname(__FILE__) . '/admin.php'); } /** * Saves activity settings from admin page. * * @return void */ private function saveAdminSettings () { check_admin_referer('alkivia-activity-settings'); if ( isset($_POST['action']) && 'update' == $_POST['action'] ) { $post = stripslashes_deep($_POST['settings']); $post = array_merge($this->defaultOptions(), $post); $settings = array( 'timeout' => intval($post['timeout']), 'list_items' => intval($post['list_items']), 'avatar_size' => intval($post['avatar_size']), 'global_wall' => intval($post['global_wall']), 'wall_template' => $post['wall_template'], 'user_template' => $post['user_template'] ); $this->setNewOptions($settings); ak_admin_notify(); } else { // Missing action wp_die('Bad form received.', $this->PID); } } /** * Gets the formated site wall content. * Uses the configured template. * * @hook filter 'the_content' * * @param string $content The original page content. * @return string Formated wall content. */ public function _globalActivityPage ( $content ) { if ( $this->getOption('global_wall') != get_the_ID() ) { // If not the activity page. Do nothing. return $content; } $wall_items = $this->getTheWall(); if ( empty($wall_items) ) { return $content; } // Load and process the page template. require_once ( AK_CLASSES . '/template.php'); $template = new akTemplate( aoc_template_paths() ); $template->textDomain($this->PID); $template->assign('items', $wall_items); // TODO: Page the wall activity. return $content . $template->getDisplay('wall-' . $this->getOption('wall_template'), 'wall-default'); } /** * Checks if an event log was recently recorded for an object/action * * @param array $args Event arguments. * @return boolean returns true if an event log was recently added. */ private function didEvent ( $args ) { global $wpdb; if ( ! is_array($args) ) { return false; } extract($args, EXTR_SKIP); $time = time() - ( (int) $this->getOption('timeout') * 60 ); $dt = date('Y-m-d H:i:s', $time); $q = "SELECT COUNT(*) FROM {$wpdb->aoc_activity} WHERE `owner_id` = '{$owner_id}' AND `object_id` = '{$object_id}' AND `object_type` = '{$object_type}' AND `event_date` >= '{$dt}' AND `object_action` = '{$object_action}'"; return ( 0 == $wpdb->get_var($q) ) ? false : true; } /** * Generic event hook. * Used to save generic actions to activity log. * * TODO: When inserting a generic hook, the display param have to be provided to know how the info has to display. * * @hook action aoc_generic_event * @param array $args Hook data. The following data is available: * - owner_id: User who started the event. By default the logged in user. * - object-type: The object where the event occurs (profile, galery, post, comment, etc.) * - object-action: The action performed on the object (insert, edit, update...) * - object-id: The object ID where event occurs. * - eventy-date: The date/time when the event was done. * - event_hook: Custom hook to display the event log description. * - event_params: An array of additional params to be used by the event_hook, If received an string, will be used as param['display'] * @return void */ public function _genericHook ( $args ) { global $wpdb; if ( ! isset($args['owner_id']) && is_user_logged_in() ) { return; } $defaults = array( 'owner_id' => 0, 'object_type' => 'generic', 'object_action' => 'default', 'object_id' => 0, 'event_date' => date('Y-m-d H:i:s'), 'event_hook' => 'default', 'event_params' => '' ); $args = wp_parse_args($args, $defaults ); if ( is_array($args['event_params']) ) { $args['event_params'] = serialize($args['event_params']); } else { $args['event_params'] = serialize(array( 'display' => $args['event_params'] )); } if ( ! $this->didEvent($args) ) { $wpdb->insert($wpdb->aoc_activity, $args); } } /** * Adds an activity entry every time a post/page is edited. * * @hook action 'wp_insert_post' * @param int $id Post or page ID (Not used by needed by action hook) * @param object post Post data object. * @return void */ public function _postHook ( $id, $post ) { if ( 'publish' != $post->post_status ) { return; } $action = ( '0000-00-00 00:00:00' != $post->post_date && $post->post_date != $post->post_modified ) ? 'edit' : 'insert'; $activity = array( 'owner_id' => $post->post_author, 'object_type' => $post->post_type, 'object_action' => $action, 'object_id' => $post->ID, 'event_date' => $post->post_modified, 'event_hook' => 'aoc_wall_post', 'event_params' => array( 'title' => $post->post_title ) ); $this->_genericHook($activity); } /** * Adds an activity entry every time a comment is added. * * @hook action 'wp_insert_comment' * @param int $id Comment ID (Not used by needed by action hook) * @param object post Comment data object. * @return void */ public function _commentHook ( $id, $comment ) { if ( 0 == $comment->user_id || 1 != $comment->comment_approved ) { return; } $activity = array( 'owner_id' => $comment->user_id, 'object_type' => 'comment', 'object_action' => 'insert', 'object_id' => $comment->comment_post_ID, 'event_date' => $comment->comment_date, 'event_hook' => 'aoc_wall_comment', 'event_params' => array( 'comment_ID' => $comment->comment_ID ) ); $this->_genericHook($activity); } /** * Adds an activity entry every time a comment is approved. * * @hook action 'transition_comment_status' * @param string $new New comment status (Only processed when 'approved') * @param string $old Old comment status (Only processed when not 'approved') * @param object post Comment data object. * @return void */ public function _commentApprovedHook ( $new, $old, $comment ) { if ( 'approved' == $new && $new != $old ) { $comment->comment_approved = 1; $this->_commentHook( $comment->comment_ID, $comment); } } /** * Adds an activity entry every time a comment is approved. * * @hook action 'personal_options_update' * @param string $user_id User profile ID * @return void */ public function _profileHook ( $user_id ) { $activity = array( 'owner_id' => $user_id, 'object_type' => 'profile', 'object_action' => 'update', 'object_id' => $user_id, 'event_date' => date('Y-m-d H:i:s'), 'event_hook' => 'aoc_wall_profile', ); $this->_genericHook($activity); } /** * Creates the content page for user activity wall. * * @param string $content Original page content. * @return string Replaced page content. */ public function _userWallContent ( $content ) { $user_name = urldecode(get_query_var('wall')); if ( ! empty($user_name) ) { $wall = $this->getUserWall($user_name); if ( $wall ) { $content = $wall; } else { $content = '

' . __('User with this name not found.', $this->PID) . '

'; } } return $content; } /** * Gets the formated user wall content. * Uses the configured template. * * @param string $user_login User login to retrieve the wall for. * @return string Formated wall content. */ private function getUserWall ( $user_login ) { if ( empty($user_login) ) { return false; } $user = get_userdatabylogin($user_login); if ( ! $user ) { return false; } $wall_items = $this->getTheWall($user->ID); if ( empty($wall_items) ) { return false; } // Load and process the page template. require_once ( AK_CLASSES . '/template.php'); $template = new akTemplate( aoc_template_paths() ); $template->textDomain($this->PID); $template->assignByRef('user', $user); $template->assign('items', $wall_items); if ( is_user_logged_in() ) { $cur_user = wp_get_current_user(); if ( $cur_user->user_login == $user->user_login ) { $link = '' . __('Manage your photo gallery', $this->PID) . '' ; } elseif (current_user_can('aoc_manage_galleries') ) { $link = '' . __('Manage this user gallery', $this->PID) . ''; } $template->assign('edit_link', $link); } // TODO: Page the wall activity. return $template->getDisplay('userwall-' . $this->getOption('user_template'), 'userwall-default'); } /** * Returns a list of arrays for activity items. * Each array will contain: * - avatar: The user avatar url. * - display: Text to display (with anchors to items) * - date: the event date. * @param $owner_id User id to get the activity from. If 0, will return flobal activity. * @return array List of activity items */ public function getTheWall ( $owner_id = 0) { global $wpdb; if ( 0 != $owner_id && ! aoc_can_show_user($owner_id)) { return array(); } $where = ( 0 == $owner_id ) ? '' : "WHERE `owner_id` = '{$owner_id}'"; $q = "SELECT * FROM {$wpdb->aoc_activity} {$where} ORDER BY `event_date` DESC LIMIT 0,{$this->getOption('list_items')}"; $wall_data = $wpdb->get_results($q, 'ARRAY_A'); if ( empty($wall_data) ) { return array(); } $wall_items = array(); foreach ( $wall_data as $item ) { $display = $this->getHookDisplay($item); if ( $display ) { $wall_items[] = array( 'avatar' => get_avatar($item['owner_id'], $this->getOption('avatar_size')), 'date' => $item['event_date'], 'text' => $display ); } } return $wall_items; } /** * Formats an activity item to be displayed. * To do it calls the function hook set for each item. * * @param array $args Item arguments, as read from DB. * @return string The item text to display. */ private function getHookDisplay ( $args ) { $hook_function = '_' . $args['event_hook']; $args['event_params'] == unserialize($args['event_params']); if ( ! isset($args['event_params']['display']) ) { $args['event_params']['display'] = ''; } if ( 'default' != $args['event_hook'] && function_exists($hook_function) ) { $text = call_user_func($hook_function, $args); } else { $text = $args['event_params']['display']; } return $text; } /** * Creates the needed rewrite rules for user wall. * * @hook action by ref 'generate_rewrite_rules' * @param object $wp_rewrite Current rewrite rules. Received by ref. * @return void */ public function _rewriteRules ( &$wp_rewrite ) { $pid = ak_get_object($this->PID)->getOption('page_id'); $slug = basename(get_page_uri($pid)); $rules = array ( $slug . '/wall/(.+)/?$' => 'index.php?page_id='. $pid .'&wall='. $wp_rewrite->preg_index(1), '(.+)/' . $slug . '/wall/(.+)/?$' => 'index.php?page_id='. $pid .'&wall=' . $wp_rewrite->preg_index(2) ); $wp_rewrite->rules = $rules + $wp_rewrite->rules; } /** * Creates the query var to get a user wall page. * * @hook filter 'query_vars' * @param array $vars Current WordPress query vars. * @return array New Query vars. */ public function _rewriteVars ( $vars ) { $vars[] = 'wall'; return $vars; } }