break; case 'publish': case 'publish_now': // Publish NOW: // Check that this action request is not a CSRF hacked request: $Session->assert_received_crumb('item'); $post_status = $action == 'publish_now' ? 'published' : param('post_status', 'string', 'published'); // Check permissions: /* TODO: Check extra categories!!! */ $current_User->check_perm('item_post!' . $post_status, 'edit', true, $edited_Item); $edited_Item->set('status', $post_status); if ($action == 'publish_now') { // Update post dates $current_User->check_perm('blog_edit_ts', 'edit', true, $Blog->ID); // fp> TODO: remove seconds ONLY if date is in the future $edited_Item->set('datestart', remove_seconds($localtimenow)); $edited_Item->set('datemodified', date('Y-m-d H:i:s', $localtimenow)); } // UPDATE POST IN DB: $edited_Item->dbupdate(); // Execute or schedule notifications & pings: $edited_Item->handle_post_processing(false); // Set the success message corresponding for the new status switch ($edited_Item->status) { case 'published': $success_message = T_('Post has been published.'); break; case 'community': $success_message = T_('The post is now visible by the community.'); break; case 'protected':
/** * Set param value * * By default, all values will be considered strings * * @todo extra_cat_IDs recording * * @param string parameter name * @param mixed parameter value * @param boolean true to set to NULL if empty value * @return boolean true, if a value has been set; false if it has not changed */ function set($parname, $parvalue, $make_null = false) { switch ($parname) { case 'main_cat_ID': $r = $this->set_param('main_cat_ID', 'number', $parvalue, false); // make sure main cat is in extracat list and there are no duplicates $this->extra_cat_IDs[] = $this->main_cat_ID; $this->extra_cat_IDs = array_unique($this->extra_cat_IDs); // Invalidate derived property: $this->blog_ID = NULL; unset($this->main_Chapter); // dereference $this->main_Chapter = NULL; unset($this->Blog); $this->Blog = NULL; return $r; case 'extra_cat_IDs': // ARRAY! We do not record this change (yet) $this->extra_cat_IDs = $parvalue; // make sure main cat is in extracat list and there are no duplicates $this->extra_cat_IDs[] = $this->main_cat_ID; $this->extra_cat_IDs = array_unique($this->extra_cat_IDs); break; case 'issue_date': case 'datestart': // Remove seconds from issue date and start date // fp> TODO: this should only be done if the date is in the future. If it's in the past there are no sideeffects to having seconds. // asimo> Why do we have two parameter with the same content if only one is stored in the database? // Also it doesn't make sense to remove seconds from a db date field because the database format has seconds anyway. // If we remove seconds from datstart field, then datestart will be always inserted into the dbchagnes even if it was not changed. // If we don't want seconds in the end of the datestart then we need to remove it in the itemlight constructor as well. // asimo> We have to set seconds to '00' and not remove them, this way the posts can appear right after creating, and the format is OK as well. $parvalue_empty_seconds = remove_seconds(strtotime($parvalue), 'Y-m-d H:i:s'); $this->issue_date = $parvalue_empty_seconds; return $this->set_param('datestart', 'date', $parvalue_empty_seconds, false); case 'ptyp_ID': case 'canonical_slug_ID': case 'tiny_slug_ID': case 'dateset': case 'excerpt_autogenerated': return $this->set_param($parname, 'number', $parvalue, true); default: return $this->set_param($parname, 'string', $parvalue, $make_null); } }
/** * Restricts the datestart param to a specific date range. * * Start date gets restricted to minutes only (to make the query more * cachable). * * Priorities: * -dstart and/or dstop * -week + m * -m * @todo -dstart + x days * @see ItemList2::get_advertised_start_date() * * @param string YYYYMMDDHHMMSS (everything after YYYY is optional) or '' * @param integer week number or '' * @param string YYYYMMDDHHMMSS to start at, '' for first available * @param string YYYYMMDDHHMMSS to stop at * @param mixed Do not show posts before this timestamp, can be 'now' * @param mixed Do not show posts after this timestamp, can be 'now' */ function where_datestart($m = '', $w = '', $dstart = '', $dstop = '', $timestamp_min = '', $timestamp_max = 'now') { global $time_difference, $DB; $this->m = $m; $this->w = $w; $this->dstart = $dstart; $this->dstop = $dstop; $this->timestamp_min = $timestamp_min; $this->timestamp_max = $timestamp_max; $start_is_set = false; $stop_is_set = false; // if a start date is specified in the querystring, crop anything before if (!empty($dstart)) { // Add trailing 0s: YYYYMMDDHHMMSS $dstart0 = $dstart . '00000000000000'; // TODO: this is NOT correct, should be 0101 for month // Start date in MySQL format: seconds get omitted (rounded to lower to minute for caching purposes) $dstart_mysql = substr($dstart0, 0, 4) . '-' . substr($dstart0, 4, 2) . '-' . substr($dstart0, 6, 2) . ' ' . substr($dstart0, 8, 2) . ':' . substr($dstart0, 10, 2); $this->WHERE_and($this->dbprefix . 'datestart >= ' . $DB->quote($dstart_mysql) . ' OR ( ' . $this->dbprefix . 'datedeadline IS NULL AND ' . $this->dbprefix . 'datestart >= ' . $DB->quote($dstart_mysql) . ' )'); $start_is_set = true; } // if a stop date is specified in the querystring, crop anything before if (!empty($dstop)) { switch (strlen($dstop)) { case '4': // We have only year, add one to year $dstop_mysql = $dstop + 1 . '-01-01 00:00:00'; break; case '6': // We have year month, add one to month $dstop_mysql = date("Y-m-d H:i:s ", mktime(0, 0, 0, substr($dstop, 4, 2) + 1, 01, substr($dstop, 0, 4))); break; case '8': // We have year mounth day, add one to day $dstop_mysql = date("Y-m-d H:i:s ", mktime(0, 0, 0, substr($dstop, 4, 2), substr($dstop, 6, 2) + 1, substr($dstop, 0, 4))); break; case '10': // We have year mounth day hour, add one to hour $dstop_mysql = date("Y-m-d H:i:s ", mktime(substr($dstop, 8, 2) + 1, 0, 0, substr($dstop, 4, 2), substr($dstop, 6, 2), substr($dstop, 0, 4))); break; case '12': // We have year mounth day hour minute, add one to minute $dstop_mysql = date("Y-m-d H:i:s ", mktime(substr($dstop, 8, 2), substr($dstop, 8, 2) + 1, 0, substr($dstop, 4, 2), substr($dstop, 6, 2), substr($dstop, 0, 4))); break; default: // add one to second // Stop date in MySQL format: seconds get omitted (rounded to lower to minute for caching purposes) $dstop_mysql = substr($dstop, 0, 4) . '-' . substr($dstop, 4, 2) . '-' . substr($dstop, 6, 2) . ' ' . substr($dstop, 8, 2) . ':' . substr($dstop, 10, 2); } $this->WHERE_and($this->dbprefix . 'datestart < ' . $DB->quote($dstop_mysql)); // NOT <= comparator because we compare to the superior stop date $stop_is_set = true; } if (!$start_is_set || !$stop_is_set) { if (!is_null($w) && strlen($m) == 4) { // If a week number is specified (with a year) // Note: we use PHP to calculate week boundaries in order to handle weeks // that overlap 2 years properly, even when start on week is monday (which MYSQL won't handle properly) $start_date_for_week = get_start_date_for_week($m, $w, locale_startofweek()); $this->WHERE_and($this->dbprefix . "datestart >= '" . date('Y-m-d', $start_date_for_week) . "'"); $this->WHERE_and($this->dbprefix . "datestart < '" . date('Y-m-d', $start_date_for_week + 604800) . "'"); // + 7 days $start_is_set = true; $stop_is_set = true; } elseif (!empty($m)) { // We want to restrict on an interval: $this->WHERE_and('EXTRACT(YEAR FROM ' . $this->dbprefix . 'datestart)=' . intval(substr($m, 0, 4))); if (strlen($m) > 5) { $this->WHERE_and('EXTRACT(MONTH FROM ' . $this->dbprefix . 'datestart)=' . intval(substr($m, 4, 2))); } if (strlen($m) > 7) { $this->WHERE_and('EXTRACT(DAY FROM ' . $this->dbprefix . 'datestart)=' . intval(substr($m, 6, 2))); } if (strlen($m) > 9) { $this->WHERE_and('EXTRACT(HOUR FROM ' . $this->dbprefix . 'datestart)=' . intval(substr($m, 8, 2))); } if (strlen($m) > 11) { $this->WHERE_and('EXTRACT(MINUTE FROM ' . $this->dbprefix . 'datestart)=' . intval(substr($m, 10, 2))); } if (strlen($m) > 13) { $this->WHERE_and('EXTRACT(SECOND FROM ' . $this->dbprefix . 'datestart)=' . intval(substr($m, 12, 2))); } $start_is_set = true; $stop_is_set = true; } } // TODO: start + x days // TODO: stop - x days // SILENT limits! // Timestamp limits: if ($timestamp_min == 'now') { // echo 'hide past'; $timestamp_min = time(); } if (!empty($timestamp_min)) { // Hide posts before // echo 'hide before '.$timestamp_min; $date_min = remove_seconds($timestamp_min + $time_difference); $this->WHERE_and($this->dbprefix . 'datestart >= ' . $DB->quote($date_min)); } if ($timestamp_max == 'now') { // echo 'hide future'; $timestamp_max = time(); } if (!empty($timestamp_max)) { // Hide posts after // echo 'after'; $date_max = remove_seconds($timestamp_max + $time_difference); $this->WHERE_and($this->dbprefix . 'datestart <= ' . $DB->quote($date_max)); } }
/** * Display the widget! * * @param array MUST contain at least the basic display params */ function display($params) { global $localtimenow; $this->init_display($params); if ($this->disp_params['order_by'] == 'RAND' && isset($this->BlockCache)) { // Do NOT cache if display order is random $this->BlockCache->abort_collect(); } global $Blog; $list_blogs = $this->disp_params['blog_ID'] ? $this->disp_params['blog_ID'] : $Blog->ID; //pre_dump( $list_blogs ); // Display photos: // TODO: permissions, complete statuses... // TODO: A FileList object based on ItemListLight but adding File data into the query? // overriding ItemListLigth::query() for starters ;) $FileCache =& get_FileCache(); $FileList = new DataObjectList2($FileCache); // Query list of files: $SQL = new SQL(); $SQL->SELECT('post_ID, post_datestart, post_datemodified, post_main_cat_ID, post_urltitle, post_canonical_slug_ID, post_tiny_slug_ID, post_ptyp_ID, post_title, post_excerpt, post_url, file_ID, file_title, file_root_type, file_root_ID, file_path, file_alt, file_desc'); $SQL->FROM('T_categories INNER JOIN T_postcats ON cat_ID = postcat_cat_ID INNER JOIN T_items__item ON postcat_post_ID = post_ID INNER JOIN T_links ON post_ID = link_itm_ID INNER JOIN T_files ON link_file_ID = file_ID'); $SQL->WHERE('cat_blog_ID IN (' . $list_blogs . ')'); // fp> TODO: want to restrict on images :] $SQL->WHERE_and('post_status = "published"'); // TODO: this is a dirty hack. More should be shown. $SQL->WHERE_and('post_datestart <= \'' . remove_seconds($localtimenow) . '\''); if (!empty($this->disp_params['item_type'])) { // Get items only with specified type $SQL->WHERE_and('post_ptyp_ID = ' . intval($this->disp_params['item_type'])); } $SQL->GROUP_BY('link_ID'); $SQL->LIMIT($this->disp_params['limit'] * 4); // fp> TODO: because we have no way of getting images only, we get 4 times more data than requested and hope that 25% at least will be images :/ $SQL->ORDER_BY(gen_order_clause($this->disp_params['order_by'], $this->disp_params['order_dir'], 'post_', 'post_ID ' . $this->disp_params['order_dir'] . ', link_ID')); $FileList->sql = $SQL->get(); $FileList->query(false, false, false, 'Media index widget'); $layout = $this->disp_params['thumb_layout']; $nb_cols = $this->disp_params['grid_nb_cols']; $count = 0; $r = ''; /** * @var File */ while ($File =& $FileList->get_next()) { if ($count >= $this->disp_params['limit']) { // We have enough images already! break; } if (!$File->is_image()) { // Skip anything that is not an image // fp> TODO: maybe this property should be stored in link_ltype_ID or in the files table continue; } if ($layout == 'grid') { if ($count % $nb_cols == 0) { $r .= $this->disp_params['grid_colstart']; } $r .= $this->disp_params['grid_cellstart']; } else { $r .= $this->disp_params['item_start']; } // 1/ Hack a dirty permalink( will redirect to canonical): // $link = url_add_param( $Blog->get('url'), 'p='.$post_ID ); // 2/ Hack a link to the right "page". Very daring!! // $link = url_add_param( $Blog->get('url'), 'paged='.$count ); // 3/ Instantiate a light object in order to get permamnent url: $ItemLight = new ItemLight($FileList->get_row_by_idx($FileList->current_idx - 1)); // index had already been incremented $r .= '<a href="' . $ItemLight->get_permanent_url() . '">'; // Generate the IMG THUMBNAIL tag with all the alt, title and desc if available $r .= $File->get_thumb_imgtag($this->disp_params['thumb_size'], '', '', $ItemLight->title); $r .= '</a>'; if ($this->disp_params['disp_image_title']) { $title = $File->get('title') ? $this->get('title') : $ItemLight->title; $r .= '<span class="note">' . $title . '</span>'; } ++$count; if ($layout == 'grid') { $r .= $this->disp_params['grid_cellend']; if ($count % $nb_cols == 0) { $r .= $this->disp_params['grid_colend']; } } else { $r .= $this->disp_params['item_end']; } } // Exit if no files found if (empty($r)) { return; } echo $this->disp_params['block_start']; // Display title if requested $this->disp_title(); if ($layout == 'grid') { echo $this->disp_params['grid_start']; } else { echo $this->disp_params['list_start']; } echo $r; if ($layout == 'grid') { if ($count && $count % $nb_cols != 0) { echo $this->disp_params['grid_colend']; } echo $this->disp_params['grid_end']; } else { echo $this->disp_params['list_end']; } echo $this->disp_params['block_end']; return true; }
/** * Retrieves all tags from published posts * * @param integer the id of the blog or array of blog ids. Set NULL to use current blog * @param integer maximum number of returned tags * @param string a comma separated list of tags to ignore/exclude * @param bool true to skip tags from pages, intro posts and sidebar stuff * @return array of tags */ function get_tags($blog_ids, $limit = 0, $filter_list = NULL, $skip_intro_posts = false) { global $DB, $localtimenow, $posttypes_specialtypes; $BlogCache =& get_BlogCache(); if (is_null($blog_ids)) { global $blog; $blog_ids = $blog; } if (is_array($blog_ids)) { // Get quoted ID list $blog_ids = $DB->quote($blog_ids); $where_cats = 'cat_blog_ID IN (' . $blog_ids . ')'; } else { $Blog =& $BlogCache->get_by_ID($blog_ids); // Get list of relevant blogs $where_cats = trim($Blog->get_sql_where_aggregate_coll_IDs('cat_blog_ID')); } // fp> verrry dirty and params; TODO: clean up // dh> oddly, this appears to not get cached by the query cache. Have experimented a bit, but not found the reason. // It worked locally somehow, but not live. // This takes up to ~50% (but more likely 15%) off the total SQL time. With the query being cached, it would be far better. // build query, only joining categories, if not using all. $sql = 'SELECT LOWER(tag_name) AS tag_name, post_datestart, COUNT(DISTINCT itag_itm_ID) AS tag_count, tag_ID, cat_blog_ID FROM T_items__tag INNER JOIN T_items__itemtag ON itag_tag_ID = tag_ID'; if ($where_cats != '1') { // we have to join the cats $sql .= ' INNER JOIN T_postcats ON itag_itm_ID = postcat_post_ID INNER JOIN T_categories ON postcat_cat_ID = cat_ID'; } $sql .= "\n\t\t INNER JOIN T_items__item ON itag_itm_ID = post_ID\n\t\t WHERE {$where_cats}\n\t\t AND post_status = 'published' AND post_datestart < '" . remove_seconds($localtimenow) . "'"; if ($skip_intro_posts) { $sql .= ' AND post_ptyp_ID NOT IN (' . implode(',', $posttypes_specialtypes) . ')'; } if (!empty($filter_list)) { // Filter tags $filter_list = explode(',', $filter_list); $filter_tags = array(); foreach ($filter_list as $l_tag) { $filter_tags[] = '"' . $DB->escape(trim($l_tag)) . '"'; } $sql .= ' AND tag_name NOT IN (' . implode(', ', $filter_tags) . ')'; } $sql .= ' GROUP BY tag_name ORDER BY tag_count DESC'; if (!empty($limit)) { $sql .= ' LIMIT ' . $limit; } return $DB->get_results($sql, OBJECT, 'Get tags'); }
/** * Retrieves all tags from published posts * * @param integer the id of the blog or array of blog ids. Set NULL to use current blog * @param integer maximum number of returned tags * @param string a comma separated list of tags to ignore/exclude * @param bool true to skip tags from pages, intro posts and sidebar stuff * @return array of tags */ function get_tags($blog_ids, $limit = 0, $filter_list = NULL, $skip_intro_posts = false) { global $DB, $localtimenow, $posttypes_specialtypes; $BlogCache =& get_BlogCache(); if (is_null($blog_ids)) { global $blog; $blog_ids = $blog; } if (is_array($blog_ids)) { // Get quoted ID list $where_cat_clause = 'cat_blog_ID IN ( ' . $DB->quote($blog_ids) . ' )'; } else { // Get list of relevant blogs $Blog =& $BlogCache->get_by_ID($blog_ids); $where_cat_clause = trim($Blog->get_sql_where_aggregate_coll_IDs('cat_blog_ID')); } // Build query to get the tags: $tags_SQL = new SQL(); $tags_SQL->SELECT('tag_name, COUNT( DISTINCT itag_itm_ID ) AS tag_count, tag_ID, cat_blog_ID'); $tags_SQL->FROM('T_items__tag'); $tags_SQL->FROM_add('INNER JOIN T_items__itemtag ON itag_tag_ID = tag_ID'); $tags_SQL->FROM_add('INNER JOIN T_items__item ON itag_itm_ID = post_ID'); $tags_SQL->FROM_add('INNER JOIN T_postcats ON itag_itm_ID = postcat_post_ID'); $tags_SQL->FROM_add('INNER JOIN T_categories ON postcat_cat_ID = cat_ID'); $tags_SQL->WHERE($where_cat_clause); $tags_SQL->WHERE_and('post_status = "published"'); $tags_SQL->WHERE_and('post_datestart < ' . $DB->quote(remove_seconds($localtimenow))); if ($skip_intro_posts) { // Skip "Intro" posts $tags_SQL->WHERE_and('post_ityp_ID NOT IN ( ' . implode(', ', $posttypes_specialtypes) . ' )'); } if (!empty($filter_list)) { // Filter tags $tags_SQL->WHERE_and('tag_name NOT IN ( ' . $DB->quote(explode(', ', $filter_list)) . ' )'); } $tags_SQL->GROUP_BY('tag_name'); $tags_SQL->ORDER_BY('tag_count DESC'); if (!empty($limit)) { // Limit $tags_SQL->LIMIT($limit); } return $DB->get_results($tags_SQL->get(), OBJECT, 'Get tags'); }
/** * Display the widget! * * @param array MUST contain at least the basic display params */ function display($params) { global $localtimenow, $DB, $Blog; $this->init_display($params); $blog_ID = intval($this->disp_params['blog_ID']); if (empty($blog_ID)) { // Use current blog by default $blog_ID = $Blog->ID; } $BlogCache =& get_BlogCache(); if (!$BlogCache->get_by_ID($blog_ID, false, false)) { // No blog exists return; } // Display photos: // TODO: permissions, complete statuses... // TODO: A FileList object based on ItemListLight but adding File data into the query? // overriding ItemListLigth::query() for starters ;) // Init caches $FileCache =& get_FileCache(); $ItemCache =& get_ItemCache(); // Query list of files and posts fields: // Note: We use ItemQuery to get attachments from all posts which should be visible ( even in case of aggregate blogs ) $ItemQuery = new ItemQuery($ItemCache->dbtablename, $ItemCache->dbprefix, $ItemCache->dbIDname); $ItemQuery->SELECT('post_ID, post_datestart, post_datemodified, post_main_cat_ID, post_urltitle, post_canonical_slug_ID, post_tiny_slug_ID, post_ityp_ID, post_title, post_excerpt, post_url, file_ID, file_type, file_title, file_root_type, file_root_ID, file_path, file_alt, file_desc, file_path_hash'); $ItemQuery->FROM_add('INNER JOIN T_links ON post_ID = link_itm_ID'); $ItemQuery->FROM_add('INNER JOIN T_files ON link_file_ID = file_ID'); $ItemQuery->where_chapter($blog_ID); if ($this->disp_params['item_visibility'] == 'public') { // Get images only of the public items $ItemQuery->where_visibility(array('published')); } else { // Get image of all available posts for current user $ItemQuery->where_visibility(NULL); } $ItemQuery->WHERE_and('( file_type = "image" ) OR ( file_type IS NULL )'); $ItemQuery->WHERE_and('post_datestart <= \'' . remove_seconds($localtimenow) . '\''); $ItemQuery->WHERE_and('link_position != "cover"'); if (!empty($this->disp_params['item_type'])) { // Get items only with specified type $ItemQuery->WHERE_and('post_ityp_ID = ' . intval($this->disp_params['item_type'])); } $ItemQuery->GROUP_BY('link_ID'); // fp> TODO: because no way of getting images only, we get 4 times more data than requested and hope that 25% at least will be images :/ // asimo> This was updated and we get images and those files where we don't know the file type yet. Now we get 2 times more data than requested. // Maybe it would be good to get only the requested amount of files, because after a very short period the file types will be set for all images. $ItemQuery->LIMIT(intval($this->disp_params['limit']) * 2); $ItemQuery->ORDER_BY(gen_order_clause($this->disp_params['order_by'], $this->disp_params['order_dir'], 'post_', 'post_ID ' . $this->disp_params['order_dir'] . ', link_ID')); // Init FileList with the above defined query $FileList = new DataObjectList2($FileCache); $FileList->sql = $ItemQuery->get(); $FileList->query(false, false, false, 'Media index widget'); $layout = $this->disp_params['thumb_layout']; $nb_cols = $this->disp_params['grid_nb_cols']; $count = 0; $r = ''; /** * @var File */ while ($File =& $FileList->get_next()) { if ($count >= $this->disp_params['limit']) { // We have enough images already! break; } if (!$File->is_image()) { // Skip anything that is not an image // Only images are selected or those files where we don't know the file type yet. // This check is only for those files where we don't know the filte type. The file type will be set during the check. continue; } if ($layout == 'grid') { // Grid layout if ($count % $nb_cols == 0) { $r .= $this->disp_params['grid_colstart']; } $r .= $this->disp_params['grid_cellstart']; } elseif ($layout == 'flow') { // Flow block layout $r .= $this->disp_params['flow_block_start']; } else { // List layout $r .= $this->disp_params['item_start']; } // 1/ Hack a dirty permalink( will redirect to canonical): // $link = url_add_param( $Blog->get('url'), 'p='.$post_ID ); // 2/ Hack a link to the right "page". Very daring!! // $link = url_add_param( $Blog->get('url'), 'paged='.$count ); // 3/ Instantiate a light object in order to get permamnent url: $ItemLight = new ItemLight($FileList->get_row_by_idx($FileList->current_idx - 1)); // index had already been incremented $r .= '<a href="' . $ItemLight->get_permanent_url() . '">'; // Generate the IMG THUMBNAIL tag with all the alt, title and desc if available $r .= $File->get_thumb_imgtag($this->disp_params['thumb_size'], '', '', $ItemLight->title); $r .= '</a>'; if ($this->disp_params['disp_image_title']) { // Dislay title of image or item $title = $File->get('title') ? $File->get('title') : $ItemLight->title; if (!empty($title)) { $r .= '<span class="note">' . $title . '</span>'; } } ++$count; if ($layout == 'grid') { // Grid layout $r .= $this->disp_params['grid_cellend']; if ($count % $nb_cols == 0) { $r .= $this->disp_params['grid_colend']; } } elseif ($layout == 'flow') { // Flow block layout $r .= $this->disp_params['flow_block_end']; } else { // List layout $r .= $this->disp_params['item_end']; } } // Exit if no files found if (empty($r)) { return; } echo $this->disp_params['block_start']; // Display title if requested $this->disp_title(); echo $this->disp_params['block_body_start']; if ($layout == 'grid') { // Grid layout echo $this->disp_params['grid_start']; } elseif ($layout == 'flow') { // Flow block layout echo $this->disp_params['flow_start']; } else { // List layout echo $this->disp_params['list_start']; } echo $r; if ($layout == 'grid') { // Grid layout if ($count && $count % $nb_cols != 0) { echo $this->disp_params['grid_colend']; } echo $this->disp_params['grid_end']; } elseif ($layout == 'flow') { // Flow block layout echo $this->disp_params['flow_end']; } else { // List layout echo $this->disp_params['list_end']; } echo $this->disp_params['block_body_end']; echo $this->disp_params['block_end']; return true; }