details: http://www.bx.psu.edu/hg/galaxy/rev/106ed169b440 changeset: 2667:106ed169b440 user: jeremy goecks <jeremy.goecks@emory.edu> date: Mon Aug 31 17:19:33 2009 -0400 description: Another pass on tagging: fixed queries and minor UI issues and reintroduced tagging fields to templates. 8 file(s) affected in this change: lib/galaxy/model/__init__.py lib/galaxy/tags/tag_handler.py lib/galaxy/web/controllers/tag.py static/scripts/autocomplete_tagging.js static/scripts/packed/autocomplete_tagging.js static/scripts/packed/galaxy.base.js templates/dataset/edit_attributes.mako templates/root/history.mako diffs (316 lines): diff -r d7a780065c91 -r 106ed169b440 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py Sat Aug 29 12:08:35 2009 -0400 +++ b/lib/galaxy/model/__init__.py Mon Aug 31 17:19:33 2009 -0400 @@ -1155,7 +1155,7 @@ self.name = name def __str__ ( self ): - return "Tag(id=%s, type=%s, parent_id=%s, name=%s)" % ( self.id, self.type, self.parent_id, self.name ) + return "Tag(id=%s, type=%i, parent_id=%s, name=%s)" % ( self.id, self.type, self.parent_id, self.name ) class ItemTagAssociation ( object ): def __init__( self, item_id=None, tag_id=None, user_tname=None, value=None ): diff -r d7a780065c91 -r 106ed169b440 lib/galaxy/tags/tag_handler.py --- a/lib/galaxy/tags/tag_handler.py Sat Aug 29 12:08:35 2009 -0400 +++ b/lib/galaxy/tags/tag_handler.py Mon Aug 31 17:19:33 2009 -0400 @@ -45,7 +45,13 @@ del item.tags[:] # Returns true if item is has a given tag. - def item_has_tag(self, item, tag_name): + def item_has_tag(self, item, tag): + # Get tag name. + if isinstance(tag, basestring): + tag_name = tag + elif isinstance(tag, Tag): + tag_name = tag.name + # Check for an item-tag association to see if item has a given tag. item_tag_assoc = self._get_item_tag_assoc(item, tag_name) if item_tag_assoc: @@ -103,11 +109,15 @@ if tag.value is not None: tag_str += ":" + tag.user_value tags_str_list.append(tag_str) - return ", ".join(tags_str_list) + return ", ".join(tags_str_list) - # Get a Tag object from a tag string. - def _get_tag(self, db_session, tag_str): - return db_session.query(Tag).filter(Tag.name==tag_str).first() + # Get a Tag object from a tag id. + def get_tag_by_id(self, db_session, tag_id): + return db_session.query(Tag).filter(Tag.id==tag_id).first() + + # Get a Tag object from a tag name (string). + def get_tag_by_name(self, db_session, tag_name): + return db_session.query(Tag).filter(Tag.name==tag_name).first() # Create a Tag object from a tag string. def _create_tag(self, db_session, tag_str): @@ -119,7 +129,7 @@ tag_name = tag_prefix + self._scrub_tag_name(sub_tag) tag = db_session.query(Tag).filter(Tag.name==tag_name).first() if not tag: - tag = Tag(type="generic", name=tag_name) + tag = Tag(type=0, name=tag_name) # Set tag parent. tag.parent = parent_tag @@ -137,7 +147,7 @@ return None # Get item tag. - tag = self._get_tag(db_session, scrubbed_tag_str) + tag = self.get_tag_by_name(db_session, scrubbed_tag_str) # Create tag if necessary. if tag is None: diff -r d7a780065c91 -r 106ed169b440 lib/galaxy/web/controllers/tag.py --- a/lib/galaxy/web/controllers/tag.py Sat Aug 29 12:08:35 2009 -0400 +++ b/lib/galaxy/web/controllers/tag.py Mon Aug 31 17:19:33 2009 -0400 @@ -63,10 +63,22 @@ @web.require_login( "get autocomplete data for an item's tags" ) def tag_autocomplete_data(self, trans, id=None, item_type=None, q=None, limit=None, timestamp=None): """ Get autocomplete data for an item's tags. """ + + # + # Get item, do security check, and get autocomplete data. + # item = self._get_item(trans, item_type, trans.security.decode_id(id)) self._do_security_check(trans, item) + if q.find(":") == -1: + return self._get_tag_autocomplete_names(trans, item, q, limit, timestamp) + else: + return self._get_tag_autocomplete_values(trans, item, q, limit, timestamp) + + def _get_tag_autocomplete_names(self, trans, item, q, limit, timestamp): + """Returns autocomplete data for tag names ordered from most frequently used to + least frequently used.""" # # Get user's item tags and usage counts. # @@ -75,55 +87,99 @@ item_tag_assoc_class = self.tag_handler.get_tag_assoc_class(item.__class__) # Build select statement. - cols_to_select = [ item_tag_assoc_class.table.c.tag_id, item_tag_assoc_class.table.c.user_tname, item_tag_assoc_class.table.c.user_value, func.count('*') ] + cols_to_select = [ item_tag_assoc_class.table.c.tag_id, func.count('*') ] from_obj = item_tag_assoc_class.table.join(item.table).join(Tag) - where_clause = self._get_column_for_filtering_item_by_user_id(item)==trans.get_user().id + where_clause = and_(self._get_column_for_filtering_item_by_user_id(item.__class__)==trans.get_user().id, + Tag.table.c.name.like(q + "%")) order_by = [ func.count("*").desc() ] - ac_for_names = not q.endswith(":") - if ac_for_names: - # Autocomplete for tag names. - where_clause = and_(where_clause, Tag.table.c.name.like(q + "%")) - group_by = item_tag_assoc_class.table.c.tag_id - else: - # Autocomplete for tag values. - tag_name_and_value = q.split(":") - tag_name = tag_name_and_value[0] - tag_value = tag_name_and_value[1] - where_clause = and_(where_clause, Tag.table.c.name==tag_name) - where_clause = and_(where_clause, item_tag_assoc_class.table.c.value.like(tag_value + "%")) - group_by = item_tag_assoc_class.table.c.value + group_by = item_tag_assoc_class.table.c.tag_id # Do query and get result set. query = select(columns=cols_to_select, from_obj=from_obj, - whereclause=where_clause, group_by=group_by, order_by=order_by) + whereclause=where_clause, group_by=group_by, order_by=order_by, limit=limit) result_set = trans.sa_session.execute(query) # Create and return autocomplete data. - if ac_for_names: - # Autocomplete for tag names. - ac_data = "#Header|Your Tags\n" - for row in result_set: - # Exclude tags that are already applied to the history. - if self.tag_handler.item_has_tag(item, row[1]): - continue - # Add tag to autocomplete data. - ac_data += row[1] + "|" + row[1] + "\n" - else: - # Autocomplete for tag values. - ac_data = "#Header|Your Values for '%s'\n" % (tag_name) - for row in result_set: - ac_data += tag_name + ":" + row[2] + "|" + row[2] + "\n" + ac_data = "#Header|Your Tags\n" + for row in result_set: + tag = self.tag_handler.get_tag_by_id(trans.sa_session, row[0]) + # Exclude tags that are already applied to the history. + if self.tag_handler.item_has_tag(item, tag): + continue + # Add tag to autocomplete data. Use the most frequent name that user + # has employed for the tag. + tag_names = self._get_usernames_for_tag(trans.sa_session, trans.get_user(), + tag, item.__class__, item_tag_assoc_class) + ac_data += tag_names[0] + "|" + tag_names[0] + "\n" + + return ac_data + + def _get_tag_autocomplete_values(self, trans, item, q, limit, timestamp): + """Returns autocomplete data for tag values ordered from most frequently used to + least frequently used.""" + + tag_name_and_value = q.split(":") + tag_name = tag_name_and_value[0] + tag_value = tag_name_and_value[1] + tag = self.tag_handler.get_tag_by_name(trans.sa_session, tag_name) + # Don't autocomplete if tag doesn't exist. + if tag is None: + return "" + + # Get item-tag association class. + item_tag_assoc_class = self.tag_handler.get_tag_assoc_class(item.__class__) + + # Build select statement. + cols_to_select = [ item_tag_assoc_class.table.c.value, func.count('*') ] + from_obj = item_tag_assoc_class.table.join(item.table).join(Tag) + where_clause = and_(self._get_column_for_filtering_item_by_user_id(item.__class__)==trans.get_user().id, + Tag.table.c.id==tag.id, + item_tag_assoc_class.table.c.value.like(tag_value + "%")) + order_by = [ func.count("*").desc(), item_tag_assoc_class.table.c.value ] + group_by = item_tag_assoc_class.table.c.value + + # Do query and get result set. + query = select(columns=cols_to_select, from_obj=from_obj, + whereclause=where_clause, group_by=group_by, order_by=order_by, limit=limit) + result_set = trans.sa_session.execute(query) + + # Create and return autocomplete data. + ac_data = "#Header|Your Values for '%s'\n" % (tag_name) + for row in result_set: + ac_data += tag.name + ":" + row[0] + "|" + row[0] + "\n" return ac_data - def _get_column_for_filtering_item_by_user_id(self, item): + def _get_usernames_for_tag(self, db_session, user, tag, item_class, item_tag_assoc_class): + """ Returns an ordered list of the user names for a tag; list is ordered from + most popular to least popular name.""" + + # Build select stmt. + cols_to_select = [ item_tag_assoc_class.table.c.user_tname, func.count('*') ] + where_clause = and_(self._get_column_for_filtering_item_by_user_id(item_class)==user.id , + item_tag_assoc_class.table.c.tag_id==tag.id) + group_by = item_tag_assoc_class.table.c.user_tname + order_by = [ func.count("*").desc() ] + + # Do query and get result set. + query = select(columns=cols_to_select, whereclause=where_clause, + group_by=group_by, order_by=order_by) + result_set = db_session.execute(query) + + user_tag_names = list() + for row in result_set: + user_tag_names.append(row[0]) + + return user_tag_names + + def _get_column_for_filtering_item_by_user_id(self, item_class): """ Returns the column to use when filtering by user id. """ - if isinstance(item, History): - return item.table.c.user_id - elif isinstance(item, HistoryDatasetAssociation): + # TODO: make this generic by using a dict() to map from item class to a "user id" column + if item_class is History: + return History.table.c.user_id + elif item_class is HistoryDatasetAssociation: # Use the user_id associated with the HDA's history. - history = item.history - return history.table.c.user_id + return History.table.c.user_id def _get_item(self, trans, item_type, id): """ Get an item based on type and id. """ diff -r d7a780065c91 -r 106ed169b440 static/scripts/autocomplete_tagging.js --- a/static/scripts/autocomplete_tagging.js Sat Aug 29 12:08:35 2009 -0400 +++ b/static/scripts/autocomplete_tagging.js Mon Aug 31 17:19:33 2009 -0400 @@ -379,9 +379,17 @@ // When the tag area blurs, go to "view tag" mode. tag_area.blur( function(e) { - add_tag_button.show(); - tag_input_field.hide(); - tag_area.removeClass("active-tag-area"); + num_tags = array_length(settings.tags); + if (num_tags != 0) + { + add_tag_button.show(); + tag_input_field.hide(); + tag_area.removeClass("active-tag-area"); + } + else + { + // No tags, so do nothing to ensure that input is still visible. + } }); tag_area.append(add_tag_button); diff -r d7a780065c91 -r 106ed169b440 static/scripts/packed/autocomplete_tagging.js --- a/static/scripts/packed/autocomplete_tagging.js Sat Aug 29 12:08:35 2009 -0400 +++ b/static/scripts/packed/autocomplete_tagging.js Mon Aug 31 17:19:33 2009 -0400 @@ -1,1 +1,1 @@ -var ac_tag_area_id_gen=1;jQuery.fn.autocomplete_tagging=function(c){var e={get_toggle_link_text_fn:function(u){var w="";var v=o(u);if(v!=0){w=v+(v!=0?" Tags":" Tag")}else{w="Add tags"}return w},tag_click_fn:function(u){},input_size:20,in_form:false,tags:{},use_toggle_link:true,item_id:"",add_tag_img:"",add_tag_img_rollover:"",delete_tag_img:"",ajax_autocomplete_tag_url:"",ajax_retag_url:"",ajax_delete_tag_url:"",ajax_add_tag_url:""};var p=jQuery.extend(e,c);var k="tag-area-"+(ac_tag_area_id_gen)++;var m=$("<div></div>").attr("id",k).addClass("tag-area");this.append(m);var o=function(u){if(u.length){return u.length}var v=0;for(element in u){v++}return v};var b=function(){var u=p.get_toggle_link_text_fn(p.tags);var v=$("<a href='/history/tags'>"+u+"</a>").addClass("toggle-link");v.click(function(){var w=(m.css("display")=="none");var x;if(w){x=function(){var y=o(p.tags);if(y==0){m.click()}}}else{x=function(){m.blur()}}m.slideToggle("fast",x);return false});return v};var s=b(); if(p.use_toggle_link){this.prepend(s)}var t=function(u){var v=new Array();for(key in u){v[v.length]=key+"-->"+u[key]}return"{"+v.join(",")+"}"};var a=function(v,u){return v+((u!=""&&u)?":"+u:"")};var h=function(u){return u.split(":")};var i=function(u){var v=$("<img src='"+p.add_tag_img+"' rollover='"+p.add_tag_img_rollover+"'/>").addClass("add-tag-button");v.click(function(){$(this).hide();m.click();return false});return v};var j=function(u){var v=$("<img src='"+p.delete_tag_img+"'/>").addClass("delete-tag-img");v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)});v.click(function(){var B=$(this).parent();var A=B.find(".tag-name").eq(0);var z=A.text();var C=h(z)[0];B.remove();delete p.tags[C];var y=p.get_toggle_link_text_fn(p.tags);s.text(y);$.ajax({url:p.ajax_delete_tag_url,data:{tag_name:C},error:function(){alert("Remove tag failed")},success:function(){}});return true});var w=$("<span>"+u+" </span>").addClass("tag-name");w.click(function(){p.tag_click_fn(u);return true});var x=$("<span></span>").addClass("tag-button");x.append(w);x.append(v);return x};var d=function(v){var u;if(p.in_form){u=$("<textarea id='history-tag-input' rows='1' cols='"+p.input_size+"' value='"+v+"'></textarea>")}else{u=$("<input id='history-tag-input' type='text' size='"+p.input_size+"' value='"+v+"'></input>")}u.keyup(function(D){if(D.keyCode==27){$(this).trigger("blur")}else{if((D.keyCode==13)||(D.keyCode==188)||(D.keyCode==32)){new_value=this.value;if(return_key_pressed_for_autocomplete==true){return_key_pressed_for_autocomplete=false;return false}if(new_value.indexOf(": ",new_value.length-2)!=-1){this.value=new_value.substring(0,new_value.length-1);return false}if((D.keyCode==188)||(D.keyCode==32)){new_value=new_value.substring(0,new_value.length-1)}new_value=new_value.replace(/^\s+|\s+$/g,"");if(new_value.length<3){return false}this.value="";var A=j(new_value);var z=m.children(".tag -button");if(z.length!=0){var E=z.slice(z.length-1);E.after(A)}else{m.prepend(A)}var y=new_value.split(":");p.tags[y[0]]=y[1];var B=p.get_toggle_link_text_fn(p.tags);s.text(B);var C=$(this);$.ajax({url:p.ajax_add_tag_url,data:{new_tag:new_value},error:function(){A.remove();var F=p.get_toggle_link_text_fn(p.tags);s.text(F);alert("Add tag failed")},success:function(){C.flushCache()}});return false}}});var w=function(A,z,y,C,B){tag_name_and_value=C.split(":");return(tag_name_and_value.length==1?tag_name_and_value[0]:tag_name_and_value[1])};var x={selectFirst:false,formatItem:w,autoFill:false,highlight:false};u.autocomplete(p.ajax_autocomplete_tag_url,x);u.addClass("tag-input");return u};for(tag_name in p.tags){var q=p.tags[tag_name];var l=a(tag_name,q);var g=j(l,s,p.tags);m.append(g)}var n=d("");var f=i(n);m.blur(function(u){f.show();n.hide();m.removeClass("active-tag-area")});m.append(f);m.append(n);n.hide();m.click(function(w){var v=$(this).hasClass("active-tag-area");if($(w. target).hasClass("delete-tag-img")&&!v){return false}if($(w.target).hasClass("tag-name")&&!v){return false}$(this).addClass("active-tag-area");f.hide();n.show();n.focus();var u=function(y){var x=m.attr("id");if(($(y.target).attr("id")!=x)&&($(y.target).parents().filter(x).length==0)){m.blur();$(document).unbind("click",u)}};$(window).click(u);return false});if(p.use_toggle_link){m.hide()}else{var r=o(p.tags);if(r==0){f.hide();n.show()}}return this.addClass("tag-element")}; \ No newline at end of file +var ac_tag_area_id_gen=1;jQuery.fn.autocomplete_tagging=function(c){var e={get_toggle_link_text_fn:function(u){var w="";var v=o(u);if(v!=0){w=v+(v!=0?" Tags":" Tag")}else{w="Add tags"}return w},tag_click_fn:function(u){},input_size:20,in_form:false,tags:{},use_toggle_link:true,item_id:"",add_tag_img:"",add_tag_img_rollover:"",delete_tag_img:"",ajax_autocomplete_tag_url:"",ajax_retag_url:"",ajax_delete_tag_url:"",ajax_add_tag_url:""};var p=jQuery.extend(e,c);var k="tag-area-"+(ac_tag_area_id_gen)++;var m=$("<div></div>").attr("id",k).addClass("tag-area");this.append(m);var o=function(u){if(u.length){return u.length}var v=0;for(element in u){v++}return v};var b=function(){var u=p.get_toggle_link_text_fn(p.tags);var v=$("<a href='/history/tags'>"+u+"</a>").addClass("toggle-link");v.click(function(){var w=(m.css("display")=="none");var x;if(w){x=function(){var y=o(p.tags);if(y==0){m.click()}}}else{x=function(){m.blur()}}m.slideToggle("fast",x);return false});return v};var s=b(); if(p.use_toggle_link){this.prepend(s)}var t=function(u){var v=new Array();for(key in u){v[v.length]=key+"-->"+u[key]}return"{"+v.join(",")+"}"};var a=function(v,u){return v+((u!=""&&u)?":"+u:"")};var h=function(u){return u.split(":")};var i=function(u){var v=$("<img src='"+p.add_tag_img+"' rollover='"+p.add_tag_img_rollover+"'/>").addClass("add-tag-button");v.click(function(){$(this).hide();m.click();return false});return v};var j=function(u){var v=$("<img src='"+p.delete_tag_img+"'/>").addClass("delete-tag-img");v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)});v.click(function(){var B=$(this).parent();var A=B.find(".tag-name").eq(0);var z=A.text();var C=h(z)[0];B.remove();delete p.tags[C];var y=p.get_toggle_link_text_fn(p.tags);s.text(y);$.ajax({url:p.ajax_delete_tag_url,data:{tag_name:C},error:function(){alert("Remove tag failed")},success:function(){}});return true});var w=$("<span>"+u+" </span>").addClass("tag-name");w.click(function(){p.tag_click_fn(u);return true});var x=$("<span></span>").addClass("tag-button");x.append(w);x.append(v);return x};var d=function(v){var u;if(p.in_form){u=$("<textarea id='history-tag-input' rows='1' cols='"+p.input_size+"' value='"+v+"'></textarea>")}else{u=$("<input id='history-tag-input' type='text' size='"+p.input_size+"' value='"+v+"'></input>")}u.keyup(function(D){if(D.keyCode==27){$(this).trigger("blur")}else{if((D.keyCode==13)||(D.keyCode==188)||(D.keyCode==32)){new_value=this.value;if(return_key_pressed_for_autocomplete==true){return_key_pressed_for_autocomplete=false;return false}if(new_value.indexOf(": ",new_value.length-2)!=-1){this.value=new_value.substring(0,new_value.length-1);return false}if((D.keyCode==188)||(D.keyCode==32)){new_value=new_value.substring(0,new_value.length-1)}new_value=new_value.replace(/^\s+|\s+$/g,"");if(new_value.length<3){return false}this.value="";var A=j(new_value);var z=m.children(".tag -button");if(z.length!=0){var E=z.slice(z.length-1);E.after(A)}else{m.prepend(A)}var y=new_value.split(":");p.tags[y[0]]=y[1];var B=p.get_toggle_link_text_fn(p.tags);s.text(B);var C=$(this);$.ajax({url:p.ajax_add_tag_url,data:{new_tag:new_value},error:function(){A.remove();var F=p.get_toggle_link_text_fn(p.tags);s.text(F);alert("Add tag failed")},success:function(){C.flushCache()}});return false}}});var w=function(A,z,y,C,B){tag_name_and_value=C.split(":");return(tag_name_and_value.length==1?tag_name_and_value[0]:tag_name_and_value[1])};var x={selectFirst:false,formatItem:w,autoFill:false,highlight:false};u.autocomplete(p.ajax_autocomplete_tag_url,x);u.addClass("tag-input");return u};for(tag_name in p.tags){var q=p.tags[tag_name];var l=a(tag_name,q);var g=j(l,s,p.tags);m.append(g)}var n=d("");var f=i(n);m.blur(function(u){r=o(p.tags);if(r!=0){f.show();n.hide();m.removeClass("active-tag-area")}else{}});m.append(f);m.append(n);n.hide();m.click(function(w){var v=$(this).hasClas s("active-tag-area");if($(w.target).hasClass("delete-tag-img")&&!v){return false}if($(w.target).hasClass("tag-name")&&!v){return false}$(this).addClass("active-tag-area");f.hide();n.show();n.focus();var u=function(y){var x=m.attr("id");if(($(y.target).attr("id")!=x)&&($(y.target).parents().filter(x).length==0)){m.blur();$(document).unbind("click",u)}};$(window).click(u);return false});if(p.use_toggle_link){m.hide()}else{var r=o(p.tags);if(r==0){f.hide();n.show()}}return this.addClass("tag-element")}; \ No newline at end of file diff -r d7a780065c91 -r 106ed169b440 static/scripts/packed/galaxy.base.js --- a/static/scripts/packed/galaxy.base.js Sat Aug 29 12:08:35 2009 -0400 +++ b/static/scripts/packed/galaxy.base.js Mon Aug 31 17:19:33 2009 -0400 @@ -1,1 +1,1 @@ -$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};jQuery(document).ready(function(){jQuery("a[confirm]").click(function(){return confirm(jQuery(this).attr("confirm"))});jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.show()})});function ensure_popup_helper(){if($("#popup-helper").length==0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function make_popupmenu(d,c){ens ure_popup_helper();var a=$(d);var b=$("<ul id='"+d.attr("id")+"-menu'></div>");$.each(c,function(g,f){if(f){$("<li/>").html(g).click(f).appendTo(b)}else{$("<li class='head'/>").html(g).appendTo(b)}});var e=$("<div class='popmenu-wrapper'>");e.append(b).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(d,e)}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){var h=$(b).offset();$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}; \ No newline at end of file +$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};jQuery(document).ready(function(){jQuery("a[confirm]").click(function(){return confirm(jQuery(this).attr("confirm"))});jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.show()})});function ensure_popup_helper(){if($("#popup-helper").length==0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function make_popupmenu(d,c){ens ure_popup_helper();var a=$(d);var b=$("<ul id='"+d.attr("id")+"-menu'></ul>");$.each(c,function(g,f){if(f){$("<li/>").html(g).click(f).appendTo(b)}else{$("<li class='head'/>").html(g).appendTo(b)}});var e=$("<div class='popmenu-wrapper'>");e.append(b).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(d,e)}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){var h=$(b).offset();$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}; \ No newline at end of file diff -r d7a780065c91 -r 106ed169b440 templates/dataset/edit_attributes.mako --- a/templates/dataset/edit_attributes.mako Sat Aug 29 12:08:35 2009 -0400 +++ b/templates/dataset/edit_attributes.mako Mon Aug 31 17:19:33 2009 -0400 @@ -84,17 +84,17 @@ </div> <div style="clear: both"></div> </div> - ## %if trans.get_user() is not None: - ## <div class="form-row"> - ## <label> - ## Tags: - ## </label> - ## <div id="dataset-tag-area" - ## style="float: left; margin-left: 1px; width: 295px; margin-right: 10px; border-style: inset; border-color: #ddd; border-width: 1px"> - ## </div> - ## <div style="clear: both"></div> - ## </div> - ## %endif + %if trans.get_user() is not None: + <div class="form-row"> + <label> + Tags: + </label> + <div id="dataset-tag-area" + style="float: left; margin-left: 1px; width: 295px; margin-right: 10px; border-style: inset; border-color: #ddd; border-width: 1px"> + </div> + <div style="clear: both"></div> + </div> + %endif %for name, spec in data.metadata.spec.items(): %if spec.visible: <div class="form-row"> diff -r d7a780065c91 -r 106ed169b440 templates/root/history.mako --- a/templates/root/history.mako Sat Aug 29 12:08:35 2009 -0400 +++ b/templates/root/history.mako Mon Aug 31 17:19:33 2009 -0400 @@ -377,8 +377,8 @@ <p></p> %endif -## <div id="history-tag-area" style="margin-bottom: 1em"> -## </div> +<div id="history-tag-area" style="margin-bottom: 1em"> +</div> <%namespace file="history_common.mako" import="render_dataset" />