mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
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:
@@ -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,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;}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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( "| " ).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>
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) %>
|
||||
|
||||
@@ -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 ),
|
||||
|
||||
Reference in New Issue
Block a user