From 1942167c0f019a52418342f626c09481be8bf4b7 Mon Sep 17 00:00:00 2001 From: Julio Montoya Date: Tue, 27 Oct 2009 11:03:24 -0500 Subject: [PATCH] First attempt to implement the user tags (interests) see DT#5508 (partial) --- main/admin/user_fields.php | 22 +- main/admin/user_fields_add.php | 25 +- main/auth/profile.php | 106 ++- main/inc/lib/database.lib.php | 8 + main/inc/lib/javascript/tag/close.gif | Bin 0 -> 90 bytes .../lib/javascript/tag/jquery.fcbkcomplete.js | 662 ++++++++++++++++++ .../javascript/tag/jquery.fcbkcomplete.min.js | 52 ++ main/inc/lib/javascript/tag/loading.gif | Bin 0 -> 1737 bytes main/inc/lib/javascript/tag/style.css | 51 ++ main/inc/lib/usermanager.lib.php | 294 +++++++- main/install/dokeos_main.sql | 23 + .../migrate-db-1.8.6.1-1.8.6.2-pre.sql | 6 +- main/social/profile.php | 64 +- main/social/search.php | 285 ++++++++ main/user/tags.php | 7 + main/user/tags/tags.php | 7 + 16 files changed, 1510 insertions(+), 102 deletions(-) create mode 100644 main/inc/lib/javascript/tag/close.gif create mode 100644 main/inc/lib/javascript/tag/jquery.fcbkcomplete.js create mode 100644 main/inc/lib/javascript/tag/jquery.fcbkcomplete.min.js create mode 100644 main/inc/lib/javascript/tag/loading.gif create mode 100644 main/inc/lib/javascript/tag/style.css create mode 100644 main/social/search.php create mode 100644 main/user/tags.php create mode 100644 main/user/tags/tags.php diff --git a/main/admin/user_fields.php b/main/admin/user_fields.php index a4bb9b7656..377390c32a 100644 --- a/main/admin/user_fields.php +++ b/main/admin/user_fields.php @@ -1,23 +1,5 @@ addElement('select','fieldtype',get_lang('FieldType'),$types,array('onchange'=>'change_image_user_field(this.value)')); $form->addRule('fieldtype', get_lang('ThisFieldIsRequired'), 'required'); // Field display name @@ -172,7 +157,7 @@ $form->setDefaults($defaults); if(isset($_GET['field_id']) && !empty($_GET['field_id'])) { $class="save"; $text=get_lang('buttonEditUserField'); - } else { + } else { $class="add"; $text=get_lang('buttonAddUserField'); } diff --git a/main/auth/profile.php b/main/auth/profile.php index f609f69ee3..e462778e3e 100644 --- a/main/auth/profile.php +++ b/main/auth/profile.php @@ -39,6 +39,11 @@ if (!(isset($_user['user_id']) && $_user['user_id']) || api_is_anonymous($_user[ } $htmlHeadXtra[] = ''; //jQuery + +$htmlHeadXtra[] = ''; //jQuery +$htmlHeadXtra[] = ''; + + $htmlHeadXtra[] = ''; - -if (api_get_setting('allow_message_tool') == 'true') { - $htmlHeadXtra[] =''; -} -$htmlHeadXtra[] =''; $interbreadcrumb[] = array('url' => '../auth/profile.php', 'name' => get_lang('ModifyProfile')); @@ -94,6 +73,16 @@ if (!empty($_GET['fe'])) { $_GET['fe'] = null; } +$jquery_ready_content = ''; +if (api_get_setting('allow_message_tool') == 'true') { + $jquery_ready_content = << $field_details) { case USER_FIELD_TYPE_DIVIDER: $form->addElement('static', $field_details[1], '
'.$field_details[3].''); break; + case USER_FIELD_TYPE_TAG: + //the magic should be here + $user_tags = UserManager::get_user_tags(api_get_user_id(),$field_details[0]); + + $pre_html = '
+
'.$field_details[3].'
+
'; + $post = '
'; + + $tag_list = ''; + if (is_array($user_tags) && count($user_tags)> 0) { + foreach ($user_tags as $tag) { + $tag_list .= ''; + } + } + $multi_select = ''; + $form->addElement('html',$pre_html.$multi_select.$post ); + $url = api_get_path(WEB_CODE_PATH).'user'; + //if cache is set to true the jquery will be called 1 time + $jquery_ready_content.= <<'; + + if (api_get_setting('profile', 'apikeys') == 'true') { $form->addElement('html', '
'); $form->addElement('text', 'api_key_generate', get_lang('MyApiKey'), array('size' => 40, 'id' => 'id_api_key_generate')); @@ -548,8 +581,8 @@ if (!empty($_SESSION['production_uploaded'])) { } elseif ($form->validate()) { $wrong_current_password = false; - $user_data = $form->exportValues(); - +// $user_data = $form->exportValues(); + $user_data = $form->getSubmitValues(); // set password if a new one was provided if (!empty($user_data['password0'])) { if (check_user_password($user_data['password0'])) { @@ -635,8 +668,19 @@ if (!empty($_SESSION['production_uploaded'])) { $sql .= " WHERE user_id = '".$_user['user_id']."'"; //var_dump($sql); exit(); Database::query($sql, __FILE__, __LINE__); - //update the extra fields - foreach ($extras as $key => $value) { + + // User tag process + //1. Deleting all user tags + $list_extra_field_type_tag = UserManager::get_all_extra_field_by_type(USER_FIELD_TYPE_TAG); + if (is_array($list_extra_field_type_tag) && count($list_extra_field_type_tag)>0) { + foreach ($list_extra_field_type_tag as $id) { + UserManager::delete_user_tags(api_get_user_id(), $id); + } + } + + //2. Update the extra fields and user tags if available + foreach ($extras as $key => $value) { + //3. Tags are process in the UserManager::update_extra_field_value by the UserManager::process_tags function $myres = UserManager::update_extra_field_value($_user['user_id'], $key, $value); } @@ -738,7 +782,7 @@ $big_image_width = $big_image_size[0]; $big_image_height = $big_image_size[1]; $url_big_image = $big_image.'?rnd='.time(); -// Style position:absolute has been removed for Opera-compatibility. +// Style position:absolute has been removed for Opera-compatibility. //echo '
'; echo '
'; diff --git a/main/inc/lib/database.lib.php b/main/inc/lib/database.lib.php index b539948f78..74bb975732 100644 --- a/main/inc/lib/database.lib.php +++ b/main/inc/lib/database.lib.php @@ -62,6 +62,14 @@ define('TABLE_MAIN_USER_FIELD', 'user_field'); define('TABLE_MAIN_USER_FIELD_OPTIONS', 'user_field_options'); define('TABLE_MAIN_USER_FIELD_VALUES', 'user_field_values'); +//User tags +define('TABLE_MAIN_USER_TAG', 'user_tag'); +define('TABLE_MAIN_USER_REL_TAG', 'user_rel_tag'); + +//User group +define('TABLE_MAIN_USER_GROUP', 'user_group'); +define('TABLE_MAIN_USER_GROUP_VALUES', 'user_group_values'); + // Search engine define('TABLE_MAIN_SPECIFIC_FIELD', 'specific_field'); define('TABLE_MAIN_SPECIFIC_FIELD_VALUES', 'specific_field_values'); diff --git a/main/inc/lib/javascript/tag/close.gif b/main/inc/lib/javascript/tag/close.gif new file mode 100644 index 0000000000000000000000000000000000000000..cc2199248c157bb6b7a5abe254cc5297adbf479b GIT binary patch literal 90 zcmZ?wbhEHbWM>d%n8?8J=g*(Xb2sIbwEX}7U-2gkBRc~#gAM}_faDpNv}4-Dv-kAh s^7OY9U)~Y@<6-H;ww5~_b!wbS4quo)DIEX$`dp>&c^f}z5e5cp0IC@wSO5S3 literal 0 HcmV?d00001 diff --git a/main/inc/lib/javascript/tag/jquery.fcbkcomplete.js b/main/inc/lib/javascript/tag/jquery.fcbkcomplete.js new file mode 100644 index 0000000000..946b955c5f --- /dev/null +++ b/main/inc/lib/javascript/tag/jquery.fcbkcomplete.js @@ -0,0 +1,662 @@ +/* + FCBKcomplete 2.6 + - Jquery version required: 1.2.x, 1.3.x + + Changelog: + + - 2.00 new version of fcbkcomplete + + - 2.01 fixed bugs & added features + fixed filter bug for preadded items + focus on the input after selecting tag + the element removed pressing backspace when the element is selected + input tag in the control has a border in IE7 + added iterate over each match and apply the plugin separately + set focus on the input after selecting tag + + - 2.02 fixed fist element selected bug + fixed defaultfilter error bug + + - 2.5 removed selected="selected" attribute due ie bug + element search algorithm changed + better performance fix added + fixed many small bugs + onselect event added + onremove event added + + - 2.6 ie6/7 support fix added + added new public method addItem due request + added new options "firstselected" that you can set true/false to select first element on dropdown list + autoexpand input element added + removeItem bug fixed + and many more bug fixed + + */ + +/* Coded by: emposha */ +/* Copyright: Emposha.com - Distributed under MIT - Keep this message! */ +/* + * json_url - url to fetch json object + * cache - use cache + * height - maximum number of element shown before scroll will apear + * newel - show typed text like a element + * firstselected - automaticly select first element from dropdown + * filter_case - case sensitive filter + * filter_selected - filter selected items from list + * complete_text - text for complete page + * maxshownitems - maximum numbers that will be shown at dropdown list (less better performance) + * onselect - fire event on item select + * onremove - fire event on item remove + */ + +jQuery( + function ($) + { + $.fn.fcbkcomplete = function (opt) + { + return this.each(function() + { + function init() + { + createFCBK(); + preSet(); + addInput(0); + } + + function createFCBK() + { + element.hide(); + element.attr("multiple","multiple"); + if (element.attr("name").indexOf("[]") == -1) + { + element.attr("name",element.attr("name")+"[]"); + } + + holder = $(document.createElement("ul")); + holder.attr("class", "holder"); + element.after(holder); + + complete = $(document.createElement("div")); + complete.addClass("facebook-auto"); + complete.append('
'+ options.complete_text +"
"); + + if (browser_msie) + { + complete.append(''); + browser_msie_frame = complete.children('.ie6fix'); + //$("input:checkbox").parent().css("z-index","-1"); + } + + feed = $(document.createElement("ul")); + feed.attr("id", elemid + "_feed"); + + complete.prepend(feed); + holder.after(complete); + feed.css("width",complete.width()); + } + + function preSet() + { + element.children("option").each( + function(i,option) + { + option = $(option); + if (option.hasClass("selected")) + { + addItem (option.text(), option.val(), true); + option.attr("selected","selected"); + } + else + { + option.removeAttr("selected"); + } + + cache.push({ + caption: option.text(), + value: option.val() + }); + search_string += "" + (cache.length - 1) + ":" + option.text() + ";"; + } + ); + } + + //public method to add new item + this.addItem = function(title, value) + { + addItem(title, value); + } + + function addItem (title, value, preadded) + { + var li = document.createElement("li"); + var txt = document.createTextNode(title); + var aclose = document.createElement("a"); + + $(li).attr({"class": "bit-box","rel": value}); + $(li).prepend(txt); + $(aclose).attr({"class": "closebutton","href": "#"}); + + li.appendChild(aclose); + holder.append(li); + + $(aclose).click( + function(){ + $(this).parent("li").fadeOut("fast", + function(){ + removeItem($(this)); + } + ); + return false; + } + ); + + if (!preadded) + { + $("#"+elemid + "_annoninput").remove(); + var _item; + addInput(1); + if (element.children("option[value=" + value + "]").length) + { + _item = element.children("option[value=" + value + "]"); + _item.get(0).setAttribute("selected", "selected"); + if (!_item.hasClass("selected")) + { + _item.addClass("selected"); + } + } + else + { + var _item = $(document.createElement("option")); + _item.attr("value", value).get(0).setAttribute("selected", "selected"); + _item.attr("value", value).addClass("selected"); + _item.text(title); + element.append(_item); + } + if (options.onselect.length) + { + funCall(options.onselect,_item) + } + } + holder.children("li.bit-box.deleted").removeClass("deleted"); + feed.hide(); + browser_msie?browser_msie_frame.hide():''; + } + + function removeItem(item) + { + if (options.onremove.length) + { + var _item = element.children("option[value=" + item.attr("rel") + "]"); + funCall(options.onremove,_item) + } + element.children("option[value=" + item.attr("rel") + "]").removeAttr("selected"); + element.children("option[value=" + item.attr("rel") + "]").removeClass("selected"); + item.remove(); + deleting = 0; + } + + function addInput(focusme) + { + var li = $(document.createElement("li")); + var input = $(document.createElement("input")); + + li.attr({"class": "bit-input","id": elemid + "_annoninput"}); + input.attr({"type": "text","class": "maininput","size": "1"}); + holder.append(li.append(input)); + + input.focus( + function() + { + complete.fadeIn("fast"); + } + ); + + input.blur( + function() + { + complete.fadeOut("fast"); + } + ); + + holder.click( + function() + { + input.focus(); + if (feed.length && input.val().length) + { + feed.show(); + } + else + { + feed.hide(); + browser_msie?browser_msie_frame.hide():''; + complete.children(".default").show(); + } + } + ); + + input.keypress( + function(event) + { + if (event.keyCode == 13) + { + return false; + } + //auto expand input + input.attr("size",input.val().length + 1); + } + ); + + input.keydown( + function(event) + { + //prevent to enter some bad chars when input is empty + if(event.keyCode == 191) + { + event.preventDefault(); + return false; + } + } + ); + + input.keyup( + function(event) + { + var etext = xssPrevent(input.val()); + + if (event.keyCode == 8 && etext.length == 0) + { + feed.hide(); + browser_msie?browser_msie_frame.hide():''; + if (holder.children("li.bit-box.deleted").length == 0) + { + holder.children("li.bit-box:last").addClass("deleted"); + return false; + } + else + { + if (deleting) + { + return; + } + deleting = 1; + holder.children("li.bit-box.deleted").fadeOut("fast", function() + { + removeItem($(this)); + return false; + }); + } + } + + if (event.keyCode != 40 && event.keyCode != 38 && etext.length != 0) + { + counter = 0; + + if (options.json_url) + { + if (options.cache && json_cache) + { + addMembers(etext); + bindEvents(); + } + else + { + //search texts > 1 fix by >J.montoya + if (etext.length > 0) { + $.getJSON(options.json_url + "&tag=" + etext, null, function(data){ + addMembers(etext, data); + json_cache = true; + bindEvents(); + }); + } + } + } + else + { + addMembers(etext); + bindEvents(); + } + complete.children(".default").hide(); + feed.show(); + } + } + ); + if (focusme) + { + setTimeout(function(){ + input.focus(); + complete.children(".default").show(); + },1); + } + } + + function addMembers(etext, data) + { + feed.html(''); + + if (!options.cache) + { + cache = new Array(); + search_string = ""; + } + + addTextItem(etext); + + if (data != null && data.length) + { + $.each(data, + function(i, val) + { + cache.push ( + { + caption: val.caption, + value: val.value + } + ); + search_string += "" + (cache.length - 1) + ":" + val.caption + ";"; + } + ); + } + + var maximum = options.maxshownitems 0) + { + var id = match[1]; + var object = cache[id]; + if (options.filter_selected && element.children("option[value=" + object.value + "]").hasClass("selected")) + { + //nothing here... + } + else + { + content += '
  • ' + itemIllumination(object.caption, etext) + '
  • '; + counter++; + maximum--; + } + match = myregexp.exec(search_string); + } + feed.append(content); + + if (options.firstselected) + { + focuson = feed.children("li:visible:first"); + focuson.addClass("auto-focus"); + } + + if (counter > options.height) + { + feed.css({"height": (options.height * 24) + "px","overflow": "auto"}); + if (browser_msie) + { + browser_msie_frame.css({"height": (options.height * 24) + "px", "width": feed.width() + "px"}).show(); + } + } + else + { + feed.css("height", "auto"); + if (browser_msie) + { + browser_msie_frame.css({"height": feed.height() + "px", "width": feed.width() + "px"}).show(); + } + } + } + + function itemIllumination(text, etext) + { + if (options.filter_case) + { + try { + eval("var text = text.replace(/(.*)(" + etext + ")(.*)/gi,'$1$2$3');"); + } catch(ex){}; + } + else + { + try { + eval("var text = text.replace(/(.*)(" + etext.toLowerCase() + ")(.*)/gi,'$1$2$3');"); + }catch(ex){}; + } + return text; + } + + function bindFeedEvent() + { + feed.children("li").mouseover( + function() + { + feed.children("li").removeClass("auto-focus"); + $(this).addClass("auto-focus"); + focuson = $(this); + } + ); + + feed.children("li").mouseout( + function() + { + $(this).removeClass("auto-focus"); + focuson = null; + } + ); + } + + function removeFeedEvent() + { + feed.children("li").unbind("mouseover"); + feed.children("li").unbind("mouseout"); + feed.mousemove( + function () + { + bindFeedEvent(); + feed.unbind("mousemove"); + } + ); + } + + function bindEvents() + { + var maininput = $("#"+elemid + "_annoninput").children(".maininput"); + bindFeedEvent(); + feed.children("li").unbind("mousedown"); + feed.children("li").mousedown( + function() + { + //click in the list + var option = $(this); + addItem(option.text(),option.attr("rel")); + feed.hide(); + browser_msie?browser_msie_frame.hide():''; + complete.hide(); + } + ); + + maininput.unbind("keydown"); + maininput.keydown( + function(event) + { + if(event.keyCode == 191) + { + event.preventDefault(); + return false; + } + + if (event.keyCode != 8) + { + holder.children("li.bit-box.deleted").removeClass("deleted"); + } + + if (event.keyCode == 13 && checkFocusOn()) + { + var option = focuson; + addItem(option.text(), option.attr("rel")); + complete.hide(); + event.preventDefault(); + focuson = null; + return false; + } + + if (event.keyCode == 13 && !checkFocusOn()) + { + if (options.newel) + { + var value = xssPrevent($(this).val()); + addItem(value, value); + complete.hide(); + event.preventDefault(); + focuson = null; + } + return false; + } + + if (event.keyCode == 40) + { + removeFeedEvent(); + if (focuson == null || focuson.length == 0) + { + focuson = feed.children("li:visible:first"); + feed.get(0).scrollTop = 0; + } + else + { + focuson.removeClass("auto-focus"); + focuson = focuson.nextAll("li:visible:first"); + var prev = parseInt(focuson.prevAll("li:visible").length,10); + var next = parseInt(focuson.nextAll("li:visible").length,10); + if ((prev > Math.round(options.height /2) || next <= Math.round(options.height /2)) && typeof(focuson.get(0)) != "undefined") + { + feed.get(0).scrollTop = parseInt(focuson.get(0).scrollHeight,10) * (prev - Math.round(options.height /2)); + } + } + feed.children("li").removeClass("auto-focus"); + focuson.addClass("auto-focus"); + } + if (event.keyCode == 38) + { + removeFeedEvent(); + if (focuson == null || focuson.length == 0) + { + focuson = feed.children("li:visible:last"); + feed.get(0).scrollTop = parseInt(focuson.get(0).scrollHeight,10) * (parseInt(feed.children("li:visible").length,10) - Math.round(options.height /2)); + } + else + { + focuson.removeClass("auto-focus"); + focuson = focuson.prevAll("li:visible:first"); + var prev = parseInt(focuson.prevAll("li:visible").length,10); + var next = parseInt(focuson.nextAll("li:visible").length,10); + if ((next > Math.round(options.height /2) || prev <= Math.round(options.height /2)) && typeof(focuson.get(0)) != "undefined") + { + feed.get(0).scrollTop = parseInt(focuson.get(0).scrollHeight,10) * (prev - Math.round(options.height /2)); + } + } + feed.children("li").removeClass("auto-focus"); + focuson.addClass("auto-focus"); + } + } + ); + } + + function addTextItem(value) + { + if (options.newel) + { + feed.children("li[fckb=1]").remove(); + if (value.length == 0) + { + return; + } + var li = $(document.createElement("li")); + li.attr({"rel": value,"fckb": "1"}).html(value); + feed.prepend(li); + counter++; + } else + { + return; + } + } + + function funCall(func,item) + { + var _object = ""; + for(i=0;i < item.get(0).attributes.length;i++) + { + if (item.get(0).attributes[i].nodeValue != null) + { + _object += "\"_" + item.get(0).attributes[i].nodeName + "\": \"" + item.get(0).attributes[i].nodeValue + "\","; + } + } + _object = "{"+ _object + " notinuse: 0}"; + try { + eval(func + "(" + _object + ")"); + }catch(ex){}; + } + + function checkFocusOn() + { + if (focuson == null) + { + return false; + } + if (focuson.length == 0) + { + return false; + } + return true; + } + + function xssPrevent(string) + { + string = string.replace(/[\"\'][\s]*javascript:(.*)[\"\']/g, "\"\""); + string = string.replace(/script(.*)/g, ""); + string = string.replace(/eval\((.*)\)/g, ""); + string = string.replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', ''); + return string; + } + + var options = $.extend({ + json_url: null, + cache: false, + height: "10", + newel: false, + firstselected: false, + filter_case: false, + filter_hide: false, + complete_text: "Start to type...", + maxshownitems: 30, + onselect: "", + onremove: "" + }, opt); + + //system variables + var holder = null; + var feed = null; + var complete = null; + var counter = 0; + var cache = new Array(); + var json_cache = false; + var search_string = ""; + var focuson = null; + var deleting = 0; + var browser_msie = "\v"=="v"; + var browser_msie_frame; + + var element = $(this); + var elemid = element.attr("id"); + init(); + + return this; + }); + }; + } +); diff --git a/main/inc/lib/javascript/tag/jquery.fcbkcomplete.min.js b/main/inc/lib/javascript/tag/jquery.fcbkcomplete.min.js new file mode 100644 index 0000000000..2cc12e1fba --- /dev/null +++ b/main/inc/lib/javascript/tag/jquery.fcbkcomplete.min.js @@ -0,0 +1,52 @@ +/* + FCBKcomplete 2.6 + - Jquery version required: 1.2.x, 1.3.x + + Changelog: + + - 2.00 new version of fcbkcomplete + + - 2.01 fixed bugs & added features + fixed filter bug for preadded items + focus on the input after selecting tag + the element removed pressing backspace when the element is selected + input tag in the control has a border in IE7 + added iterate over each match and apply the plugin separately + set focus on the input after selecting tag + + - 2.02 fixed fist element selected bug + fixed defaultfilter error bug + + - 2.5 removed selected="selected" attribute due ie bug + element search algorithm changed + better performance fix added + fixed many small bugs + onselect event added + onremove event added + + - 2.6 ie6/7 support fix added + added new public method addItem due request + added new options "firstselected" that you can set true/false to select first element on dropdown list + autoexpand input element added + removeItem bug fixed + and many more bug fixed + + */ + +/* Coded by: emposha */ +/* Copyright: Emposha.com - Distributed under MIT - Keep this message! */ +/* + * json_url - url to fetch json object + * cache - use cache + * height - maximum number of element shown before scroll will apear + * newel - show typed text like a element + * firstselected - automaticly select first element from dropdown + * filter_case - case sensitive filter + * filter_selected - filter selected items from list + * complete_text - text for complete page + * maxshownitems - maximum numbers that will be shown at dropdown list (less better performance) + * onselect - fire event on item select + * onremove - fire event on item remove + */ + + jQuery(function($){$.fn.fcbkcomplete=function(opt){return this.each(function(){function init(){createFCBK();preSet();addInput(0);}function createFCBK(){element.hide();element.attr("multiple","multiple");if(element.attr("name").indexOf("[]")==-1){element.attr("name",element.attr("name")+"[]");}holder=$(document.createElement("ul"));holder.attr("class","holder");element.after(holder);complete=$(document.createElement("div"));complete.addClass("facebook-auto");complete.append('
    '+options.complete_text+"
    ");if(browser_msie){complete.append('');browser_msie_frame=complete.children('.ie6fix');}feed=$(document.createElement("ul"));feed.attr("id",elemid+"_feed");complete.prepend(feed);holder.after(complete);feed.css("width",complete.width());}function preSet(){element.children("option").each(function(i,option){option=$(option);if(option.hasClass("selected")){addItem(option.text(),option.val(),true);option.attr("selected","selected");}else{option.removeAttr("selected");}cache.push({caption:option.text(),value:option.val()});search_string+=""+(cache.length-1)+":"+option.text()+";";});}this.addItem=function(title,value){addItem(title,value);};function addItem(title,value,preadded){var li=document.createElement("li");var txt=document.createTextNode(title);var aclose=document.createElement("a");$(li).attr({"class":"bit-box","rel":value});$(li).prepend(txt);$(aclose).attr({"class":"closebutton","href":"#"});li.appendChild(aclose);holder.append(li);$(aclose).click(function(){$(this).parent("li").fadeOut("fast",function(){removeItem($(this));});return false;});if(!preadded){$("#"+elemid+"_annoninput").remove();var _item;addInput(1);if(element.children("option[value="+value+"]").length){_item=element.children("option[value="+value+"]");_item.get(0).setAttribute("selected","selected");if(!_item.hasClass("selected")){_item.addClass("selected");}}else{var _item=$(document.createElement("option"));_item.attr("value",value).get(0).setAttribute("selected","selected");_item.attr("value",value).addClass("selected");_item.text(title);element.append(_item);}if(options.onselect.length){funCall(options.onselect,_item);}}holder.children("li.bit-box.deleted").removeClass("deleted");feed.hide();browser_msie?browser_msie_frame.hide():'';}function removeItem(item){if(options.onremove.length){var _item=element.children("option[value="+item.attr("rel")+"]");funCall(options.onremove,_item);}element.children("option[value="+item.attr("rel")+"]").removeAttr("selected");element.children("option[value="+item.attr("rel")+"]").removeClass("selected");item.remove();deleting=0;}function addInput(focusme){var li=$(document.createElement("li"));var input=$(document.createElement("input"));li.attr({"class":"bit-input","id":elemid+"_annoninput"});input.attr({"type":"text","class":"maininput","size":"1"});holder.append(li.append(input));input.focus(function(){complete.fadeIn("fast");});input.blur(function(){complete.fadeOut("fast");});holder.click(function(){input.focus();if(feed.length&&input.val().length){feed.show();}else{feed.hide();browser_msie?browser_msie_frame.hide():'';complete.children(".default").show();}});input.keypress(function(event){if(event.keyCode==13){return false;}input.attr("size",input.val().length+1);});input.keydown(function(event){if(event.keyCode==191){event.preventDefault();return false;}});input.keyup(function(event){var etext=xssPrevent(input.val());if(event.keyCode==8&&etext.length==0){feed.hide();browser_msie?browser_msie_frame.hide():'';if(holder.children("li.bit-box.deleted").length==0){holder.children("li.bit-box:last").addClass("deleted");return false;}else{if(deleting){return;}deleting=1;holder.children("li.bit-box.deleted").fadeOut("fast",function(){removeItem($(this));return false;});}}if(event.keyCode!=40&&event.keyCode!=38&&etext.length!=0){counter=0;if(options.json_url){if(options.cache&&json_cache){addMembers(etext);bindEvents();}else{$.getJSON(options.json_url+"?tag="+etext,null,function(data){addMembers(etext,data);json_cache=true;bindEvents();});}}else{addMembers(etext);bindEvents();}complete.children(".default").hide();feed.show();}});if(focusme){setTimeout(function(){input.focus();complete.children(".default").show();},1);}}function addMembers(etext,data){feed.html('');if(!options.cache){cache=new Array();search_string="";}addTextItem(etext);if(data!=null&&data.length){$.each(data,function(i,val){cache.push({caption:val.caption,value:val.value});search_string+=""+(cache.length-1)+":"+val.caption+";";});}var maximum=options.maxshownitems0){var id=match[1];var object=cache[id];if(options.filter_selected&&element.children("option[value="+object.value+"]").hasClass("selected")){}else{content+='
  • '+itemIllumination(object.caption,etext)+'
  • ';counter++;maximum--;}match=myregexp.exec(search_string);}feed.append(content);if(options.firstselected){focuson=feed.children("li:visible:first");focuson.addClass("auto-focus");}if(counter>options.height){feed.css({"height":(options.height*24)+"px","overflow":"auto"});if(browser_msie){browser_msie_frame.css({"height":(options.height*24)+"px","width":feed.width()+"px"}).show();}}else{feed.css("height","auto");if(browser_msie){browser_msie_frame.css({"height":feed.height()+"px","width":feed.width()+"px"}).show();}}}function itemIllumination(text,etext){if(options.filter_case){try{eval("var text = text.replace(/(.*)("+etext+")(.*)/gi,'$1$2$3');");}catch(ex){}}else{try{eval("var text = text.replace(/(.*)("+etext.toLowerCase()+")(.*)/gi,'$1$2$3');");}catch(ex){}}return text;}function bindFeedEvent(){feed.children("li").mouseover(function(){feed.children("li").removeClass("auto-focus");$(this).addClass("auto-focus");focuson=$(this);});feed.children("li").mouseout(function(){$(this).removeClass("auto-focus");focuson=null;});}function removeFeedEvent(){feed.children("li").unbind("mouseover");feed.children("li").unbind("mouseout");feed.mousemove(function(){bindFeedEvent();feed.unbind("mousemove");});}function bindEvents(){var maininput=$("#"+elemid+"_annoninput").children(".maininput");bindFeedEvent();feed.children("li").unbind("mousedown");feed.children("li").mousedown(function(){var option=$(this);addItem(option.text(),option.attr("rel"));feed.hide();browser_msie?browser_msie_frame.hide():'';complete.hide();});maininput.unbind("keydown");maininput.keydown(function(event){if(event.keyCode==191){event.preventDefault();return false;}if(event.keyCode!=8){holder.children("li.bit-box.deleted").removeClass("deleted");}if(event.keyCode==13&&checkFocusOn()){var option=focuson;addItem(option.text(),option.attr("rel"));complete.hide();event.preventDefault();focuson=null;return false;}if(event.keyCode==13&&!checkFocusOn()){if(options.newel){var value=xssPrevent($(this).val());addItem(value,value);complete.hide();event.preventDefault();focuson=null;}return false;}if(event.keyCode==40){removeFeedEvent();if(focuson==null||focuson.length==0){focuson=feed.children("li:visible:first");feed.get(0).scrollTop=0;}else{focuson.removeClass("auto-focus");focuson=focuson.nextAll("li:visible:first");var prev=parseInt(focuson.prevAll("li:visible").length,10);var next=parseInt(focuson.nextAll("li:visible").length,10);if((prev>Math.round(options.height/2)||next<=Math.round(options.height/2))&&typeof(focuson.get(0))!="undefined"){feed.get(0).scrollTop=parseInt(focuson.get(0).scrollHeight,10)*(prev-Math.round(options.height/2));}}feed.children("li").removeClass("auto-focus");focuson.addClass("auto-focus");}if(event.keyCode==38){removeFeedEvent();if(focuson==null||focuson.length==0){focuson=feed.children("li:visible:last");feed.get(0).scrollTop=parseInt(focuson.get(0).scrollHeight,10)*(parseInt(feed.children("li:visible").length,10)-Math.round(options.height/2));}else{focuson.removeClass("auto-focus");focuson=focuson.prevAll("li:visible:first");var prev=parseInt(focuson.prevAll("li:visible").length,10);var next=parseInt(focuson.nextAll("li:visible").length,10);if((next>Math.round(options.height/2)||prev<=Math.round(options.height/2))&&typeof(focuson.get(0))!="undefined"){feed.get(0).scrollTop=parseInt(focuson.get(0).scrollHeight,10)*(prev-Math.round(options.height/2));}}feed.children("li").removeClass("auto-focus");focuson.addClass("auto-focus");}});}function addTextItem(value){if(options.newel){feed.children("li[fckb=1]").remove();if(value.length==0){return;}var li=$(document.createElement("li"));li.attr({"rel":value,"fckb":"1"}).html(value);feed.prepend(li);counter++;}else{return;}}function funCall(func,item){var _object="";for(i=0;iDG(0N}=r8~gU{i^XCdkH_!#XEK??hY#PmbLYsB zBQ-TOdcEFcGGQ3zcDqxl)R{A9Dk>_n+3b@iPo6$~y0EaYd-v|n&dz8wdiLzuYuB#f zIId7A#>dBxA3q+6M7D3=e*gY`mSv?<>Cn*7!Gi~r$>gb1r$~|%i^V5SoY=pAe_2@> zpU;2v=+TQ8FUH2ka=F~gmoFiN$BrF){rdHrH*e0LKY#i1-Mo2odV2cVvuD?@ zUw`o6!QH!euU@^nxVSh!KY#Duy<4|#J%9fE_U+r-wrx9k^5pF7Y2!hz{_K}ehqtPf73O8@wtkGyFib|)`J9qAU_vSxN)!U+Q3$?~abhem{rh;Yf zPOJW1?#m|PBZ)!HR~tu$H1Sk?EIk|_G;Yi!he6f93Ps{~w+!$1-$w+3U+n~t&M}Ot zdSwI42rFvpj1a?ZjJW9IPwE??&E&#p9=}A*VhFMy6cm+?16Q*P7E0tZ7Ed$$XgM+i z94(M9MQ3b;8w!dQGl;PjLVE4Y?<_P{QXeB^>`CbQFBvD_OT*#R6y9XGLopA~j#=m{7->pd6t>(Yi=3 zP}uo3V`jwsY7W}yU|ZK5z%f|Z?m9z~uy*yuIRu5uFfoVs{~5H(gmaYV0%%rtm$QvL z{~uwK+1e_?@y3>z>U)~d^%(jpL%ul0QBAF>DcN4YPSb$5Y$@O`J^)4H(vAG6fyV-$ zpp9QFT`ozb8GCfYP#22&x;IILw01D;WQQ6$Rd7I-5KT1ikgn+a(|F~;N5`@Dq1(|Cs^MXKy8P08>2 ze(zaIH}OH&tXIpzRp|f=nv{at6&u#2!XbvJ4yIM4%e%g~!%0&*vz<+T(e8jvJ$eoV zB?T~K5as2YK8NWfo<~YPgTv`>IqKsqR5<(dp)cEDK=00n>#89cvunA{%L%$M)}%E- z+Y7LTL0WhsE#TMm`-=Xj>PZ!`)~ z(K&8wwg6)N&3|FkYgh7U3x!zd`r4lqi5ZME;n0{F5#}>?i)rEJH#GeJiA|m+eZ8DO zSU?f>fl#Gzi`dPes#t)+aBX={4?$xdcO&byCid?1(awZqf1}Z}+v{|OO zt|T#wanNsY&NCKu;zG`1RYSh)xbINpDJ|DFE; D?d&fs literal 0 HcmV?d00001 diff --git a/main/inc/lib/javascript/tag/style.css b/main/inc/lib/javascript/tag/style.css new file mode 100644 index 0000000000..285ef5417a --- /dev/null +++ b/main/inc/lib/javascript/tag/style.css @@ -0,0 +1,51 @@ +/* TextboxList sample CSS */ +ul.holder { margin: 0; border: 1px solid #999; overflow: hidden; height: auto !important; height: 1%; padding: 4px 5px 0; } +*:first-child+html ul.holder { padding-bottom: 2px; } * html ul.holder { padding-bottom: 2px; } /* ie7 and below */ +ul.holder li { float: left; list-style-type: none; margin: 0 5px 4px 0; white-space:nowrap;} +ul.holder li.bit-box, ul.holder li.bit-input input { font: 11px "Lucida Grande", "Verdana"; } +ul.holder li.bit-box { -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; border: 1px solid #CAD8F3; background: #DEE7F8; padding: 1px 5px 2px; } +ul.holder li.bit-box-focus { border-color: #598BEC; background: #598BEC; color: #fff; } +ul.holder li.bit-input input { width: auto; overflow:visible; margin: 0; border: 0px; outline: 0; padding: 3px 0px 2px; } /* no left/right padding here please */ +ul.holder li.bit-input input.smallinput { width: 20px; } + +/* Facebook demo CSS */ +#add { border: 1px solid #999; width: 550px; margin: 50px; padding: 20px 30px 10px; } +form ol li { list-style-type: none; } +form ol { font: 11px "Lucida Grande", "Verdana"; margin: 0; padding: 0; } +form ol li.input-text { margin-bottom: 10px; list-style-type: none; padding-bottom: 10px; } +form ol li.input-text label { font-weight: bold; cursor: pointer; display: block; font-size: 13px; margin-bottom: 10px; } +form ol li.input-text input { background-image:none; width: 500px; padding: 5px 5px 6px; font: 11px "Lucida Grande", "Verdana"; border: 1px solid #999; } + +/* This hide the shadow.gid image of dokeos*/ +ul.holder input[type="text"]:focus, ul.holder input-text input[type="password"]:focus { + background-image:none; +} + + + + +form ul.holder { width: 500px; } +form ul { margin: 0 !important } +ul.holder li.bit-box, #apple-list ul.holder li.bit-box { padding-right: 15px; position: relative; z-index:1000;} +#apple-list ul.holder li.bit-input { margin: 0; } +#apple-list ul.holder li.bit-input input.smallinput { width: 5px; } +ul.holder li.bit-hover { background: #BBCEF1; border: 1px solid #6D95E0; } +ul.holder li.bit-box-focus { border-color: #598BEC; background: #598BEC; color: #fff; } +ul.holder li.bit-box a.closebutton { position: absolute; right: 4px; top: 5px; display: block; width: 7px; height: 7px; font-size: 1px; background: url('close.gif'); } +ul.holder li.bit-box a.closebutton:hover { background-position: 7px; } +ul.holder li.bit-box-focus a.closebutton, ul.holder li.bit-box-focus a.closebutton:hover { background-position: bottom; } + +/* Autocompleter */ + +.facebook-auto { z-index:2000; display: none; position: absolute; width: 512px; background: #eee; } +.facebook-auto .default { z-index:2000; padding: 5px 7px; border: 1px solid #ccc; border-width: 0 1px 1px;font-family:"Lucida Grande","Verdana"; font-size:11px; } +.facebook-auto ul { display: none; margin: 0; padding: 0; overflow: auto; position:absolute; z-index:9999} +.facebook-auto ul li { padding: 5px 12px; z-index: 1000; cursor: pointer; margin: 0; list-style-type: none; border: 1px solid #ccc; border-width: 0 1px 1px; font: 11px "Lucida Grande", "Verdana"; background-color: #eee } +.facebook-auto ul li em { font-weight: bold; font-style: normal; background: #ccc; } +.facebook-auto ul li.auto-focus { background: #4173CC; color: #fff; } +.facebook-auto ul li.auto-focus em { background: none; } +.deleted { background-color:#4173CC !important; color:#ffffff !important;} +.hidden { display:none;} + +#demo ul.holder li.bit-input input { padding: 2px 0 1px; border: 1px solid #999; } +.ie6fix {height:1px;width:1px; position:absolute;top:0px;left:0px;z-index:1;} \ No newline at end of file diff --git a/main/inc/lib/usermanager.lib.php b/main/inc/lib/usermanager.lib.php index a5a5afb94a..91fe74fa61 100644 --- a/main/inc/lib/usermanager.lib.php +++ b/main/inc/lib/usermanager.lib.php @@ -19,6 +19,7 @@ define('USER_FIELD_TYPE_DATE', 6); define('USER_FIELD_TYPE_DATETIME', 7); define('USER_FIELD_TYPE_DOUBLE_SELECT', 8); define('USER_FIELD_TYPE_DIVIDER', 9); +define('USER_FIELD_TYPE_TAG', 10); class UserManager { @@ -893,6 +894,7 @@ class UserManager if ($user_id != strval(intval($user_id))) return false; if ($user_id === false) return false; $fvalues = ''; + //echo '
    '; print_r($fvalue);
     		if (is_array($fvalue)) {
     			foreach($fvalue as $val) {
     				$fvalues .= Database::escape_string($val).';';
    @@ -910,6 +912,11 @@ class UserManager
     			// Check if enumerated field, if the option is available
     			$rowuf = Database::fetch_array($resuf);
     			switch ($rowuf['field_type']) {
    +				case 10 :	
    +					//Tags are process here	
    +					UserManager::process_tags(explode(';', $fvalues), $user_id, $rowuf['id']);
    +					return true;
    +				break;
     				case 3:
     				case 4:
     				case 5:
    @@ -932,7 +939,7 @@ class UserManager
     					}
     					break;
     				case 1:
    -				case 2:
    +				case 2:		
     				default:
     					break;
     			}
    @@ -1433,6 +1440,19 @@ class UserManager
     		}
     		return $return;
     	}
    +		
    +	public static function get_all_extra_field_by_type($field_type) {
    +		// database table definition
    +		$table_field 			= Database::get_main_table(TABLE_MAIN_USER_FIELD);
    +
    +		// all the information of the field
    +		$sql = "SELECT * FROM $table_field WHERE field_type='".Database::escape_string($field_type)."'";
    +		$result = Database::query($sql, __FILE__, __LINE__);
    +		while ($row = Database::fetch_array($result)) {
    +			$return[] = $row['id'];
    +		}
    +		return $return;
    +	}
     
     	/**
     	 * Get all the extra field information of a certain field (also the options)
    @@ -1460,7 +1480,7 @@ class UserManager
     		}
     		return $return;
     	}
    -
    +	
     	/** Get extra user data by value
     	 * @param string the internal variable name of the field
     	 * @param string the internal value of the field
    @@ -2223,4 +2243,274 @@ class UserManager
     			$rs = Database::query($sql_insert_outbox, __FILE__, __LINE__);
     		}
     	}
    +	
    +	/*
    +	 * 
    +	 * USER TAGS
    +	 * 
    +	 * Intructions to create a new user tag
    +	 * 
    +	 * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible. Called it "books" for example.
    +	 * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags. 
    +	 * 3. Step 2 will not work since this special input needs a file called "main/user/books.php" In this case. In order to have this file copy and paste from this file main/user/tag.php
    +	 * 4. All the tags are registered in the user_tag table and the relationship between user and tags is in the user_rel_tag table
    +	 * 5. Test and enjoy.
    +	 * 
    +	 */
    +	
    +	/**
    +	 * Gets the tags of a specific field_id 
    +	 * 
    +	 * @param int field_id
    +	 * @param string how we are going to result value in array or in a string (json)
    +	 * @return mixed 
    +	 */
    +	public static function get_tags($tag, $field_id, $return_format='json',$limit=10) {
    +		// database table definition
    +		$table_user_tag			= Database::get_main_table(TABLE_MAIN_USER_TAG);
    +		$table_user_tag_values	= Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
    +		$field_id = intval($field_id);			 //like '%$tag%' 
    +		$limit = intval($limit);	
    +		$tag = Database::escape_string($tag);			 
    +		// all the information of the field
    +		$sql = "SELECT id, tag from $table_user_tag
    +				WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
    +		$result = Database::query($sql, __FILE__, __LINE__);
    +		$return = array();		
    +		if (Database::num_rows($result)>0) {
    +			while ($row = Database::fetch_array($result,'ASSOC')) {
    +				$return[] = array('caption'=>$row['tag'], 'value'=>$row['tag']);
    +			}
    +		}		
    +		if ($return_format=='json') {
    +			$return =  json_encode($return);
    +		}
    +		return $return;
    +	}
    +	
    +	public static function get_top_tags($field_id, $limit=100) {		
    +		// database table definition
    +		$table_user_tag			= Database::get_main_table(TABLE_MAIN_USER_TAG);
    +		$table_user_tag_values	= Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
    +		$field_id = intval($field_id);
    +		$limit = intval($limit);	
    +		// all the information of the field
    +		$sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv INNER JOIN $table_user_tag ut ON(ut.id = uv.tag_id)
    +				WHERE field_id = $field_id GROUP BY tag_id ORDER BY count DESC LIMIT $limit";
    +		$result = Database::query($sql, __FILE__, __LINE__);
    +		$return = array();
    +		if (Database::num_rows($result)>0) {
    +			while ($row = Database::fetch_array($result,'ASSOC')) {
    +				$return[] = $row;
    +			}
    +		}
    +		return $return;		
    +	}
    +	
    +	/**
    +	 * Get user's tags
    +	 * @param int field_id
    +	 * @param int user_id
    +	 * @return array
    +	 */
    +	public static function get_user_tags($user_id,$field_id) {
    +		// database table definition
    +		$table_user_tag			= Database::get_main_table(TABLE_MAIN_USER_TAG);
    +		$table_user_tag_values	= Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
    +		$field_id = intval($field_id);
    +		$user_id = intval($user_id);
    +	
    +		// all the information of the field
    +		$sql = "SELECT ut.id, tag,count FROM $table_user_tag ut INNER JOIN $table_user_tag_values uv ON (uv.tag_id=ut.ID) 
    +				WHERE field_id = $field_id AND user_id = $user_id ORDER BY tag";
    +		$result = Database::query($sql, __FILE__, __LINE__);
    +		$return = array();
    +		if (Database::num_rows($result)> 0) {
    +			while ($row = Database::fetch_array($result,'ASSOC')) {
    +				$return[$row['id']] = array($row['tag'],$row['count']);
    +			}
    +		}
    +		return $return;
    +	}
    +	
    +	/**
    +	 * Searchs user with a specific tag
    +	 * @param string the tag
    +	 * @param int field id of the tag
    +	 * @return array
    +	 */
    +	public static function get_all_user_tags($tag, $field_id, $from, $number_of_items) {
    +		// database table definition
    +		$table_user_tag			= Database::get_main_table(TABLE_MAIN_USER_TAG);
    +		$table_user_tag_values	= Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
    +		$field_id = intval($field_id);
    +		$tag = Database::escape_string($tag);
    +		$from = intval($from);
    +    	$number_of_items = intval($number_of_items);
    +			
    +		// all the information of the field
    +		$sql = "SELECT u.user_id,u.username,firstname, lastname, tag FROM $table_user_tag ut INNER JOIN $table_user_tag_values uv ON (uv.tag_id=ut.ID)
    +					 INNER JOIN user u ON(uv.user_id =u.user_id)
    +				WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag";
    +				
    +		$sql .= " LIMIT $from,$number_of_items";				
    +		$result = Database::query($sql, __FILE__, __LINE__);
    +		$return = array();
    +		if (Database::num_rows($result)> 0) {
    +			while ($row = Database::fetch_array($result,'ASSOC')) {
    +				$return[$row['user_id']] = $row;
    +			}
    +		}
    +		return $return;
    +	}
    +	
    +
    +	/**
    +	 * Get the tag id
    +	 * @param int $tag
    +	 * @param int $field_id
    +	 * @return int 0 if fails otherwise the tag id
    +	 */
    +	public function get_tag_id($tag, $field_id) {
    +		$table_user_tag			= Database::get_main_table(TABLE_MAIN_USER_TAG);
    +		$tag = Database::escape_string($tag);
    +		$field_id = intval($field_id);
    +		//with COLLATE latin1_bin to select query in a case sensitive mode  
    +		$sql = "SELECT id FROM $table_user_tag WHERE tag COLLATE latin1_bin  LIKE '$tag' AND field_id = $field_id";
    +		$result = Database::query($sql, __FILE__, __LINE__);
    +		if (Database::num_rows($result)>0) {
    +			$row = Database::fetch_array($result,'ASSOC');
    +			return $row['id'];
    +		} else {
    +			return 0;
    +		}
    +	}
    +	
    +	/**
    +	 * Get the tag id
    +	 * @param int $tag
    +	 * @param int $field_id
    +	 * @return int 0 if fails otherwise the tag id
    +	 */
    +	public function get_tag_id_from_id($tag_id, $field_id) {
    +		$table_user_tag			= Database::get_main_table(TABLE_MAIN_USER_TAG);
    +		$tag_id = intval($tag_id);
    +		$field_id = intval($field_id);
    +		$sql = "SELECT id FROM $table_user_tag WHERE id = '$tag_id' AND field_id = $field_id";
    +		$result = Database::query($sql, __FILE__, __LINE__);
    +		if (Database::num_rows($result)>0) {
    +			$row = Database::fetch_array($result,'ASSOC');
    +			return $row['id'];
    +		} else {
    +			return false;
    +		}
    +	}
    +	
    +	
    +	/**
    +	 * Adds a user-tag value
    +	 * @param mixed $tag
    +	 * @param int $user_id
    +	 * @param int $field_id
    +	 * @return bool
    +	 */
    +	public function add_tag($tag, $user_id, $field_id) {
    +		// database table definition
    +		$table_user_tag			= Database::get_main_table(TABLE_MAIN_USER_TAG);
    +		$table_user_tag_values	= Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
    +		$tag = Database::escape_string($tag);
    +		$user_id = intval($user_id);
    +		$field_id = intval($field_id);		
    +			
    +		//&&  (substr($tag,strlen($tag)-1) == '@')
    +		/*$sent_by_user = false;		 
    +		if ( substr($tag,0,1) == '@')  {
    +			//is a value sent by the list
    +			$sent_by_user = true;
    +			$tag = substr($tag,1,strlen($tag)-2);
    +		}
    +		*/		
    +		$tag_id = UserManager::get_tag_id($tag,$field_id);		
    +		//@todo we don't create tags with numbers
    +		if (is_numeric($tag)) {
    +			//the form is sending an id this means that the user select it from the list so it MUST exists
    +			/*$new_tag_id = UserManager::get_tag_id_from_id($tag,$field_id);
    +			if ($new_tag_id !== false) {
    +				$sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
    +				$result = Database::query($sql, __FILE__, __LINE__);			
    +				$last_insert_id = $new_tag_id;
    +			} else {
    +				$sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
    +				$result = Database::query($sql, __FILE__, __LINE__);
    +				$last_insert_id = Database::get_last_insert_id();
    +			}*/
    +		} else {
    +			//this is a new tag			
    +			if ($tag_id == 0) {
    +				//the tag doesn't exist
    +				$sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
    +				$result = Database::query($sql, __FILE__, __LINE__);
    +				$last_insert_id = Database::get_last_insert_id();
    +			} else {
    +				//the tag exists we update it
    +				$sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
    +				$result = Database::query($sql, __FILE__, __LINE__);			
    +				$last_insert_id = $tag_id;	
    +			}			
    +		}
    +		
    +		if (!empty($last_insert_id) && ($last_insert_id!=0)) {
    +			//we insert the relationship user-tag
    +			$sql_select ="SELECT tag_id FROM $table_user_tag_values WHERE user_id = $user_id AND tag_id = $last_insert_id ";
    +			$result = Database::query($sql_select, __FILE__, __LINE__);
    +			//if the relationship does not exist we create it
    +			if (Database::num_rows($result)==0) {
    +				$sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
    +				$result = Database::query($sql, __FILE__, __LINE__);
    +			}		
    +		}
    +	}
    +	/**
    +	 * Deletes an user tag
    +	 * @param int user id
    +	 * @param int field id
    +	 * 
    +	 */
    +	public function delete_user_tags($user_id, $field_id) {
    +		// database table definition
    +		$table_user_tag			= Database::get_main_table(TABLE_MAIN_USER_TAG);
    +		$table_user_tag_values	= Database::get_main_table(TABLE_MAIN_USER_REL_TAG);	
    +		$tags = UserManager::get_user_tags($user_id, $field_id);		
    +		//echo '
    ';var_dump($tags);
    +		if(is_array($tags) && count($tags)>0) {
    +			foreach ($tags as $key=>$tag) {
    +				if ($tag[1]>'0') {				
    +					$sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
    +					$result = Database::query($sql, __FILE__, __LINE__);
    +				}
    +				$sql = "DELETE FROM $table_user_tag_values WHERE user_id = $user_id AND tag_id = $key";
    +				$result = Database::query($sql, __FILE__, __LINE__);
    +			}	
    +			
    +		}		
    +	}
    +	
    +	/**
    +	 * Process the tag list comes from the UserManager::update_extra_field_value() function
    +	 * @param array the tag list that will be added 
    +	 * @param int user id
    +	 * @param int field id
    +	 * @return bool
    +	 */
    +	public function process_tags($tags, $user_id, $field_id) {
    +		//We loop the tags and add it to the DB
    +		if (is_array($tags)) {
    +			foreach($tags as $tag) {
    +				UserManager::add_tag($tag, $user_id, $field_id);
    +			}			
    +		} else {
    +			UserManager::add_tag($tags,$user_id, $field_id);
    +		}
    +		return true;
    +	}	
     }
    diff --git a/main/install/dokeos_main.sql b/main/install/dokeos_main.sql
    index 2b58a7e80a..b447737d62 100644
    --- a/main/install/dokeos_main.sql
    +++ b/main/install/dokeos_main.sql
    @@ -2259,3 +2259,26 @@ CREATE TABLE session_category (
       date_end date default NULL,
       PRIMARY KEY  (id)
     );
    +
    +
    +--
    +-- Table structure for table user tag
    +--
    +
    +
    +CREATE TABLE  user_tag (
    +	id int NOT NULL auto_increment,
    +	tag varchar(255) NOT NULL,
    +	field_id int NOT NULL,
    +	count int NOT NULL,
    +	PRIMARY KEY  (id)
    +);
    +
    +
    +CREATE TABLE user_rel_tag (
    +	id int NOT NULL auto_increment,
    +	user_id int NOT NULL,
    +	tag_id int NOT NULL,  
    +	PRIMARY KEY  (id)
    +);
    +
    diff --git a/main/install/migrate-db-1.8.6.1-1.8.6.2-pre.sql b/main/install/migrate-db-1.8.6.1-1.8.6.2-pre.sql
    index 2c9e94d9c3..4ed485912f 100755
    --- a/main/install/migrate-db-1.8.6.1-1.8.6.2-pre.sql
    +++ b/main/install/migrate-db-1.8.6.1-1.8.6.2-pre.sql
    @@ -20,7 +20,6 @@ ALTER TABLE session_rel_course_rel_user ADD COLUMN visibility int NOT NULL defau
     ALTER TABLE session_rel_course_rel_user ADD COLUMN status int NOT NULL default 0;
     CREATE TABLE session_category (id int(11) NOT NULL auto_increment, name varchar(100) default NULL, date_start date default NULL, date_end date default NULL, PRIMARY KEY  (id));
     
    -
     INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES ('allow_coach_to_edit_course_session', NULL, 'radio', 'Course', 'false', 'AllowCoachsToEditInsideTrainingSessions', 'AllowCoachsToEditInsideTrainingSessionsComment', NULL, NULL, 0);
     INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable) VALUES ('show_courses_descriptions_in_catalog', NULL, 'radio', 'Course', 'true', 'ShowCoursesDescriptionsInCatalogTitle', 'ShowCoursesDescriptionsInCatalogComment', NULL, NULL, 1, 1);
     
    @@ -30,6 +29,11 @@ INSERT INTO settings_options (variable, value, display_text) VALUES ('show_cours
     INSERT INTO settings_options (variable, value, display_text) VALUES ('allow_coach_to_edit_course_session', 'true', 'Yes');
     INSERT INTO settings_options (variable, value, display_text) VALUES ('allow_coach_to_edit_course_session', 'false', 'No');
     
    +CREATE TABLE user_tag (id int NOT NULL auto_increment, tag varchar(255) NOT NULL, field_id int NOT NULL, count int NOT NULL, PRIMARY KEY  (id));
    +CREATE TABLE user_rel_tag (id int NOT NULL auto_increment,user_id int NOT NULL,tag_id int NOT NULL, PRIMARY KEY  (id));
    +
    +
    +
     -- xxSTATSxx
     
     -- xxUSERxx
    diff --git a/main/social/profile.php b/main/social/profile.php
    index 6aba32d0ff..2d74d10101 100644
    --- a/main/social/profile.php
    +++ b/main/social/profile.php
    @@ -731,38 +731,46 @@ echo '
    '; $extra_information .= get_lang('ExtraInformation'); $extra_information .= '

    '; $extra_information .='