/** * Skip to previous/next Item * * If several items share the same spot (like same issue datetime) then they'll get all skipped at once. * * @param string prev | next (relative to the current sort order) */ function &get_prevnext_Item($direction = 'next', $types = '', $featured = NULL, $post_navigation = 'same_blog') { global $DB, $ItemCache, $posttypes_specialtypes; if (!$this->single_post) { // We are not on a single post: $r = NULL; return $r; } /** * @var Item */ $current_Item = $this->get_by_idx(0); if (is_null($current_Item)) { // This happens if we are on a single post that we do not actually have permission to view $r = NULL; return $r; } if (in_array($current_Item->ptyp_ID, $posttypes_specialtypes)) { // We are not on a REGULAR post -- we cannot navigate: $r = NULL; return $r; } if (!empty($this->prevnext_Item[$direction][$post_navigation])) { return $this->prevnext_Item[$direction][$post_navigation]; } $next_Query = new ItemQuery($this->Cache->dbtablename, $this->Cache->dbprefix, $this->Cache->dbIDname); // GENERATE THE QUERY: /* * filtering stuff: */ $next_Query->where_chapter2($this->Blog, $this->filters['cat_array'], $this->filters['cat_modifier'], $this->filters['cat_focus']); $next_Query->where_author($this->filters['authors']); $next_Query->where_author_logins($this->filters['authors_login']); $next_Query->where_assignees($this->filters['assignees']); $next_Query->where_assignees_logins($this->filters['assignees_login']); $next_Query->where_author_assignee($this->filters['author_assignee']); $next_Query->where_locale($this->filters['lc']); $next_Query->where_statuses($this->filters['statuses']); // types param is kept only for the case when some custom types should be displayed $next_Query->where_types(!empty($types) ? $types : $this->filters['types']); $next_Query->where_keywords($this->filters['keywords'], $this->filters['phrase'], $this->filters['exact']); // $next_Query->where_ID( $this->filters['post_ID'], $this->filters['post_title'] ); $next_Query->where_datestart($this->filters['ymdhms'], $this->filters['week'], $this->filters['ymdhms_min'], $this->filters['ymdhms_max'], $this->filters['ts_min'], $this->filters['ts_max']); $next_Query->where_visibility($this->filters['visibility_array']); $next_Query->where_featured($featured); /* * ORDER BY stuff: */ if ($direction == 'next' && $this->filters['order'] == 'DESC' || $direction == 'prev' && $this->filters['order'] == 'ASC') { $order = 'DESC'; $operator = ' < '; } else { $order = 'ASC'; $operator = ' > '; } $orderby = str_replace(' ', ',', $this->filters['orderby']); $orderby_array = explode(',', $orderby); // Format each order param with default column names: $orderbyorder_array = preg_replace('#^(.+)$#', $this->Cache->dbprefix . '$1 ' . $order, $orderby_array); // Add an ID parameter to make sure there is no ambiguity in ordering on similar items: $orderbyorder_array[] = $this->Cache->dbIDname . ' ' . $order; $order_by = implode(', ', $orderbyorder_array); // Special case for RAND: $order_by = str_replace($this->Cache->dbprefix . 'RAND ', 'RAND() ', $order_by); $next_Query->order_by($order_by); // LIMIT to 1 single result $next_Query->LIMIT('1'); // fp> TODO: I think some additional limits need to come back here (for timespans) /* * Position right after the current element depending on current sorting params * * If there are several items on the same issuedatetime for example, we'll then differentiate on post ID * WARNING: you cannot combine criterias with AND here; you need stuf like a>a0 OR (a=a0 AND b>b0) */ switch ($orderby_array[0]) { case 'datestart': // special var name: $next_Query->WHERE_and($this->Cache->dbprefix . $orderby_array[0] . $operator . $DB->quote($current_Item->issue_date) . ' OR ( ' . $this->Cache->dbprefix . $orderby_array[0] . ' = ' . $DB->quote($current_Item->issue_date) . ' AND ' . $this->Cache->dbIDname . $operator . $current_Item->ID . ')'); break; case 'title': case 'ptyp_ID': case 'datecreated': case 'datemodified': case 'last_touched_ts': case 'urltitle': case 'priority': $next_Query->WHERE_and($this->Cache->dbprefix . $orderby_array[0] . $operator . $DB->quote($current_Item->{$orderby_array[0]}) . ' OR ( ' . $this->Cache->dbprefix . $orderby_array[0] . ' = ' . $DB->quote($current_Item->{$orderby_array[0]}) . ' AND ' . $this->Cache->dbIDname . $operator . $current_Item->ID . ')'); break; case 'order': // We have to integrate a rounding error margin $comp_order_value = $current_Item->order; $and_clause = ''; if (is_null($comp_order_value)) { // current Item has NULL order if ($operator == ' < ') { // This is needed when browsing through a descending ordered list and we reach the limit where orders are not set/NULL (ex: b2evo screenshots) $and_clause .= $this->Cache->dbprefix . $orderby_array[0] . ' IS NULL AND '; } else { // This is needed when browsing through a descending ordered list and we want to browse back into the posts that have numbers (pb appears if first NULL posts is the highest ID) $and_clause .= $this->Cache->dbprefix . $orderby_array[0] . ' IS NOT NULL OR '; } $and_clause .= $this->Cache->dbIDname . $operator . $current_Item->ID; } else { if ($operator == ' < ') { // This is needed when browsing through a descending ordered list and we reach the limit where orders are not set/NULL (ex: b2evo screenshots) $and_clause .= $this->Cache->dbprefix . $orderby_array[0] . ' IS NULL OR '; } $and_clause .= $this->Cache->dbprefix . $orderby_array[0] . $operator . ($operator == ' < ' ? $comp_order_value - 1.0E-9 : $comp_order_value + 1.0E-9) . ' OR ( ' . $this->Cache->dbprefix . $orderby_array[0] . ($operator == ' < ' ? ' <= ' . ($comp_order_value + 1.0E-9) : ' >= ' . ($comp_order_value - 1.0E-9)) . ' AND ' . $this->Cache->dbIDname . $operator . $current_Item->ID . ')'; } $next_Query->WHERE_and($and_clause); break; case 'RAND': // Random order. Don't show current item again. $next_Query->WHERE_and($this->Cache->dbprefix . 'ID <> ' . $current_Item->ID); break; default: echo 'WARNING: unhandled sorting: ' . htmlspecialchars($orderby_array[0]); } // GET DATA ROWS: // We are going to proceed in two steps (we simulate a subquery) // 1) we get the IDs we need // 2) we get all the other fields matching these IDs // This is more efficient than manipulating all fields at once. // Step 1: $step1_sql = 'SELECT DISTINCT ' . $this->Cache->dbIDname . $next_Query->get_from() . $next_Query->get_where() . $next_Query->get_group_by() . $next_Query->get_order_by() . $next_Query->get_limit(); //echo $DB->format_query( $step1_sql ); // Get list of the IDs we need: $next_ID = $DB->get_var($step1_sql, 0, 0, 'Get ID of next item'); //pre_dump( $next_ID ); // Step 2: get the item (may be NULL): $this->prevnext_Item[$direction][$post_navigation] =& $ItemCache->get_by_ID($next_ID, true, false); return $this->prevnext_Item[$direction][$post_navigation]; }
/** * Get datetime of the last post/item * @todo dh> Optimize this, if this can be said after having done {@link query()} already. * @todo dh> Cache result * @param string Date format (see {@link date()}) * @return string 'Y-m-d H:i:s' formatted; If there are no items this will be {@link $localtimenow}. */ function get_lastpostdate($dateformat = 'Y-m-d H:i:s') { global $localtimenow, $DB; if (empty($this->filters)) { // Filters have no been set before, we'll use the default filterset: // echo ' Query:Setting default filterset '; $this->set_filters($this->default_filters); } // GENERATE THE QUERY: // The SQL Query object: $lastpost_ItemQuery = new ItemQuery($this->Cache->dbtablename, $this->Cache->dbprefix, $this->Cache->dbIDname); /* * filtering stuff: */ $lastpost_ItemQuery->where_chapter2($this->Blog, $this->filters['cat_array'], $this->filters['cat_modifier'], $this->filters['cat_focus'], $this->filters['coll_IDs']); $lastpost_ItemQuery->where_author($this->filters['authors']); $lastpost_ItemQuery->where_author_logins($this->filters['authors_login']); $lastpost_ItemQuery->where_assignees($this->filters['assignees']); $lastpost_ItemQuery->where_assignees_logins($this->filters['assignees_login']); $lastpost_ItemQuery->where_locale($this->filters['lc']); $lastpost_ItemQuery->where_statuses($this->filters['statuses']); $lastpost_ItemQuery->where_types($this->filters['types']); $lastpost_ItemQuery->where_keywords($this->filters['keywords'], $this->filters['phrase'], $this->filters['exact'], $this->filters['keyword_scope']); $lastpost_ItemQuery->where_ID($this->filters['post_ID'], $this->filters['post_title']); $lastpost_ItemQuery->where_datestart($this->filters['ymdhms'], $this->filters['week'], $this->filters['ymdhms_min'], $this->filters['ymdhms_max'], $this->filters['ts_min'], $this->filters['ts_max']); $lastpost_ItemQuery->where_visibility($this->filters['visibility_array']); /* * order by stuff: * LAST POST FIRST!!! (That's the whole point!) */ $lastpost_ItemQuery->order_by($this->Cache->dbprefix . 'datestart DESC'); /* * Paging limits: * ONLY THE LAST POST!!! */ $lastpost_ItemQuery->LIMIT('1'); // Select the datestart: $lastpost_ItemQuery->select($this->Cache->dbprefix . 'datestart'); $lastpostdate = $DB->get_var($lastpost_ItemQuery->get(), 0, 0, 'Get last post date'); if (empty($lastpostdate)) { // echo 'we have no last item'; $lastpostdate = date($dateformat, $localtimenow); } elseif ($dateformat != 'Y-m-d H:i:s') { $lastpostdate = date($dateformat, strtotime($lastpostdate)); } // echo $lastpostdate; return $lastpostdate; }
/** * Get links to navigate between month / year. * * Unless min/max_timestamp='query' has been specified, this will not do any (time consuming!) queries to check where the posts are. * * @param string 'prev' / 'next' * @return array */ function getNavLinks($direction) { global $DB, $localtimenow; //pre_dump( 'get_nav_links', $direction ); $r = array(); if ($this->params['min_timestamp'] == 'query' || $this->params['max_timestamp'] == 'query') { // Do inits: // WE NEED SPECIAL QUERY PARAMS WHEN MOVING THOUGH MONTHS ( NO dstart especially! ) $nav_ItemQuery = new ItemQuery($this->dbtable, $this->dbprefix, $this->dbIDname); // TEMP object // Restrict to selected blog/categories: $nav_ItemQuery->where_chapter2($this->ItemQuery->Blog, $this->ItemQuery->cat_array, $this->ItemQuery->cat_modifier); // Restrict to the statuses we want to show: $nav_ItemQuery->where_visibility($this->ItemQuery->show_statuses); // Restrict to selected authors: $nav_ItemQuery->where_author($this->ItemQuery->author); // if a month is specified in the querystring, load that month: $nav_ItemQuery->where_datestart('', '', '', '', $this->ItemQuery->timestamp_min, $this->ItemQuery->timestamp_max); // Keyword search stuff: $nav_ItemQuery->where_keywords($this->ItemQuery->keywords, $this->ItemQuery->phrase, $this->ItemQuery->exact); // Exclude pages and intros: $nav_ItemQuery->where_types($this->ItemQuery->types); } switch ($direction) { case 'prev': //pre_dump( $this->params['min_timestamp'] ); $r[] = ''; if (empty($this->month)) { // if $this->month is empty, we're in mode "year" with no selected month $use_range_month = 12; $use_range_day = 31; } else { $use_range_month = $this->month; $use_range_day = 1; // Note: cannot use current day since all months do not have same number of days } /* * << (PREV YEAR) */ if ($this->browseyears) { // We want arrows to move one year at a time if ($this->params['min_timestamp'] == 'query') { // Let's query to find the correct year: if ($row = $DB->get_row('SELECT EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) AS year, EXTRACT(MONTH FROM ' . $this->dbprefix . 'datestart) AS month FROM (' . $this->dbtable . ' INNER JOIN T_postcats ON ' . $this->dbIDname . ' = postcat_post_ID) INNER JOIN T_categories ON postcat_cat_ID = cat_ID WHERE EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) < ' . $this->year . ' ' . $nav_ItemQuery->get_where(' AND ') . $nav_ItemQuery->get_group_by(' GROUP BY ') . ' ORDER BY EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) DESC, ABS( ' . $use_range_month . ' - EXTRACT(MONTH FROM ' . $this->dbprefix . 'datestart) ) ASC LIMIT 1', OBJECT, 0, 'Calendar: find prev year with posts')) { $prev_year_year = $row->year; $prev_year_month = $row->month; } } else { // Let's see if the previous year is in the desired navigation range: $prev_year_ts = mktime(0, 0, 0, $use_range_month, $use_range_day, $this->year - 1); if ($prev_year_ts >= $this->params['min_timestamp']) { $prev_year_year = date('Y', $prev_year_ts); $prev_year_month = date('m', $prev_year_ts); } } } if (!empty($prev_year_year)) { // We have a link to display: $r[] = $this->archive_link('<<', sprintf($this->mode == 'month' ? T_('Previous year (%04d-%02d)') : T_('Previous year (%04d)'), $prev_year_year, $prev_year_month), $prev_year_year, $this->mode == 'month' ? $prev_year_month : NULL); } /* * < (PREV MONTH) */ if ($this->mode == 'month') { // We are browsing months, we'll display arrows to move one month at a time: if ($this->params['min_timestamp'] == 'query') { // Let's query to find the correct month: if ($row = $DB->get_row('SELECT EXTRACT(MONTH FROM ' . $this->dbprefix . 'datestart) AS month, EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) AS year FROM (' . $this->dbtable . ' INNER JOIN T_postcats ON ' . $this->dbIDname . ' = postcat_post_ID) INNER JOIN T_categories ON postcat_cat_ID = cat_ID WHERE ( EXTACT(YEAR FROM ' . $this->dbprefix . 'datestart) < ' . $this->year . ' OR ( EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) = ' . $this->year . ' AND EXTRACT(MONTH FROM ' . $this->dbprefix . 'datestart) < ' . $this->month . ' ) ) ' . $nav_ItemQuery->get_where(' AND ') . $nav_ItemQuery->get_group_by(' GROUP BY ') . ' ORDER BY EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) DESC, EXTRACT(MONTH FROM ' . $this->dbprefix . 'datestart) DESC LIMIT 1', OBJECT, 0, 'Calendar: Find prev month with posts')) { $prev_month_year = $row->year; $prev_month_month = $row->month; } } else { // Let's see if the previous month is in the desired navigation range: $prev_month_ts = mktime(0, 0, 0, $this->month - 1, 1, $this->year); // Note: cannot use current day since all months do not have same number of days if ($prev_month_ts >= $this->params['min_timestamp']) { $prev_month_year = date('Y', $prev_month_ts); $prev_month_month = date('m', $prev_month_ts); } } } if (!empty($prev_month_year)) { // We have a link to display: $r[] = $this->archive_link('<', sprintf(T_('Previous month (%04d-%02d)'), $prev_month_year, $prev_month_month), $prev_month_year, $prev_month_month); } break; case 'next': //pre_dump( $this->params['max_timestamp'] ); /* * > (NEXT MONTH) */ if ($this->mode == 'month') { // We are browsing months, we'll display arrows to move one month at a time: if ($this->params['max_timestamp'] == 'query') { // Let's query to find the correct month: if ($row = $DB->get_row('SELECT EXTRACT(MONTH FROM ' . $this->dbprefix . 'datestart) AS month, EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) AS year FROM (' . $this->dbtable . ' INNER JOIN T_postcats ON ' . $this->dbIDname . ' = postcat_post_ID) INNER JOIN T_categories ON postcat_cat_ID = cat_ID WHERE ( EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) > ' . $this->year . ' OR ( EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) = ' . $this->year . ' AND EXTRACT(MONTH FROM ' . $this->dbprefix . 'datestart) > ' . $this->month . ' ) ) ' . $nav_ItemQuery->get_where(' AND ') . $nav_ItemQuery->get_group_by(' GROUP BY ') . ' ORDER BY EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart), EXTRACT(MONTH FROM ' . $this->dbprefix . 'datestart) ASC LIMIT 1', OBJECT, 0, 'Calendar: Find next month with posts')) { $next_month_year = $row->year; $next_month_month = $row->month; } } else { // Let's see if the next month is in the desired navigation range: $next_month_ts = mktime(0, 0, 0, $this->month + 1, 1, $this->year); // Note: cannot use current day since all months do not have same number of days if ($next_month_ts <= $this->params['max_timestamp']) { $next_month_year = date('Y', $next_month_ts); $next_month_month = date('m', $next_month_ts); } } } if (!empty($next_month_year)) { // We have a link to display: $r[] = $this->archive_link('>', sprintf(T_('Next month (%04d-%02d)'), $next_month_year, $next_month_month), $next_month_year, $next_month_month); } if (empty($this->month)) { // if $this->month is empty, we're in mode "year" with no selected month $use_range_month = 12; $use_range_day = 31; } else { $use_range_month = $this->month; $use_range_day = 1; // Note: cannot use current day since all months do not have same number of days } /* * >> (NEXT YEAR) */ if ($this->browseyears) { // We want arrows to move one year at a time if ($this->params['max_timestamp'] == 'query') { // Let's query to find the correct year: if ($row = $DB->get_row('SELECT EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) AS year, EXTRACT(MONTH FROM ' . $this->dbprefix . 'datestart) AS month FROM (' . $this->dbtable . ' INNER JOIN T_postcats ON ' . $this->dbIDname . ' = postcat_post_ID) INNER JOIN T_categories ON postcat_cat_ID = cat_ID WHERE EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) > ' . $this->year . ' ' . $nav_ItemQuery->get_where(' AND ') . $nav_ItemQuery->get_group_by(' GROUP BY ') . ' ORDER BY EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart) ASC, ABS( ' . $use_range_month . ' - EXTRACT(MONTH FROM ' . $this->dbprefix . 'datestart) ) ASC LIMIT 1', OBJECT, 0, 'Calendar: find next year with posts')) { $next_year_year = $row->year; $next_year_month = $row->month; } } else { // Let's see if the next year is in the desired navigation range: $next_year_ts = mktime(0, 0, 0, $use_range_month, $use_range_day, $this->year + 1); if ($next_year_ts <= $this->params['max_timestamp']) { $next_year_year = date('Y', $next_year_ts); $next_year_month = date('m', $next_year_ts); } } if (!empty($next_year_year)) { // We have a link to display: $r[] = $this->archive_link('>>', sprintf($this->mode == 'month' ? T_('Next year (%04d-%02d)') : T_('Next year (%04d)'), $next_year_year, $next_year_month), $next_year_year, $this->mode == 'month' ? $next_year_month : NULL); } } break; } return $r; }