diff --git a/include/functions_category.inc.php b/include/functions_category.inc.php
index 7bf8fb0f2..d0124aac9 100644
--- a/include/functions_category.inc.php
+++ b/include/functions_category.inc.php
@@ -602,4 +602,192 @@ function remove_computed_category(&$cats, $cat)
unset($cats[$cat['cat_id']]);
}
+/**
+ * Return the list of image ids corresponding to given categories.
+ * AND & OR mode supported.
+ *
+ * @param int[] $cat_ids
+ * @param string mode
+ * @param string $extra_images_where_sql - optionally apply a sql where filter to retrieved images
+ * @param string $order_by - optionally overwrite default photo order
+ * @param bool $user_permissions
+ * @return array
+ */
+function get_image_ids_for_categories($cat_ids, $mode='AND', $extra_images_where_sql='', $order_by='', $use_permissions=true)
+{
+ global $conf;
+
+ if (empty($cat_ids))
+ {
+ return array();
+ }
+
+ $query = '
+SELECT id
+ FROM '.IMAGES_TABLE.' i
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' ic ON id=ic.image_id
+ WHERE category_id IN ('.implode(',', $cat_ids).')';
+
+ if ($use_permissions)
+ {
+ $query.= get_sql_condition_FandF(
+ array(
+ 'forbidden_categories' => 'category_id',
+ 'visible_categories' => 'category_id',
+ 'visible_images' => 'id'
+ ),
+ "\n AND"
+ );
+ }
+
+ $query.= (empty($extra_images_where_sql) ? '' : " \nAND (".$extra_images_where_sql.')').'
+ GROUP BY id';
+
+ if ($mode=='AND' and count($cat_ids)>1)
+ {
+ $query .= '
+ HAVING COUNT(DISTINCT category_id)='.count($cat_ids);
+ }
+ $query .= "\n".(empty($order_by) ? $conf['order_by'] : $order_by);
+
+ return query2array($query, null, 'id');
+}
+
+/**
+ * Return a list of categories corresponding to given items.
+ *
+ * @param int[] $items
+ * @param int $max
+ * @param int[] $excluded_cat_ids
+ * @return array [id, name, counter, url_name]
+ */
+function get_common_categories($items, $max=null, $excluded_cat_ids=array())
+{
+ if (empty($items))
+ {
+ return array();
+ }
+
+ $query = '
+SELECT
+ c.id,
+ c.uppercats,
+ count(*) AS counter
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ INNER JOIN '.CATEGORIES_TABLE.' c ON category_id = id
+ WHERE image_id IN ('.implode(',', $items).')';
+
+ if (!empty($excluded_cat_ids))
+ {
+ $query.='
+ AND category_id NOT IN ('.implode(',', $excluded_cat_ids).')';
+ }
+
+ $query .='
+ GROUP BY c.id
+ ORDER BY ';
+ if (isset($max))
+ {
+ $query .= 'counter DESC
+ LIMIT '.$max;
+ }
+ else
+ {
+ $query .= 'NULL';
+ }
+
+ $result = pwg_query($query);
+ $cats = array();
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $cats[ $row['id'] ] = $row;
+ }
+
+ return $cats;
+}
+
+function get_related_categories_menu($items, $excluded_cat_ids=array())
+{
+ global $page;
+
+ $common_cats = get_common_categories($items, null, $excluded_cat_ids);
+ // echo '
'; print_r($common_cats); echo '
';
+
+ if (count($common_cats) == 0)
+ {
+ return array();
+ }
+
+ $cat_ids = array();
+ // now we add the upper categories and useful values such as depth level and url
+ foreach ($common_cats as $cat)
+ {
+ foreach (explode(',', $cat['uppercats']) as $uppercat)
+ {
+ @$cat_ids[$uppercat]++;
+ }
+ }
+
+ $query = '
+SELECT
+ id,
+ name,
+ permalink,
+ id_uppercat,
+ uppercats,
+ global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.implode(',', array_keys($cat_ids)).')
+;';
+ $cats = query2array($query);
+ usort($cats, 'global_rank_compare');
+
+ $index_of_cat = array();
+
+ foreach ($cats as $idx => $cat)
+ {
+ $index_of_cat[ $cat['id'] ] = $idx;
+ $cats[$idx]['LEVEL'] = substr_count($cat['global_rank'], '.') + 1;
+
+ // if the category is directly linked to the items, we add an URL + counter
+ if (isset($common_cats[ $cat['id'] ]))
+ {
+ $cats[$idx]['count_images'] = $common_cats[ $cat['id'] ]['counter'];
+
+ $url_params = array();
+ if (isset($page['category']))
+ {
+ $url_params['category'] = $page['category'];
+
+ $url_params['combined_categories'] = array($cat);
+ if (isset($page['combined_categories']))
+ {
+ $url_params['combined_categories'] = array_merge($page['combined_categories'], array($cat));
+ }
+ }
+ else
+ {
+ $url_params['category'] = $cat;
+ }
+
+ $cats[$idx]['url'] = make_index_url($url_params);
+ }
+
+ // let's find how many sub-categories we have for each category. 3 options:
+ // 1. direct sub-albums
+ // 2. total indirect sub-albums
+ // 3. number of sub-albums containing photos
+ //
+ // Option 3 seems more appropriate here.
+ if (!empty($cat['id_uppercat']) and @$cats[$idx]['count_images'] > 0)
+ {
+ foreach (array_slice(explode(',', $cat['uppercats']), 0, -1) as $uppercat_id)
+ {
+ @$cats[ $index_of_cat[ $uppercat_id ] ]['count_categories']++;
+ }
+ }
+ }
+
+ return $cats;
+}
?>
diff --git a/include/functions_html.inc.php b/include/functions_html.inc.php
index 417b5f19f..01c54e79d 100644
--- a/include/functions_html.inc.php
+++ b/include/functions_html.inc.php
@@ -441,6 +441,55 @@ function get_tags_content_title()
return $title;
}
+/**
+ * Returns the breadcrumb to be displayed above thumbnails on combined categories page.
+ *
+ * @return string
+ */
+function get_combined_categories_content_title()
+{
+ global $page;
+
+ $title = l10n('Albums').' ';
+
+ $is_first = true;
+ $all_categories = array_merge(array($page['category']), $page['combined_categories']);
+ foreach ($all_categories as $idx => $category)
+ {
+ $title.= $is_first ? '' : ' + ';
+ $is_first = false;
+
+ $title.= get_cat_display_name(array($category));
+
+ if (count($all_categories) > 1) // should be always the case
+ {
+ $other_cats = $all_categories;
+ unset($other_cats[$idx]);
+
+ $params = array(
+ 'category' => array_shift($other_cats),
+ );
+
+ if (count($other_cats) > 0)
+ {
+ $params['combined_categories'] = $other_cats;
+ }
+ $remove_url = make_index_url($params);
+
+ $title.=
+ '
'
+ .''
+ .'';
+ }
+ }
+
+ return $title;
+}
+
/**
* Sets the http status header (200,401,...)
* @param int $code
@@ -501,6 +550,7 @@ function register_default_menubar_blocks($menu_ref_arr)
$menu->register_block( new RegisteredBlock( 'mbTags', 'Related tags', 'piwigo'));
$menu->register_block( new RegisteredBlock( 'mbSpecials', 'Specials', 'piwigo'));
$menu->register_block( new RegisteredBlock( 'mbMenu', 'Menu', 'piwigo'));
+ $menu->register_block( new RegisteredBlock( 'mbRelatedCategories', 'Related albums', 'piwigo') );
// We hide the quick identification menu on the identification page. It
// would be confusing.
diff --git a/include/functions_url.inc.php b/include/functions_url.inc.php
index a0637f8c3..0a27d03d3 100644
--- a/include/functions_url.inc.php
+++ b/include/functions_url.inc.php
@@ -348,6 +348,18 @@ function make_section_in_url($params)
{
$section_string.= $params['category']['permalink'];
}
+
+ if (isset($params['combined_categories']))
+ {
+ foreach ($params['combined_categories'] as $category)
+ {
+ $section_string.= '/'.$category['id'];
+ if ( $conf['category_url_style']=='id-name' )
+ {
+ $section_string.= '-'.str2url($category['name']);
+ }
+ }
+ }
}
break;
@@ -421,13 +433,23 @@ function parse_section_url( $tokens, &$next_token)
$page['section'] = 'categories';
$next_token++;
- if (isset($tokens[$next_token]) )
+ $i = $next_token;
+
+ while (isset($tokens[$next_token]))
{
if (preg_match('/^(\d+)(?:-(.+))?$/', $tokens[$next_token], $matches))
{
if ( isset($matches[2]) )
$page['hit_by']['cat_url_name'] = $matches[2];
- $page['category'] = $matches[1];
+
+ if (!isset($page['category']))
+ {
+ $page['category'] = $matches[1];
+ }
+ else
+ {
+ $page['combined_categories'][] = $matches[1];
+ }
$next_token++;
}
else
@@ -480,6 +502,24 @@ function parse_section_url( $tokens, &$next_token)
}
$page['category']=$result;
}
+
+ if (isset($page['combined_categories']))
+ {
+ $combined_categories = array();
+
+ foreach ($page['combined_categories'] as $cat_id)
+ {
+ $result = get_cat_info($cat_id);
+ if (empty($result))
+ {
+ page_not_found(l10n('Requested album does not exist'));
+ }
+
+ $combined_categories[] = $result;
+ }
+
+ $page['combined_categories'] = $combined_categories;
+ }
}
elseif ( 'tags' == @$tokens[$next_token] )
{
diff --git a/include/menubar.inc.php b/include/menubar.inc.php
index 7cb65c3e5..46fd265bc 100644
--- a/include/menubar.inc.php
+++ b/include/menubar.inc.php
@@ -101,6 +101,34 @@ function initialize_menu()
$block->template = 'menubar_categories.tpl';
}
+//------------------------------------------------------------ related categories
+ $block = $menu->get_block('mbRelatedCategories');
+
+ if ($block != null and !empty($page['items']))
+ {
+ $exclude_cat_ids = array();
+ if (isset($page['category']))
+ {
+ $exclude_cat_ids = array($page['category']['id']);
+ if (isset($page['combined_categories']))
+ {
+ foreach ($page['combined_categories'] as $cat)
+ {
+ $exclude_cat_ids[] = $cat['id'];
+ }
+ }
+ }
+
+ $block->data = array(
+ 'MENU_CATEGORIES' => get_related_categories_menu($page['items'], $exclude_cat_ids),
+ );
+
+ if (!empty($block->data['MENU_CATEGORIES']) )
+ {
+ $block->template = 'menubar_related_categories.tpl';
+ }
+ }
+
//------------------------------------------------------------------------ tags
$block = $menu->get_block('mbTags');
if ( $block!=null and 'picture' != script_basename() )
diff --git a/include/section_init.inc.php b/include/section_init.inc.php
index 586aec7fc..db4c30460 100644
--- a/include/section_init.inc.php
+++ b/include/section_init.inc.php
@@ -211,7 +211,11 @@ $forbidden = get_sql_condition_FandF(
// +-----------------------------------------------------------------------+
if ('categories' == $page['section'])
{
- if (isset($page['category']))
+ if (isset($page['combined_categories']))
+ {
+ $page['title'] = get_combined_categories_content_title();
+ }
+ elseif (isset($page['category']))
{
$page = array_merge(
$page,
@@ -231,7 +235,17 @@ if ('categories' == $page['section'])
}
// GET IMAGES LIST
- if
+ if (isset($page['combined_categories']))
+ {
+ $cat_ids = array($page['category']['id']);
+ foreach ($page['combined_categories'] as $category)
+ {
+ $cat_ids[] = $category['id'];
+ }
+
+ $page['items'] = get_image_ids_for_categories($cat_ids);
+ }
+ elseif
(
$page['startcat'] == 0 and
(!isset($page['chronology_field'])) and // otherwise the calendar will requery all subitems
diff --git a/language/en_UK/common.lang.php b/language/en_UK/common.lang.php
index a6a4a0657..649387757 100644
--- a/language/en_UK/common.lang.php
+++ b/language/en_UK/common.lang.php
@@ -414,4 +414,5 @@ $lang['Link: %s'] = 'Link: %s';
$lang['Your authentication key is no longer valid.'] = 'Your authentication key is no longer valid.';
$lang['Invalid username or password!'] = 'Invalid username or password!';
$lang['generate random password'] = 'generate random password';
+$lang['Related albums'] = 'Related albums';
?>
diff --git a/language/fr_FR/common.lang.php b/language/fr_FR/common.lang.php
index 082d80b2e..96e4861cf 100644
--- a/language/fr_FR/common.lang.php
+++ b/language/fr_FR/common.lang.php
@@ -412,4 +412,5 @@ $lang['Album name, Z → A'] = 'Nom de l\'album, Z → A';
$lang['Link: %s'] = 'Lien: %s';
$lang['Your authentication key is no longer valid.'] = 'Votre clef d\'identification n\'est plus valide.';
$lang['Invalid username or password!'] = 'Nom d\'utilisateur ou mot de passe invalide !';
-$lang['generate random password'] = 'générer un mot de passe aléatoire';
\ No newline at end of file
+$lang['generate random password'] = 'générer un mot de passe aléatoire';
+$lang['Related albums'] = 'Albums liés';
\ No newline at end of file
diff --git a/themes/default/template/menubar_related_categories.tpl b/themes/default/template/menubar_related_categories.tpl
new file mode 100644
index 000000000..1d1e0837e
--- /dev/null
+++ b/themes/default/template/menubar_related_categories.tpl
@@ -0,0 +1,28 @@
+
+ {'Related albums'|@translate}
+
+
+{assign var='ref_level' value=0}
+{foreach from=$block->data.MENU_CATEGORIES item=cat}
+ {if $cat.LEVEL > $ref_level}
+ '|@str_repeat:($ref_level-$cat.LEVEL)}
+ {/if}
+
+ {if isset($cat.url)}
+ {$cat.name}
+ {else}
+ {$cat.name}
+ {/if}
+ {if $cat.count_images > 0}
+ {$cat.count_images}
+ {/if}
+ {if $cat.count_categories > 0}
+ {$cat.count_categories}
+ {/if}
+ {assign var='ref_level' value=$cat.LEVEL}
+{/foreach}
+{''|@str_repeat:$ref_level}
+
diff --git a/themes/default/theme.css b/themes/default/theme.css
index 4930d6143..8b1d94944 100644
--- a/themes/default/theme.css
+++ b/themes/default/theme.css
@@ -113,6 +113,10 @@
padding:0 25px;
}
+#menubar .badge {
+ margin-left:5px;
+}
+
.badge::before {
content: '[';
}
@@ -121,6 +125,34 @@
content: ']';
}
+.badge.badgeCategories::before {
+ content:"(";
+}
+.badge.badgeCategories::after {
+ content:")";
+}
+
+/* A nicer (more modern) way to present badge, with a background color and no brackets */
+
+/*
+.badge::before, .badge::after {
+ content:none;
+}
+
+.badge {
+ display: inline-block;
+ min-width: 5px;
+ padding: 3px 7px;
+ color: #eee;
+ line-height: 1;
+ vertical-align: middle;
+ white-space: nowrap;
+ text-align: center;
+ background-color: #666;
+ border-radius: 10px;
+}
+*/
+
/* category and tag results paragraphs on a quick search */
.search_results {
font-size: 16px;
@@ -796,4 +828,4 @@ LEGEND {
#TagsGroupRemoveTag span{
display:none;
-}
\ No newline at end of file
+}