Introducing Pillar Framework
Refactor of pillar-server and pillar-web into a single python package. This simplifies the overall architecture of pillar applications. Special thanks @sybren and @venomgfx
This commit is contained in:
78
src/templates/users/edit_embed.jade
Normal file
78
src/templates/users/edit_embed.jade
Normal file
@@ -0,0 +1,78 @@
|
||||
| {% block body %}
|
||||
|
||||
#user-edit-container
|
||||
|
||||
#user-edit-header
|
||||
.user-edit-name {{user.full_name}}
|
||||
.user-edit-username {{user.username}}
|
||||
.user-edit-email {{user.email}}
|
||||
|
||||
form(
|
||||
id="user-edit-form",
|
||||
method="POST",
|
||||
enctype="multipart/form-data",
|
||||
action="{{url_for('users.users_edit', user_id=user._id)}}")
|
||||
|
||||
| {% for field in form %}
|
||||
|
||||
| {% if field.name == 'csrf_token' %}
|
||||
| {{ field }}
|
||||
|
||||
| {% else %}
|
||||
|
||||
| {% if field.type == 'HiddenField' %}
|
||||
| {{ field }}
|
||||
|
||||
| {% else %}
|
||||
|
||||
.form-group(class="{{field.name}}{% if field.errors %} error{% endif %}")
|
||||
| {{ field.label }}
|
||||
| {{ field(class='form-control') }}
|
||||
|
||||
| {% if field.errors %}
|
||||
ul.error
|
||||
| {% for error in field.errors %}
|
||||
li {{ error }}
|
||||
| {% endfor %}
|
||||
| {% endif %}
|
||||
|
||||
|
||||
| {% endif %}
|
||||
|
||||
| {% endif %}
|
||||
|
||||
| {% endfor %}
|
||||
|
||||
|
||||
a#button-cancel.btn.btn-default(href="#", data-user-id='{{user._id}}') Cancel
|
||||
|
||||
input#submit_edit_user.btn.btn-default(
|
||||
data-user-id="{{user._id}}",
|
||||
type="submit" value="Submit")
|
||||
|
||||
#user-edit-notification
|
||||
|
||||
|
||||
script(type="text/javascript").
|
||||
$('#roles').select2();
|
||||
|
||||
$('#user-edit-form').submit(function(e){
|
||||
e.preventDefault();
|
||||
//- console.log($(this).serialize());
|
||||
$.post($(this).attr('action'), $(this).serialize())
|
||||
.done(function(data){
|
||||
$('#user-edit-notification').addClass('success').html('Success!');
|
||||
})
|
||||
.fail(function(data){
|
||||
$('#user-edit-notification').addClass('fail').html('Houston!');
|
||||
});
|
||||
//- $("#user-edit-form").submit();
|
||||
});
|
||||
|
||||
$('#button-cancel').click(function(e){
|
||||
$('#user-container').html('')
|
||||
});
|
||||
|
||||
|
||||
|
||||
| {% endblock %}
|
120
src/templates/users/index.jade
Normal file
120
src/templates/users/index.jade
Normal file
@@ -0,0 +1,120 @@
|
||||
| {% extends 'layout.html' %}
|
||||
| {% block page_title %}Users{% endblock %}
|
||||
|
||||
| {% block body %}
|
||||
|
||||
#search-container
|
||||
#search-sidebar
|
||||
input.search-field(
|
||||
type="text",
|
||||
name="q",
|
||||
id="q",
|
||||
autocomplete="off",
|
||||
spellcheck="false",
|
||||
autocorrect="false",
|
||||
placeholder="Search by Full Name, Username...")
|
||||
|
||||
.search-list-filters
|
||||
#accordion.panel-group.accordion(role="tablist", aria-multiselectable="true")
|
||||
#facets
|
||||
|
||||
#pagination
|
||||
|
||||
.search-list-stats
|
||||
#stats
|
||||
|
||||
#search-list
|
||||
#hits
|
||||
|
||||
#search-details
|
||||
#search-hit-container
|
||||
|
||||
|
||||
| {% raw %}
|
||||
// Facet template
|
||||
script(type="text/template", id="facet-template")
|
||||
.panel.panel-default
|
||||
a(data-toggle='collapse', data-parent='#accordion', href='#filter_{{ facet }}', aria-expanded='true', aria-controls='filter_{{ facet }}')
|
||||
.panel-heading(role='tab')
|
||||
.panel-title {{ title }}
|
||||
.panel-collapse.collapse.in(id='filter_{{ facet }}', role='tabpanel', aria-labelledby='headingOne')
|
||||
.panel-body
|
||||
| {{#values}}
|
||||
a.facet_link.toggleRefine(
|
||||
class='{{#refined}}refined{{/refined}}',
|
||||
data-facet='{{ facet }}',
|
||||
data-value='{{ value }}',
|
||||
href='#')
|
||||
span
|
||||
| {{ label }}
|
||||
small.facet_count.text-muted.pull-right {{ count }}
|
||||
| {{/values}}
|
||||
|
||||
|
||||
// Hit template
|
||||
script(type="text/template", id="hit-template")
|
||||
.search-hit.users(data-user-id='{{ objectID }}')
|
||||
.search-hit-name
|
||||
| {{{ _highlightResult.full_name.value }}}
|
||||
small ({{{ username }}})
|
||||
.search-hit-roles
|
||||
| {{{ roles }}}
|
||||
|
||||
|
||||
// Pagination template
|
||||
script(type="text/template", id="pagination-template")
|
||||
ul.search-pagination.
|
||||
<li {{^prev_page}}class="disabled"{{/prev_page}}><a href="#" {{#prev_page}} class="gotoPage" data-page="{{ prev_page }}" {{/prev_page}}><i class="pi-angle-left"></i></a></li>
|
||||
{{#pages}}
|
||||
<li class="{{#current}}active{{/current}}{{#disabled}}disabled{{/disabled}}"><a href="#" {{^disabled}} class="gotoPage" data-page="{{ number }}" {{/disabled}}>{{ number }}</a></li>
|
||||
{{/pages}}
|
||||
<li {{^next_page}}class="disabled"{{/next_page}}><a href="#" {{#next_page}} class="gotoPage" data-page="{{ next_page }}" {{/next_page}}><i class="pi-angle-right"></i></a></li>
|
||||
|
||||
// Stats template
|
||||
script(type="text/template", id="stats-template")
|
||||
h5 {{ nbHits }} result{{#nbHits_plural}}s{{/nbHits_plural}}
|
||||
span ({{ processingTimeMS }}ms)
|
||||
| {% endraw %}
|
||||
|
||||
| {% endblock %}
|
||||
|
||||
| {% block footer_scripts %}
|
||||
script().
|
||||
var APPLICATION_ID = '{{config.ALGOLIA_USER}}';
|
||||
var SEARCH_ONLY_API_KEY = '{{config.ALGOLIA_PUBLIC_KEY}}';
|
||||
var INDEX_NAME = '{{config.ALGOLIA_INDEX_USERS}}';
|
||||
var sortByCountDesc = null;
|
||||
var FACET_CONFIG = [
|
||||
{ name: 'roles', title: 'Roles', disjunctive: false, sortFunction: sortByCountDesc },
|
||||
];
|
||||
|
||||
script(src="//cdn.jsdelivr.net/algoliasearch/3/algoliasearch.min.js")
|
||||
script(src="//cdn.jsdelivr.net/algoliasearch.helper/2/algoliasearch.helper.min.js")
|
||||
script(src="//cdn.jsdelivr.net/hogan.js/3.0.0/hogan.common.js")
|
||||
script(src="{{ url_for('static_pillar', filename='assets/js/algolia_search.min.js') }}")
|
||||
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.select2.min.js') }}")
|
||||
|
||||
script(type="text/javascript").
|
||||
|
||||
if (typeof Ps !== 'undefined'){
|
||||
Ps.initialize(document.getElementById('hits'), {suppressScrollX: true});
|
||||
}
|
||||
|
||||
function displayUser(userId) {
|
||||
var url = '/u/' + userId + '/edit?embed=1';
|
||||
$.get(url, function(dataHtml){
|
||||
$('#search-hit-container').html(dataHtml);
|
||||
});
|
||||
}
|
||||
|
||||
$('body').on('click', '.search-hit', function(){
|
||||
displayUser($(this).data('user-id'));
|
||||
});
|
||||
|
||||
// Remove focus from search input so that the click event bound to .user-hit
|
||||
// can be fired on the first click.
|
||||
$('#search-list').hover(function(){
|
||||
$('#q').blur();
|
||||
});
|
||||
|
||||
| {% endblock %}
|
45
src/templates/users/login.jade
Normal file
45
src/templates/users/login.jade
Normal file
@@ -0,0 +1,45 @@
|
||||
| {% extends 'layout.html' %}
|
||||
|
||||
| {% block body %}
|
||||
.container
|
||||
#login-container
|
||||
.login-title Welcome back!
|
||||
|
||||
.login-info
|
||||
| Log in using your shared username and password.
|
||||
|
||||
.login-form
|
||||
form#login-form(method="POST", action="{{url_for('users.login_local')}}")
|
||||
.form-group
|
||||
| {{ form.username.label }}
|
||||
| {{ form.username(class='form-control') }}
|
||||
|
||||
.form-group
|
||||
| {{ form.password.label }}
|
||||
| {{ form.password(class='form-control') }}
|
||||
|
||||
.buttons
|
||||
.login-button-container
|
||||
//a.forgot(href="https://blender.org/id/reset") forgot your password?
|
||||
button.btn.btn-default.button-login(type="submit")
|
||||
i.pi-log-in
|
||||
| Login
|
||||
|
||||
//a.btn.btn-default.button-register(href="https://blender.org/id/register", target="_blank")
|
||||
// i.pi-star-outline
|
||||
// | Create Account
|
||||
|
||||
|
||||
| {% endblock %}
|
||||
|
||||
| {% block footer_scripts %}
|
||||
script.
|
||||
$('.button-login').on('click', function(e){
|
||||
e.preventDefault();
|
||||
$(this).html('<i class="pi-spin spin"></i> Hold on...');
|
||||
|
||||
$('#login-form').submit();
|
||||
});
|
||||
| {% endblock %}
|
||||
|
||||
| {% block footer_container %}{% endblock %}
|
20
src/templates/users/settings/_sidebar.jade
Normal file
20
src/templates/users/settings/_sidebar.jade
Normal file
@@ -0,0 +1,20 @@
|
||||
#settings-sidebar
|
||||
.settings-header
|
||||
.settings-title Settings
|
||||
.settings-content
|
||||
ul
|
||||
a(class="{% if title == 'profile' %}active{% endif %}",
|
||||
href="{{ url_for('users.settings_profile') }}")
|
||||
li
|
||||
i.pi-vcard
|
||||
| Profile
|
||||
a(class="{% if title == 'emails' %}active{% endif %}",
|
||||
href="{{ url_for('users.settings_emails') }}")
|
||||
li
|
||||
i.pi-email
|
||||
| Emails
|
||||
a(class="{% if title == 'billing' %}active{% endif %}",
|
||||
href="{{ url_for('users.settings_billing') }}")
|
||||
li
|
||||
i.pi-credit-card
|
||||
| Subscription
|
55
src/templates/users/settings/billing.jade
Normal file
55
src/templates/users/settings/billing.jade
Normal file
@@ -0,0 +1,55 @@
|
||||
| {% extends 'layout.html' %}
|
||||
| {% block body %}
|
||||
.container
|
||||
#settings
|
||||
include _sidebar
|
||||
#settings-container
|
||||
.settings-header
|
||||
.settings-title Subscription
|
||||
|
||||
.settings-content
|
||||
|
||||
| {% if store_user['cloud_access'] %}
|
||||
h3.subscription-active
|
||||
i.pi-check
|
||||
| Your subscription is active
|
||||
h4 Thank you for supporting us!
|
||||
|
||||
hr
|
||||
|
||||
p Subscription expires on: <strong>{{ store_user['expiration_date'][:10] }}</strong>
|
||||
a(href="https://store.blender.org/my-account/") Manage your subscription on Blender Store
|
||||
|
||||
hr
|
||||
|
||||
| {# This text is confusing (refers to the total payments ever made by the user)
|
||||
.settings-billing-info.
|
||||
Paid balance: {{ store_user['paid_balance'] }} {{ store_user['balance_currency'] }}
|
||||
| #}
|
||||
|
||||
| {% else %}
|
||||
|
||||
| {% if 'demo' in groups %}
|
||||
h3.subscription-demo
|
||||
i.pi-heart-filled
|
||||
| You have a free account
|
||||
|
||||
hr
|
||||
|
||||
p You have full access to the Blender Cloud, provided by the Blender Institute. This account is meant for free evaluation of the service. Get in touch with #[a(href="mailto:cloudsupport@blender.org") cloudsupport@blender.org] if you have any questions.
|
||||
|
||||
| {% else %}
|
||||
h3.subscription-missing
|
||||
i.pi-info
|
||||
| You do not have an active subscription.
|
||||
h3
|
||||
a(href="https://store.blender.org/product/membership/") Get full access to Blender Cloud now!
|
||||
| {% endif %}
|
||||
|
||||
| {% endif %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| {% endblock %}
|
27
src/templates/users/settings/emails.jade
Normal file
27
src/templates/users/settings/emails.jade
Normal file
@@ -0,0 +1,27 @@
|
||||
| {% extends 'layout.html' %}
|
||||
| {% block body %}
|
||||
.container
|
||||
#settings
|
||||
include _sidebar
|
||||
#settings-container
|
||||
.settings-header
|
||||
.settings-title Emails
|
||||
|
||||
.settings-content
|
||||
|
||||
.settings-form
|
||||
form#settings-form(method='POST', action="{{url_for('users.settings_emails')}}")
|
||||
| {{ form.csrf_token }}
|
||||
| {% for subfield in form.email_communications %}
|
||||
.form-group.
|
||||
{{ subfield }}
|
||||
{{ subfield.label }}
|
||||
| {% endfor %}
|
||||
|
||||
.buttons
|
||||
button.btn.btn-default.button-submit(type='submit')
|
||||
i.pi-check
|
||||
| Save Changes
|
||||
|
||||
|
||||
| {% endblock %}
|
45
src/templates/users/settings/profile.jade
Normal file
45
src/templates/users/settings/profile.jade
Normal file
@@ -0,0 +1,45 @@
|
||||
| {% extends 'layout.html' %}
|
||||
| {% block body %}
|
||||
.container
|
||||
#settings
|
||||
include _sidebar
|
||||
#settings-container
|
||||
.settings-header
|
||||
.settings-title Profile
|
||||
|
||||
.settings-content
|
||||
.settings-form
|
||||
form#settings-form(method='POST', action="{{url_for('users.settings_profile')}}")
|
||||
.left
|
||||
.form-group
|
||||
| {{ form.full_name.label }}
|
||||
| {{ form.full_name(size=20, class='form-control') }}
|
||||
| {% if form.full_name.errors %}
|
||||
| {% for error in form.full_name.errors %}{{ error|e }}{% endfor %}
|
||||
| {% endif %}
|
||||
|
||||
.form-group
|
||||
| {{ form.username.label }}
|
||||
| {{ form.username(size=20, class='form-control') }}
|
||||
| {% if form.username.errors %}
|
||||
| {% for error in form.username.errors %}{{ error|e }}{% endfor %}
|
||||
| {% endif %}
|
||||
|
||||
|
||||
.form-group.settings-password
|
||||
| Change your password at the
|
||||
a(href="https://blender.org/id/change") Blender ID
|
||||
|
||||
.right
|
||||
.settings-avatar
|
||||
a(href="https://gravatar.com/")
|
||||
img(src="{{ current_user.gravatar }}")
|
||||
span Change Gravatar
|
||||
|
||||
.buttons
|
||||
button.btn.btn-default.button-submit(type='submit')
|
||||
i.pi-check
|
||||
| Save Changes
|
||||
|
||||
|
||||
| {% endblock %}
|
170
src/templates/users/tasks.jade
Normal file
170
src/templates/users/tasks.jade
Normal file
@@ -0,0 +1,170 @@
|
||||
| {% extends 'layout.html' %}
|
||||
|
||||
| {% block header_items %}
|
||||
link(href='//cdn.datatables.net/plug-ins/1.10.7/integration/bootstrap/3/dataTables.bootstrap.css', rel='stylesheet')
|
||||
| {% endblock %}
|
||||
|
||||
| {% block body %}
|
||||
#shots-main.col-md-8
|
||||
table#user_tasks.table.table-striped.table-hover(
|
||||
cellpadding='0', cellspacing='0', border='0')
|
||||
thead
|
||||
tr
|
||||
th {# 0 #}
|
||||
th {# 1 #}
|
||||
th {# 2 #}
|
||||
th {# 3 #}
|
||||
th {# 4 #} Shot
|
||||
th {# 5 #} Name
|
||||
th {# 6 #} Description
|
||||
th {# 7 #} Duration
|
||||
th {# 8 #} Status
|
||||
| {% endblock %}
|
||||
| {% block sidebar %}
|
||||
#shots-sidebar.col-md-4
|
||||
#shot_details_container
|
||||
#task_details_container
|
||||
| {% endblock %}
|
||||
|
||||
| {% block footer_scripts %}
|
||||
script(type='text/javascript', src='//cdn.datatables.net/1.10.7/js/jquery.dataTables.min.js')
|
||||
script().
|
||||
$(document).ready(function(){
|
||||
|
||||
function render_timing(timing) {
|
||||
var timing_text = '';
|
||||
if (timing['cut_in'] && timing['cut_out']) {
|
||||
timing_frames = timing['cut_out'] - timing['cut_in'];
|
||||
|
||||
timing_text += '<span title="';
|
||||
timing_text += timing_frames;
|
||||
timing_text += 'f">';
|
||||
timing_text += Math.round(timing_frames / 24);
|
||||
timing_text += 's</span>';
|
||||
}
|
||||
return timing_text;
|
||||
}
|
||||
|
||||
function render_status_options(status) {
|
||||
var selected = false;
|
||||
var options = []
|
||||
|
||||
statuses = ['todo', 'in_progress', 'on_hold', 'review', 'approved', 'final']
|
||||
$.each(statuses, function(key, value) {
|
||||
selected = false;
|
||||
if (status === value) {
|
||||
selected = true;
|
||||
};
|
||||
option = $("<option />", {
|
||||
value: value,
|
||||
text: value,
|
||||
selected: selected
|
||||
});
|
||||
options.push(option);
|
||||
});
|
||||
return options;
|
||||
}
|
||||
|
||||
function render_status_label(task, task_name) {
|
||||
switch(task.status) {
|
||||
case 'todo':
|
||||
label_text = 'ToDo';
|
||||
break;
|
||||
case 'in_progress':
|
||||
label_text = 'In progress';
|
||||
break;
|
||||
case 'on_hold':
|
||||
label_text = 'On Hold';
|
||||
break;
|
||||
case 'cbb':
|
||||
label_text = 'Could Be Better';
|
||||
break;
|
||||
case 'review':
|
||||
label_text = 'Review';
|
||||
break;
|
||||
case 'approved':
|
||||
label_text = 'Approved';
|
||||
break;
|
||||
case 'final':
|
||||
label_text = 'Final';
|
||||
break;
|
||||
case 'conflict':
|
||||
label_text = 'Conflict';
|
||||
break;
|
||||
default:
|
||||
label_text = task.status;
|
||||
break
|
||||
}
|
||||
|
||||
if (task.is_conflicting) {
|
||||
label_text = 'Conflict';
|
||||
task.status = 'conflict';
|
||||
}
|
||||
|
||||
return tag = '<span task-edit-url="' + task.url_edit + '" class="load-task-view label label-' + task.status + '">' + label_text + '</span>'
|
||||
}
|
||||
|
||||
var shots_table = $('#user_tasks').DataTable({
|
||||
"paging": false,
|
||||
"order": [[ 7, "desc" ]],
|
||||
"data": {{tasks_data | safe}},
|
||||
"columns": [
|
||||
/* */ {"data": "_id"},
|
||||
/* */ {"data": "order"},
|
||||
/* 0 */ {"data": "picture", "width": "80px", "className": "shots-shot_thumbnail"},
|
||||
/* 1 */ {"data": "parent.name"},
|
||||
/* 2 */ {"data": "name", "className": "shots-shot_name"},
|
||||
/* 3 */ {"data": "description", "className": "shots-shot_description"},
|
||||
/* 4 */ {"data": null,},
|
||||
/* 5 */ {"data": "status"},
|
||||
/* 6 */ {"data": null}
|
||||
],
|
||||
"columnDefs": [
|
||||
{
|
||||
"targets": [0, 1],
|
||||
"visible": false,
|
||||
"searchable": false
|
||||
},
|
||||
],
|
||||
|
||||
"rowCallback": function ( row, data, index ) {
|
||||
if ( data.picture) {
|
||||
var img_tag = '<img alt="' + data.name + '" src="' + data.picture_thumbnail + '" class="table-thumbnail">';
|
||||
$('td', row).eq(0).html('<a href="' + data.url_view + '">' + img_tag + '</a>');
|
||||
}
|
||||
|
||||
$('td', row).eq(2).html('<a class="load-shot-view" shot-view-url="' + data.url_edit + '" href="' + data.url_view + '">' + data.name + '</a>');
|
||||
$('td', row).eq(4).html(render_timing(data.timing));
|
||||
$('td', row).eq(5).html(render_status_label(data, data.name));
|
||||
|
||||
var view_tag = '<span class="btn btn-default btn-xs load-shot-view" shot-view-url="' + data.url_edit + '"><i class="glyphicon glyphicon-edit"></i> View</span>';
|
||||
$('td', row).eq(6).html(view_tag);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$(document).on("click", ".load-task-view", function() {
|
||||
$(".task-update").off( "click" );
|
||||
task_view_url = $(this).attr('task-edit-url');
|
||||
$.get(task_view_url, function(data) {
|
||||
$('#shot_details_container').hide();
|
||||
$('#task_details_container').html(data);
|
||||
$('#task_details_container').show();
|
||||
});
|
||||
|
||||
$('.load-task-view').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
|
||||
var shots_table = $('#user_tasks').DataTable();
|
||||
var row = shots_table.row($(this).closest('tr'))
|
||||
|
||||
// Remove class 'active' from rows, and add to current one
|
||||
shots_table.rows('.active').nodes().to$().removeClass('active updated');
|
||||
shots_table.row(row).nodes().to$().addClass('active');
|
||||
|
||||
shots_table.draw();
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
| {% endblock %}
|
Reference in New Issue
Block a user