Enables viewing of an importable/sharable history. Using the syntax /history/view?id=<encoded_history_id> produces a page that renders a history for viewing. A user need not be logged in to view the history.

This commit is contained in:
Jeremy Goecks
2009-10-08 13:38:32 -04:00
parent b37b2e973b
commit 4c401cddc1
11 changed files with 521 additions and 115 deletions
+34
View File
@@ -371,6 +371,7 @@ class HistoryController( BaseController ):
# Rename
history.name = new_name
trans.sa_session.flush()
@web.expose
def imp( self, trans, id=None, confirm=False, **kwd ):
"""Import another user's history via a shared URL"""
@@ -423,6 +424,38 @@ class HistoryController( BaseController ):
Warning! If you import this history, you will lose your current
history. Click <a href="%s">here</a> to confirm.
""" % web.url_for( id=id, confirm=True ) )
@web.expose
def view( self, trans, id=None ):
"""View a history. If a history is importable, then it is viewable by any user."""
# Get history to view.
if not id:
return trans.show_error_message( "You must specify a history you want to view." )
history_to_view = get_history( trans, id, False)
# Integrity checks.
if not history_to_view:
return trans.show_error_message( "The specified history does not exist.")
# TODO: Use a new flag to determine if history is viewable?
if not history_to_view.importable:
error( "The owner of this history has not published this history." )
# View history.
query = trans.sa_session.query( model.HistoryDatasetAssociation ) \
.filter( model.HistoryDatasetAssociation.history == history_to_view ) \
.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_to_view.user )
return trans.stream_template_mako( "history/view.mako",
history = history_to_view,
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 ):
@@ -699,6 +732,7 @@ class HistoryController( BaseController ):
@web.expose
@web.require_login( "share histories with other users" )
def sharing( self, trans, histories=[], id=None, **kwd ):
"""Performs sharing of histories among users."""
# histories looks like: [ historyX, historyY ]
params = util.Params( kwd )
msg = util.restore_text ( params.get( 'msg', '' ) )
+1 -1
View File
@@ -1,7 +1,7 @@
body{background:#C1C9E5;color:#303030;background-image:url(menu_bg.png);background-repeat:repeat-x;background-position:top;margin:5px;border:0;padding:0;}
a{color:#303030;}
div.historyLinks{padding:5px;margin:5px 0 5px 0;}
div.historyItem{margin:5px -5px 5px 0px;padding:5px 11px 5px 5px;border:solid #888888 1px;border-left:solid #888888 5px;border-right:none;background:#EEEEEE;}
div.historyItem{margin:5px -5px 5px 0px;padding:5px 11px 5px 5px;border:solid #888888 1px;border-left:solid #888888 5px;border-right:solid #888888 1px;background:#EEEEEE;}
div.historyItem .state-icon{display:inline-block;vertical-align:middle;width:16px;height:16px;background-position:0 1px;background-repeat:no-repeat;}
div.historyItem .historyItemTitle{line-height:16px;}
div.historyItem div.historyItem{margin-right:-11px;}
+1 -1
View File
@@ -25,7 +25,7 @@ div.historyItem {
padding: 5px 11px 5px 5px;
border: solid $history_queued_border 1px;
border-left: solid $history_queued_border 5px;
border-right: none;
border-right: solid $history_queued_border 1px;
background: $history_queued_bg;
.state-icon {
display: inline-block;
+101 -95
View File
@@ -12,30 +12,31 @@ jQuery.fn.autocomplete_tagging = function(options) {
//
var defaults =
{
get_toggle_link_text_fn: function(tags)
{
var text = "";
var num_tags = array_length(tags);
if (num_tags != 0)
text = num_tags + (num_tags != 0 ? " Tags" : " Tag");
else
// No tags.
text = "Add tags";
return text;
},
tag_click_fn : function (name, value) { },
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: ""
get_toggle_link_text_fn: function(tags)
{
var text = "";
var num_tags = array_length(tags);
if (num_tags != 0)
text = num_tags + (num_tags != 0 ? " Tags" : " Tag");
else
// No tags.
text = "Add tags";
return text;
},
tag_click_fn : function (name, value) { },
editable: true,
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: ""
};
//
@@ -206,32 +207,32 @@ jQuery.fn.autocomplete_tagging = function(options) {
// Delete tag.
$.ajax({
url: settings.ajax_delete_tag_url,
data: { tag_name: tag_name },
error: function()
{
// Failed. Roll back changes and show alert.
settings.tags[tag_name] = tag_value;
if (prev_button.hasClass("tag-button"))
prev_button.after(tag_button);
else
tag_area.prepend(tag_button);
var new_text = settings.get_toggle_link_text_fn(settings.tags);
alert( "Remove tag failed" );
url: settings.ajax_delete_tag_url,
data: { tag_name: tag_name },
error: function()
{
// Failed. Roll back changes and show alert.
settings.tags[tag_name] = tag_value;
if (prev_button.hasClass("tag-button"))
prev_button.after(tag_button);
else
tag_area.prepend(tag_button);
var new_text = settings.get_toggle_link_text_fn(settings.tags);
alert( "Remove tag failed" );
toggle_link.text(new_text);
toggle_link.text(new_text);
// TODO: no idea why it's necessary to set this up again.
delete_img.mouseenter( function ()
{
$(this).attr("src", settings.delete_tag_img_rollover);
});
delete_img.mouseleave( function ()
{
$(this).attr("src", settings.delete_tag_img);
});
},
success: function() {}
// TODO: no idea why it's necessary to set this up again.
delete_img.mouseenter( function ()
{
$(this).attr("src", settings.delete_tag_img_rollover);
});
delete_img.mouseleave( function ()
{
$(this).attr("src", settings.delete_tag_img);
});
},
success: function() {}
});
return true;
@@ -248,7 +249,9 @@ jQuery.fn.autocomplete_tagging = function(options) {
var tag_button = $("<span></span>").addClass("tag-button");
tag_button.append(tag_name_elt);
tag_button.append(delete_img);
// Allow delete only if element is editable.
if (settings.editable)
tag_button.append(delete_img);
return tag_button;
};
@@ -411,55 +414,58 @@ jQuery.fn.autocomplete_tagging = function(options) {
}
});
tag_area.append(add_tag_button);
tag_area.append(tag_input_field);
tag_input_field.hide();
// On click, enable user to add tags.
tag_area.click( function(e)
if (settings.editable)
{
var is_active = $(this).hasClass("active-tag-area");
// If a "delete image" object was pressed and area is inactive, do nothing.
if ($(e.target).hasClass("delete-tag-img") && !is_active)
return false;
// If a "tag name" object was pressed and area is inactive, do nothing.
if ($(e.target).hasClass("tag-name") && !is_active)
return false;
// Hide add tag button, show tag_input field. Change background to show
// area is active.
$(this).addClass("active-tag-area");
add_tag_button.hide();
tag_input_field.show();
tag_input_field.focus();
// Add handler to document that will call blur when the tag area is blurred;
// a tag area is blurred when a user clicks on an element outside the area.
var handle_document_click = function(e)
tag_area.append(add_tag_button);
tag_area.append(tag_input_field);
tag_input_field.hide();
// On click, enable user to add tags.
tag_area.click( function(e)
{
var tag_area_id = tag_area.attr("id");
// Blur the tag area if the element clicked on is not in the tag area.
if (
($(e.target).attr("id") != tag_area_id) &&
($(e.target).parents().filter(tag_area_id).length == 0)
)
{
tag_area.blur();
$(document).unbind("click", handle_document_click);
}
};
// TODO: we should attach the click handler to all frames in order to capture
// clicks outside the frame that this element is in.
//window.parent.document.onclick = handle_document_click;
//var temp = $(window.parent.document.body).contents().find("iframe").html();
//alert(temp);
//$(document).parent().click(handle_document_click);
$(window).click(handle_document_click);
var is_active = $(this).hasClass("active-tag-area");
// If a "delete image" object was pressed and area is inactive, do nothing.
if ($(e.target).hasClass("delete-tag-img") && !is_active)
return false;
return false;
});
// If a "tag name" object was pressed and area is inactive, do nothing.
if ($(e.target).hasClass("tag-name") && !is_active)
return false;
// Hide add tag button, show tag_input field. Change background to show
// area is active.
$(this).addClass("active-tag-area");
add_tag_button.hide();
tag_input_field.show();
tag_input_field.focus();
// Add handler to document that will call blur when the tag area is blurred;
// a tag area is blurred when a user clicks on an element outside the area.
var handle_document_click = function(e)
{
var tag_area_id = tag_area.attr("id");
// Blur the tag area if the element clicked on is not in the tag area.
if (
($(e.target).attr("id") != tag_area_id) &&
($(e.target).parents().filter(tag_area_id).length == 0)
)
{
tag_area.blur();
$(document).unbind("click", handle_document_click);
}
};
// TODO: we should attach the click handler to all frames in order to capture
// clicks outside the frame that this element is in.
//window.parent.document.onclick = handle_document_click;
//var temp = $(window.parent.document.body).contents().find("iframe").html();
//alert(temp);
//$(document).parent().click(handle_document_click);
$(window).click(handle_document_click);
return false;
});
}
// If using toggle link, hide the tag area. Otherwise, if there are no tags,
// hide the "add tags" button and show the input field.
+2 -1
View File
@@ -44,13 +44,14 @@
<!--[if lt IE 7]>
${h.js( 'IE7', 'ie7-recalc' )}
<![endif]-->
${h.js( 'jquery' )}
</%def>
## Default late-load javascripts
<%def name="late_javascripts()">
## Scripts can be loaded later since they progressively add features to
## the panels, but do not change layout
${h.js( 'jquery', 'jquery.event.drag', 'jquery.event.hover', 'jquery.form', 'galaxy.base', 'galaxy.panels' )}
${h.js( 'jquery.event.drag', 'jquery.event.hover', 'jquery.form', 'galaxy.base', 'galaxy.panels' )}
<script type="text/javascript">
ensure_dd_helper();
+8 -1
View File
@@ -33,11 +33,18 @@
%endfor
%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 ) %>
<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.
</div>
</div>
<div class="form-row">
<% url = h.url_for( controller='history', action='imp', id=trans.security.encode_id(history.id), qualified=True ) %>
<a href="${url}">${url}</a>
<div class="toolParamHelp" style="clear: both;">
Send the above URL to users as an easy way for them to import the history, making a copy of their own
Send the above link to users as an easy way for them to import the history, making a copy of their own.
</div>
</div>
<div class="form-row">
+349
View File
@@ -0,0 +1,349 @@
<%inherit file="/base_panels.mako"/>
<%def name="javascripts()">
${parent.javascripts()}
${h.js( "jquery", "json2", "jquery.jstore-all", "jquery.autocomplete", "autocomplete_tagging" )}
</%def>
<%def name="stylesheets()">
${parent.stylesheets()}
${h.css( "history", "autocomplete_tagging" )}
</%def>
<%namespace file="/root/history_common.mako" import="render_dataset" />
<%def name="init()">
<%
self.has_left_panel=False
self.has_right_panel=False
self.message_box_visible=False
%>
</%def>
<%def name="center_panel()">
## For now, turn off inline editing so that view is external only.
<%
user_owns_history = False
%>
<div style="overflow: auto; height: 100%;">
<div style="padding: 10px;">
## 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( "|&nbsp;" ).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')}</a> |
%endif
<a href="${h.url_for( controller='history', action='view', id=trans.security.encode_id(history.id) )}">${_('refresh')}</a>
%if show_deleted:
| <a href="${h.url_for('history', show_deleted=False)}">${_('hide deleted')}</a>
%endif
</div>
<div id="history-name-area" class="historyLinks" style="color: gray; font-weight: bold; padding: 0px 0px 5px 0px">
%if user_owns_history:
<div style="float: right"><a id="history-rename" title="Rename" class="icon-button edit" target="galaxy_main" href="${h.url_for( controller='history', action='rename' )}"></a></div>
%endif
<div id="history-name">${history.get_display_name()}</div>
</div>
%if history.deleted:
<div class="warningmessagesmall">
${_('You are currently viewing a deleted history!')}
</div>
<p></p>
%endif
<div id="history-tag-area" style="margin-bottom: 1em">
</div>
<%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(history, "history-tag-area", get_toggle_link_text_fn='get_toggle_link_text', editable=user_owns_history)}
%endif
%if not datasets:
<div class="infomessagesmall" id="emptyHistoryMessage">
%else:
## Render requested datasets, ordered from newest to oldest
%for data in reversed( datasets ):
%if data.visible:
<div class="historyItemContainer" id="historyItemContainer-${data.id}">
${render_dataset( data, data.hid, show_deleted_on_refresh = show_deleted, user_owns_dataset=user_owns_history )}
</div>
%endif
%endfor
<div class="infomessagesmall" id="emptyHistoryMessage" style="display:none;">
%endif
${_("Your history is empty. Click 'Get Data' on the left pane to start")}
</div>
</div>
</div>
</%def>
+1 -1
View File
@@ -321,7 +321,7 @@ var updater_callback = function ( tracked_datasets ) {
%for data in reversed( datasets ):
%if data.visible:
<div class="historyItemContainer" id="historyItemContainer-${data.id}">
${render_dataset( data, data.hid, show_deleted_on_refresh = show_deleted )}
${render_dataset( data, data.hid, show_deleted_on_refresh = show_deleted, user_owns_dataset = True )}
</div>
%endif
%endfor
+20 -12
View File
@@ -1,6 +1,6 @@
<% _=n_ %>
## Render the dataset `data` as history item, using `hid` as the displayed id
<%def name="render_dataset( data, hid, show_deleted_on_refresh = False )">
<%def name="render_dataset( data, hid, show_deleted_on_refresh = False, user_owns_dataset = True )">
<%
if data.state in ['no state','',None]:
data_state = "queued"
@@ -19,21 +19,27 @@
<strong>This dataset has been deleted. Click <a href="${h.url_for( controller='dataset', action='undelete', id=data.id )}" class="historyItemUndelete" id="historyItemUndeleter-${data.id}" target="galaxy_history">here</a> to undelete.</strong>
</div>
%endif
## Header row for history items (name, state, action buttons)
<div style="overflow: hidden;" class="historyItemTitleBar">
<div class="historyItemButtons">
%if data_state == "upload":
## TODO: Make these CSS, just adding a "disabled" class to the normal
## links should be enough. However the number of datasets being uploaded
## at a time is usually small so the impact of these images is also small.
<img src="${h.url_for('/static/images/eye_icon_grey.png')}" width='16' height='16' alt='display data' title='display data' class='button display' border='0'>
<img src="${h.url_for('/static/images/pencil_icon_grey.png')}" width='16' height='16' alt='edit attributes' title='edit attributes' class='button edit' border='0'>
## TODO: Make these CSS, just adding a "disabled" class to the normal
## links should be enough. However the number of datasets being uploaded
## at a time is usually small so the impact of these images is also small.
<img src="${h.url_for('/static/images/eye_icon_grey.png')}" width='16' height='16' alt='display data' title='display data' class='button display' border='0'>
%if user_owns_dataset:
<img src="${h.url_for('/static/images/pencil_icon_grey.png')}" width='16' height='16' alt='edit attributes' title='edit attributes' class='button edit' border='0'>
%endif
%else:
<a class="icon-button display" title="display data" href="${h.url_for( controller='dataset', dataset_id=data.id, action='display', filename='index')}" target="galaxy_main"></a>
<a class="icon-button edit" title="edit attributes" href="${h.url_for( controller='root', action='edit', id=data.id )}" target="galaxy_main"></a>
<a class="icon-button display" title="display data" href="${h.url_for( controller='dataset', dataset_id=data.id, action='display', filename='index')}" target="galaxy_main"></a>
%if user_owns_dataset:
<a class="icon-button edit" title="edit attributes" href="${h.url_for( controller='root', action='edit', id=data.id )}" target="galaxy_main"></a>
%endif
%endif
<a class="icon-button delete" title="delete" href="${h.url_for( action='delete', id=data.id, show_deleted_on_refresh=show_deleted_on_refresh )}" id="historyItemDeleter-${data.id}"></a>
%if user_owns_dataset:
<a class="icon-button delete" title="delete" href="${h.url_for( action='delete', id=data.id, show_deleted_on_refresh=show_deleted_on_refresh )}" id="historyItemDeleter-${data.id}"></a>
%endif
</div>
<span class="state-icon"></span>
<span class="historyItemTitle"><b>${hid}: ${data.display_name().decode('utf-8')}</b></span>
@@ -80,8 +86,10 @@
<div class="info">${_('Info: ')}${data.display_info()}</div>
<div>
%if data.has_data:
<a href="${h.url_for( action='display', id=data.id, tofile='yes', toext=data.ext )}" target="_blank">save</a>
| <a href="${h.url_for( controller='tool_runner', action='rerun', id=data.id )}" target="galaxy_main">rerun</a>
<a href="${h.url_for( controller='root', action='display', id=data.id, tofile='yes', toext=data.ext )}" target="_blank">save</a>
%if user_owns_dataset:
| <a href="${h.url_for( controller='tool_runner', action='rerun', id=data.id )}" target="galaxy_main">rerun</a>
%endif
%for display_app in data.datatype.get_display_types():
<% target_frame, display_links = data.datatype.get_display_links( data, display_app, app, request.base ) %>
%if len( display_links ) > 0:
+2 -1
View File
@@ -4,7 +4,7 @@
%endif
## Render the tags 'tags' as an autocomplete element.
<%def name="render_tagging_element(tagged_item, elt_id, use_toggle_link='true', in_form='false', input_size='15', tag_click_fn='default_tag_click_fn', get_toggle_link_text_fn='default_get_toggle_link_text_fn')">
<%def name="render_tagging_element(tagged_item, elt_id, use_toggle_link='true', in_form='false', input_size='15', tag_click_fn='default_tag_click_fn', get_toggle_link_text_fn='default_get_toggle_link_text_fn', editable='true')">
<script type="text/javascript">
//
// Set up autocomplete tagger.
@@ -83,6 +83,7 @@
var options =
{
tags : ${h.to_json_string(tag_names_and_values)},
editable : ${str(editable).lower()},
get_toggle_link_text_fn: ${get_toggle_link_text_fn},
tag_click_fn: ${tag_click_fn},
<% tagged_item_id = trans.security.encode_id(tagged_item.id) %>
+2 -2
View File
@@ -164,7 +164,7 @@ class TestHistory( TwillTestCase ):
# Enable importing history3 via a URL
self.enable_import_via_link( self.security.encode_id( history3.id ),
check_str='Unshare',
check_str_after_submit='Send the above URL to users' )
check_str_after_submit='Send the above link to users' )
# Make sure history3 is now import-able
history3.refresh()
if not history3.importable:
@@ -175,7 +175,7 @@ class TestHistory( TwillTestCase ):
check_str_after_submit='You cannot import your own history.' )
# Disable the import link for history3
self.disable_import_via_link( self.security.encode_id( history3.id ),
check_str='Send the above URL to users',
check_str='Send the above link to users',
check_str_after_submit='Enable import via link' )
# Try importing history3 after disabling the URL
self.import_history_via_url( self.security.encode_id( history3.id ),