/** * function add_comment * adds a comment to a post, if the comment content is not NULL * @param array An associative array of content found in the $_POST array */ public function act_add_comment() { Utils::check_request_method(array('POST')); // We need to get the post anyway to redirect back to the post page. $post = Post::get(array('id' => $this->handler_vars['id'])); if (!$post) { // trying to comment on a non-existent post? Weirdo. header('HTTP/1.1 403 Forbidden', true, 403); die; } // Allow theme action hooks to work Themes::create(); $form = $post->comment_form(); $form->get(null, false); // Disallow non-FormUI comments if (!$form->submitted) { // Trying to submit a non-FormUI comment header('HTTP/1.1 403 Forbidden', true, 403); die; } else { // To be eventually incorporated more fully into FormUI. Plugins::act('comment_form_submit', $form); if ($form->success) { $this->add_comment($post->id, $form->cf_commenter->value, $form->cf_email->value, $form->cf_url->value, $form->cf_content->value, $form->get_values()); } else { Session::error(_t('There was a problem submitting your comment.')); foreach ($form->validate() as $error) { Session::error($error); } $form->bounce(); } } }
/** * Handles AJAX requests from the dashboard */ public function ajax_dashboard($handler_vars) { Utils::check_request_method(array('POST')); $this->create_theme(); $this->get_additem_form(); $available_modules = Plugins::filter('dashboard_block_list', array()); $user_id = User::identify()->id; $dashboard_area = 'dashboard_' . $user_id; switch ($handler_vars['action']) { case 'updateModules': $modules = $_POST['moduleOrder']; $order = 0; foreach ($modules as $module) { $order++; DB::query('UPDATE {blocks_areas} SET display_order = :display_order WHERE block_id = :id AND area = :dashboardarea', array('display_order' => $order, 'id' => $module, 'dashboardarea' => $dashboard_area)); } $ar = new AjaxResponse(200, _t('Modules updated.')); break; case 'addModule': $type = $handler_vars['module_name']; $title = $available_modules[$type]; $block = new Block(array('title' => $title, 'type' => $type)); $block->insert(); $max_display_order = DB::get_value('SELECT max(display_order) FROM {blocks_areas} WHERE area = :dashboardarea and scope_id = 0;', array('dashboardarea' => $dashboard_area)); $max_display_order++; DB::query('INSERT INTO {blocks_areas} (block_id, area, scope_id, display_order) VALUES (:block_id, :dashboardarea, 0, :display_order)', array('block_id' => $block->id, 'display_order' => $max_display_order, 'dashboardarea' => $dashboard_area)); $ar = new AjaxResponse(200, _t('Added module %s.', array($title))); $ar->html('modules', $this->theme->fetch('dashboard_modules')); break; case 'removeModule': $block_id = $handler_vars['moduleid']; DB::delete('{blocks}', array('id' => $block_id)); DB::delete('{blocks_areas}', array('block_id' => $block_id)); $ar = new AjaxResponse(200, _t('Removed module.')); $ar->html('modules', $this->theme->fetch('dashboard_modules')); break; case 'configModule': $block_id = $handler_vars['moduleid']; $block = DB::get_row('SELECT * FROM {blocks} b WHERE b.id = :id', array('id' => $block_id), 'Block'); /** Block $block */ $form = $block->get_form(); $form->_ajax = true; $form->set_option('success_message', _t('Module Configuration Saved.') . '<script type="text/javascript">window.setTimeout(function(){$(".form_message").fadeOut();}, 2000);</script>'); $control_id = new FormControlHidden('moduleid', 'null:null'); $control_id->value = $block->id; $control_id->id = 'moduleid'; $form->append($control_id); $control_action = new FormControlHidden('action', 'null:null'); $control_action->value = 'configModule'; $control_action->id = 'action'; $form->append($control_action); $form->out(); $form_id = $form->name; exit; break; } $ar->out(); }
/** * Handles AJAX from /admin/tags * Used to delete and rename tags */ public function ajax_tags($handler_vars) { Utils::check_request_method(array('POST')); $wsse = Utils::WSSE($handler_vars['nonce'], $handler_vars['timestamp']); if ($handler_vars['digest'] != $wsse['digest']) { Session::error(_t('WSSE authentication failed.')); echo Session::messages_get(true, array('Format', 'json_messages')); return; } $tag_names = array(); $theme_dir = Plugins::filter('admin_theme_dir', Site::get_dir('admin_theme', true)); $this->theme = Themes::create('admin', 'RawPHPEngine', $theme_dir); $action = $this->handler_vars['action']; switch ($action) { case 'delete': foreach ($_POST as $id => $delete) { // skip POST elements which are not tag ids if (preg_match('/^tag_\\d+/', $id) && $delete) { $id = substr($id, 4); $tag = Tags::get_by_id($id); $tag_names[] = $tag->term_display; Tags::vocabulary()->delete_term($tag); } } $msg_status = _n(_t('Tag %s has been deleted.', array(implode('', $tag_names))), _t('%d tags have been deleted.', array(count($tag_names))), count($tag_names)); Session::notice($msg_status); break; case 'rename': if (!isset($this->handler_vars['master'])) { Session::error(_t('Error: New name not specified.')); echo Session::messages_get(true, array('Format', 'json_messages')); return; } $master = $this->handler_vars['master']; $tag_names = array(); foreach ($_POST as $id => $rename) { // skip POST elements which are not tag ids if (preg_match('/^tag_\\d+/', $id) && $rename) { $id = substr($id, 4); $tag = Tags::get_by_id($id); $tag_names[] = $tag->term_display; } } Tags::vocabulary()->merge($master, $tag_names); $msg_status = sprintf(_n('Tag %1$s has been renamed to %2$s.', 'Tags %1$s have been renamed to %2$s.', count($tag_names)), implode($tag_names, ', '), $master); Session::notice($msg_status); break; } $this->theme->tags = Tags::vocabulary()->get_tree(); $this->theme->max = Tags::vocabulary()->max_count(); echo json_encode(array('msg' => Session::messages_get(true, 'array'), 'tags' => $this->theme->fetch('tag_collection'))); }
/** * Handles AJAX from /admin/tags * Used to delete and rename tags */ public function ajax_tags($handler_vars) { Utils::check_request_method(array('POST')); $response = new AjaxResponse(); $wsse = Utils::WSSE($handler_vars['nonce'], $handler_vars['timestamp']); if ($handler_vars['digest'] != $wsse['digest']) { $response->message = _t('WSSE authentication failed.'); $response->out(); return; } $tag_names = array(); $this->create_theme(); $action = $this->handler_vars['action']; switch ($action) { case 'delete': foreach ($_POST as $id => $delete) { // skip POST elements which are not tag ids if (preg_match('/^tag_\\d+/', $id) && $delete) { $id = substr($id, 4); $tag = Tags::get_by_id($id); $tag_names[] = $tag->term_display; Tags::vocabulary()->delete_term($tag); } } $response->message = _n(_t('Tag %s has been deleted.', array(implode('', $tag_names))), _t('%d tags have been deleted.', array(count($tag_names))), count($tag_names)); break; case 'rename': if (!isset($this->handler_vars['master'])) { $response->message = _t('Error: New name not specified.'); $response->out(); return; } $master = $this->handler_vars['master']; $tag_names = array(); foreach ($_POST as $id => $rename) { // skip POST elements which are not tag ids if (preg_match('/^tag_\\d+/', $id) && $rename) { $id = substr($id, 4); $tag = Tags::get_by_id($id); $tag_names[] = $tag->term_display; } } Tags::vocabulary()->merge($master, $tag_names); $response->message = sprintf(_n('Tag %1$s has been renamed to %2$s.', 'Tags %1$s have been renamed to %2$s.', count($tag_names)), implode($tag_names, ', '), $master); break; } $this->theme->tags = Tags::vocabulary()->get_tree('term_display ASC'); $this->theme->max = Tags::vocabulary()->max_count(); $response->data = $this->theme->fetch('tag_collection'); $response->out(); }
/** * Handles asyncronous cron calls. * * @todo next_cron should be the actual next run time and update it when new * crons are added instead of just maxing out at one day.. */ function act_poll_cron() { Utils::check_request_method(array('GET', 'HEAD', 'POST')); $time = doubleval($this->handler_vars['time']); if ($time != Options::get('cron_running')) { return; } // allow script to run for 10 minutes set_time_limit(600); $time = HabariDateTime::date_create(); $crons = DB::get_results('SELECT * FROM {crontab} WHERE start_time <= ? AND next_run <= ?', array($time->sql, $time->sql), 'CronJob'); if ($crons) { foreach ($crons as $cron) { $cron->execute(); } } // set the next run time to the lowest next_run OR a max of one day. $next_cron = DB::get_value('SELECT next_run FROM {crontab} ORDER BY next_run ASC LIMIT 1', array()); Options::set('next_cron', min(intval($next_cron), $time->modify('+1 day')->int)); Options::set('cron_running', false); }
/** * Handles AJAX from /comments. * Used to edit comments inline. */ public function action_auth_ajax_in_edit(ActionHandler $handler) { Utils::check_request_method(array('POST')); $handler_vars = $handler->handler_vars; $wsse = Utils::WSSE($handler_vars['nonce'], $handler_vars['timestamp']); if ($handler_vars['digest'] != $wsse['digest']) { Session::error(_t('WSSE authentication failed.')); echo Session::messages_get(true, array('Format', 'json_messages')); return; } $comment = Comment::get($handler_vars['id']); if (!ACL::access_check($comment->get_access(), 'edit')) { Session::error(_t('You do not have permission to edit this comment.')); echo Session::messages_get(true, array('Format', 'json_messages')); return; } if (isset($handler_vars['author']) && $handler_vars['author'] != '') { $comment->name = $handler_vars['author']; } if (isset($handler_vars['url'])) { $comment->url = $handler_vars['url']; } if (isset($handler_vars['email']) && $handler_vars['email'] != '') { $comment->email = $handler_vars['email']; } if (isset($handler_vars['content']) && $handler_vars['content'] != '') { $comment->content = $handler_vars['content']; } if (isset($handler_vars['time']) && $handler_vars['time'] != '' && isset($handler_vars['date']) && $handler_vars['date'] != '') { $seconds = date('s', strtotime($comment->date)); $date = date('Y-m-d H:i:s', strtotime($handler_vars['date'] . ' ' . $handler_vars['time'] . ':' . $seconds)); $comment->date = $date; } $comment->update(); Session::notice(_t('Updated 1 comment.')); echo Session::messages_get(true, array('Format', 'json_messages')); }
/** * Handles AJAX requests from the dashboard */ public function ajax_dashboard($handler_vars) { Utils::check_request_method(array('POST')); $theme_dir = Plugins::filter('admin_theme_dir', Site::get_dir('admin_theme', true)); $this->theme = Themes::create('admin', 'RawPHPEngine', $theme_dir); switch ($handler_vars['action']) { case 'updateModules': $modules = array(); foreach ($_POST as $key => $module) { // skip POST elements which are not module names if (preg_match('/^module\\d+$/', $key)) { list($module_id, $module_name) = explode(':', $module, 2); // remove non-sortable modules from the list if ($module_id != 'nosort') { $modules[$module_id] = $module_name; } } } Modules::set_active($modules); $ar = new AjaxResponse(200, _t('Modules updated.')); break; case 'addModule': $id = Modules::add($handler_vars['module_name']); $this->fetch_dashboard_modules(); $ar = new AjaxResponse(200, _t('Added module %s.', array($handler_vars['module_name']))); $ar->html('modules', $this->theme->fetch('dashboard_modules')); break; case 'removeModule': Modules::remove($handler_vars['moduleid']); $this->fetch_dashboard_modules(); $ar = new AjaxResponse(200, _t('Removed module.')); $ar->html('modules', $this->theme->fetch('dashboard_modules')); break; } $ar->out(); }
/** * Handles AJAX requests from the groups page. */ public function ajax_groups($handler_vars) { Utils::check_request_method(array('GET', 'HEAD')); $theme_dir = Plugins::filter('admin_theme_dir', Site::get_dir('admin_theme', true)); $this->theme = Themes::create('admin', 'RawPHPEngine', $theme_dir); $output = ''; foreach (UserGroups::get_all() as $group) { $this->theme->group = $group; $group = UserGroup::get_by_id($group->id); $users = array(); foreach ($group->members as $id) { $user = $id == 0 ? User::anonymous() : User::get_by_id($id); if ($user->id == 0) { $users[] = '<strong>' . $user->displayname . '</strong>'; } else { $users[] = '<strong><a href="' . URL::get('admin', 'page=user&id=' . $user->id) . '">' . $user->displayname . '</a></strong>'; } } $this->theme->users = $users; $output .= $this->theme->fetch('groups_item'); } echo json_encode(array('items' => $output)); }
/** * Dispatches the request to the defined method. (ie: post_{page}) */ public function act($action) { if (null === $this->handler_vars) { $this->handler_vars = new SuperGlobal(array()); } $this->action = $action; $action_method = 'act_' . $action; $before_action_method = 'before_' . $action_method; $after_action_method = 'after_' . $action_method; if (method_exists($this, $before_action_method)) { $this->{$before_action_method}(); } /** * Plugin action to allow plugins to execute before a certain * action is triggered * * @see ActionHandler::$action * @action before_act_{$action} */ Plugins::act($before_action_method, $this); // The body of this function IS the action method, do it. // If I was smarter, I'd pass this code as a closure to the parent act() via a parameter for DRY, but I'm lazy, ha ha $page = $action == 'admin' ? $this->handler_vars['page'] : $action; $page = filter_var($page, FILTER_SANITIZE_STRING); if (isset($this->handler_vars['content_type'])) { $type_name = $this->handler_vars['content_type']; $type = Plugins::filter('post_type_display', Post::type_name($type_name), 'singular'); } elseif ($page == 'publish' && isset($this->handler_vars['id'])) { $type_name = Post::type_name(Post::get(array('status' => Post::status('any'), 'id' => intval($this->handler_vars['id'])))->content_type); $type = Plugins::filter('post_type_display', $type_name, 'singular'); } else { $type_name = ''; $type = ''; } $this->setup_admin_theme($page, $type); // Access check to see if the user is allowed the requested page Utils::check_request_method(array('GET', 'HEAD', 'POST')); if (!$this->access_allowed($page, $type_name)) { Session::error(_t('Access to that page has been denied by the administrator.')); $this->get_blank(); } if (method_exists($this, $action_method)) { call_user_func(array($this, $action_method)); } else { switch ($_SERVER['REQUEST_METHOD']) { case 'POST': // Let plugins try to handle the page Plugins::act('admin_theme_post_' . $page, $this, $this->theme); // Handle POSTs to the admin pages $fn = 'post_' . $page; if (method_exists($this, $fn)) { $this->{$fn}(); } else { $classname = get_class($this); _e('%1$s->%2$s() does not exist.', array($classname, $fn)); exit; } break; case 'GET': case 'HEAD': // Let plugins try to handle the page Plugins::act('admin_theme_get_' . $page, $this, $this->theme); // Handle GETs of the admin pages $fn = 'get_' . $page; if (method_exists($this, $fn)) { $this->{$fn}(); exit; } // If a get_ function doesn't exist, fail if ($this->theme->template_exists($page)) { Session::error(_t('There is no handler for this page. This is propably an error.')); $this->get_blank(); } else { // The requested console page doesn't exist header('HTTP/1.1 404 Not Found', true, 404); $this->get_blank(_t('The page you were looking for was not found.')); } break; } } /** * Plugin action to allow plugins to execute after a certain * action is triggered * * @see ActionHandler::$action * @action before_act_{$action} */ Plugins::act($after_action_method); if (method_exists($this, $after_action_method)) { $this->{$after_action_method}(); } }
/** * Called from the themes page to save the blocks instances into areas * * @param mixed $handler_vars * @return */ public function ajax_save_areas($handler_vars) { Utils::check_request_method(array('POST')); $scope = $_POST['scope']; $msg = ''; $response = new AjaxResponse(); if (isset($_POST['area_blocks'])) { $area_blocks = $_POST['area_blocks']; DB::query('DELETE FROM {blocks_areas} WHERE scope_id = :scope_id', array('scope_id' => $scope)); foreach ((array) $area_blocks as $area => $blocks) { $display_order = 0; // if there are no blocks for a given area, skip it if (empty($blocks)) { continue; } foreach ($blocks as $block) { $display_order++; DB::query('INSERT INTO {blocks_areas} (block_id, area, scope_id, display_order) VALUES (:block_id, :area, :scope_id, :display_order)', array('block_id' => $block, 'area' => $area, 'scope_id' => $scope, 'display_order' => $display_order)); } } // $msg = json_encode( _t( 'Saved block areas settings.' ) ); // $msg = '<script type="text/javascript"> // human_msg.display_msg(' . $msg . '); // spinner.stop(); // </script>'; $response->message = _t('Saved block areas settings.'); } $this->setup_admin_theme(''); $blocks_areas_t = DB::get_results('SELECT b.*, ba.scope_id, ba.area, ba.display_order FROM {blocks} b INNER JOIN {blocks_areas} ba ON ba.block_id = b.id ORDER BY ba.scope_id ASC, ba.area ASC, ba.display_order ASC', array()); $blocks_areas = array(); foreach ($blocks_areas_t as $block) { if (!isset($blocks_areas[$block->scope_id])) { $blocks_areas[$block->scope_id] = array(); } $blocks_areas[$block->scope_id][$block->area][$block->display_order] = $block; } $this->theme->blocks_areas = $blocks_areas; $this->theme->scopeid = $scope; $this->theme->areas = $this->get_areas($scope); $scopes = DB::get_results('SELECT * FROM {scopes} ORDER BY name ASC;'); $scopes = Plugins::filter('get_scopes', $scopes); $this->theme->scopes = $scopes; $this->theme->active_theme = Themes::get_active_data(true); $output = $this->theme->fetch('block_areas'); $response->html('block_areas', $output); $response->out(); }
/** * Called from the themes page to save the blocks instances into areas * * @param mixed $handler_vars * @return */ public function ajax_save_areas($handler_vars) { Utils::check_request_method(array('POST')); $area_blocks = $_POST['area_blocks']; $scope = $_POST['scope']; DB::query('DELETE FROM {blocks_areas} WHERE scope_id = :scope_id', array('scope_id' => $scope)); foreach ((array) $area_blocks as $area => $blocks) { $display_order = 0; foreach ($blocks as $block) { $display_order++; DB::query('INSERT INTO {blocks_areas} (block_id, area, scope_id, display_order) VALUES (:block_id, :area, :scope_id, :display_order)', array('block_id' => $block, 'area' => $area, 'scope_id' => $scope, 'display_order' => $display_order)); } } $this->setup_admin_theme(''); $blocks_areas_t = DB::get_results('SELECT b.*, ba.scope_id, ba.area, ba.display_order FROM {blocks} b INNER JOIN {blocks_areas} ba ON ba.block_id = b.id ORDER BY ba.scope_id ASC, ba.area ASC, ba.display_order ASC', array()); $blocks_areas = array(); foreach ($blocks_areas_t as $block) { if (!isset($blocks_areas[$block->scope_id])) { $blocks_areas[$block->scope_id] = array(); } $blocks_areas[$block->scope_id][$block->area][$block->display_order] = $block; } $this->theme->blocks_areas = $blocks_areas; $this->theme->scopes = DB::get_results('SELECT * FROM {scopes} ORDER BY id ASC;'); $this->theme->active_theme = Themes::get_active_data(true); $this->display('block_areas'); $msg = json_encode(_t('Saved block areas settings.')); echo '<script type="text/javascript"> humanMsg.displayMsg(' . $msg . '); reset_block_form(); spinner.stop(); </script>'; }
/** * Handles AJAX requests to update comments, comment moderation */ public function ajax_update_comment( $handler_vars ) { Utils::check_request_method( array( 'POST' ) ); // check WSSE authentication $wsse = Utils::WSSE( $handler_vars['nonce'], $handler_vars['timestamp'] ); if ( $handler_vars['digest'] != $wsse['digest'] ) { Session::error( _t( 'WSSE authentication failed.' ) ); echo Session::messages_get( true, array( 'Format', 'json_messages' ) ); return; } $ids = array(); foreach ( $_POST as $id => $update ) { // skip POST elements which are not comment ids if ( preg_match( '/^p\d+$/', $id ) && $update ) { $ids[] = (int) substr( $id, 1 ); } } if ( ( ! isset( $ids ) || empty( $ids ) ) && $handler_vars['action'] == 'delete' ) { Session::notice( _t( 'No comments selected.' ) ); echo Session::messages_get( true, array( 'Format', 'json_messages' ) ); return; } $comments = Comments::get( array( 'id' => $ids, 'nolimit' => true ) ); Plugins::act( 'admin_moderate_comments', $handler_vars['action'], $comments, $this ); $status_msg = _t( 'Unknown action "%s"', array( $handler_vars['action'] ) ); switch ( $handler_vars['action'] ) { case 'delete_spam': Comments::delete_by_status( Comment::STATUS_SPAM ); $status_msg = _t( 'Deleted all spam comments' ); break; case 'delete_unapproved': Comments::delete_by_status( Comment::STATUS_UNAPPROVED ); $status_msg = _t( 'Deleted all unapproved comments' ); break; case 'delete': // Comments marked for deletion Comments::delete_these( $comments ); $status_msg = sprintf( _n( 'Deleted %d comment', 'Deleted %d comments', count( $ids ) ), count( $ids ) ); break; case 'spam': // Comments marked as spam Comments::moderate_these( $comments, Comment::STATUS_SPAM ); $status_msg = sprintf( _n( 'Marked %d comment as spam', 'Marked %d comments as spam', count( $ids ) ), count( $ids ) ); break; case 'approve': case 'approved': // Comments marked for approval Comments::moderate_these( $comments, Comment::STATUS_APPROVED ); $status_msg = sprintf( _n( 'Approved %d comment', 'Approved %d comments', count( $ids ) ), count( $ids ) ); break; case 'unapprove': case 'unapproved': // Comments marked for unapproval Comments::moderate_these( $comments, Comment::STATUS_UNAPPROVED ); $status_msg = sprintf( _n( 'Unapproved %d comment', 'Unapproved %d comments', count( $ids ) ), count( $ids ) ); break; default: // Specific plugin-supplied action $status_msg = Plugins::filter( 'admin_comments_action', $status_msg, $handler_vars['action'], $comments ); break; } Session::notice( $status_msg ); echo Session::messages_get( true, array( 'Format', 'json_messages' ) ); }
/** * function add_comment * adds a comment to a post, if the comment content is not NULL * @param array An associative array of content found in the $_POST array */ public function act_add_comment() { Utils::check_request_method(array('POST')); $defaults = array('name' => '', 'email' => '', 'url' => '', 'content' => ''); // We need to get the post anyway to redirect back to the post page. $post = Post::get(array('id' => $this->handler_vars['id'])); if (!$post) { // trying to comment on a non-existent post? Weirdo. header('HTTP/1.1 403 Forbidden', true, 403); die; } // make sure all our default values are set so we don't throw undefined index errors foreach ($defaults as $k => $v) { if (!isset($this->handler_vars[$k])) { $this->handler_vars[$k] = $v; } } // let's do some basic sanity checking on the submission if (1 == Options::get('comments_require_id') && (empty($this->handler_vars['name']) || empty($this->handler_vars['email']))) { Session::error(_t('Both name and e-mail address must be provided.')); } if (empty($this->handler_vars['content'])) { Session::error(_t('You did not provide any content for your comment!')); } if (Session::has_errors()) { // save whatever was provided in session data Session::add_to_set('comment', $this->handler_vars['name'], 'name'); Session::add_to_set('comment', $this->handler_vars['email'], 'email'); Session::add_to_set('comment', $this->handler_vars['url'], 'url'); Session::add_to_set('comment', $this->handler_vars['content'], 'content'); // now send them back to the form Utils::redirect($post->permalink . '#respond'); } if ($post->info->comments_disabled) { // comments are disabled, so let's just send // them back to the post's permalink Session::error(_t('Comments on this post are disabled!')); Utils::redirect($post->permalink); } /* Sanitize data */ foreach ($defaults as $k => $v) { $this->handler_vars[$k] = InputFilter::filter($this->handler_vars[$k]); } /* Sanitize the URL */ if (!empty($this->handler_vars['url'])) { $url = $this->handler_vars['url']; $parsed = InputFilter::parse_url($url); if ($parsed['is_relative']) { // guess if they meant to use an absolute link $parsed = InputFilter::parse_url('http://' . $url); if (!$parsed['is_error']) { $url = InputFilter::glue_url($parsed); } else { // disallow relative URLs $url = ''; } } if ($parsed['is_pseudo'] || $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') { // allow only http(s) URLs $url = ''; } else { // reconstruct the URL from the error-tolerant parsing // http:moeffju.net/blog/ -> http://moeffju.net/blog/ $url = InputFilter::glue_url($parsed); } $this->handler_vars['url'] = $url; } if (preg_match('/^\\p{Z}*$/u', $this->handler_vars['content'])) { Session::error(_t('Comment contains only whitespace/empty comment')); Utils::redirect($post->permalink); } /* Create comment object*/ $comment = new Comment(array('post_id' => $this->handler_vars['id'], 'name' => $this->handler_vars['name'], 'email' => $this->handler_vars['email'], 'url' => $this->handler_vars['url'], 'ip' => sprintf("%u", ip2long($_SERVER['REMOTE_ADDR'])), 'content' => $this->handler_vars['content'], 'status' => Comment::STATUS_UNAPPROVED, 'date' => HabariDateTime::date_create(), 'type' => Comment::COMMENT)); // Should this really be here or in a default filter? // In any case, we should let plugins modify the status after we set it here. $user = User::identify(); if ($user->loggedin && $comment->email == $user->email) { $comment->status = Comment::STATUS_APPROVED; } // Allow themes to work with comment hooks Themes::create(); $spam_rating = 0; $spam_rating = Plugins::filter('spam_filter', $spam_rating, $comment, $this->handler_vars); $comment->insert(); $anchor = ''; // If the comment was saved if ($comment->id) { $anchor = '#comment-' . $comment->id; // store in the user's session that this comment is pending moderation if ($comment->status == Comment::STATUS_UNAPPROVED) { Session::notice(_t('Your comment is pending moderation.'), 'comment_' . $comment->id); } // if no cookie exists, we should set one // but only if the user provided some details $cookie = 'comment_' . Options::get('GUID'); if (!isset($_COOKIE[$cookie]) && (!empty($this->handler_vars['name']) || !empty($this->handler_vars['email']) || !empty($this->handler_vars['url']))) { $cookie_content = $comment->name . '#' . $comment->email . '#' . $comment->url; $site_url = Site::get_path('base', true); setcookie($cookie, $cookie_content, time() + 31536000, $site_url); } } // Return the commenter to the original page. Utils::redirect($post->permalink . $anchor); }
/** * Grabs post data and inserts that data into the internal * handler_vars array, which eventually gets extracted into * the theme's ( and thereby the template_engine's ) local * symbol table for use in the theme's templates * * This is the default, generic function to grab posts. To * "filter" the posts retrieved, simply pass any filters to * the handler_vars variables associated with the post retrieval. * For instance, to filter by tag, ensure that handler_vars['tag'] * contains the tag to filter by. Simple as that. */ public function act_display($paramarray = array('user_filters' => array())) { Utils::check_request_method(array('GET', 'HEAD', 'POST')); // Get any full-query parameters $possible = array('user_filters', 'fallback', 'posts', 'post', 'content_type'); foreach ($possible as $varname) { if (isset($paramarray[$varname])) { ${$varname} = $paramarray[$varname]; } } /** * Since handler_vars no longer contains $_GET and $_POST, we have broken out our valid filters into * an array based upon where we should expect them to be. We then only merge those specific pieces together. * * These are ordered so that handler vars gets overwritten by POST, which gets overwritten by GET, should the * same key exist multiple places. This seemed logical to me at the time, but needs further thought. */ $where_filters = array(); $where_filters_hv = Controller::get_handler_vars()->filter_keys($this->valid_filters['handler_vars']); $where_filters_post = $_POST->filter_keys($this->valid_filters['POST']); $where_filters_get = $_GET->filter_keys($this->valid_filters['GET']); $where_filters = $where_filters_hv->merge($where_filters_post, $where_filters_get); $where_filters['vocabulary'] = array(); if (array_key_exists('tag', $where_filters)) { $tags = Tags::parse_url_tags($where_filters['tag']); $not_tag = $tags['exclude_tag']; $all_tag = $tags['include_tag']; if (count($not_tag) > 0) { $where_filters['vocabulary'] = array_merge($where_filters['vocabulary'], array(Tags::vocabulary()->name . ':not:term' => $not_tag)); } if (count($all_tag) > 0) { $where_filters['vocabulary'] = array_merge($where_filters['vocabulary'], array(Tags::vocabulary()->name . ':all:term' => $all_tag)); } $where_filters['tag_slug'] = Utils::slugify($where_filters['tag']); unset($where_filters['tag']); } if (!isset($_GET['preview'])) { $where_filters['status'] = Post::status('published'); } if (!isset($posts)) { $user_filters = Plugins::filter('template_user_filters', $user_filters); // Work around the tags parameters to Posts::get() being subsumed by the vocabulary parameter if (isset($user_filters['not:tag'])) { $user_filters['vocabulary'] = array(Tags::vocabulary()->name . ':not:term' => $user_filters['not:tag']); unset($user_filters['not:tag']); } if (isset($user_filters['tag'])) { $user_filters['vocabulary'] = array(Tags::vocabulary()->name . ':term_display' => $user_filters['tag']); unset($user_filters['tag']); } $where_filters = $where_filters->merge($user_filters); $where_filters = Plugins::filter('template_where_filters', $where_filters); $posts = Posts::get($where_filters); } $this->assign('posts', $posts); if ($posts !== false && count($posts) > 0) { if (count($posts) == 1) { $post = $posts instanceof Post ? $posts : reset($posts); Stack::add('body_class', Post::type_name($post->content_type) . '-' . $post->id); Stack::add('body_class', 'single'); } else { $post = reset($posts); Stack::add('body_class', 'multiple'); } $this->assign('post', $post); $type = Post::type_name($post->content_type); } elseif ($posts === false || isset($where_filters['page']) && $where_filters['page'] > 1 && count($posts) == 0) { if ($this->template_exists('404')) { $fallback = array('404'); // Replace template variables with the 404 rewrite rule $this->request->{URL::get_matched_rule()->name} = false; $this->request->{URL::set_404()->name} = true; $this->matched_rule = URL::get_matched_rule(); // 404 status header sent in act_display_404, but we're past // that, so send it now. header('HTTP/1.1 404 Not Found', true, 404); } else { $this->display('header'); echo '<h2>'; _e("Whoops! 404. The page you were trying to access is not really there. Please try again."); echo '</h2>'; header('HTTP/1.1 404 Not Found', true, 404); $this->display('footer'); die; } } $extract = $where_filters->filter_keys('page', 'type', 'id', 'slug', 'posttag', 'year', 'month', 'day', 'tag', 'tag_slug'); foreach ($extract as $key => $value) { ${$key} = $value; } $this->assign('page', isset($page) ? $page : 1); if (!isset($fallback)) { // Default fallbacks based on the number of posts $fallback = array('{$type}.{$id}', '{$type}.{$slug}', '{$type}.tag.{$posttag}'); if (count($posts) > 1) { $fallback[] = '{$type}.multiple'; $fallback[] = 'multiple'; } else { $fallback[] = '{$type}.single'; $fallback[] = 'single'; } } $searches = array('{$id}', '{$slug}', '{$year}', '{$month}', '{$day}', '{$type}', '{$tag}'); $replacements = array(isset($post) && $post instanceof Post ? $post->id : '-', isset($post) && $post instanceof Post ? $post->slug : '-', isset($year) ? $year : '-', isset($month) ? $month : '-', isset($day) ? $day : '-', isset($type) ? $type : '-', isset($tag_slug) ? $tag_slug : '-'); $fallback[] = 'home'; $fallback = Plugins::filter('template_fallback', $fallback, $posts, isset($post) ? $post : null); $fallback = array_values(array_unique(MultiByte::str_replace($searches, $replacements, $fallback))); for ($z = 0; $z < count($fallback); $z++) { if (MultiByte::strpos($fallback[$z], '{$posttag}') !== false && isset($post) && $post instanceof Post) { $replacements = array(); if ($alltags = $post->tags) { foreach ($alltags as $current_tag) { $replacements[] = MultiByte::str_replace('{$posttag}', $current_tag->term, $fallback[$z]); } array_splice($fallback, $z, 1, $replacements); } else { break; } } } return $this->display_fallback($fallback); }
/** * Handles AJAX from /admin/tags * Used to search for, delete and rename tags */ public function ajax_tags() { Utils::check_request_method(array('POST', 'HEAD')); $this->create_theme(); $params = $_POST['query']; // Get a usable array with filter parameters from the odd syntax we received from the faceted search $fetch_params = array(); if (isset($params)) { foreach ($params as $param) { $key = key($param); // Revert translation if ($key != 'text') { $key = self::$facets[$key]; } $value = current($param); if (array_key_exists($key, $fetch_params)) { $fetch_params[$key] = Utils::single_array($fetch_params[$key]); $fetch_params[$key][] = $value; } else { $fetch_params[$key] = $value; } } } // Grab facets / params $search = array_key_exists('text', $fetch_params) ? $fetch_params['text'] : null; $min = array_key_exists('morethan', $fetch_params) ? $fetch_params['morethan'] + 1 : 0; $max = array_key_exists('lessthan', $fetch_params) ? $fetch_params['lessthan'] - 1 : null; $orderby_code = array_key_exists('orderby', $fetch_params) ? $fetch_params['orderby'] : null; $orderby = isset($orderby_code) ? explode('_', self::$facet_values['orderby'][$orderby_code]) : ['alphabetical', 'asc']; $this->theme->tags = Tags::get_by_frequency(null, null, $min, $max, $search, self::$orderby_translate[$orderby[0]], $orderby[1] == 'asc'); // Create FormUI elements (list items) from the filtered tag list $this->theme->max = Tags::vocabulary()->max_count(); $this->theme->min = Tags::vocabulary()->min_count(); $output = ''; if (count($this->theme->tags) > 0) { $listitems = $this->get_tag_listitems(); // Get HTML from FormUI foreach ($listitems as $listitem) { $output .= $listitem->get($this->theme); } } $ar = new AjaxResponse(); $ar->html('#tag_collection', $output); $ar->out(); }
/** * Handles AJAX from /logs. * Used to delete logs. */ public function ajax_delete_logs($handler_vars) { Utils::check_request_method(array('POST')); $count = 0; $wsse = Utils::WSSE($handler_vars['nonce'], $handler_vars['timestamp']); if ($handler_vars['digest'] != $wsse['digest']) { Session::error(_t('WSSE authentication failed.')); echo Session::messages_get(true, array('Format', 'json_messages')); return; } foreach ($_POST as $id => $delete) { // skip POST elements which are not log ids if (preg_match('/^p\\d+$/', $id) && $delete) { $id = (int) substr($id, 1); $ids[] = array('id' => $id); } } if ((!isset($ids) || empty($ids)) && $handler_vars['action'] != 'purge') { Session::notice(_t('No logs selected.')); echo Session::messages_get(true, array('Format', 'json_messages')); return; } switch ($handler_vars['action']) { case 'delete': $to_delete = EventLog::get(array('date' => 'any', 'where' => $ids, 'nolimit' => 1)); foreach ($to_delete as $log) { $log->delete(); $count++; } Session::notice(_t('Deleted %d logs.', array($count))); break; case 'purge': $result = EventLog::purge(); Session::notice(_t('Logs purged.')); break; } echo Session::messages_get(true, array('Format', 'json_messages')); }
/** * Handles AJAX from /manage/posts. * Used to delete posts. */ public function ajax_update_posts($handler_vars) { Utils::check_request_method(array('POST')); $response = new AjaxResponse(); $wsse = Utils::WSSE($handler_vars['nonce'], $handler_vars['timestamp']); if ($handler_vars['digest'] != $wsse['digest']) { $response->message = _t('WSSE authentication failed.'); $response->out(); return; } $ids = array(); foreach ($_POST as $id => $delete) { // skip POST elements which are not post ids if (preg_match('/^p\\d+$/', $id) && $delete) { $ids[] = (int) substr($id, 1); } } if (count($ids) == 0) { $posts = new Posts(); } else { $posts = Posts::get(array('id' => $ids, 'nolimit' => true)); } Plugins::act('admin_update_posts', $handler_vars['action'], $posts, $this); $status_msg = _t('Unknown action "%s"', array($handler_vars['action'])); switch ($handler_vars['action']) { case 'delete': $deleted = 0; foreach ($posts as $post) { if (ACL::access_check($post->get_access(), 'delete')) { $post->delete(); $deleted++; } } if ($deleted != count($posts)) { $response->message = _t('You did not have permission to delete some posts.'); } else { $response->message = sprintf(_n('Deleted %d post', 'Deleted %d posts', count($ids)), count($ids)); } break; default: // Specific plugin-supplied action Plugins::act('admin_posts_action', $response, $handler_vars['action'], $posts); break; } $response->out(); exit; }
/** * Handles AJAX requests from the logs page. */ public function ajax_logs() { Utils::check_request_method(array('GET', 'HEAD')); $this->create_theme(); $this->fetch_logs(); $items = $this->theme->fetch('logs_items'); $timeline = $this->theme->fetch('timeline_items'); $item_ids = array(); foreach ($this->theme->logs as $log) { $item_ids['p' . $log->id] = 1; } $ar = new AjaxResponse(); $ar->data = array('items' => $items, 'item_ids' => $item_ids, 'timeline' => $timeline); $ar->out(); }
/** * Handle incoming requests for the Atom entry collection for comments on an entry */ function act_entry_comments() { Utils::check_request_method( array( 'GET', 'HEAD', 'POST' ) ); if ( isset( $this->handler_vars['slug'] ) ) { $this->act_comments( array( 'slug' => $this->handler_vars['slug'] ) ); } else { $this->act_comments( array( 'id' => $this->handler_vars['id'] ) ); } }
/** * Dispatches the request to the defined method. (ie: post_{page}) */ public function act_admin() { $page = isset($this->handler_vars['page']) && !empty($this->handler_vars['page']) ? $this->handler_vars['page'] : 'dashboard'; $page = filter_var($page, FILTER_SANITIZE_STRING); if (isset($this->handler_vars['content_type'])) { $type = Plugins::filter('post_type_display', Post::type_name($this->handler_vars['content_type']), 'singular'); } elseif ($page == 'publish' && isset($this->handler_vars['id'])) { $type = Post::type_name(Post::get(array('status' => Post::status('any'), 'id' => intval($this->handler_vars['id'])))->content_type); $type = Plugins::filter('post_type_display', $type, 'singular'); } else { $type = ''; } $this->setup_admin_theme($page, $type); // Access check to see if the user is allowed the requested page Utils::check_request_method(array('GET', 'HEAD', 'POST')); if (!$this->access_allowed($page, $type)) { Session::error(_t('Access to that page has been denied by the administrator.')); $this->get_blank(); } switch ($_SERVER['REQUEST_METHOD']) { case 'POST': // Let plugins try to handle the page Plugins::act('admin_theme_post_' . $page, $this, $this->theme); // Handle POSTs to the admin pages $fn = 'post_' . $page; if (method_exists($this, $fn)) { $this->{$fn}(); } else { $classname = get_class($this); _e('%1$s->%2$s() does not exist.', array($classname, $fn)); exit; } break; case 'GET': case 'HEAD': // Let plugins try to handle the page Plugins::act('admin_theme_get_' . $page, $this, $this->theme); // Handle GETs of the admin pages $fn = 'get_' . $page; if (method_exists($this, $fn)) { $this->{$fn}(); exit; } // If a get_ function doesn't exist, just load the template and display it if ($this->theme->template_exists($page)) { $this->display($page); } else { // The requested console page doesn't exist header('HTTP/1.1 404 Not Found', true, 404); $this->get_blank(_t('The page you were looking for was not found.')); } break; } }
/** * Handles AJAX requests from the groups page. */ public function ajax_groups($handler_vars) { Utils::check_request_method(array('GET', 'HEAD')); $this->create_theme(); $output = ''; foreach (UserGroups::get_all() as $group) { $this->theme->group = $group; $group = UserGroup::get_by_id($group->id); $users = array(); foreach ($group->members as $id) { $user = $id == 0 ? User::anonymous() : User::get_by_id($id); if ($user->id == 0) { $users[] = '<strong>' . $user->displayname . '</strong>'; } else { $users[] = '<strong><a href="' . URL::get('admin', 'page=user&id=' . $user->id) . '">' . $user->displayname . '</a></strong>'; } } $this->theme->users = $users; $output .= $this->theme->fetch('groups_item'); } $ar = new AjaxResponse(); $ar->data = array('items' => $output); $ar->out(); }
public function ajax_media_panel($handler_vars) { Utils::check_request_method(array('POST')); $path = $handler_vars['path']; $panelname = $handler_vars['panel']; $rpath = $path; $silo = Media::get_silo($rpath, true); // get_silo sets $rpath by reference to the path inside the silo $panel = ''; $panel = Plugins::filter('media_panels', $panel, $silo, $rpath, $panelname); $controls = array(); $controls = Plugins::filter('media_controls', $controls, $silo, $rpath, $panelname); $controls_out = ''; foreach ($controls as $k => $v) { if (is_numeric($k)) { $controls_out .= "<li>{$v}</li>"; } else { $controls_out .= "<li class=\"{$k}\">{$v}</li>"; } } $output = array('controls' => $controls_out, 'panel' => $panel); echo json_encode($output); }
/** * Grabs post data and inserts that data into the internal * handler_vars array, which eventually gets extracted into * the theme's ( and thereby the template_engine's ) local * symbol table for use in the theme's templates * * This is the default, generic function to grab posts. To * "filter" the posts retrieved, simply pass any filters to * the handler_vars variables associated with the post retrieval. * For instance, to filter by tag, ensure that handler_vars['tag'] * contains the tag to filter by. Simple as that. */ public function act_display($paramarray = array('user_filters' => array())) { Utils::check_request_method(array('GET', 'HEAD', 'POST')); // Get any full-query parameters $possible = array('user_filters', 'fallback', 'posts', 'post', 'content_type'); foreach ($possible as $varname) { if (isset($paramarray[$varname])) { ${$varname} = $paramarray[$varname]; } } $where_filters = array(); $where_filters = Controller::get_handler()->handler_vars->filter_keys($this->valid_filters); //$where_filters['status'] = Post::status( 'published' ); if (array_key_exists('tag', $where_filters)) { $where_filters['tag_slug'] = Utils::slugify($where_filters['tag']); unset($where_filters['tag']); } if (User::identify()->loggedin) { $where_filters['status'] = isset($_GET['preview']) ? Post::status('any') : Post::status('published'); } else { $where_filters['status'] = Post::status('published'); } if (!isset($posts)) { $user_filters = Plugins::filter('template_user_filters', $user_filters); $user_filters = array_intersect_key($user_filters, array_flip($this->valid_filters)); $where_filters = $where_filters->merge($user_filters); $where_filters = Plugins::filter('template_where_filters', $where_filters); $posts = Posts::get($where_filters); } $this->assign('posts', $posts); /* if( !isset( $this->page ) ) { if( isset( $page ) ) { $this->assign( 'page', $page ); } elseif( isset( Controller::get_handler()->handler_vars['page'] ) ) { $this->assign( 'page', Controller::get_handler()->handler_vars['page'] ); } }*/ if ($posts !== false && count($posts) > 0) { $post = count($posts) > 1 ? $posts[0] : $posts; $this->assign('post', $post); $types = array_flip(Post::list_active_post_types()); $type = $types[$post->content_type]; } elseif ($posts === false || isset($where_filters['page']) && $where_filters['page'] > 1 && count($posts) == 0) { if ($this->template_exists('404')) { $fallback = array('404'); // Replace template variables with the 404 rewrite rule $this->request->{URL::get_matched_rule()->name} = false; $this->request->{URL::set_404()->name} = true; $this->matched_rule = URL::get_matched_rule(); // 404 status header sent in act_display_404, but we're past // that, so send it now. header('HTTP/1.1 404 Not Found'); } else { $this->display('header'); echo '<h2>'; _e("Whoops! 404. The page you were trying to access is not really there. Please try again."); echo '</h2>'; header('HTTP/1.1 404 Not Found'); $this->display('footer'); die; } } $extract = $where_filters->filter_keys('page', 'type', 'id', 'slug', 'posttag', 'year', 'month', 'day', 'tag', 'tag_slug'); foreach ($extract as $key => $value) { ${$key} = $value; } $this->assign('page', isset($page) ? $page : 1); if (!isset($fallback)) { // Default fallbacks based on the number of posts $fallback = array('{$type}.{$id}', '{$type}.{$slug}', '{$type}.tag.{$posttag}'); if (count($posts) > 1) { $fallback[] = '{$type}.multiple'; $fallback[] = 'multiple'; } else { $fallback[] = '{$type}.single'; $fallback[] = 'single'; } } $searches = array('{$id}', '{$slug}', '{$year}', '{$month}', '{$day}', '{$type}', '{$tag}'); $replacements = array(isset($post) && $post instanceof Post ? $post->id : '-', isset($post) && $post instanceof Post ? $post->slug : '-', isset($year) ? $year : '-', isset($month) ? $month : '-', isset($day) ? $day : '-', isset($type) ? $type : '-', isset($tag_slug) ? $tag_slug : '-'); $fallback[] = 'home'; $fallback = Plugins::filter('template_fallback', $fallback); $fallback = array_values(array_unique(str_replace($searches, $replacements, $fallback))); for ($z = 0; $z < count($fallback); $z++) { if (strpos($fallback[$z], '{$posttag}') !== false && isset($post) && $post instanceof Post) { $replacements = array(); if ($alltags = $post->tags) { foreach ($alltags as $tag_slug => $tag_text) { $replacements[] = str_replace('{$posttag}', $tag_slug, $fallback[$z]); } array_splice($fallback, $z, 1, $replacements); } else { break; } } } return $this->display_fallback($fallback); }
/** * Handle password reset confirmations */ public function act_password_reset() { Utils::check_request_method(array('GET', 'HEAD', 'POST')); $id = $this->handler_vars['id']; $hash = $this->handler_vars['hash']; $name = ''; if ($user = User::get($id)) { if ($user->info->password_reset == md5($hash)) { // Send a new random password $password = Utils::random_password(); $user->password = Utils::crypt($password); if ($user->update()) { $message = _t("Your password for %1\$s has been reset. Your credentials are as follows---\nUsername: %2\$s\nPassword: %3\$s", array(Site::get_url('habari'), $user->username, $password)); Utils::mail($user->email, _t('[%1$s] Password has been reset for %2$s', array(Options::get('title'), $user->displayname)), $message); Session::notice(_t('A new password has been sent to the user.')); } else { Session::notice(_t('There was a problem resetting the password. It was not reset.')); } // Clear the request - it should only work once unset($user->info->password_reset); $user->info->commit(); $name = $user->username; } else { Session::notice(_t('The supplied password reset token has expired or is invalid.')); } } // Display the login form. $this->login_form($name); }
/** * Handles AJAX requests to update comments, comment moderation */ public function ajax_update_comment($handler_vars) { Utils::check_request_method(array('POST')); $ar = new AjaxResponse(); // check WSSE authentication $wsse = Utils::WSSE($_POST['nonce'], $_POST['timestamp']); if ($_POST['digest'] != $wsse['digest']) { $ar->message = _t('WSSE authentication failed.'); $ar->out(); return; } $ids = $_POST['selected']; if ((!isset($ids) || empty($ids)) && $_POST['action'] == 'delete') { $ar->message = _t('No comments selected.'); $ar->out(); return; } $comments = Comments::get(array('id' => $ids, 'nolimit' => true)); Plugins::act('admin_moderate_comments', $_POST['action'], $comments, $this); $status_msg = _t('Unknown action "%s"', array($handler_vars['action'])); switch ($_POST['action']) { case 'delete_spam': Comments::delete_by_status('spam'); $status_msg = _t('Deleted all spam comments'); break; case 'delete_unapproved': Comments::delete_by_status('unapproved'); $status_msg = _t('Deleted all unapproved comments'); break; case 'delete': // Comments marked for deletion Comments::delete_these($comments); $status_msg = sprintf(_n('Deleted %d comment', 'Deleted %d comments', count($ids)), count($ids)); break; case 'spam': // Comments marked as spam Comments::moderate_these($comments, 'spam'); $status_msg = sprintf(_n('Marked %d comment as spam', 'Marked %d comments as spam', count($ids)), count($ids)); break; case 'approve': case 'approved': // Comments marked for approval Comments::moderate_these($comments, 'approved'); $status_msg = sprintf(_n('Approved %d comment', 'Approved %d comments', count($ids)), count($ids)); break; case 'unapprove': case 'unapproved': // Comments marked for unapproval Comments::moderate_these($comments, 'unapproved'); $status_msg = sprintf(_n('Unapproved %d comment', 'Unapproved %d comments', count($ids)), count($ids)); break; default: // Specific plugin-supplied action $status_msg = Plugins::filter('admin_comments_action', $status_msg, $_POST['action'], $comments); break; } $ar->message = $status_msg; $ar->out(); }