diff --git a/lib/galaxy/web/buildapp.py b/lib/galaxy/web/buildapp.py index a9ab866cdc2..797a4aca263 100644 --- a/lib/galaxy/web/buildapp.py +++ b/lib/galaxy/web/buildapp.py @@ -57,6 +57,8 @@ def app_factory( global_conf, **kwargs ): # Create the universe WSGI application webapp = galaxy.web.framework.WebApplication( app, session_cookie='galaxysession' ) add_controllers( webapp, app ) + # Force /history to go to /root/history -- needed since the tests assume this + webapp.add_route( '/history', controller='root', action='history' ) # These two routes handle our simple needs at the moment webapp.add_route( '/async/:tool_id/:data_id/:data_secret', controller='async', action='index', tool_id=None, data_id=None, data_secret=None ) webapp.add_route( '/:controller/:action', action='index' ) diff --git a/lib/galaxy/web/controllers/root.py b/lib/galaxy/web/controllers/root.py index e6abee11d59..faa2dd2540b 100644 --- a/lib/galaxy/web/controllers/root.py +++ b/lib/galaxy/web/controllers/root.py @@ -371,128 +371,13 @@ class RootController( BaseController ): """Displays a list of history related actions""" return trans.fill_template( "/history/options.mako", user = trans.get_user(), history = trans.get_history() ) - + @web.expose - def history_delete( self, trans, id=None, **kwd): - """Deletes a list of histories, ensures that histories are owned by current user""" - if trans.app.memory_usage: - # Keep track of memory usage - m0 = self.app.memory_usage.memory() - history_names = [] - if id: - if isinstance( id, list ): - history_ids = id - else: - history_ids = [ id ] - user = trans.get_user() - for hid in history_ids: - try: - int( hid ) - except: - return trans.show_message( "Invalid history: %s" % str( hid ) ) - history = self.app.model.History.get( hid ) - if history: - if history.user_id != None and user: - assert user.id == history.user_id, "History does not belong to current user" - # Delete DefaultHistoryPermissions - for dhp in history.default_permissions: - dhp.delete() - dhp.flush() - # Mark history as deleted in db - history.deleted = True - history_names.append(history.name) - # If deleting the current history, make a new current. - if history == trans.get_history(): - trans.new_history() - else: - return trans.show_message( "Not able to find history %s" % str( hid ) ) - self.app.model.flush() - trans.log_event( "History id %s marked as deleted" % str(hid) ) - else: - return trans.show_message( "You must select at least one history to delete." ) - if self.app.memory_usage: - m1 = trans.app.memory_usage.memory( m0, pretty=True ) - log.info( "End of root/history_delete, memory used increased by %s" % m1 ) - return trans.show_message( "History deleted: %s" % ",".join(history_names), - refresh_frames=['history']) - - @web.expose - def history_undelete( self, trans, id=[], **kwd): - """Undeletes a list of histories, ensures that histories are owned by current user""" - history_names = [] - errors = [] - ok_msg = "" - if id: - if not isinstance( id, list ): - id = id.split( "," ) - user = trans.get_user() - for hid in id: - try: - int( hid ) - except: - errors.append( "Invalid history: %s" % str( hid ) ) - continue - history = self.app.model.History.get( hid ) - if history: - if history.user != user: - errors.append( "History does not belong to current user." ) - continue - if history.purged: - errors.append( "History has already been purged and can not be undeleted." ) - continue - history_names.append( history.name ) - history.deleted = False - else: - errors.append( "Not able to find history %s." % str( hid ) ) - trans.log_event( "History id %s marked as undeleted" % str(hid) ) - self.app.model.flush() - if history_names: - ok_msg = "Histories (%s) have been undeleted." % ", ".join( history_names ) - else: - errors.append( "You must select at least one history to undelete." ) - return self.history_available( trans, id=','.join( id ), show_deleted=True, ok_msg = ok_msg, error_msg = " ".join( errors ) ) - - @web.expose - def history_undelete( self, trans, id=[], **kwd): - """Undeletes a list of histories, ensures that histories are owned by current user""" - if trans.app.memory_usage: - # Keep track of memory usage - m0 = self.app.memory_usage.memory() - history_names = [] - errors = [] - ok_msg = "" - if id: - if not isinstance( id, list ): - id = id.split( "," ) - user = trans.get_user() - for hid in id: - try: - int( hid ) - except: - errors.append( "Invalid history: %s" % str( hid ) ) - continue - history = self.app.model.History.get( hid ) - if history: - if history.user != user: - errors.append( "History does not belong to current user." ) - continue - if history.purged: - errors.append( "History has already been purged and can not be undeleted." ) - continue - history_names.append( history.name ) - history.deleted = False - else: - errors.append( "Not able to find history %s." % str( hid ) ) - trans.log_event( "History id %s marked as undeleted" % str(hid) ) - self.app.model.flush() - if history_names: - ok_msg = "Histories (%s) have been undeleted." % ", ".join( history_names ) - else: - errors.append( "You must select at least one history to undelete." ) - if self.app.memory_usage: - m1 = trans.app.memory_usage.memory( m0, pretty=True ) - log.info( "End of root/history_undelete, memory used increased by %s" % m1 ) - return self.history_available( trans, id=','.join( id ), show_deleted=True, ok_msg = ok_msg, error_msg = " ".join( errors ) ) + def history_delete( self, trans, id ): + """ + Backward compatibility with check_galaxy script. + """ + return trans.webapp.controllers['history'].list( trans, id, operation='delete' ) @web.expose def clear_history( self, trans ): @@ -510,117 +395,6 @@ class RootController( BaseController ): m1 = trans.app.memory_usage.memory( m0, pretty=True ) log.info( "End of root/clear_history, memory used increased by %s" % m1 ) trans.response.send_redirect( url_for("/index" ) ) - - @web.expose - @web.require_login( "share histories with other users" ) - def history_share( self, trans, id=None, email="", **kwd ): - send_to_err = "" - if not id: - id = trans.get_history().id - if not isinstance( id, list ): - id = [ id ] - histories = [] - history_names = [] - for hid in id: - histories.append( trans.app.model.History.get( hid ) ) - history_names.append(histories[-1].name) - if not email: - return trans.fill_template("/history/share.mako", histories=histories, email=email, send_to_err=send_to_err) - user = trans.get_user() - send_to_user = trans.app.model.User.filter( trans.app.model.User.table.c.email==email ).first() - params = util.Params( kwd ) - action = params.get( 'action', None ) - if action == "no_share": - trans.response.send_redirect( url_for( action='history_options' ) ) - if not send_to_user: - send_to_err = "No such user" - elif user.email == email: - send_to_err = "You can't send histories to yourself" - else: - if 'history_share_btn' in kwd or action != 'share': - # The user is attempting to share a history whose datasets cannot all be accessed by the other user. In this case, - # the user sharing the history can chose to make the datasets public ( action == 'public' ) if he has the authority - # to do so, or automatically create a new "sharing role" that allows the user to share his private datasets only with the - # desired user ( action == 'private' ). - can_change = {} - cannot_change = {} - for history in histories: - for hda in history.activatable_datasets: - # Only deal with datasets that have not been purged - if not trans.app.security_agent.allow_action( send_to_user, - trans.app.security_agent.permitted_actions.DATASET_ACCESS, - dataset=hda ): - # The user with which we are sharing the history does not have access permission on the current dataset - if trans.app.security_agent.allow_action( user, - trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS, - dataset=hda ) and not hda.dataset.library_associations: - # The current user has authority to change permissions on the current dataset because - # they have permission to manage permissions on the dataset and the dataset is not associated - # with a library. - if action == "private": - trans.app.security_agent.privately_share_dataset( hda.dataset, users=[ user, send_to_user ] ) - elif action == "public": - trans.app.security_agent.make_dataset_public( hda.dataset ) - elif history not in can_change: - # Build the set of histories / datasets on which the current user has authority - # to "manage permissions". This is used in /history/share.mako - can_change[ history ] = [ hda ] - else: - can_change[ history ].append( hda ) - else: - if action in [ "private", "public" ]: - # Don't change stuff that the user doesn't have permission to change - continue - elif history not in cannot_change: - # Build the set of histories / datasets on which the current user does - # not have authority to "manage permissions". This is used in /history/share.mako - cannot_change[ history ] = [ hda ] - else: - cannot_change[ history ].append( hda ) - if can_change or cannot_change: - return trans.fill_template( "/history/share.mako", - histories=histories, - email=email, - send_to_err=send_to_err, - can_change=can_change, - cannot_change=cannot_change ) - for history in histories: - new_history = history.copy( target_user=send_to_user ) - new_history.name = history.name + " from " + user.email - new_history.user_id = send_to_user.id - trans.log_event( "History share, id: %s, name: '%s': to new id: %s" % ( str( history.id ), history.name, str( new_history.id ) ) ) - self.app.model.flush() - return trans.show_message( "History (%s) has been shared with: %s" % ( ",".join( history_names ),email ) ) - return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err ) - - @web.expose - @web.require_login( "work with multiple histories" ) - def history_available( self, trans, id=[], do_operation = "view", show_deleted = False, ok_msg = "", error_msg="", as_xml=False, **kwd ): - """ - List all available histories - """ - if trans.app.memory_usage: - # Keep track of memory usage - m0 = self.app.memory_usage.memory() - if as_xml: - trans.response.set_content_type('text/xml') - return trans.fill_template( "/history/list_as_xml.mako" ) - if not isinstance( id, list ): - id = id.split( "," ) - trans.log_event( "History id %s available" % str( id ) ) - history_operations = dict( share=self.history_share, rename=self.history_rename, delete=self.history_delete, undelete=self.history_undelete ) - if self.app.memory_usage: - m1 = trans.app.memory_usage.memory( m0, pretty=True ) - log.info( "End of root/history_available, memory used increased by %s" % m1 ) - if do_operation in history_operations: - return history_operations[do_operation]( trans, id=id, show_deleted=show_deleted, ok_msg=ok_msg, error_msg=error_msg, **kwd ) - return trans.fill_template( "/history/list.mako", ids=id, - user=trans.get_user(), - current_history=trans.get_history(), - show_deleted=util.string_as_bool( show_deleted ), - ok_msg=ok_msg, error_msg=error_msg ) - - @web.expose def history_import( self, trans, id=None, confirm=False, **kwd ): @@ -677,34 +451,6 @@ class RootController( BaseController ): Warning! If you import this history, you will lose your current history. Click here to confirm. """ % web.url_for( id=id, confirm=True ) ) - - @web.expose - @web.require_login( "switch histories" ) - def history_switch( self, trans, id=None ): - if not id: - return trans.response.send_redirect( web.url_for( action='history_available' ) ) - else: - if trans.app.memory_usage: - # Keep track of memory usage - m0 = self.app.memory_usage.memory() - new_history = trans.app.model.History.get( id ) - if new_history: - galaxy_session = trans.get_galaxy_session() - try: - association = trans.app.model.GalaxySessionToHistoryAssociation.filter_by( session_id=galaxy_session.id, history_id=new_history.id ).first() - except: - association = None - new_history.add_galaxy_session( galaxy_session, association=association ) - new_history.flush() - trans.set_history( new_history ) - trans.log_event( "History switched to id: %s, name: '%s'" % (str(new_history.id), new_history.name ) ) - if self.app.memory_usage: - m1 = trans.app.memory_usage.memory( m0, pretty=True ) - log.info( "End of root/history_switch, memory used increased by %s" % m1 ) - return trans.show_message( "History switched to: %s" % new_history.name, - refresh_frames=['history']) - else: - return trans.show_error_message( "History not found" ) @web.expose def history_new( self, trans ): @@ -718,53 +464,6 @@ class RootController( BaseController ): log.info( "End of root/history_new, memory used increased by %s" % m1 ) return trans.show_message( "New history created", refresh_frames = ['history'] ) - @web.expose - @web.require_login( "renames histories" ) - def history_rename( self, trans, id=None, name=None, **kwd ): - if trans.app.memory_usage: - # Keep track of memory usage - m0 = self.app.memory_usage.memory() - user = trans.get_user() - - if not isinstance( id, list ): - if id != None: - id = [ id ] - if not isinstance( name, list ): - if name != None: - name = [ name ] - histories = [] - cur_names = [] - if not id: - if not trans.get_history().user: - return trans.show_error_message( "You must save your history before renaming it." ) - id = [trans.get_history().id] - for history_id in id: - history = trans.app.model.History.get( history_id ) - if history and history.user_id == user.id: - histories.append(history) - cur_names.append(history.name) - if not name or len(histories)!=len(name): - return trans.fill_template( "/history/rename.mako",histories=histories ) - change_msg = "" - for i in range(len(histories)): - if histories[i].user_id == user.id: - if name[i] == histories[i].name: - change_msg = change_msg + "
History: "+cur_names[i]+" is already named: "+name[i]+"
" - elif name[i] not in [None,'',' ']: - name[i] = escape(name[i]) - histories[i].name = name[i] - histories[i].flush() - change_msg = change_msg + "History: "+cur_names[i]+" renamed to: "+name[i]+"
" - trans.log_event( "History renamed: id: %s, renamed to: '%s'" % (str(histories[i].id), name[i] ) ) - else: - change_msg = change_msg + "You must specify a valid name for History: "+cur_names[i]+"
" - else: - change_msg = change_msg + "History: "+cur_names[i]+" does not appear to belong to you.
" - if self.app.memory_usage: - m1 = trans.app.memory_usage.memory( m0, pretty=True ) - log.info( "End of root/history_rename, memory used increased by %s" % m1 ) - return trans.show_message( "%s" % change_msg, refresh_frames=['history'] ) - @web.expose def history_add_to( self, trans, history_id=None, file_data=None, name="Data Added to History",info=None,ext="txt",dbkey="?",copy_access_from=None,**kwd ): """Adds a POSTed file to a History""" diff --git a/static/june_2007_style/base.css.tmpl b/static/june_2007_style/base.css.tmpl index b780a8acda6..fc06c2008ff 100644 --- a/static/june_2007_style/base.css.tmpl +++ b/static/june_2007_style/base.css.tmpl @@ -142,6 +142,7 @@ div.form-row label select, input, textarea { + font: inherit; font-size: 115%; } @@ -444,6 +445,30 @@ ul.manage-table-actions li { margin-left: 0.5em; } +## State colors + +.state-color-queued { + border-color: $history_queued_border; + background: $history_queued_bg; +} + +.state-color-ok { + border-color: $history_ok_border; + background: $history_ok_bg; +} + +.state-color-error { + border-color: $history_error_border; + background: $history_error_bg; +} + +.state-color-running { + border-color: $history_running_border; + background: $history_running_bg; +} + +## Button styles + .action-button { background: #eeeeee; color: #333; @@ -459,6 +484,9 @@ ul.manage-table-actions li { -moz-border-radius: 0.5em; -webkit-border-radius: 0.5em; border-radius: 0.5em; + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; } .action-button > * { @@ -467,13 +495,15 @@ ul.manage-table-actions li { .action-button:hover { color: black; - background: #aaaaaa; + background: #dddddd; } .action-button:active { color: white; background: #aaaaaa; } +## Popup menu styles + div.popupmenu { display: none; background: #eeeeee; @@ -488,6 +518,9 @@ div.popupmenu { -moz-border-radius: 0.5em; -webkit-border-radius: 0.5em; border-radius: 0.5em; + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; } div.popupmenu-item { @@ -515,3 +548,35 @@ div.popupmenu-item:hover { div.permissionContainer { padding-left: 20px; } + +## Data grid style + +.grid { + padding-top: 1em; + border-collapse: collapse; + width: 100%; +} +.grid tbody td { + border-top: solid #DDDDDD 1px; + border-bottom: solid #DDDDDD 1px; + padding: 0.5em 1em; +} +.grid thead th { + background: $table_header_bg; + background-image: url(form_title_bg.png); + background-repeat: repeat-x; + background-position: top; + border-top: solid $table_border 1px; + border-bottom: solid $table_border 1px; + padding: 0.5em 1em; + text-align: left; +} +.grid tfoot td { + background-color: #F8F8F8; + border-top: solid #DDDDDD 1px; + border-bottom: solid #DDDDDD 1px; + padding: 0.5em 1em; +} +.grid .current { + background-color: #EEEEFF; +} \ No newline at end of file diff --git a/static/june_2007_style/blue/base.css b/static/june_2007_style/blue/base.css index 173f0d6dfa4..2962cfb40b2 100644 --- a/static/june_2007_style/blue/base.css +++ b/static/june_2007_style/blue/base.css @@ -142,6 +142,7 @@ div.form-row label select, input, textarea { + font: inherit; font-size: 115%; } @@ -152,7 +153,7 @@ select, textarea, input[type="text"], input[type="file"] /* Messages */ -.errormessage, .warningmessage, .donemessage, .infomessage, .welcomeBlue, .welcomeRed, .screencastBox, .yellowbox, .redbox, .bluebox, .greenbox +.errormessage, .warningmessage, .donemessage, .infomessage, .welcomeBlue, .welcomeRed , .screencastBox, .yellowbox, .redbox, .bluebox, .greenbox { padding: 10px; padding-left: 52px; @@ -443,6 +444,28 @@ ul.manage-table-actions li { margin-left: 0.5em; } + +.state-color-queued { + border-color: #888888; + background: #EEEEEE; +} + +.state-color-ok { + border-color: #66AA66; + background: #CCFFCC; +} + +.state-color-error { + border-color: #AA6666; + background: #FFCCCC; +} + +.state-color-running { + border-color: #AAAA66; + background: #FFFFCC; +} + + .action-button { background: #eeeeee; color: #333; @@ -458,6 +481,9 @@ ul.manage-table-actions li { -moz-border-radius: 0.5em; -webkit-border-radius: 0.5em; border-radius: 0.5em; + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; } .action-button > * { @@ -466,13 +492,14 @@ ul.manage-table-actions li { .action-button:hover { color: black; - background: #aaaaaa; + background: #dddddd; } .action-button:active { color: white; background: #aaaaaa; } + div.popupmenu { display: none; background: #eeeeee; @@ -487,6 +514,9 @@ div.popupmenu { -moz-border-radius: 0.5em; -webkit-border-radius: 0.5em; border-radius: 0.5em; + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; } div.popupmenu-item { @@ -514,3 +544,34 @@ div.popupmenu-item:hover { div.permissionContainer { padding-left: 20px; } + + +.grid { + padding-top: 1em; + border-collapse: collapse; + width: 100%; +} +.grid tbody td { + border-top: solid #DDDDDD 1px; + border-bottom: solid #DDDDDD 1px; + padding: 0.5em 1em; +} +.grid thead th { + background: #ebd9b2; + background-image: url(form_title_bg.png); + background-repeat: repeat-x; + background-position: top; + border-top: solid #d8b365 1px; + border-bottom: solid #d8b365 1px; + padding: 0.5em 1em; + text-align: left; +} +.grid tfoot td { + background-color: #F8F8F8; + border-top: solid #DDDDDD 1px; + border-bottom: solid #DDDDDD 1px; + padding: 0.5em 1em; +} +.grid .current { + background-color: #EEEEFF; +} \ No newline at end of file diff --git a/static/june_2007_style/blue/panel_layout.css b/static/june_2007_style/blue/panel_layout.css index 7dda79706a8..e48f52009af 100644 --- a/static/june_2007_style/blue/panel_layout.css +++ b/static/june_2007_style/blue/panel_layout.css @@ -131,31 +131,6 @@ body right: 0px; z-index: 200; } -td.panel-header -{ - background: #f5f5f5; - background-image: url(panel_header_bg.png); - background-position: top center; - background-repeat: repeat-x; - padding: 0; - border-bottom: solid #999 1px; -} -div.panel-header -{ - margin: 0; - padding: 4px; - padding-right: 10px; - padding-left: 10px; - color: #333; - font-weight: bold; -} - -table.column-layout -{ - position: absolute; - width: 100%; - height: 100%; -} .unified-panel-header { height: 2em; @@ -171,6 +146,9 @@ table.column-layout padding-left: 10px; color: #333; font-weight: bold; + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; } .unified-panel-header-inner { @@ -214,43 +192,6 @@ div.unified-panel-body { background: #aaaaaa; } -div.popupmenu { - display: none; - background: #eee; - position: fixed; - z-index: 20000; - background: #cccccc; - background-image: url(panel_header_bg.png); - background-position: top center; - background-repeat: repeat-x; - border: solid #999 1px; - padding-top: 3px; - padding-bottom: 3px; -} - -div.popupmenu-top { - padding-bottom: 3px; -} -div.popupmenu-top-inner { - height: 10px; - width: 20px; - margin-top: -13px; - margin-left: auto; - margin-right: 20px; - background: url(popupmenu_callout_top.png); -} - -div.popupmenu-item { - padding: 3px; - padding-left: 10px; - padding-right: 10px; - cursor: pointer; -} - -div.popupmenu-item:hover { - background: #AAAAEE; -} - #overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; diff --git a/static/june_2007_style/make_style.py b/static/june_2007_style/make_style.py index f3a6a08503a..5d62c7f1f56 100755 --- a/static/june_2007_style/make_style.py +++ b/static/june_2007_style/make_style.py @@ -1,11 +1,11 @@ #!/usr/bin/env python -from galaxy import eggs -import pkg_resources -pkg_resources.require("Cheetah") +#from galaxy import eggs +#import pkg_resources +#pkg_resources.require("Cheetah") import sys, string, os.path -from galaxy import eggs +#from galaxy import eggs import pkg_resources pkg_resources.require( "Cheetah" ) diff --git a/static/june_2007_style/panel_layout.css.tmpl b/static/june_2007_style/panel_layout.css.tmpl index 4df44dba18a..016b228a0e0 100644 --- a/static/june_2007_style/panel_layout.css.tmpl +++ b/static/june_2007_style/panel_layout.css.tmpl @@ -131,31 +131,6 @@ body right: 0px; z-index: 200; } -td.panel-header -{ - background: ${panel_header_bg_top}; - background-image: url(panel_header_bg.png); - background-position: top center; - background-repeat: repeat-x; - padding: 0; - border-bottom: solid ${layout_border} 1px; -} -div.panel-header -{ - margin: 0; - padding: 4px; - padding-right: 10px; - padding-left: 10px; - color: #333; - font-weight: bold; -} - -table.column-layout -{ - position: absolute; - width: 100%; - height: 100%; -} .unified-panel-header { height: 2em; @@ -171,6 +146,9 @@ table.column-layout padding-left: 10px; color: #333; font-weight: bold; + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; } .unified-panel-header-inner { @@ -214,43 +192,6 @@ div.unified-panel-body { background: #aaaaaa; } -div.popupmenu { - display: none; - background: #eee; - position: fixed; - z-index: 20000; - background: ${panel_header_bg_bottom}; - background-image: url(panel_header_bg.png); - background-position: top center; - background-repeat: repeat-x; - border: solid ${layout_border} 1px; - padding-top: 3px; - padding-bottom: 3px; -} - -div.popupmenu-top { - padding-bottom: 3px; -} -div.popupmenu-top-inner { - height: 10px; - width: 20px; - margin-top: -13px; - margin-left: auto; - margin-right: 20px; - background: url(popupmenu_callout_top.png); -} - -div.popupmenu-item { - padding: 3px; - padding-left: 10px; - padding-right: 10px; - cursor: pointer; -} - -div.popupmenu-item:hover { - background: ${layout_hover}; -} - #overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; diff --git a/static/scripts/galaxy.base.js b/static/scripts/galaxy.base.js index a14244dcac8..f343c31269a 100644 --- a/static/scripts/galaxy.base.js +++ b/static/scripts/galaxy.base.js @@ -1,3 +1,19 @@ +$.fn.makeAbsolute = function(rebase) { + return this.each(function() { + var el = $(this); + var pos = el.position(); + el.css({ + position: "absolute", + marginLeft: 0, marginTop: 0, + top: pos.top, left: pos.left, + right: $(window).width() - ( pos.left + el.width() ) + }); + if (rebase) { + el.remove().appendTo("body"); + } + }); +} + jQuery(document).ready( function() { // Links with confirmation jQuery( "a[@confirm]" ).click( function() { diff --git a/templates/history/list.mako b/templates/history/list.mako index a30e5c5f7c2..a5de3273c68 100644 --- a/templates/history/list.mako +++ b/templates/history/list.mako @@ -1,65 +1,201 @@ <%inherit file="/base.mako"/> <%def name="title()">Your saved histories%def> -%if error_msg: -
-
- - -%endif -%if ok_msg: --
- - +%if message: ++
+ + %endif -%if user.histories: -