details:
http://www.bx.psu.edu/hg/galaxy/rev/369f6b15f3f1
changeset: 3188:369f6b15f3f1
user: jeremy goecks <jeremy.goecks at emory.edu>
date: Mon Dec 21 09:33:46 2009 -0500
description:
Added functionality for public repositories of histories and pages. Features added
include: (a) unique slugs for histories; (b) pretty URLs for histories; (c) public grids
for histories and pages; (d) links between lists of public pages and histories and
individual pages and histories. Also fixed some bugs in grid framework.
diffstat:
lib/galaxy/model/mapping.py | 3 +-
lib/galaxy/model/migrate/versions/0030_history_slug_column.py | 38 +
lib/galaxy/web/base/controller.py | 16 +
lib/galaxy/web/buildapp.py | 1 +
lib/galaxy/web/controllers/history.py | 106 +-
lib/galaxy/web/controllers/page.py | 46 +-
lib/galaxy/web/framework/helpers/grids.py | 9 +-
templates/base_panels.mako | 4 +-
templates/display_common.mako | 12 +
templates/grid_base.mako | 16 +-
templates/grid_common.mako | 29 +-
templates/history/list_public.mako | 23 +
templates/history/sharing.mako | 3 +-
templates/history/view.mako | 615 +++++----
templates/page/display.mako | 31 +-
templates/page/editor.mako | 17 +-
16 files changed, 628 insertions(+), 341 deletions(-)
diffs (1332 lines):
diff -r e7244f7d613b -r 369f6b15f3f1 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Fri Dec 18 14:18:56 2009 -0500
+++ b/lib/galaxy/model/mapping.py Mon Dec 21 09:33:46 2009 -0500
@@ -78,7 +78,8 @@
Column( "deleted", Boolean, index=True, default=False ),
Column( "purged", Boolean, index=True, default=False ),
Column( "genome_build", TrimmedString( 40 ) ),
- Column( "importable", Boolean, default=False ) )
+ Column( "importable", Boolean, default=False ),
+ Column( "slug", TEXT, index=True ) )
HistoryUserShareAssociation.table = Table( "history_user_share_association",
metadata,
Column( "id", Integer, primary_key=True ),
diff -r e7244f7d613b -r 369f6b15f3f1
lib/galaxy/model/migrate/versions/0030_history_slug_column.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/model/migrate/versions/0030_history_slug_column.py Mon Dec 21 09:33:46
2009 -0500
@@ -0,0 +1,38 @@
+"""
+Migration script to add column for a history slug.
+"""
+
+from sqlalchemy import *
+from migrate import *
+from migrate.changeset import *
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData( migrate_engine )
+
+def upgrade():
+
+ print __doc__
+ metadata.reflect()
+
+ History_table = Table( "history", metadata, autoload=True )
+
+ # Create slug column.
+ c = Column( "slug", TEXT, index=True )
+ c.create( History_table )
+ assert c is History_table.c.slug
+
+ # Create slug index.
+ try:
+ i = Index( "ix_history_slug", History_table.c.slug )
+ i.create()
+ except:
+ # Mysql doesn't have a named index, but alter should work
+ History_table.c.slug.alter( unique=False )
+
+def downgrade():
+ metadata.reflect()
+
+ History_table = Table( "history", metadata, autoload=True )
+ History_table.c.slug.drop()
diff -r e7244f7d613b -r 369f6b15f3f1 lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py Fri Dec 18 14:18:56 2009 -0500
+++ b/lib/galaxy/web/base/controller.py Mon Dec 21 09:33:46 2009 -0500
@@ -8,11 +8,27 @@
from galaxy import config, tools, web, model, util
from galaxy.web import error, form, url_for
from galaxy.model.orm import *
+from galaxy.web.framework.helpers import grids
from Cheetah.Template import Template
log = logging.getLogger( __name__ )
+# Useful columns in many grids used by controllers.
+
+# Item's user/owner.
+class OwnerColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, item ):
+ return item.user.username
+
+# Item's public URL based on username and slug.
+class PublicURLColumn( grids.TextColumn ):
+ def get_link( self, trans, grid, item ):
+ if item.user.username:
+ return dict( action='display_by_username_and_slug',
username=item.user.username, slug=item.slug )
+ else:
+ return None
+
class BaseController( object ):
"""
Base class for Galaxy web application controllers.
diff -r e7244f7d613b -r 369f6b15f3f1 lib/galaxy/web/buildapp.py
--- a/lib/galaxy/web/buildapp.py Fri Dec 18 14:18:56 2009 -0500
+++ b/lib/galaxy/web/buildapp.py Mon Dec 21 09:33:46 2009 -0500
@@ -80,6 +80,7 @@
webapp.add_route( '/:action', controller='root',
action='index' )
webapp.add_route( '/datasets/:dataset_id/:action/:filename',
controller='dataset', action='index', dataset_id=None, filename=None)
webapp.add_route( '/u/:username/p/:slug', controller='page',
action='display_by_username_and_slug' )
+ webapp.add_route( '/u/:username/h/:slug', controller='history',
action='display_by_username_and_slug' )
webapp.finalize_config()
# Wrap the webapp in some useful middleware
if kwargs.get( 'middleware', True ):
diff -r e7244f7d613b -r 369f6b15f3f1 lib/galaxy/web/controllers/history.py
--- a/lib/galaxy/web/controllers/history.py Fri Dec 18 14:18:56 2009 -0500
+++ b/lib/galaxy/web/controllers/history.py Mon Dec 21 09:33:46 2009 -0500
@@ -10,6 +10,7 @@
import webhelpers, logging, operator
from datetime import datetime
from cgi import escape
+import re
log = logging.getLogger( __name__ )
@@ -171,7 +172,33 @@
return session.query( self.model_class ).join( 'users_shared_with' )
def apply_default_filter( self, trans, query, **kwargs ):
return query.filter( model.HistoryUserShareAssociation.user == trans.user )
-
+
+class PublicHistoryListGrid( grids.Grid ):
+ title = "Public Histories"
+ model_class = model.History
+ default_sort_key = "-update_time"
+ default_filter = dict( public_url="All", username="All",
tags="All" )
+ use_async = True
+ columns = [
+ PublicURLColumn( "Name", key="name",
model_class=model.History, filterable="advanced"),
+ OwnerColumn( "Owner", key="username", model_class=model.User,
filterable="advanced", sortable=False ),
+ grids.GridColumn( "Created", key="create_time",
format=time_ago ),
+ grids.GridColumn( "Last Updated", key="update_time",
format=time_ago )
+ ]
+ columns.append(
+ grids.MulticolFilterColumn(
+ "Search",
+ cols_to_filter=[ columns[0], columns[1] ],
+ key="free-text-search", visible=False, filterable="standard"
)
+ )
+ operations = []
+ def build_initial_query( self, session ):
+ # Join so that searching history.user makes sense.
+ return session.query( self.model_class ).join( model.User.table )
+ def apply_default_filter( self, trans, query, **kwargs ):
+ # A public history is importable, has a slug, and is not deleted.
+ return query.filter( self.model_class.importable==True ).filter(
self.model_class.slug != None ).filter( self.model_class.deleted == False )
+
class HistoryController( BaseController ):
@web.expose
def index( self, trans ):
@@ -183,6 +210,17 @@
stored_list_grid = HistoryListGrid()
shared_list_grid = SharedHistoryListGrid()
+ public_list_grid = PublicHistoryListGrid()
+
+ @web.expose
+ @web.require_login()
+ def list_public( self, trans, **kwargs ):
+ grid = self.public_list_grid( trans, **kwargs )
+ if 'async' in kwargs:
+ return grid
+ else:
+ # Render grid wrapped in panels
+ return trans.fill_template( "history/list_public.mako", grid=grid
)
@web.expose
@web.require_login( "work with multiple histories" )
@@ -234,7 +272,7 @@
elif operation == "enable import via link":
for history in histories:
if not history.importable:
- history.importable = True
+ self.make_history_importable( trans.sa_session, history )
elif operation == "disable import via link":
if history_ids:
histories = [ self.get_history( trans, history_id ) for
history_id in history_ids ]
@@ -372,26 +410,31 @@
trans.sa_session.flush()
@web.expose
- @web.require_login( "get history name" )
- def get_name_async( self, trans, id=None ):
- """ Returns the name for a given history. """
+ @web.require_login( "get history name, slug, and owner's username" )
+ def get_name_slug_username_async( self, trans, id=None ):
+ """ Returns the name, slug, and owner's username for a given
history. """
history = self.get_history( trans, id, False )
- # To get name: user must own history, history must be importable.
- if history.user == trans.get_user() or history.importable or trans.get_user() in
history.users_shared_with:
- return history.name
+ # To get info: user must own history.
+ if history.user == trans.get_user():
+ slug = iff( history.slug, history.slug, "" )
+ username = iff ( history.user.username, history.user.username, ""
)
+ return history.name + "," + slug + "," + username
return
@web.expose
@web.require_login( "set history's importable flag" )
def set_importable_async( self, trans, id=None, importable=False ):
- """ Set history's importable attribute. """
+ """ Set history's importable attribute and sets history's
slug. """
history = self.get_history( trans, id, True )
# Only set if importable value would change; this prevents a change in the
update_time unless attribute really changed.
importable = importable in ['True', 'true', 't',
'T'];
if history and history.importable != importable:
- history.importable = importable
+ if importable:
+ self.make_history_importable( trans.sa_session, history )
+ else:
+ history.importable = importable
trans.sa_session.flush()
return
@@ -492,6 +535,31 @@
datasets = query.all(),
user_owns_history = user_owns_history,
show_deleted = False )
+
+ @web.expose
+ def display_by_username_and_slug( self, trans, username, slug ):
+ session = trans.sa_session
+ user = session.query( model.User ).filter_by( username=username ).first()
+ if user is None:
+ raise web.httpexceptions.HTTPNotFound()
+ history = trans.sa_session.query( model.History ).filter_by( user=user, slug=slug,
deleted=False, importable=True ).first()
+ if history is None:
+ raise web.httpexceptions.HTTPNotFound()
+
+ query = trans.sa_session.query( model.HistoryDatasetAssociation ) \
+ .filter( model.HistoryDatasetAssociation.history ==
history ) \
+ .options( eagerload( "children" ) ) \
+ .join( "dataset" ).filter( model.Dataset.purged
== False ) \
+ .options( eagerload_all( "dataset.actions" ) )
+ # Do not show deleted datasets.
+ query = query.filter( model.HistoryDatasetAssociation.deleted == False )
+ user_owns_history = ( trans.get_user() == history.user )
+ return trans.stream_template_mako( "history/view.mako",
+ history = history,
+ datasets = query.all(),
+ user_owns_history = user_owns_history,
+ show_deleted = False )
+
@web.expose
@web.require_login( "share histories with other users" )
def share( self, trans, id=None, email="", **kwd ):
@@ -780,7 +848,7 @@
for history in histories:
trans.sa_session.add( history )
if params.get( 'enable_import_via_link', False ):
- history.importable = True
+ self.make_history_importable( trans.sa_session, history )
trans.sa_session.flush()
elif params.get( 'disable_import_via_link', False ):
history.importable = False
@@ -896,4 +964,18 @@
msg = 'Clone with name "%s" is now included in your previously
stored histories.' % new_history.name
else:
msg = '%d cloned histories are now included in your previously stored
histories.' % len( histories )
- return trans.show_ok_message( msg )
\ No newline at end of file
+ return trans.show_ok_message( msg )
+
+ def make_history_importable( self, sa_session, history ):
+ """ Makes history importable and sets history's slug. Does not
flush/commit changes, however. """
+ history.importable = True
+
+ # Set history slug. Slug must be unique among user's importable pages.
+ slug_base = re.sub( "\s+", "-", history.name.lower() )
+ slug = slug_base
+ count = 1
+ while sa_session.query( model.History ).filter_by( user=history.user, slug=slug,
importable=True ).count() != 0:
+ # Slug taken; choose a new slug based on count. This approach can handle
numerous histories with the same name gracefully.
+ slug = '%s-%i' % ( slug_base, count )
+ count += 1
+ history.slug = slug
\ No newline at end of file
diff -r e7244f7d613b -r 369f6b15f3f1 lib/galaxy/web/controllers/page.py
--- a/lib/galaxy/web/controllers/page.py Fri Dec 18 14:18:56 2009 -0500
+++ b/lib/galaxy/web/controllers/page.py Mon Dec 21 09:33:46 2009 -0500
@@ -13,20 +13,6 @@
else:
return ""
-class PublicURLColumn( grids.TextColumn ):
- def get_value( self, trans, grid, item ):
- username = item.user.username or "???"
- return username + "/" + item.slug
- def get_link( self, trans, grid, item ):
- if item.user.username:
- return dict( action='display_by_username_and_slug',
username=item.user.username, slug=item.slug )
- else:
- return None
-
-class OwnerColumn( grids.TextColumn ):
- def get_value( self, trans, grid, item ):
- return item.user.username
-
class PageListGrid( grids.Grid ):
# Grid definition
use_panels = True
@@ -58,18 +44,28 @@
class PageAllPublishedGrid( grids.Grid ):
# Grid definition
use_panels = True
- title = "Published Pages From All Users"
+ use_async = True
+ title = "Published Pages"
model_class = model.Page
- default_sort_key = "-create_time"
+ default_sort_key = "-update_time"
+ default_filter = dict( title="All", username="All" )
columns = [
- grids.TextColumn( "Title", model_class=model.Page,
key="title", filterable="standard" ),
- PublicURLColumn( "Public URL" ),
- OwnerColumn( "Published by", model_class=model.User,
key="username" ),
+ PublicURLColumn( "Title", key="title",
model_class=model.Page, filterable="advanced"),
+ OwnerColumn( "Owner", key="username", model_class=model.User,
filterable="advanced", sortable=False ),
grids.GridColumn( "Created", key="create_time",
format=time_ago ),
- grids.GridColumn( "Last Updated", key="update_time",
format=time_ago ),
+ grids.GridColumn( "Last Updated", key="update_time",
format=time_ago )
]
+ columns.append(
+ grids.MulticolFilterColumn(
+ "Search",
+ cols_to_filter=[ columns[0], columns[1] ],
+ key="free-text-search", visible=False, filterable="standard"
)
+ )
+ def build_initial_query( self, session ):
+ # Join so that searching history.user makes sense.
+ return session.query( self.model_class ).join( model.User.table )
def apply_default_filter( self, trans, query, **kwargs ):
- return query.filter_by( deleted=False, published=True )
+ return query.filter( self.model_class.deleted==False ).filter(
self.model_class.published==True )
class HistorySelectionGrid( grids.Grid ):
# Custom columns.
@@ -169,11 +165,13 @@
return trans.fill_template( "page/index.mako", grid=grid )
@web.expose
- @web.require_login()
def list_published( self, trans, *args, **kwargs ):
grid = self._all_published_list( trans, *args, **kwargs )
- # Render grid wrapped in panels
- return trans.fill_template( "page/index.mako", grid=grid )
+ if 'async' in kwargs:
+ return grid
+ else:
+ # Render grid wrapped in panels
+ return trans.fill_template( "page/index.mako", grid=grid )
@web.expose
diff -r e7244f7d613b -r 369f6b15f3f1 lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py Fri Dec 18 14:18:56 2009 -0500
+++ b/lib/galaxy/web/framework/helpers/grids.py Mon Dec 21 09:33:46 2009 -0500
@@ -122,6 +122,9 @@
column_filter = from_json_string_recurse( column_filter )
if len( column_filter ) == 1:
column_filter = column_filter[0]
+ # Interpret ',' as a separator for multiple terms.
+ if isinstance( column_filter, basestring ) and
column_filter.find(',') != -1:
+ column_filter = column_filter.split(',')
# If filter criterion is empty, do nothing.
if column_filter == '':
continue
@@ -273,8 +276,8 @@
class GridColumn( object ):
def __init__( self, label, key=None, model_class=None, method=None, format=None,
link=None, attach_popup=False, visible=True, ncells=1,
- # Valid values for filterable are ['default',
'advanced', None]
- filterable=None ):
+ # Valid values for filterable are ['standard',
'advanced', None]
+ filterable=None, sortable=True ):
self.label = label
self.key = key
self.model_class = model_class
@@ -287,7 +290,7 @@
self.filterable = filterable
# Currently can only sort of columns that have a database
# representation, not purely derived.
- if self.key:
+ if self.key and sortable:
self.sortable = True
else:
self.sortable = False
diff -r e7244f7d613b -r 369f6b15f3f1 templates/base_panels.mako
--- a/templates/base_panels.mako Fri Dec 18 14:18:56 2009 -0500
+++ b/templates/base_panels.mako Mon Dec 21 09:33:46 2009 -0500
@@ -245,8 +245,8 @@
%>
<li><a target="${logout_target}"
href="${logout_url}">Logout</a></li>
<li><hr style="color: inherit; background-color:
gray"/></li>
- <li><a target="galaxy_main" href="${h.url_for(
controller='history', action='list'
)}">Histories</a></li>
- <li><a target="galaxy_main" href="${h.url_for(
controller='dataset', action='list'
)}">Datasets</a></li>
+ <li><a target="galaxy_main" href="${h.url_for(
controller='/history', action='list'
)}">Histories</a></li>
+ <li><a target="galaxy_main" href="${h.url_for(
controller='/dataset', action='list'
)}">Datasets</a></li>
%if app.config.get_bool( 'enable_pages', False ):
<li><a href="${h.url_for( controller='page'
)}">Pages</a></li>
%endif
diff -r e7244f7d613b -r 369f6b15f3f1 templates/display_common.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/display_common.mako Mon Dec 21 09:33:46 2009 -0500
@@ -0,0 +1,12 @@
+##
+## A set of useful methods for displaying different items.
+##
+
+## Return a link to view a history.
+<%def name="get_history_link( history, qualify=False )">
+ %if history.slug and history.user.username:
+ <% return h.url_for( controller='/history',
action='display_by_username_and_slug', username=history.user.username,
slug=history.slug, qualified=qualify ) %>
+ %else:
+ <% return h.url_for( controller='/history', action='view',
id=trans.security.encode_id( history.id ), qualified=qualify ) %>
+ %endif
+</%def>
\ No newline at end of file
diff -r e7244f7d613b -r 369f6b15f3f1 templates/grid_base.mako
--- a/templates/grid_base.mako Fri Dec 18 14:18:56 2009 -0500
+++ b/templates/grid_base.mako Mon Dec 21 09:33:46 2009 -0500
@@ -34,6 +34,12 @@
$(document).ready(function() {
init_grid_elements();
init_grid_controls();
+
+ // Initalize filter elements.
+ $('input[type=text]').each( function()
+ {
+ $(this).click( function() { $(this).attr('value', '') }
);
+ });
});
## Can this be moved into base.mako?
%if refresh_frames:
@@ -69,11 +75,11 @@
// Code to handle grid operations: filtering, sorting, paging, and operations.
//
- // Operations that are not async (AJAX) compatible.
- var no_async_ops = new Object();
+ // Operations that are async (AJAX) compatible.
+ var async_ops = new Object();
%for operation in grid.operations:
- %if not operation.async_compatible:
- no_async_ops['${operation.label.lower()}'] = "True";
+ %if operation.async_compatible:
+ async_ops['${operation.label.lower()}'] = "True";
%endif
%endfor
@@ -536,7 +542,7 @@
url_args['id'] = item_ids;
// If operation cannot be performed asynchronously, redirect to location.
Otherwise do operation.
- var no_async = ( no_async_ops[operation] != undefined &&
no_async_ops[operation] != null);
+ var no_async = ( async_ops[operation] == undefined || async_ops[operation] ==
null);
if (no_async)
{
go_to_URL();
diff -r e7244f7d613b -r 369f6b15f3f1 templates/grid_common.mako
--- a/templates/grid_common.mako Fri Dec 18 14:18:56 2009 -0500
+++ b/templates/grid_common.mako Mon Dec 21 09:33:46 2009 -0500
@@ -1,4 +1,7 @@
-<%! from galaxy.web.framework.helpers.grids import TextColumn, GridColumnFilter %>
+<%!
+ from galaxy.web.framework.helpers.grids import TextColumn, GridColumnFilter
+ from galaxy.web.framework.helpers import iff
+%>
## Render a filter UI for a grid column. Filter is rendered as a table row.
<%def name="render_grid_column_filter(column)">
@@ -8,7 +11,9 @@
if column.filterable == "advanced":
column_label = column_label.lower()
%>
- <td align="left" style="padding-left:
10px">${column_label}:</td>
+ %if column.filterable == "advanced":
+ <td align="left" style="padding-left:
10px">${column_label}:</td>
+ %endif
<td>
%if isinstance(column, TextColumn):
<form class="text-filter-form"
column_key="${column.key}" action="${url( dict() )}"
method="get" >
@@ -43,13 +48,14 @@
%if i > 0:
,
%endif
- <span style="font-style:
italic">${filter}</span>
- <%
- new_filter = list( column_filter )
- del new_filter[ i ]
- new_column_filter = GridColumnFilter( "", {
column.key : h.to_json_string( new_filter ) } )
- %>
- <a href="${url( new_column_filter.get_url_args()
)}"><img
src="${h.url_for('/static/images/delete_tag_icon_gray.png')}"/></a>
+ <span class='text-filter-val'>${filter}
+ <%
+ new_filter = list( column_filter )
+ del new_filter[ i ]
+ new_column_filter = GridColumnFilter( "", {
column.key : h.to_json_string( new_filter ) } )
+ %>
+ <a href="${url( new_column_filter.get_url_args()
)}"><img
src="${h.url_for('/static/images/delete_tag_icon_gray.png')}"/></a>
+ </span>
%endfor
%endif
@@ -57,7 +63,8 @@
</span>
## Print input field for column.
<span>
- <input class="no-padding-or-margin"
id="input-${column.key}-filter" name="f-${column.key}"
type="text" value="" size="15"/>
+ <% value = iff( column.filterable == "standard",
column.label.lower(), "") %>
+ <input class="no-padding-or-margin"
id="input-${column.key}-filter" name="f-${column.key}"
type="text" value="${value}" size="15"/>
<input class='submit-image' type='image'
src='${h.url_for('/static/images/mag_glass.png')}'
alt='Filter'/>
</span>
</form>
@@ -131,7 +138,7 @@
if 'advanced-search' in kwargs and kwargs['advanced-search'] in
['True', 'true']:
advanced_search_display = "block"
- for column in grid.columns:
+ for column in grid.columns:
if column.filterable == "advanced":
## Show div if current filter has value that is different from the
default filter.
if column.key in cur_filter_dict and column.key in default_filter_dict
and \
diff -r e7244f7d613b -r 369f6b15f3f1 templates/history/list_public.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/history/list_public.mako Mon Dec 21 09:33:46 2009 -0500
@@ -0,0 +1,23 @@
+<%inherit file="/base_panels.mako"/>
+
+<%def name="init()">
+<%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.active_view="page"
+ self.message_box_visible=False
+%>
+</%def>
+
+<%def name="center_panel()">
+
+ ## <iframe name="galaxy_main" id="galaxy_main"
frameborder="0" style="position: absolute; width: 100%; height: 100%;"
src="${h.url_for( controller="page", action="list" )}">
</iframe>
+
+ <div style="overflow: auto; height: 100%;">
+ <div class="page-container" style="padding: 10px;">
+ ${grid}
+ </div>
+ </div>
+
+
+</%def>
diff -r e7244f7d613b -r 369f6b15f3f1 templates/history/sharing.mako
--- a/templates/history/sharing.mako Fri Dec 18 14:18:56 2009 -0500
+++ b/templates/history/sharing.mako Mon Dec 21 09:33:46 2009 -0500
@@ -1,5 +1,6 @@
<%inherit file="/base.mako"/>
<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/display_common.mako" import="get_history_link"
/>
##<h2>Import via link</h2>
@@ -34,7 +35,7 @@
%endif
%if history.importable:
<div class="form-row">
- <% url = h.url_for( controller='history',
action='view', id=trans.security.encode_id(history.id), qualified=True ) %>
+ <% url = get_history_link( history, True )%>
<a href="${url}"
target="_blank">${url}</a>
<div class="toolParamHelp" style="clear:
both;">
Send the above link to users as an easy way for them to view
the history.
diff -r e7244f7d613b -r 369f6b15f3f1 templates/history/view.mako
--- a/templates/history/view.mako Fri Dec 18 14:18:56 2009 -0500
+++ b/templates/history/view.mako Mon Dec 21 09:33:46 2009 -0500
@@ -1,8 +1,271 @@
<%inherit file="/base_panels.mako"/>
+<%namespace file="/display_common.mako" import="get_history_link"
/>
+<%namespace file="/root/history_common.mako"
import="render_dataset" />
+<%namespace file="../tagging_common.mako"
import="render_tagging_element, render_community_tagging_element" />
<%def name="javascripts()">
${parent.javascripts()}
${h.js( "galaxy.base", "jquery", "json2",
"jquery.jstore-all", "jquery.autocomplete",
"autocomplete_tagging" )}
+ <script type="text/javascript">
+ $(function() {
+ // Load jStore for local storage
+ $.extend(jQuery.jStore.defaults, { project: 'galaxy', flash:
'/static/jStore.Flash.html' })
+ $.jStore.load(); // Auto-select best storage
+
+ $.jStore.ready(function(engine) {
+ engine.ready(function() {
+ // Init stuff that requires the local storage to be running
+ initShowHide();
+ setupHistoryItem( $("div.historyItemWrapper") );
+ });
+ });
+
+ // Generate 'collapse all' link
+ $("#top-links").append( "| " ).append( $("<a
href='#'>${_('collapse all')}</a>").click( function() {
+ $( "div.historyItemBody:visible" ).each( function() {
+ if ( $.browser.mozilla ) {
+ $(this).find( "pre.peek" ).css( "overflow",
"hidden" );
+ }
+ $(this).slideUp( "fast" );
+ });
+ $.jStore.remove("history_expand_state");
+ }));
+
+ $("#history-rename").click( function() {
+ var old_name = $("#history-name").text()
+ var t = $("<input type='text' value='" + old_name +
"'></input>" );
+ t.blur( function() {
+ $(this).remove();
+ $("#history-name").show();
+ });
+ t.keyup( function( e ) {
+ if ( e.keyCode == 27 ) {
+ // Escape key
+ $(this).trigger( "blur" );
+ } else if ( e.keyCode == 13 ) {
+ // Enter key
+ new_value = this.value;
+ $(this).trigger( "blur" );
+ $.ajax({
+ url: "${h.url_for( controller='history',
action='rename_async', id=history.id )}",
+ data: { "_": true, new_name: new_value },
+ error: function() { alert( "Rename failed" ) },
+ success: function() {
+ $("#history-name").text( new_value );
+ }
+ });
+ }
+ });
+ $("#history-name").hide();
+ $("#history-name-area").append( t );
+ t.focus();
+ return false;
+ });
+ // Updater
+ updater({
+ <% updateable = [data for data in reversed( datasets ) if data.visible and
data.state not in [ "deleted", "empty", "error",
"ok" ]] %>
+ ${ ",".join( map(lambda data: "\"%s\" :
\"%s\"" % (data.id, data.state), updateable) ) }
+ });
+ });
+ // Functionized so AJAX'd datasets can call them
+ function initShowHide() {
+
+ // Load saved state and show as necessary
+ try {
+ var stored = $.jStore.store("history_expand_state");
+ if (stored) {
+ var st = JSON.parse(stored);
+ for (var id in st) {
+ $("#" + id + " div.historyItemBody" ).show();
+ }
+ }
+ } catch(err) {
+ // Something was wrong with values in storage, so clear storage
+ $.jStore.remove("history_expand_state");
+ }
+
+ // If Mozilla, hide scrollbars in hidden items since they cause animation bugs
+ if ( $.browser.mozilla ) {
+ $( "div.historyItemBody" ).each( function() {
+ if ( ! $(this).is( ":visible" ) ) $(this).find(
"pre.peek" ).css( "overflow", "hidden" );
+ })
+ }
+ }
+ // Add show/hide link and delete link to a history item
+ function setupHistoryItem( query ) {
+ query.each( function() {
+ var id = this.id;
+ var body = $(this).children( "div.historyItemBody" );
+ var peek = body.find( "pre.peek" )
+ $(this).children( ".historyItemTitleBar" ).find(
".historyItemTitle" ).wrap( "<a href='#'></a>"
).click( function() {
+ if ( body.is(":visible") ) {
+ // Hiding stuff here
+ if ( $.browser.mozilla ) { peek.css( "overflow",
"hidden" ) }
+ body.slideUp( "fast" );
+
+ // Save setting
+ var stored = $.jStore.store("history_expand_state")
+ var prefs = stored ? JSON.parse(stored) : null
+ if (prefs) {
+ delete prefs[id];
+ $.jStore.store("history_expand_state",
JSON.stringify(prefs));
+ }
+ } else {
+ // Showing stuff here
+ body.slideDown( "fast", function() {
+ if ( $.browser.mozilla ) { peek.css( "overflow",
"auto" ); }
+ });
+
+ // Save setting
+ var stored = $.jStore.store("history_expand_state")
+ var prefs = stored ? JSON.parse(stored) : new Object;
+ prefs[id] = true;
+ $.jStore.store("history_expand_state",
JSON.stringify(prefs));
+ }
+ return false;
+ });
+ // Delete link
+ $(this).find( "div.historyItemButtons > .delete" ).each(
function() {
+ var data_id = this.id.split( "-" )[1];
+ $(this).click( function() {
+ $( '#historyItem-' + data_id + ">
div.historyItemTitleBar" ).addClass( "spinner" );
+ $.ajax({
+ url: "${h.url_for( action='delete_async',
id='XXX' )}".replace( 'XXX', data_id ),
+ error: function() { alert( "Delete failed" ) },
+ success: function() {
+ %if show_deleted:
+ var to_update = {};
+ to_update[data_id] = "none";
+ updater( to_update );
+ %else:
+ $( "#historyItem-" + data_id ).fadeOut(
"fast", function() {
+ $( "#historyItemContainer-" + data_id
).remove();
+ if ( $( "div.historyItemContainer" ).length
< 1 ) {
+ $( "#emptyHistoryMessage" ).show();
+ }
+ });
+ %endif
+ }
+ });
+ return false;
+ });
+ });
+ // Undelete link
+ $(this).find( "a.historyItemUndelete" ).each( function() {
+ var data_id = this.id.split( "-" )[1];
+ $(this).click( function() {
+ $( '#historyItem-' + data_id + " >
div.historyItemTitleBar" ).addClass( "spinner" );
+ $.ajax({
+ url: "${h.url_for( controller='dataset',
action='undelete_async', id='XXX' )}".replace( 'XXX', data_id
),
+ error: function() { alert( "Undelete failed" ) },
+ success: function() {
+ var to_update = {};
+ to_update[data_id] = "none";
+ updater( to_update );
+ }
+ });
+ return false;
+ });
+ });
+ });
+ };
+ // Looks for changes in dataset state using an async request. Keeps
+ // calling itself (via setTimeout) until all datasets are in a terminal
+ // state.
+ var updater = function ( tracked_datasets ) {
+ // Check if there are any items left to track
+ var empty = true;
+ for ( i in tracked_datasets ) {
+ empty = false;
+ break;
+ }
+ if ( ! empty ) {
+ // console.log( "Updater running in 3 seconds" );
+ setTimeout( function() { updater_callback( tracked_datasets ) }, 3000 );
+ } else {
+ // console.log( "Updater finished" );
+ }
+ };
+ var updater_callback = function ( tracked_datasets ) {
+ // Build request data
+ var ids = []
+ var states = []
+ var force_history_refresh = false
+ $.each( tracked_datasets, function ( id, state ) {
+ ids.push( id );
+ states.push( state );
+ });
+ // Make ajax call
+ $.ajax( {
+ type: "POST",
+ url: "${h.url_for( controller='root',
action='history_item_updates' )}",
+ dataType: "json",
+ data: { ids: ids.join( "," ), states: states.join( "," )
},
+ success : function ( data ) {
+ $.each( data, function( id, val ) {
+ // Replace HTML
+ var container = $("#historyItemContainer-" + id);
+ container.html( val.html );
+ setupHistoryItem( container.children( ".historyItemWrapper"
) );
+ initShowHide();
+ // If new state was terminal, stop tracking
+ if (( val.state == "ok") || ( val.state ==
"error") || ( val.state == "empty") || ( val.state ==
"deleted" ) || ( val.state == "discarded" )) {
+ if ( val.force_history_refresh ){
+ force_history_refresh = true;
+ }
+ delete tracked_datasets[ parseInt(id) ];
+ } else {
+ tracked_datasets[ parseInt(id) ] = val.state;
+ }
+ });
+ if ( force_history_refresh ) {
+ parent.frames.galaxy_history.location.reload();
+ } else {
+ // Keep going (if there are still any items to track)
+ updater( tracked_datasets );
+ }
+ },
+ error: function() {
+ // Just retry, like the old method, should try to be smarter
+ updater( tracked_datasets );
+ }
+ });
+ };
+
+ //
+ // Function provides text for tagging toggle link.
+ //
+ var get_toggle_link_text = function(tags)
+ {
+ var text = "";
+ var num_tags = array_length(tags);
+ if (num_tags != 0)
+ {
+ text = num_tags + (num_tags != 1 ? " Tags" : " Tag");
+ }
+ else
+ {
+ // No tags.
+ text = "Add tags to history";
+ }
+ return text;
+ };
+
+ //
+ // Handle click on community tag.
+ //
+ function community_tag_click(tag_name, tag_value)
+ {
+ // Do nothing until community tags are implemented in public histories grid.
+ /*
+ var href = '${h.url_for( controller='/history',
action='list_public')}';
+ href = href + "?f-tags=" + tag_name;
+ if (tag_value != null && tag_value != "")
+ href = href + ":" + tag_value;
+ self.location = href;
+ */
+ }
+ </script>
</%def>
<%def name="stylesheets()">
@@ -13,12 +276,40 @@
padding-right: 3px;
border-right-style: solid;
border-right-color: #66AA66;
- };
+ }
+ .page-body
+ {
+ padding: 10px;
+ float: left;
+ width: 65%;
+ }
+ .page-meta
+ {
+ float: right;
+ width: 27%;
+ padding: 0.5em;
+ margin: 0.25em;
+ vertical-align: text-top;
+ border: 2px solid #DDDDDD;
+ border-top: 4px solid #DDDDDD;
+ }
</style>
+
+ <style>
+ .historyItemBody {
+ display: none;
+ }
+ </style>
+
+ <noscript>
+ <style>
+ .historyItemBody {
+ display: block;
+ }
+ </style>
+ </noscript>
</%def>
-<%namespace file="/root/history_common.mako"
import="render_dataset" />
-
<%def name="init()">
<%
self.has_left_panel=False
@@ -33,274 +324,28 @@
user_owns_history = False
%>
- <div style="overflow: auto; height: 100%;">
- <div style="padding: 10px;">
+ ## Get URL to other histories owned by user that owns this history.
+ <%
+ ##TODO: is there a better way to create this URL? Can't use
'f-username' as a key b/c it's not a valid identifier.
+ href_to_user_histories = h.url_for( controller='/history',
action='list_public', xxx=history.user.username)
+ href_to_user_histories = href_to_user_histories.replace( 'xxx',
'f-username')
+ %>
+
+ <div class="unified-panel-header" unselectable="on">
+ <div class="unified-panel-header-inner">
+ <a href="${h.url_for ( controller='/history',
action='list_public' )}">Public Histories</a> |
+ <a
href="${href_to_user_histories}">${history.user.username}</a> |
${history.name}
+ </div>
+ </div>
+
+ <div class="unified-panel-body">
+ <div class="page-body">
## Render view of history.
- <script type="text/javascript">
- $(function() {
- // Load jStore for local storage
- $.extend(jQuery.jStore.defaults, { project: 'galaxy', flash:
'/static/jStore.Flash.html' })
- $.jStore.load(); // Auto-select best storage
-
- $.jStore.ready(function(engine) {
- engine.ready(function() {
- // Init stuff that requires the local storage to be running
- initShowHide();
- setupHistoryItem( $("div.historyItemWrapper") );
- });
- });
-
- // Generate 'collapse all' link
- $("#top-links").append( "| " ).append(
$("<a href='#'>${_('collapse all')}</a>").click(
function() {
- $( "div.historyItemBody:visible" ).each( function() {
- if ( $.browser.mozilla ) {
- $(this).find( "pre.peek" ).css(
"overflow", "hidden" );
- }
- $(this).slideUp( "fast" );
- });
- $.jStore.remove("history_expand_state");
- }));
-
- $("#history-rename").click( function() {
- var old_name = $("#history-name").text()
- var t = $("<input type='text' value='" +
old_name + "'></input>" );
- t.blur( function() {
- $(this).remove();
- $("#history-name").show();
- });
- t.keyup( function( e ) {
- if ( e.keyCode == 27 ) {
- // Escape key
- $(this).trigger( "blur" );
- } else if ( e.keyCode == 13 ) {
- // Enter key
- new_value = this.value;
- $(this).trigger( "blur" );
- $.ajax({
- url: "${h.url_for( controller='history',
action='rename_async', id=history.id )}",
- data: { "_": true, new_name: new_value },
- error: function() { alert( "Rename failed" )
},
- success: function() {
- $("#history-name").text( new_value );
- }
- });
- }
- });
- $("#history-name").hide();
- $("#history-name-area").append( t );
- t.focus();
- return false;
- });
- // Updater
- updater({
- <% updateable = [data for data in reversed( datasets ) if
data.visible and data.state not in [ "deleted", "empty",
"error", "ok" ]] %>
- ${ ",".join( map(lambda data: "\"%s\" :
\"%s\"" % (data.id, data.state), updateable) ) }
- });
- });
- // Functionized so AJAX'd datasets can call them
- function initShowHide() {
-
- // Load saved state and show as necessary
- try {
- var stored = $.jStore.store("history_expand_state");
- if (stored) {
- var st = JSON.parse(stored);
- for (var id in st) {
- $("#" + id + " div.historyItemBody"
).show();
- }
- }
- } catch(err) {
- // Something was wrong with values in storage, so clear storage
- $.jStore.remove("history_expand_state");
- }
-
- // If Mozilla, hide scrollbars in hidden items since they cause animation
bugs
- if ( $.browser.mozilla ) {
- $( "div.historyItemBody" ).each( function() {
- if ( ! $(this).is( ":visible" ) ) $(this).find(
"pre.peek" ).css( "overflow", "hidden" );
- })
- }
- }
- // Add show/hide link and delete link to a history item
- function setupHistoryItem( query ) {
- query.each( function() {
- var id = this.id;
- var body = $(this).children( "div.historyItemBody" );
- var peek = body.find( "pre.peek" )
- $(this).children( ".historyItemTitleBar" ).find(
".historyItemTitle" ).wrap( "<a href='#'></a>"
).click( function() {
- if ( body.is(":visible") ) {
- // Hiding stuff here
- if ( $.browser.mozilla ) { peek.css( "overflow",
"hidden" ) }
- body.slideUp( "fast" );
-
- // Save setting
- var stored =
$.jStore.store("history_expand_state")
- var prefs = stored ? JSON.parse(stored) : null
- if (prefs) {
- delete prefs[id];
- $.jStore.store("history_expand_state",
JSON.stringify(prefs));
- }
- } else {
- // Showing stuff here
- body.slideDown( "fast", function() {
- if ( $.browser.mozilla ) { peek.css(
"overflow", "auto" ); }
- });
-
- // Save setting
- var stored =
$.jStore.store("history_expand_state")
- var prefs = stored ? JSON.parse(stored) : new Object;
- prefs[id] = true;
- $.jStore.store("history_expand_state",
JSON.stringify(prefs));
- }
- return false;
- });
- // Delete link
- $(this).find( "div.historyItemButtons > .delete" ).each(
function() {
- var data_id = this.id.split( "-" )[1];
- $(this).click( function() {
- $( '#historyItem-' + data_id + ">
div.historyItemTitleBar" ).addClass( "spinner" );
- $.ajax({
- url: "${h.url_for( action='delete_async',
id='XXX' )}".replace( 'XXX', data_id ),
- error: function() { alert( "Delete failed" )
},
- success: function() {
- %if show_deleted:
- var to_update = {};
- to_update[data_id] = "none";
- updater( to_update );
- %else:
- $( "#historyItem-" + data_id ).fadeOut(
"fast", function() {
- $( "#historyItemContainer-" + data_id
).remove();
- if ( $( "div.historyItemContainer"
).length < 1 ) {
- $( "#emptyHistoryMessage"
).show();
- }
- });
- %endif
- }
- });
- return false;
- });
- });
- // Undelete link
- $(this).find( "a.historyItemUndelete" ).each( function() {
- var data_id = this.id.split( "-" )[1];
- $(this).click( function() {
- $( '#historyItem-' + data_id + " >
div.historyItemTitleBar" ).addClass( "spinner" );
- $.ajax({
- url: "${h.url_for( controller='dataset',
action='undelete_async', id='XXX' )}".replace( 'XXX', data_id
),
- error: function() { alert( "Undelete failed" )
},
- success: function() {
- var to_update = {};
- to_update[data_id] = "none";
- updater( to_update );
- }
- });
- return false;
- });
- });
- });
- };
- // Looks for changes in dataset state using an async request. Keeps
- // calling itself (via setTimeout) until all datasets are in a terminal
- // state.
- var updater = function ( tracked_datasets ) {
- // Check if there are any items left to track
- var empty = true;
- for ( i in tracked_datasets ) {
- empty = false;
- break;
- }
- if ( ! empty ) {
- // console.log( "Updater running in 3 seconds" );
- setTimeout( function() { updater_callback( tracked_datasets ) }, 3000
);
- } else {
- // console.log( "Updater finished" );
- }
- };
- var updater_callback = function ( tracked_datasets ) {
- // Build request data
- var ids = []
- var states = []
- var force_history_refresh = false
- $.each( tracked_datasets, function ( id, state ) {
- ids.push( id );
- states.push( state );
- });
- // Make ajax call
- $.ajax( {
- type: "POST",
- url: "${h.url_for( controller='root',
action='history_item_updates' )}",
- dataType: "json",
- data: { ids: ids.join( "," ), states: states.join(
"," ) },
- success : function ( data ) {
- $.each( data, function( id, val ) {
- // Replace HTML
- var container = $("#historyItemContainer-" + id);
- container.html( val.html );
- setupHistoryItem( container.children(
".historyItemWrapper" ) );
- initShowHide();
- // If new state was terminal, stop tracking
- if (( val.state == "ok") || ( val.state ==
"error") || ( val.state == "empty") || ( val.state ==
"deleted" ) || ( val.state == "discarded" )) {
- if ( val.force_history_refresh ){
- force_history_refresh = true;
- }
- delete tracked_datasets[ parseInt(id) ];
- } else {
- tracked_datasets[ parseInt(id) ] = val.state;
- }
- });
- if ( force_history_refresh ) {
- parent.frames.galaxy_history.location.reload();
- } else {
- // Keep going (if there are still any items to track)
- updater( tracked_datasets );
- }
- },
- error: function() {
- // Just retry, like the old method, should try to be smarter
- updater( tracked_datasets );
- }
- });
- };
-
- //
- // Function provides text for tagging toggle link.
- //
- var get_toggle_link_text = function(tags)
- {
- var text = "";
- var num_tags = array_length(tags);
- if (num_tags != 0)
- {
- text = num_tags + (num_tags != 1 ? " Tags" : "
Tag");
- }
- else
- {
- // No tags.
- text = "Add tags to history";
- }
- return text;
- };
- </script>
-
- <style>
- .historyItemBody {
- display: none;
- }
- </style>
-
- <noscript>
- <style>
- .historyItemBody {
- display: block;
- }
- </style>
- </noscript>
-
<div id="top-links" class="historyLinks"
style="padding: 0px 0px 5px 0px">
%if not user_owns_history:
<a href="${h.url_for( controller='history',
action='imp', id=trans.security.encode_id(history.id) )}">import and start
using history</a> |
%endif
- <a href="${h.url_for( controller='history',
action='view', id=trans.security.encode_id(history.id)
)}">${_('refresh')}</a>
+ <a href="${get_history_link( history
)}">${_('refresh')}</a>
%if show_deleted:
| <a href="${h.url_for('history',
show_deleted=False)}">${_('hide deleted')}</a>
%endif
@@ -320,13 +365,6 @@
<p></p>
%endif
- <%namespace file="../tagging_common.mako"
import="render_tagging_element" />
-
- %if trans.get_user() is not None:
- <div id='history-tag-area'
class="tag-element"></div>
- ${render_tagging_element(tagged_item=history,
elt_context="history/view.mako", use_toggle_link=False,
get_toggle_link_text_fn='get_toggle_link_text', editable=user_owns_history)}
- %endif
-
%if not datasets:
<div class="infomessagesmall"
id="emptyHistoryMessage">
@@ -334,7 +372,7 @@
%else:
## Render requested datasets, ordered from newest to oldest
- %for data in reversed( datasets ):
+ %for data in datasets:
%if data.visible:
<div class="historyItemContainer
visible-right-border" id="historyItemContainer-${data.id}">
${render_dataset( data, data.hid, show_deleted_on_refresh =
show_deleted, user_owns_dataset=user_owns_history )}
@@ -347,7 +385,32 @@
${_("Your history is empty. Click 'Get Data' on the left
pane to start")}
</div>
</div>
+
+ <div class="page-meta">
+ ## Histories.
+ <div><strong>Related Histories</strong></div>
+ <p>
+ <a href="${h.url_for ( controller='/history',
action='list_public' )}">All public histories</a><br>
+ <a href="${href_to_user_histories}">Histories owned by
${history.user.username}</a>
+
+ ## Tags.
+ <div><strong>Tags</strong></div>
+ <p>
+ ## Community tags.
+ <div>
+ Community:
+ ${render_community_tagging_element( tagged_item=history,
tag_click_fn='community_tag_click', use_toggle_link=False )}
+ %if len ( history.tags ) == 0:
+ none
+ %endif
+ </div>
+ ## User tags.
+ <p>
+ <div>
+ ##Yours:
+ ##${render_tagging_element( tagged_item=history,
elt_context='view.mako', use_toggle_link=False )}
+ </div>
+ </div>
+
</div>
-
-
</%def>
diff -r e7244f7d613b -r 369f6b15f3f1 templates/page/display.mako
--- a/templates/page/display.mako Fri Dec 18 14:18:56 2009 -0500
+++ b/templates/page/display.mako Mon Dec 21 09:33:46 2009 -0500
@@ -150,7 +150,14 @@
//
function community_tag_click(tag_name, tag_value)
{
- alert("community tag click: " + tag_name);
+ // Do nothing until community tags implemented in published pages grid.
+ /*
+ var href = '${h.url_for( controller='/page',
action='list_published')}';
+ href = href + "?f-tags=" + tag_name;
+ if (tag_value != null && tag_value != "")
+ href = href + ":" + tag_value;
+ self.location = href;
+ */
}
</script>
</%def>
@@ -173,6 +180,7 @@
margin: 0.25em;
vertical-align: text-top;
border: 2px solid #DDDDDD;
+ border-top: 4px solid #DDDDDD;
}
</style>
</%def>
@@ -190,9 +198,17 @@
<%def name="center_panel()">
+ ## Get URL to other pages owned by user that owns this page.
+ <%
+ ##TODO: is there a better way to create this URL? Can't use
'f-username' as a key b/c it's not a valid identifier.
+ href_to_user_pages = h.url_for( controller='/page',
action='list_published', xxx=page.user.username)
+ href_to_user_pages = href_to_user_pages.replace( 'xxx',
'f-username')
+ %>
+
<div class="unified-panel-header" unselectable="on">
<div class="unified-panel-header-inner">
- ${page.user.username} :: ${page.title}
+ <a href="${h.url_for ( controller='/page',
action='list_published' )}">Published Pages</a> |
+ <a
href="${href_to_user_pages}">${page.user.username}</a> | ${page.title}
</div>
</div>
@@ -202,6 +218,13 @@
${page.latest_revision.content.decode( "utf-8" )}
</div>
<div class="page-meta">
+ ## Pages.
+ <div><strong>Related Pages</strong></div>
+ <p>
+ <a href="${h.url_for ( controller='/page',
action='list_published' )}">All published pages</a><br>
+ <a href="${href_to_user_pages}">Pages published by
${page.user.username}</a>
+
+ ## Tags.
<div><strong>Tags</strong></div>
<p>
## Community tags.
@@ -215,8 +238,8 @@
## User tags.
<p>
<div>
- Yours:
- ${render_tagging_element( tagged_item=page,
elt_context='display.mako', use_toggle_link=False )}
+ ##Yours:
+ ##${render_tagging_element( tagged_item=page,
elt_context='display.mako', use_toggle_link=False )}
</div>
</div>
</div>
diff -r e7244f7d613b -r 369f6b15f3f1 templates/page/editor.mako
--- a/templates/page/editor.mako Fri Dec 18 14:18:56 2009 -0500
+++ b/templates/page/editor.mako Mon Dec 21 09:33:46 2009 -0500
@@ -248,8 +248,21 @@
// User selected no text; create link from
scratch and use default text.
// Get history name.
- $.get( '${h.url_for(
controller='history', action='get_name_async' )}?id=' + item_id,
function( history_name ) {
- var href = '${h.url_for(
controller='history', action='view' )}?id=' + item_id;
+ $.get( '${h.url_for(
controller='history', action='get_name_slug_username_async' )}?id=' +
item_id,
+ function( history_info ) {
+ // Parse history info.
+ history_info =
history_info.split(",");
+ var
+ history_name = history_info[0],
+ history_slug = history_info[1],
+ history_user_username = history_info[2];
+
+ // Build href from history info.
+ var href;
+ if (history_slug != "" &&
history_user_username != "")
+ var href = "/u/" +
history_user_username + "/h/" + history_slug;
+ else
+ var href = '${h.url_for(
controller='/history', action='view' )}?id=' + item_id;
wym.insert("<a href='" +
href + "'>History '" + history_name + "'</a>");
});
}