aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorplegall <plg@piwigo.org>2006-04-02 22:26:19 +0000
committerplegall <plg@piwigo.org>2006-04-02 22:26:19 +0000
commit42abf4c57664d2596872d437f70b95193f9a5d18 (patch)
treea1262b8601d5ac5b04b5b2e71af52c453712b9df /include
parent68ed2ea617ede199a0e2f15fdd4886095ae600cb (diff)
improvement: tags replace keywords. Better data model, less
limitations. Each image can be associated to as many tag as needed. Tags can contain non ASCII characters. Oriented navigation with tags by association. git-svn-id: http://piwigo.org/svn/trunk@1119 68402e56-0260-453c-a942-63ccdbb3a9ee
Diffstat (limited to 'include')
-rw-r--r--include/category_default.inc.php2
-rw-r--r--include/common.inc.php1
-rw-r--r--include/config_default.inc.php13
-rw-r--r--include/constants.php2
-rw-r--r--include/functions.inc.php33
-rw-r--r--include/functions_html.inc.php53
-rw-r--r--include/functions_search.inc.php68
-rw-r--r--include/functions_tag.inc.php218
-rw-r--r--include/functions_url.inc.php34
-rw-r--r--include/functions_user.inc.php1
-rw-r--r--include/section_init.inc.php117
11 files changed, 496 insertions, 46 deletions
diff --git a/include/category_default.inc.php b/include/category_default.inc.php
index 31d40ed90..6c909647a 100644
--- a/include/category_default.inc.php
+++ b/include/category_default.inc.php
@@ -79,7 +79,7 @@ foreach ($pictures as $row)
{
$thumbnail_title .= ' : '.$row['filesize'].' KB';
}
-
+
// link on picture.php page
$url = duplicate_picture_url(
array(
diff --git a/include/common.inc.php b/include/common.inc.php
index 42697353e..f66d1ee7f 100644
--- a/include/common.inc.php
+++ b/include/common.inc.php
@@ -148,6 +148,7 @@ SELECT id
if (count(array_diff($existing, $applied)) > 0)
{
ob_start();// buffer output so that cookies work
+
echo
'<p>'
.'Some database upgrades are missing, '
diff --git a/include/config_default.inc.php b/include/config_default.inc.php
index 3675f2ed6..57a4520c3 100644
--- a/include/config_default.inc.php
+++ b/include/config_default.inc.php
@@ -439,6 +439,18 @@ $conf['picture_url_style'] = 'id';
$conf['php_extension_in_urls'] = true;
// +-----------------------------------------------------------------------+
+// | tags |
+// +-----------------------------------------------------------------------+
+
+// full_tag_cloud_items_number: number of tags to show in the full tag
+// cloud. Only the most represented tags will be shown
+$conf['full_tag_cloud_items_number'] = 200;
+
+// tags_levels: number of levels to use for display. Each level is bind to a
+// CSS class tagLevelX.
+$conf['tags_levels'] = 10;
+
+// +-----------------------------------------------------------------------+
// | Notification by mail |
// +-----------------------------------------------------------------------+
@@ -455,5 +467,4 @@ $conf['nbm_list_all_enabled_users_to_send'] = false;
// Max mails sended on one pass
$conf['nbm_max_mails_send'] = 35;
-
?>
diff --git a/include/constants.php b/include/constants.php
index 089e3b65d..02b352cc3 100644
--- a/include/constants.php
+++ b/include/constants.php
@@ -70,4 +70,6 @@ define('UPGRADE_TABLE', $prefixeTable.'upgrade');
define('SEARCH_TABLE', $prefixeTable.'search');
define('USER_MAIL_NOTIFICATION_TABLE', $prefixeTable.'user_mail_notification');
define('CATEGORIES_LINK_TABLE', $prefixeTable.'categories_link');
+define('TAGS_TABLE', $prefixeTable.'tags');
+define('IMAGE_TAG_TABLE', $prefixeTable.'image_tag');
?>
diff --git a/include/functions.inc.php b/include/functions.inc.php
index 981da55c4..d8b86743f 100644
--- a/include/functions.inc.php
+++ b/include/functions.inc.php
@@ -31,6 +31,7 @@ include_once( PHPWG_ROOT_PATH .'include/functions_category.inc.php' );
include_once( PHPWG_ROOT_PATH .'include/functions_xml.inc.php' );
include_once( PHPWG_ROOT_PATH .'include/functions_group.inc.php' );
include_once( PHPWG_ROOT_PATH .'include/functions_html.inc.php' );
+include_once( PHPWG_ROOT_PATH .'include/functions_tag.inc.php' );
include_once( PHPWG_ROOT_PATH .'include/functions_url.inc.php' );
//----------------------------------------------------------- generic functions
@@ -267,6 +268,35 @@ function get_picture_size( $original_width, $original_height,
$picture_size[1] = $height;
return $picture_size;
}
+
+/**
+ * simplify a string to insert it into an URL
+ *
+ * based on str2url function from Dotclear
+ *
+ * @param string
+ * @return string
+ */
+function str2url($str)
+{
+ $str = strtr(
+ $str,
+ 'ÀÁÂÃÄÅàáâãäåÇçÒÓÔÕÖØòóôõöøÈÉÊËèéêëÌÍÎÏìíîïÙÚÛÜùúûü¾ÝÿýÑñ',
+ 'AAAAAAaaaaaaCcOOOOOOooooooEEEEeeeeIIIIiiiiUUUUuuuuYYyyNn'
+ );
+
+ $str = str_replace('Æ', 'AE', $str);
+ $str = str_replace('æ', 'ae', $str);
+ $str = str_replace('¼', 'OE', $str);
+ $str = str_replace('½', 'oe', $str);
+
+ $str = preg_replace('/[^a-z0-9_\s\'\:\/\[\]-]/','',strtolower($str));
+ $str = preg_replace('/[\s\'\:\/\[\]-]+/',' ',trim($str));
+ $res = str_replace(' ','_',$str);
+
+ return $res;
+}
+
//-------------------------------------------- PhpWebGallery specific functions
/**
@@ -829,5 +859,4 @@ function get_available_upgrade_ids()
return $available_upgrade_ids;
}
-
-?> \ No newline at end of file
+?>
diff --git a/include/functions_html.inc.php b/include/functions_html.inc.php
index 7e7df7c41..13c3dc206 100644
--- a/include/functions_html.inc.php
+++ b/include/functions_html.inc.php
@@ -5,7 +5,6 @@
// | Copyright (C) 2003-2005 PhpWebGallery Team - http://phpwebgallery.net |
// +-----------------------------------------------------------------------+
// | branch : BSF (Best So Far)
-// | file : $Id$
// | last update : $Date$
// | last modifier : $Author$
// | revision : $Revision$
@@ -495,6 +494,56 @@ function get_cat_display_name_from_id($cat_id,
}
/**
+ * Returns an HTML list of tags. It can be a multi select field or a list of
+ * checkboxes.
+ *
+ * @param string HTML field name
+ * @param array selected tag ids
+ * @return array
+ */
+function get_html_tag_selection(
+ $tags,
+ $fieldname,
+ $selecteds = array(),
+ $forbidden_categories = null
+ )
+{
+ global $conf;
+
+ $output = '<ul class="tagSelection">';
+ foreach ($tags as $tag)
+ {
+ $output.=
+ '<li>'
+ .'<label>'
+ .'<input type="checkbox" name="'.$fieldname.'[]"'
+ .' value="'.$tag['tag_id'].'"'
+ ;
+
+ if (in_array($tag['tag_id'], $selecteds))
+ {
+ $output.= ' checked="checked"';
+ }
+
+ $output.=
+ ' />'
+ .' '.$tag['name']
+ .'</label>'
+ .'</li>'
+ ."\n"
+ ;
+ }
+ $output.= '</ul>';
+
+ return $output;
+}
+
+function name_compare($a, $b)
+{
+ return strcmp($a['name'], $b['name']);
+}
+
+/**
* exits the current script (either exit or redirect)
*/
function access_denied()
@@ -519,4 +568,4 @@ function access_denied()
redirect($login_url);
}
}
-?> \ No newline at end of file
+?>
diff --git a/include/functions_search.inc.php b/include/functions_search.inc.php
index 2ca87969e..7e55160c8 100644
--- a/include/functions_search.inc.php
+++ b/include/functions_search.inc.php
@@ -84,7 +84,7 @@ function get_sql_search_clause($search_id)
// construction
$clauses = array();
- foreach (array('file','name','comment','keywords','author') as $textfield)
+ foreach (array('file','name','comment','author') as $textfield)
{
if (isset($search['fields'][$textfield]))
{
@@ -109,7 +109,7 @@ function get_sql_search_clause($search_id)
if (isset($search['fields']['allwords']))
{
- $fields = array('file', 'name', 'comment', 'keywords', 'author');
+ $fields = array('file', 'name', 'comment', 'author');
// in the OR mode, request bust be :
// ((field1 LIKE '%word1%' OR field2 LIKE '%word1%')
// OR (field1 LIKE '%word2%' OR field2 LIKE '%word2%'))
@@ -208,12 +208,68 @@ function get_sql_search_clause($search_id)
$search_clause = $where_separator;
- if (isset($forbidden))
+ return $search_clause;
+}
+
+/**
+ * returns the list of items corresponding to the search id
+ *
+ * @param int search id
+ * @return array
+ */
+function get_search_items($search_id)
+{
+ $items = array();
+
+ $search_clause = get_sql_search_clause($search_id);
+
+ if (!empty($search_clause))
{
- $search_clause.= "\n AND ".$forbidden;
+ $query = '
+SELECT DISTINCT(id)
+ FROM '.IMAGES_TABLE.'
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON id = ic.image_id
+ WHERE '.$search_clause.'
+;';
+ $items = array_from_query($query, 'id');
}
- return $search_clause;
-}
+ $search = get_search_array($search_id);
+ if (isset($search['fields']['tags']))
+ {
+ $tag_items = get_image_ids_for_tags(
+ $search['fields']['tags']['words'],
+ $search['fields']['tags']['mode']
+ );
+
+ switch ($search['mode'])
+ {
+ case 'AND':
+ {
+ if (empty($search_clause))
+ {
+ $items = $tag_items;
+ }
+ else
+ {
+ $items = array_intersect($items, $tag_items);
+ }
+ break;
+ }
+ case 'OR':
+ {
+ $items = array_unique(
+ array_merge(
+ $items,
+ $tag_items
+ )
+ );
+ break;
+ }
+ }
+ }
+
+ return $items;
+}
?> \ No newline at end of file
diff --git a/include/functions_tag.inc.php b/include/functions_tag.inc.php
new file mode 100644
index 000000000..5dd4884f4
--- /dev/null
+++ b/include/functions_tag.inc.php
@@ -0,0 +1,218 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | PhpWebGallery - a PHP based picture gallery |
+// | Copyright (C) 2002-2003 Pierrick LE GALL - pierrick@phpwebgallery.net |
+// | Copyright (C) 2003-2006 PhpWebGallery Team - http://phpwebgallery.net |
+// +-----------------------------------------------------------------------+
+// | branch : BSF (Best So Far)
+// | file : $RCSfile$
+// | last update : $Date: 2006-03-16 23:58:16 +0100 (jeu, 16 mar 2006) $
+// | last modifier : $Author: rub $
+// | revision : $Revision: 1085 $
+// | revision : $Revision: 1085 $
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+
+/**
+ * Tags available. Each return tag is represented as an array with its id,
+ * its name, its weight (count), its url name. Tags are not sorted.
+ *
+ * The returned list can be a subset of all existing tags due to
+ * permissions, only if a list of forbidden categories is provided
+ *
+ * @param array forbidden categories
+ * @return array
+ */
+function get_available_tags($forbidden_categories = null)
+{
+ // we can find top fatter tags among reachable images
+ $tags_query = '
+SELECT tag_id, name, url_name, count(*) counter
+ FROM '.IMAGE_TAG_TABLE.'
+ INNER JOIN '.TAGS_TABLE.' ON tag_id = id';
+
+ if (!is_null($forbidden_categories))
+ {
+ // first we need all reachable image ids
+ $images_query = '
+SELECT DISTINCT image_id
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE category_id NOT IN ('.implode(',', $forbidden_categories).')
+;';
+ $image_ids = array_from_query($images_query, 'image_id');
+
+ $tags_query.= '
+ WHERE image_id IN ('.
+ wordwrap(
+ implode(', ', $image_ids),
+ 80,
+ "\n"
+ ).')';
+ }
+
+ $tags_query.= '
+ GROUP BY tag_id
+;';
+
+ $result = pwg_query($tags_query);
+
+ $tags = array();
+
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($tags, $row);
+ }
+
+ return $tags;
+}
+
+/**
+ * All tags, even tags associated to no image.
+ *
+ * @return array
+ */
+function get_all_tags()
+{
+ $query = '
+SELECT id AS tag_id, name, url_name
+ FROM '.TAGS_TABLE.'
+ ORDER BY name
+;';
+ $result = pwg_query($query);
+
+ $tags = array();
+
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($tags, $row);
+ }
+
+ return $tags;
+}
+
+/**
+ * Giving a set of tags with a counter for each one, calculate the display
+ * level of each tag.
+ *
+ * The level of each tag depends on the average count of tags. This
+ * calcylation method avoid having very different levels for tags having
+ * nearly the same count when set are small.
+ *
+ * @param array tags
+ * @return array
+ */
+function add_level_to_tags($tags)
+{
+ global $conf;
+
+ if (count($tags) == 0)
+ {
+ return $tags;
+ }
+
+ $total_count = 0;
+
+ foreach ($tags as $tag)
+ {
+ $total_count+= $tag['counter'];
+ }
+
+ // average count of available tags will determine the level of each tag
+ $tag_average_count = $total_count / count($tags);
+
+ // tag levels threshold calculation: a tag with an average rate must have
+ // the middle level.
+ for ($i = 1; $i < $conf['tags_levels']; $i++)
+ {
+ $threshold_of_level[$i] =
+ 2 * $i * $tag_average_count / $conf['tags_levels'];
+ }
+
+ // display sorted tags
+ foreach (array_keys($tags) as $k)
+ {
+ $tags[$k]['level'] = 1;
+
+ // based on threshold, determine current tag level
+ for ($i = $conf['tags_levels'] - 1; $i >= 1; $i--)
+ {
+ if ($tags[$k]['counter'] > $threshold_of_level[$i])
+ {
+ $tags[$k]['level'] = $i + 1;
+ break;
+ }
+ }
+ }
+
+ return $tags;
+}
+
+/**
+ * return the list of image ids corresponding to given tags. AND & OR mode
+ * supported.
+ *
+ * @param array tag ids
+ * @param string mode
+ * @return array
+ */
+function get_image_ids_for_tags($tag_ids, $mode = 'AND')
+{
+ switch ($mode)
+ {
+ case 'AND':
+ {
+ // strategy is to list images associated to each tag
+ $tag_images = array();
+
+ foreach ($tag_ids as $tag_id)
+ {
+ $query = '
+SELECT image_id
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id = '.$tag_id.'
+;';
+ $tag_images[$tag_id] = array_from_query($query, 'image_id');
+ }
+
+ // then we calculate the intersection, the images that are associated to
+ // every tags
+ $items = array_shift($tag_images);
+ foreach ($tag_images as $images)
+ {
+ $items = array_intersect($items, $images);
+ }
+
+ return array_unique($items);
+ break;
+ }
+ case 'OR':
+ {
+ $query = '
+SELECT DISTINCT image_id
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id IN ('.implode(',', $tag_ids).')
+;';
+ return array_from_query($query, 'image_id');
+ break;
+ }
+ default:
+ {
+ die('get_image_ids_for_tags: unknown mode, only AND & OR are supported');
+ }
+ }
+}
+?> \ No newline at end of file
diff --git a/include/functions_url.inc.php b/include/functions_url.inc.php
index 0e0583872..a50f789c8 100644
--- a/include/functions_url.inc.php
+++ b/include/functions_url.inc.php
@@ -258,23 +258,18 @@ function make_section_in_URL($params)
{
$section_string = '';
- if (!isset($params['section']))
+ $section_of = array(
+ 'category' => 'categories',
+ 'tags' => 'tags',
+ 'list' => 'list',
+ 'search' => 'search',
+ );
+
+ foreach ($section_of as $param => $section)
{
- if (isset($params['category']))
- {
- $params['section'] = 'categories';
- }
- else if (isset($params['tags']))
- {
- $params['section'] = 'tags';
- }
- else if (isset($params['list']))
- {
- $params['section'] = 'list';
- }
- else if (isset($params['search']))
+ if (isset($params[$param]))
{
- $params['section'] = 'search';
+ $params['section'] = $section;
}
}
@@ -289,7 +284,7 @@ function make_section_in_URL($params)
{
if (!isset($params['category']))
{
- //$section_string.= '/categories';
+ $section_string.= '/categories';
}
else
{
@@ -309,7 +304,12 @@ function make_section_in_URL($params)
foreach ($params['tags'] as $tag)
{
- $section_string.= '/'.$tag;
+ $section_string.= '/'.$tag['id'];
+
+ if (isset($tag['url_name']))
+ {
+ $section_string.= '-'.$tag['url_name'];
+ }
}
break;
diff --git a/include/functions_user.inc.php b/include/functions_user.inc.php
index cfa4d53aa..c5efa98ad 100644
--- a/include/functions_user.inc.php
+++ b/include/functions_user.inc.php
@@ -633,5 +633,4 @@ function is_adviser()
return ($user['adviser'] == 'true');
}
-
?> \ No newline at end of file
diff --git a/include/section_init.inc.php b/include/section_init.inc.php
index cc7c074c2..e2ed07b42 100644
--- a/include/section_init.inc.php
+++ b/include/section_init.inc.php
@@ -145,15 +145,11 @@ else if (0 === strpos($tokens[$next_token], 'tag'))
$page['tags'] = array();
$next_token++;
+ $i = $next_token;
- for ($i = $next_token; ; $i++)
+ while (isset($tokens[$i]))
{
- if (!isset($tokens[$i]))
- {
- break;
- }
-
- preg_match('/^(\d+)/', $tokens[$i], $matches);
+ preg_match('/^(\d+)(?:-(.*))?/', $tokens[$i], $matches);
if (!isset($matches[1]))
{
if (0 == count($page['tags']))
@@ -165,7 +161,16 @@ else if (0 === strpos($tokens[$next_token], 'tag'))
break;
}
}
- array_push($page['tags'], $matches[1]);
+
+ array_push(
+ $page['tags'],
+ array(
+ 'id' => $matches[1],
+ 'url_name' => isset($matches[2]) ? $matches[2] : '',
+ )
+ );
+
+ $i++;
}
$next_token = $i;
@@ -225,13 +230,10 @@ else if ('list' == $tokens[$next_token])
$next_token++;
}
-for ($i = $next_token; ; $i++)
-{
- if (!isset($tokens[$i]))
- {
- break;
- }
+$i = $next_token;
+while (isset($tokens[$i]))
+{
if (preg_match('/^start-(\d+)/', $tokens[$i], $matches))
{
$page['start'] = $matches[1];
@@ -240,9 +242,12 @@ for ($i = $next_token; ; $i++)
if (preg_match('/^posted|created/', $tokens[$i] ))
{
$chronology_tokens = explode('-', $tokens[$i] );
+
$page['chronology_field'] = $chronology_tokens[0];
+
array_shift($chronology_tokens);
$page['chronology_style'] = $chronology_tokens[0];
+
array_shift($chronology_tokens);
if ( count($chronology_tokens)>0 )
{
@@ -255,6 +260,8 @@ for ($i = $next_token; ; $i++)
$page['chronology_date'] = $chronology_tokens;
}
}
+
+ $i++;
}
@@ -338,19 +345,97 @@ else
$forbidden = ' 1 = 1';
}
// +-----------------------------------------------------------------------+
+// | tags section |
+// +-----------------------------------------------------------------------+
+ if ($page['section'] == 'tags')
+ {
+ $page['tag_ids'] = array();
+ foreach ($page['tags'] as $tag)
+ {
+ array_push($page['tag_ids'], $tag['id']);
+ }
+
+ $items = get_image_ids_for_tags($page['tag_ids']);
+
+ // permissions depends on category, so to only keep images that are
+ // reachable to the connected user, we need to check category
+ // associations
+ if (!empty($user['forbidden_categories']))
+ {
+ $query = '
+SELECT image_id
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE image_id IN ('.implode(',', $items).')
+ AND '.$forbidden.'
+;';
+ $items = array_unique(
+ array_from_query($query, 'image_id')
+ );
+ }
+
+ // tag names
+ $query = '
+SELECT name, url_name, id
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(',', $page['tag_ids']).')
+;';
+ $result = pwg_query($query);
+ $tag_infos = array();
+
+ while ($row = mysql_fetch_array($result))
+ {
+ $tag_infos[ $row['id'] ]['name'] = $row['name'];
+ $tag_infos[ $row['id'] ]['url_name'] = $row['url_name'];
+ }
+
+ $title = count($page['tags']) > 1 ? l10n('Tags') : l10n('Tag');
+ $title.= ' ';
+
+ $tag_num = 1;
+ foreach ($page['tag_ids'] as $tag_id)
+ {
+ $title.=
+ ($tag_num++ > 1 ? ' + ' : '')
+ .'<a href="'
+ .make_index_url(
+ array(
+ 'tags' => array(
+ array(
+ 'id' => $tag_id,
+ 'url_name' => $tag_infos[$tag_id]['url_name'],
+ ),
+ )
+ )
+ )
+ .'">'
+ .$tag_infos[$tag_id]['name']
+ .'</a>';
+ }
+
+ $page = array_merge(
+ $page,
+ array(
+ 'title' => $title,
+ 'items' => array_values($items),
+ 'thumbnails_include' => 'include/category_default.inc.php',
+ )
+ );
+ }
+// +-----------------------------------------------------------------------+
// | search section |
// +-----------------------------------------------------------------------+
if ($page['section'] == 'search')
{
include_once( PHPWG_ROOT_PATH .'include/functions_search.inc.php' );
+
$query = '
SELECT DISTINCT(id)
FROM '.IMAGES_TABLE.'
INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON id = ic.image_id
- WHERE '.get_sql_search_clause($page['search']).'
+ WHERE id IN ('.implode(',', get_search_items($page['search'])).')
AND '.$forbidden.'
'.$conf['order_by'].'
-;';
+;';
$page = array_merge(
$page,