Templates & Flask end-points for managing assets.

This commit is contained in:
2016-11-09 14:57:46 +01:00
parent 3346bb1364
commit 0e170464e6
8 changed files with 507 additions and 65 deletions

View File

@@ -34,18 +34,21 @@ function item_open(item_id, item_type, pushState, project_url)
$('[id^="' + item_type + '-"]').removeClass('active');
$('#' + item_type + '-' + item_id).addClass('active');
// Special case to highlight the shot row when opening task in shot context
if (ProjectUtils.context() == 'shot' && item_type == 'task'){
// Special case to highlight the shot row when opening task in shot or asset context
var pu_ctx = ProjectUtils.context();
var pc_ctx_shot_asset = (pu_ctx == 'shot' || pu_ctx == 'asset');
if (pc_ctx_shot_asset && item_type == 'task'){
$('[id^="shot-"]').removeClass('active');
$('[id^="asset-"]').removeClass('active');
$('#task-' + item_id).closest('.table-row').addClass('active');
}
var item_url = '/attract/' + project_url + '/' + item_type + 's/' + item_id;
var push_url = item_url;
if (ProjectUtils.context() == 'shot' && item_type == 'task'){
push_url = '/attract/' + project_url + '/shots/with-task/' + item_id;
if (pc_ctx_shot_asset && item_type == 'task'){
push_url = '/attract/' + project_url + '/' + pu_ctx + 's/with-task/' + item_id;
}
item_url += '?context=' + ProjectUtils.context();
item_url += '?context=' + pu_ctx;
statusBarSet('default', 'Loading ' + item_type + '…');
@@ -94,6 +97,11 @@ function shot_open(shot_id)
item_open(shot_id, 'shot');
}
function asset_open(asset_id)
{
item_open(asset_id, 'asset');
}
window.onpopstate = function(event)
{
var state = event.state;
@@ -102,26 +110,26 @@ window.onpopstate = function(event)
}
/**
* Create a task and show it in the #item-details div.
* NOTE: Not used at the moment, we're creating shots via Blender's VSE
* Create a asset and show it in the #item-details div.
* NOTE: Not used at the moment, we're creating assets via Blender's VSE
*/
function shot_create(project_url)
function asset_create(project_url)
{
if (project_url === undefined) {
throw new ReferenceError("shot_create(" + project_url+ ") called.");
throw new ReferenceError("asset_create(" + project_url+ ") called.");
}
var url = '/attract/' + project_url + '/shots/create';
var url = '/attract/' + project_url + '/assets/create';
data = {
project_url: project_url
};
$.post(url, data, function(shot_data) {
shot_open(shot_data.shot_id);
$.post(url, data, function(asset_data) {
asset_open(asset_data.asset_id);
})
.fail(function(xhr) {
if (console) {
console.log('Error creating task');
console.log('Error creating asset');
console.log('XHR:', xhr);
}
$('#item-details').html(xhr.responseText);
@@ -156,17 +164,17 @@ function task_add(shot_id, task_id, task_type)
<span class="due_date">-</span>\
</a>\
');
} else if (context == 'shot') {
} else if (context == 'shot' || context == 'asset') {
if (shot_id === undefined) {
throw new ReferenceError("task_add(" + shot_id + ", " + task_id + ", " + task_type + ") called in shot context.");
throw new ReferenceError("task_add(" + shot_id + ", " + task_id + ", " + task_type + ") called in " + context + " context.");
}
var $shot_cell = $('#shot-' + shot_id + ' .table-cell.task-type.' + task_type);
var url = '/attract/' + project_url + '/shots/with-task/' + task_id;
var $list_cell = $('#' + context + '-' + shot_id + ' .table-cell.task-type.' + task_type);
var url = '/attract/' + project_url + '/' + context + 's/with-task/' + task_id;
/* WARNING: This is a copy of an element of attract/shots/for_project #task-list.col-list
* If that changes, change this too. */
$shot_cell.append('\
$list_cell.append('\
<a class="status-todo task-link active"\
title="-save your task first-"\
href="' + url + '"\
@@ -175,7 +183,9 @@ function task_add(shot_id, task_id, task_type)
</a>\
');
$shot_cell.find('.task-add.task-add-link').addClass('hidden');
$list_cell.find('.task-add.task-add-link').addClass('hidden');
} else {
if (console) console.log('task_add: not doing much in context', context);
}
}
@@ -322,6 +332,36 @@ function shot_save(shot_id, shot_url) {
});
}
function asset_save(asset_id, asset_url) {
return attract_form_save('shot_form', 'asset-' + asset_id, asset_url, {
done: function($asset, saved_asset) {
// Update the asset list.
// NOTE: this is tightly linked to the HTML of the asset list in for_project.jade.
$('.asset-name-' + saved_asset._id).text(saved_asset.name).flashOnce();
$asset.find('span.name').text(saved_asset.name);
$asset.find('span.due_date').text(moment().to(saved_asset.properties.due_date));
$asset.find('span.status').text(saved_asset.properties.status.replace('_', ' '));
$asset
.removeClassPrefix('status-')
.addClass('status-' + saved_asset.properties.status)
.flashOnce()
;
asset_open(asset_id);
},
fail: function($item, xhr_or_response_data) {
if (xhr_or_response_data.status == 412) {
// TODO: implement something nice here. Just make sure we don't throw
// away the user's edits. It's up to the user to handle this.
} else {
$('#item-details').html(xhr_or_response_data.responseText);
}
},
type: 'asset'
});
}
function task_delete(task_id, task_etag, task_delete_url) {
if (task_id === undefined || task_etag === undefined || task_delete_url === undefined) {
throw new ReferenceError("task_delete(" + task_id + ", " + task_etag + ", " + task_delete_url + ") called.");

View File

@@ -0,0 +1,142 @@
| {% extends 'attract/layout.html' %}
| {% block bodyattrs %}{{ super() }} data-context='asset'{% endblock %}
| {% block page_title %}Assets - {{ project.name }}{% endblock %}
| {% block body %}
#col_main
.col_header.task-list-header
| {{ assets | count }} assets
a.task-project(href="{{url_for('projects.view', project_url=project.url)}}") {{ project.name }}
a#task-add(href="javascript:asset_create('{{ project.url }}');") + Create Asset
#shot-list
.table
.table-head
.table-row
.table-cell.asset-status
.table-cell.asset-thumbnail
span.collapser.thumbnails(title="Collapse thumbnails") Thumbnail
.table-cell.asset-name
span.collapser(title="Collapse name column") Name
| {% for task_type in task_types %}
.table-cell.task-type(class="{{ task_type }}")
span.collapser(title="Collapse {{ task_type or 'Other' }} column") {{ task_type or 'other' }}
| {% endfor %}
.table-body
| {% for asset in assets %}
.table-row(
id="asset-{{ asset._id }}",
class="status-{{ asset.properties.status }} {{ asset.properties.used_in_edit | yesno(' ,not-in-edit, ') }}")
.table-cell.asset-status(
title="Status: {{ asset.properties.status | undertitle }}")
.table-cell.asset-thumbnail
a(
data-asset-id="{{ asset._id }}",
href="{{ url_for('attract.assets.perproject.view_asset', project_url=project.url, asset_id=asset._id) }}",
class="status-{{ asset.properties.status }} asset-link")
img(src="{{ asset._thumbnail }}",
alt="Thumbnail",
style='width: 110px; height: {{ asset._thumbnail_height }}')
.table-cell.asset-name
a(
data-asset-id="{{ asset._id }}",
href="{{ url_for('attract.assets.perproject.view_asset', project_url=project.url, asset_id=asset._id) }}",
class="status-{{ asset.properties.status }} asset-link")
span(class="asset-name-{{ asset._id }}") {{ asset.name }}
| {% for task_type in task_types %}
.table-cell.task-type(class="{{ task_type }}")
| {% for task in tasks_for_assets[asset._id][task_type] %}
a(
data-task-id="{{ task._id }}",
id="task-{{ task._id }}",
href="{{ url_for('attract.assets.perproject.with_task', project_url=project.url, task_id=task._id) }}",
class="status-{{ task.properties.status }} task-link",
title="{{ task.properties.status | undertitle }} task: {{ task.name }}")
| {# First letter of the status. Disabled until we provide the user setting to turn it off
span {{ task.properties.status[0] }}
| #}
| {% endfor %}
//- Dirty hack, assume a user can create a task for a asset if they can edit the asset.
| {% if 'PUT' in asset.allowed_methods %}
a.task-add(
title="Add a new '{{ task_type }}' task",
class="task-add-link {% if tasks_for_assets[asset._id][task_type] %}hidden{% endif %}"
href="javascript:task_create('{{ asset._id }}', '{{ task_type }}');")
i.pi-plus
| Task
| {% endif %}
| {% endfor %}
| {% endfor %}
.col-splitter
#col_right
.col_header
span.header_text
#status-bar
#item-details
.item-details-empty
| Select a asset or Task
| {% endblock %}
| {% block footer_scripts %}
script.
{% if open_task_id %}
$(function() { item_open('{{ open_task_id }}', 'task', false); });
{% endif %}
{% if open_asset_id %}
$(function() { item_open('{{ open_asset_id }}', 'asset', false); });
{% endif %}
var same_cells;
/* Collapse columns by clicking on the title */
$('.table-head .table-cell span.collapser').on('click', function(e){
e.stopPropagation();
/* We need to find every cell matching the same classes */
same_cells = '.' + $(this).parent().attr('class').split(' ').join('.');
$(same_cells).hide();
/* Add the spacer which we later click to expand */
$('<div class="table-cell-spacer ' + $(this).text() + '" title="Expand ' + $(this).text() + '"></div>').insertAfter(same_cells);
});
$('body').on('click', '.table-cell-spacer', function(){
/* We need to find every cell matching the same classes */
same_cells = '.' + $(this).prev().attr('class').split(' ').join('.');
$(same_cells).show();
$(same_cells).next().remove();
});
$('.table-body .table-cell').mouseenter(function(){
same_cells = '.' + $(this).attr('class').split(' ').join('.');
$('.table-head ' + same_cells).addClass('highlight');
}).mouseleave(function(){
same_cells = '.' + $(this).attr('class').split(' ').join('.');
$('.table-head ' + same_cells).removeClass('highlight');
});
$('.table-head .table-cell').mouseenter(function(){
same_cells = '.' + $(this).attr('class').split(' ').join('.');
$('.table-body ' + same_cells).addClass('highlight');
}).mouseleave(function(){
same_cells = '.' + $(this).attr('class').split(' ').join('.');
$('.table-body ' + same_cells).removeClass('highlight');
});
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/clipboard.min.js')}}")
script(src="{{ url_for('static_attract', filename='assets/js/vendor/jquery-resizable.min.js')}}")
script.
$("#col_main").resizable({
handleSelector: ".col-splitter",
resizeHeight: false
});
// Set height of asset-list and item details so we can scroll inside them
$(window).on('load resize', function(){
var window_height = $(window).height() - 50; // header is 50px
$('#asset-list').css({'height': window_height});
$('#item-details').css({'height': window_height});
});
| {% endblock footer_scripts %}

View File

@@ -0,0 +1,124 @@
.attract-box.shot.with-status(class="status-{{ asset.properties.status }}")
form#shot_form(onsubmit="return asset_save('{{asset._id}}', '{{ url_for('attract.assets.perproject.save', project_url=project['url'], asset_id=asset._id) }}')")
input(type='hidden',name='_etag',value='{{ asset._etag }}')
| {% if 'PUT' in asset.allowed_methods %}
input.item-name(
name="name",
type="text",
placeholder='Asset name',
value="{{ asset.name | hide_none }}")
| {% else %}
span.item-name {{ asset.name | hide_none }}
| {% endif %}
button.copy-to-clipboard.btn.item-id(
style="margin-left: auto",
name="Copy to Clipboard",
type="button",
data-clipboard-text="{{ asset._id }}",
title="Copy ID to clipboard")
| ID
| {% if 'PUT' in asset.allowed_methods %}
.input-group
textarea#item-description.input-transparent(
name="description",
type="text",
rows=1,
placeholder='Description') {{ asset.description | hide_none }}
.input-group
label(for="item-status") Status:
select#item-status.input-transparent(
name="status")
| {% for status in asset_node_type.dyn_schema.status.allowed %}
| <option value="{{ status }}" {% if status == asset.properties.status %}selected{% endif %}>{{ status | undertitle }}</option>
| {% endfor %}
.input-group
textarea#item-notes.input-transparent(
name="notes",
type="text",
rows=1,
placeholder='Notes') {{ asset.properties.notes | hide_none }}
.input-group-separator
.input-group
button#item-save.btn.btn-default.btn-block(type='submit')
i.pi-check
| Save Asset
| {% else %}
//- NOTE: read-only versions of the fields above.
| {% if asset.description %}
p.item-description {{ asset.description | hide_none }}
| {% endif %}
.table.item-properties
.table-body
.table-row.properties-status.js-help(
data-url="{{ url_for('attract.help', project_url=project.url) }}")
.table-cell Status
.table-cell(class="status-{{ asset.properties.status }}")
| {{ asset.properties.status | undertitle }}
| {% if asset.properties.notes %}
.table-row
.table-cell Notes
.table-cell
| {{ asset.properties.notes | hide_none }}
| {% endif %}
| {% endif %}
#item-view-feed
#activities
#comments-embed
| {% if config.DEBUG %}
.debug-info
a.debug-info-toggle(role='button',
data-toggle='collapse',
href='#debug-content',
aria-expanded='false',
aria-controls='debug-content')
i.pi-info
| Debug Info
#debug-content.collapse
pre.
{{ asset.to_dict() | pprint }}
| {% endif %}
script.
var clipboard = new Clipboard('.copy-to-clipboard');
clipboard.on('success', function(e) {
statusBarSet('info', 'Copied asset ID to clipboard', 'pi-check');
});
var activities_url = "{{ url_for('.activities', project_url=project.url, asset_id=asset['_id']) }}";
loadActivities(activities_url); // from 10_tasks.js
loadComments("{{ url_for('nodes.comments_for_node', node_id=asset['_id']) }}");
$('body').on('pillar:comment-posted', function(e, comment_node_id) {
loadActivities(activities_url)
.done(function() {
$('#' + comment_node_id).scrollHere();
});
});
$('.js-help').openModalUrl('Help', "{{ url_for('attract.help', project_url=project.url) }}");
{% if 'PUT' in asset.allowed_methods %}
/* Resize textareas */
var textAreaFields = $('#item-description, #item-notes');
textAreaFields.each(function(){
$(this)
.autoResize()
.blur();
});
$('#item-status').change(function(){
$("#item-save").trigger( "click" );
});
{% endif %}

View File

@@ -41,6 +41,9 @@ html(lang="en")
li
a.navbar-item.shots(href="{{ url_for('attract.shots.perproject.index', project_url=project.url) }}",
title='Shots for project {{ project.name }}') S
li
a.navbar-item.shots(href="{{ url_for('attract.assets.perproject.index', project_url=project.url) }}",
title='Assets for project {{ project.name }}') A
| {% else %}
| {% if current_user.is_authenticated %}
li