зеркало из https://github.com/mozilla/CSOL-site.git
Merge branch 'master' of https://github.com/mgballard/CSOL-site
Conflicts: static/media/less/core.less
This commit is contained in:
Коммит
be84537d57
1
api.js
1
api.js
|
@ -23,6 +23,7 @@ function middleware (method, default_query) {
|
|||
|
||||
// Build query from various inputs
|
||||
var query = _.extend(
|
||||
{},
|
||||
DEFAULT_QUERY,
|
||||
default_query || {},
|
||||
req.query || {},
|
||||
|
|
1
app.js
1
app.js
|
@ -33,6 +33,7 @@ app.use(flash());
|
|||
|
||||
app.use(helpers.addCsrfToken);
|
||||
app.use(helpers.addRangeMethod);
|
||||
app.use(helpers.addPaginateMethod);
|
||||
app.use(helpers.addMessages);
|
||||
|
||||
require('./controllers/auth')(app);
|
||||
|
|
|
@ -86,9 +86,42 @@ module.exports = function (app) {
|
|||
], function (req, res, next) {
|
||||
var data = req.remote;
|
||||
|
||||
// XXX: replace with API call to openbadger
|
||||
var similar = [
|
||||
{
|
||||
url: "/mybadges/this-badge",
|
||||
image: "http://openbadger-csol.mofostaging.net/badge/image/this-badge.png",
|
||||
name: "Test Badge CLM",
|
||||
description: "This is a test badge!"
|
||||
},
|
||||
{
|
||||
url: "/mybadges/this-badge",
|
||||
image: "http://openbadger-csol.mofostaging.net/badge/image/this-badge.png",
|
||||
name: "Test Badge CLM",
|
||||
description: "This is a test badge!"
|
||||
},
|
||||
{
|
||||
url: "/mybadges/this-badge",
|
||||
image: "http://openbadger-csol.mofostaging.net/badge/image/this-badge.png",
|
||||
name: "Test Badge CLM",
|
||||
description: "This is a test badge!"
|
||||
},
|
||||
{
|
||||
url: "/mybadges/this-badge",
|
||||
image: "http://openbadger-csol.mofostaging.net/badge/image/this-badge.png",
|
||||
name: "Test Badge CLM",
|
||||
description: "This is a test badge!"
|
||||
}
|
||||
];
|
||||
|
||||
const NSIMILAR = 4;
|
||||
|
||||
console.log(data.badge);
|
||||
|
||||
res.render('user/badge.html', {
|
||||
badge: data.badge
|
||||
badge: data.badge,
|
||||
user: req.session.user,
|
||||
similar: similar.slice(0, NSIMILAR)
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -11,96 +11,22 @@ var evidence = db.model('Evidence');
|
|||
|
||||
module.exports = function (app) {
|
||||
|
||||
function getFilters() {
|
||||
var filters = [],
|
||||
requested;
|
||||
function getFilters(query, subset) {
|
||||
var all = badger.getFilters(),
|
||||
filters = [];
|
||||
|
||||
if (arguments.length) {
|
||||
requested = Array.prototype.splice.call(arguments, 0);
|
||||
} else {
|
||||
requested = ['categories', 'grouped_programs', 'ages'];
|
||||
}
|
||||
query = query || {};
|
||||
|
||||
requested.forEach(function(filter) {
|
||||
switch (filter) {
|
||||
case 'categories':
|
||||
case 'category':
|
||||
filters.push({
|
||||
name: 'category',
|
||||
label: 'Category',
|
||||
options: {
|
||||
science: 'Science',
|
||||
technology: 'Technology',
|
||||
engineering: 'Engineering',
|
||||
art: 'Art',
|
||||
math: 'Math'
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'orgs':
|
||||
case 'org':
|
||||
filters.push({
|
||||
name: 'org',
|
||||
label: 'Organization',
|
||||
options: {
|
||||
'org1': 'Org 1',
|
||||
'org2': 'Org 2',
|
||||
'org3': 'Org 3',
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'programs':
|
||||
case 'program':
|
||||
filters.push({
|
||||
name: 'program',
|
||||
label: 'Program',
|
||||
options: {
|
||||
'p1': 'Program 1',
|
||||
'p2': 'Program 2',
|
||||
'p3': 'Program 3',
|
||||
'p4': 'Program 4',
|
||||
'p5': 'Program 5',
|
||||
'p6': 'Program 6'
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'grouped_programs':
|
||||
case 'grouped_program':
|
||||
filters.push({
|
||||
name: 'program',
|
||||
label: 'Program',
|
||||
options: {
|
||||
'Org 1': {
|
||||
'p1': 'Program 1',
|
||||
'p2': 'Program 2'
|
||||
},
|
||||
'Org 2': {
|
||||
'p3': 'Program 3',
|
||||
'p4': 'Program 4'
|
||||
},
|
||||
'Org 3': {
|
||||
'p5': 'Program 5',
|
||||
'p6': 'Program 6'
|
||||
}
|
||||
},
|
||||
is_grouped: true
|
||||
});
|
||||
break;
|
||||
case 'ages':
|
||||
case 'age':
|
||||
filters.push({
|
||||
name: 'age',
|
||||
label: 'Age Group',
|
||||
options: {
|
||||
'lt-13': 'Under 13',
|
||||
'13-14': '13 to 14',
|
||||
'15-16': '15 to 16',
|
||||
'17-18': '17 to 18',
|
||||
'gt-18': 'Over 18'
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (!subset || !subset.length)
|
||||
subset = _.keys(all);
|
||||
|
||||
if (subset && !_.isArray(subset))
|
||||
subset = [subset];
|
||||
|
||||
_.each(subset, function (item) {
|
||||
var filter = all[item] || {name:item, label: item, options: []};
|
||||
filter.selected = query[filter.name];
|
||||
filters.push(filter);
|
||||
});
|
||||
|
||||
return filters;
|
||||
|
@ -120,7 +46,7 @@ module.exports = function (app) {
|
|||
var data = req.remote;
|
||||
|
||||
res.render('programs/list.html', {
|
||||
filters: getFilters('categories', 'orgs', 'ages'),
|
||||
filters: getFilters(req.query, ['categories', 'orgs', 'ageRanges', 'activityTypes']),
|
||||
items: data.programs,
|
||||
page: data.page,
|
||||
pages: data.pages
|
||||
|
@ -285,7 +211,7 @@ module.exports = function (app) {
|
|||
var data = req.remote;
|
||||
|
||||
res.render('badges/list.html', {
|
||||
filters: getFilters(),
|
||||
filters: getFilters(req.query, ['categories', 'ageRanges', 'badgeTypes', 'activityTypes']),
|
||||
items: data.badges,
|
||||
page: data.page,
|
||||
pages: data.pages
|
||||
|
|
112
helpers.js
112
helpers.js
|
@ -1,3 +1,5 @@
|
|||
const querystring = require('querystring');
|
||||
const url = require('url');
|
||||
const _ = require('underscore');
|
||||
|
||||
|
||||
|
@ -13,6 +15,116 @@ exports.addRangeMethod = function addRangeMethod (req, res, next) {
|
|||
next();
|
||||
};
|
||||
|
||||
exports.addPaginateMethod = function addPaginateMethod (req, res, next) {
|
||||
function page (options) {
|
||||
var path = options.path,
|
||||
pageNum = options.pageNum,
|
||||
display = options.display,
|
||||
className = options.className,
|
||||
el = options.el;
|
||||
|
||||
var query = querystring.parse(path.query),
|
||||
content,
|
||||
href;
|
||||
|
||||
if (pageNum === 1)
|
||||
delete query.page
|
||||
else
|
||||
query.page = pageNum
|
||||
|
||||
query = querystring.stringify(query);
|
||||
href = path.href.replace(/\?.*$/, '') + (query ? '?' + query : '');
|
||||
|
||||
if (!pageNum) {
|
||||
content = '<span>' + display + '</span>';
|
||||
} else {
|
||||
content = '<a href="' + href + '">' + (display || pageNum) + '</a>';
|
||||
}
|
||||
|
||||
return '<' + el + (className ? ' class="' + className + '"' : '') + '>' + content + '</' + el + '>';
|
||||
}
|
||||
|
||||
function generatePageNumbers (total, current, maxItems) {
|
||||
maxItems = maxItems || 12;
|
||||
|
||||
if (total <= maxItems)
|
||||
return Array(total).join(',').split(',').map(function(e,i){return i+1;});
|
||||
|
||||
var paged = {}
|
||||
pages = [],
|
||||
count = 1,
|
||||
extraItems = Math.min(3, Math.floor((maxItems - 3) / 4));
|
||||
|
||||
paged[current] = current;
|
||||
|
||||
for (var i = 1, max = Math.min(total, extraItems + 1); i <= max; ++i)
|
||||
(paged[i] = i) && count++;
|
||||
|
||||
for (var i = Math.max(1, total - extraItems); i <= total; ++i)
|
||||
(paged[i] = i) && count++;
|
||||
|
||||
var i = 1;
|
||||
|
||||
while (count < maxItems - 1) {
|
||||
if (!paged[current - i] && (current - i) >= 1)
|
||||
(paged[current - i] = current - i) && count++;
|
||||
if (!paged[current + i] && (current + i) <= total)
|
||||
(paged[current + i] = current + i) && count++;
|
||||
i++;
|
||||
}
|
||||
|
||||
var previous,
|
||||
current;
|
||||
|
||||
for (var i in paged) {
|
||||
current = paged[i];
|
||||
if (previous === current - 2)
|
||||
pages.push(current - 1);
|
||||
else if (previous <= current - 3)
|
||||
pages.push('...');
|
||||
pages.push(current);
|
||||
previous = current;
|
||||
}
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
res.locals.paginate = function (count, current, path, extraItems, el) {
|
||||
current = current || 1;
|
||||
path = url.parse(path || req.url);
|
||||
el = el || 'li';
|
||||
|
||||
var pages = [],
|
||||
pageNums = generatePageNumbers(count, current, extraItems),
|
||||
pageNum;
|
||||
|
||||
if (current === 1)
|
||||
pages.push(page({path:path, display:'«', className:'disabled', el:el}));
|
||||
else
|
||||
pages.push(page({path:path, pageNum:current - 1, display:'«', el:el}));
|
||||
|
||||
for (var i = 0, l = pageNums.length; i < l; ++i) {
|
||||
pageNum = pageNums[i];
|
||||
|
||||
if (pageNum === current)
|
||||
pages.push(page({path:path, display:pageNum, className:'active', el:el}));
|
||||
else if (!parseInt(pageNum,10))
|
||||
pages.push(page({path:path, display:pageNum, el:el}));
|
||||
else
|
||||
pages.push(page({path:path, pageNum:pageNum, el:el}));
|
||||
}
|
||||
|
||||
if (current === count)
|
||||
pages.push(page({path:path, display:'»', className:'disabled', el:el}));
|
||||
else
|
||||
pages.push(page({path:path, pageNum:current + 1, display:'»', el:el}));
|
||||
|
||||
return pages.join('');
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
function extractMessageData (req) {
|
||||
var messages = {};
|
||||
var fields = {};
|
||||
|
|
168
openbadger.js
168
openbadger.js
|
@ -49,30 +49,135 @@ function normalizeProgram(program, id) {
|
|||
return program;
|
||||
}
|
||||
|
||||
function filterBadges(data, query) {
|
||||
// TO DO - We should probably be a little less naive about this, and make sure
|
||||
// that these values are from an allowed list
|
||||
var categories = [
|
||||
{label: 'Science', value: 'science'},
|
||||
{label: 'Technology', value: 'technology'},
|
||||
{label: 'Engineering', value: 'engineering'},
|
||||
{label: 'Art', value: 'art'},
|
||||
{label: 'Math', value: 'math'}
|
||||
];
|
||||
var ageRanges = [
|
||||
{label: 'Under 13', value: '0-13'},
|
||||
{label: '13-18', value: '13-18'},
|
||||
{label: '19-24', value: '19-24'}
|
||||
];
|
||||
var activityTypes = [
|
||||
{label: 'Online', value: 'online'},
|
||||
{label: 'Offline', value: 'offline'}
|
||||
];
|
||||
var badgeTypes = [
|
||||
{label: 'Participation', value: 'participation'},
|
||||
{label: 'Skill', value: 'skill'},
|
||||
{label: 'Activity', value: 'activity'}
|
||||
];
|
||||
var orgs = [];
|
||||
|
||||
var category = query.category,
|
||||
ageGroup = query.age,
|
||||
program = query.program;
|
||||
function updateOrgs (callback) {
|
||||
if (typeof callback !== 'function')
|
||||
callback = function () {};
|
||||
|
||||
data = _.filter(data, function(item) {
|
||||
if (category && !_.contains(item.categories, category))
|
||||
return false;
|
||||
openbadger.getOrgs(function (err, data) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (ageGroup && !_.contains(item.ageRange, ageGroup))
|
||||
return false;
|
||||
orgs = [];
|
||||
|
||||
if (program && item.program !== program)
|
||||
return false;
|
||||
(data.orgs || data.issuers).forEach(function (org) {
|
||||
orgs.push({
|
||||
label: org.name,
|
||||
value: org.shortname
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
orgs.sort(function(a, b) {
|
||||
var aVal = (a && a.label || '').toLowerCase().replace(/^\s*the\s+/, ''),
|
||||
bVal = (b && b.label || '').toLowerCase().replace(/^\s*the\s+/, '');
|
||||
|
||||
return aVal.localeCompare(bVal);
|
||||
});
|
||||
|
||||
callback(null, orgs);
|
||||
});
|
||||
}
|
||||
|
||||
function confirmFilterValue (value, list) {
|
||||
if (!value && value !== 0)
|
||||
return null;
|
||||
|
||||
for (var i = 0, l = list.length; i < l; ++i)
|
||||
if (list[i].value === value)
|
||||
return value;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function applyFilter (data, query) {
|
||||
return _.filter(data, function(item) {
|
||||
return _.reduce(query, function(memo, value, field) {
|
||||
if (!memo) // We've already failed a test - no point in continuing
|
||||
return memo;
|
||||
|
||||
if (!value && value !== 0)
|
||||
return memo;
|
||||
|
||||
var data = item;
|
||||
|
||||
if (field.indexOf('.') > -1) {
|
||||
var fieldParts = field.split('.').reverse();
|
||||
|
||||
while (data && fieldParts.length > 1) {
|
||||
data = data[fieldParts.pop()];
|
||||
}
|
||||
|
||||
field = fieldParts.reverse().join('.');
|
||||
}
|
||||
|
||||
var itemValue = data ? data[field] : null;
|
||||
|
||||
if (_.isArray(itemValue))
|
||||
return memo && _.contains(itemValue, value);
|
||||
|
||||
return memo && (itemValue === value);
|
||||
}, true);
|
||||
})
|
||||
}
|
||||
|
||||
function filterBadges (data, query) {
|
||||
var category = confirmFilterValue(query.category, categories),
|
||||
ageGroup = confirmFilterValue(query.age, ageRanges),
|
||||
badgeType = confirmFilterValue(query.type, badgeTypes),
|
||||
activityType = confirmFilterValue(query.activity, activityTypes);
|
||||
|
||||
if (!category && !ageGroup && !badgeType && !activityType)
|
||||
return data;
|
||||
|
||||
return applyFilter(data, {
|
||||
'categories': category,
|
||||
'ageRange': ageGroup,
|
||||
'badgeType': badgeType,
|
||||
'activityType': activityType
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function filterPrograms (data, query) {
|
||||
var category = confirmFilterValue(query.category, categories),
|
||||
org = confirmFilterValue(query.org, orgs),
|
||||
ageGroup = confirmFilterValue(query.age, ageRanges),
|
||||
activityType = confirmFilterValue(query.activity, activityTypes);
|
||||
|
||||
if (!category && !org && !ageGroup && !activityType)
|
||||
return data;
|
||||
|
||||
return applyFilter(data, {
|
||||
'categories': category,
|
||||
'issuer.shortname': org,
|
||||
'ageRange': ageGroup,
|
||||
'activityType': activityType
|
||||
});
|
||||
}
|
||||
|
||||
function getJWTToken(email) {
|
||||
var claims = {
|
||||
prn: email,
|
||||
|
@ -126,6 +231,7 @@ var openbadger = new Api(ENDPOINT, {
|
|||
});
|
||||
});
|
||||
},
|
||||
filters: filterPrograms,
|
||||
paginate: true,
|
||||
key: 'programs'
|
||||
},
|
||||
|
@ -168,8 +274,6 @@ var openbadger = new Api(ENDPOINT, {
|
|||
if (err)
|
||||
return callback(err, data);
|
||||
|
||||
|
||||
console.log(data);
|
||||
badges = _.map(data.badges, normalizeBadgeInstance)
|
||||
|
||||
return callback(null, {
|
||||
|
@ -229,4 +333,36 @@ var openbadger = new Api(ENDPOINT, {
|
|||
},
|
||||
});
|
||||
|
||||
updateOrgs();
|
||||
|
||||
module.exports = openbadger;
|
||||
module.exports.getFilters = function getFilters () {
|
||||
return {
|
||||
categories: {
|
||||
name: 'category',
|
||||
label: 'Category',
|
||||
options: categories
|
||||
},
|
||||
ageRanges: {
|
||||
name: 'age',
|
||||
label: 'Age',
|
||||
options: ageRanges
|
||||
},
|
||||
orgs: {
|
||||
name: 'org',
|
||||
label: 'Organization',
|
||||
options: orgs
|
||||
},
|
||||
activityTypes: {
|
||||
name: 'activity',
|
||||
label: 'Activity',
|
||||
options: activityTypes
|
||||
},
|
||||
badgeTypes: {
|
||||
name: 'type',
|
||||
label: 'Type',
|
||||
options: badgeTypes
|
||||
}
|
||||
};
|
||||
}
|
||||
module.exports.updateOrgs = updateOrgs;
|
|
@ -72,7 +72,7 @@
|
|||
$template = $(options.template),
|
||||
$buttons = $(options.buttonContainer),
|
||||
$description = $(document.createElement('div')),
|
||||
$label = $(document.createElement('label')),
|
||||
$btn = $(document.createElement('span')),
|
||||
itemSelector = '.' + $template[0].className.replace(/\s+/g, '.'),
|
||||
itemCount = 0,
|
||||
xhr = (window.XMLHttpRequest && new XMLHttpRequest()) || {};
|
||||
|
@ -81,16 +81,20 @@
|
|||
|
||||
$template.remove();
|
||||
|
||||
$template.find('input[type="file"]').attr('tabIndex', -1);
|
||||
|
||||
$description
|
||||
.addClass('description')
|
||||
.html('<span><strong>Drop photos and videos here</strong> <em>or</em></span>')
|
||||
.prependTo($template);
|
||||
|
||||
$label
|
||||
$btn
|
||||
.addClass('btn')
|
||||
.text('Choose photos and videos to upload')
|
||||
.appendTo($description)
|
||||
.click(function() { $(this).parents('.item').find('input').click(); });
|
||||
.attr('tabIndex', 0)
|
||||
.click(function() { $(this).parents('.item').find('input').click(); })
|
||||
.keypress(function() { $(this).click(); });
|
||||
|
||||
if (xhr.upload && window.FormData) {
|
||||
goAsync();
|
||||
|
|
|
@ -5,26 +5,25 @@
|
|||
{% block filter %}
|
||||
{% if filters %}
|
||||
<div class="navbar filter">
|
||||
<form class="navbar-inner navbar-form form-inline" method="get">
|
||||
<form class="navbar-inner navbar-form form-inline text-center" method="get">
|
||||
{% for filter in filters %}
|
||||
<label for="filter-{{ filter.name }}" class="filter-icon filter-{{ filter.name }}">{{ filter.label }}</label>
|
||||
{% if filter.options %}
|
||||
<select id="filter-{{ filter.name }}" name="{{ filter.name }}" class="input-medium{% if filter.class %} {{ filter.class }}{% endif %}">
|
||||
<option></option>
|
||||
{% if filter.is_grouped %}
|
||||
{% for label, options in filter.options %}
|
||||
<optgroup label="{{ label }}">
|
||||
{% for value, label in options %}
|
||||
<option value="{{ value }}"{% if filter.selected == value %} selected="selected"{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</optgroup>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<option selected="selected" value="{{ value }}">{{ filter.name }}</option>
|
||||
{% for value, label in filter.options %}
|
||||
<option value="{{ value }}"{% if filter.selected == value %} selected="selected"{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if filter.is_grouped %}
|
||||
{% for label, options in filter.options %}
|
||||
<optgroup label="{{ label }}">
|
||||
{% for value, label in options %}
|
||||
<option value="{{ value }}"{% if filter.selected == value %} selected="selected"{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</optgroup>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for item in filter.options %}
|
||||
<option value="{{ item.value }}"{% if filter.selected == item.value %} selected="selected"{% endif %}>{{ item.label }}</option>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</select>
|
||||
{% else %}
|
||||
<input type="{{ filter.type | default('text') }}" id="filter-{{ filter.name }}" name="{{ filter.name }}" class="input-medium{% if filter.class %} {{ filter.class }}{% endif %}"{% if filter.value %} value="{{ filter.value }}"{% endif %}>
|
||||
|
@ -36,62 +35,33 @@
|
|||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block list_wrapper %}
|
||||
{% if items.length %}
|
||||
<ul class="thumbnails">
|
||||
{% block list %}
|
||||
{% for item in items %}
|
||||
{% block item %}
|
||||
<li class="span3">
|
||||
{% if item.template %}
|
||||
{% include item.template %}
|
||||
{% else %}
|
||||
<figure class="thumbnail">
|
||||
<a href="{{ item.url }}"><img src="{{ item.image }}"></a>
|
||||
<figcaption class="caption">
|
||||
<p>{{ item.name }}</p>
|
||||
<p class="description">{{ item.description | default('description tbd') | truncate(20) }}</p>
|
||||
{% block item_actions_wrapper %}
|
||||
<p class="text-right">
|
||||
{% block item_actions %}
|
||||
<a href="{{ item.url }}" class="btn">Details</a>
|
||||
{% endblock %}
|
||||
</p>
|
||||
{% endblock %}
|
||||
</figcaption>
|
||||
</figure>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{% block no_data %}
|
||||
No data found.
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
{% if items.length %}
|
||||
<ul class="thumbnails">
|
||||
{% block list %}
|
||||
{% for item in items %}
|
||||
{% block item %}
|
||||
<li class="span3">
|
||||
{% if item.template %}
|
||||
{% include item.template %}
|
||||
{% else %}
|
||||
{% include "includes/badge-thumbnail.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{% block no_data %}
|
||||
No data found.
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block pagination %}
|
||||
{% if pages > 1 %}
|
||||
<nav class="pagination pagination-centered">
|
||||
<ul>
|
||||
{% if page == 1 %}
|
||||
<li class="disabled"><span>«</span></li>
|
||||
{% else %}
|
||||
<li><a href="?page={{ page - 1 }}">«</a></li>
|
||||
{% endif %}
|
||||
{% for pageNum in range(1, (pages + 1)) %}
|
||||
{% if page == pageNum %}
|
||||
<li class="disabled"><span>{{ pageNum }}</span></li>
|
||||
{% else %}
|
||||
<li><a href="?page={{ pageNum }}">{{ pageNum }}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if page == pages %}
|
||||
<li class="disabled"><span>»</span></li>
|
||||
{% else %}
|
||||
<li><a href="?page={{ page + 1 }}">»</a></li>
|
||||
{% endif %}
|
||||
{{ paginate(pages, page) }}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{# A badge in a list or grid context #}
|
||||
<figure class="thumbnail">
|
||||
<a href="{{ item.url }}"><img src="{{ item.image }}"></a>
|
||||
<figcaption class="caption">
|
||||
<p>{{ item.name }}</p>
|
||||
{% if item.description %}
|
||||
<p class="description">{{ item.description | truncate(20) }}</p>
|
||||
{% endif %}
|
||||
{% block item_actions_wrapper %}
|
||||
<p class="text-right">
|
||||
{% block item_actions %}
|
||||
<a href="{{ item.url }}" class="btn">Details</a>
|
||||
{% endblock %}
|
||||
</p>
|
||||
{% endblock %}
|
||||
</figcaption>
|
||||
</figure>
|
|
@ -3,45 +3,46 @@
|
|||
{% set navItem = 'backpack' %}
|
||||
|
||||
{% block list %}
|
||||
{{ super() }}
|
||||
<li class="span3">
|
||||
<figure class="thumbnail">
|
||||
<a href="/claim"><img src="/media/img/add-badge.png"></a>
|
||||
<figcaption class="caption">
|
||||
<p>Claim another badge!</p>
|
||||
<p class="text-right"><a href="/claim" class="btn">+1</a></p>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</li>
|
||||
{{ super() }}
|
||||
<li class="span3">
|
||||
<figure class="thumbnail">
|
||||
<a href="/claim"><img src="/media/img/add-badge.png"></a>
|
||||
<figcaption class="caption">
|
||||
<p>Claim another badge!</p>
|
||||
<p class="text-right"><a href="/claim" class="btn">+1</a></p>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block item_actions %}
|
||||
{{ super() }}
|
||||
<a href="#" title="Delete this badge"><i class="icon-trash"></i></a>
|
||||
<a class="btn show-tooltip" title="Add to your favorites" href="{{ item.url }}/favorite"><i class="icon-heart"></i></a>
|
||||
{#
|
||||
{{ super() }}
|
||||
<a class="btn show-tooltip" title="Add to your favorites" href="{{ item.url }}/favorite"><i class="icon-heart"></i></a>
|
||||
#}
|
||||
{% endblock %}
|
||||
|
||||
{% block modal %}
|
||||
<!-- Modal -->
|
||||
<div id="shareModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Share This Badge</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<a href="#" class="btn btn-block"><i class="icon-twitter"></i> Twitter</a>
|
||||
<a href="#" class="btn btn-block"><i class="icon-facebook"></i> Facebook</a>
|
||||
<a href="#" class="btn btn-block">Mozilla Backpack</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="trashModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Are you sure you want to delete this badge?</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<a href="#" class="btn btn-block">Cancel</a>
|
||||
<a href="#" class="btn btn-block">Yes</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal -->
|
||||
<div id="shareModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Share This Badge</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<a href="#" class="btn btn-block"><i class="icon-twitter"></i> Twitter</a>
|
||||
<a href="#" class="btn btn-block"><i class="icon-facebook"></i> Facebook</a>
|
||||
<a href="#" class="btn btn-block">Mozilla Backpack</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="trashModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Are you sure you want to delete this badge?</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<a href="#" class="btn btn-block">Cancel</a>
|
||||
<a href="#" class="btn btn-block">Yes</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -8,8 +8,36 @@
|
|||
<img src="{{ badge.image }}">
|
||||
</div>
|
||||
<div class="span8">
|
||||
<h3>What is this badge about?</h3>
|
||||
<p>{{ badge.description }}</p>
|
||||
<p>
|
||||
<b>Issued by:</b>
|
||||
{% if badge.program.issuer.url %}
|
||||
<a href="{{ badge.program.issuer.url }}">
|
||||
{% endif %}
|
||||
{{ badge.program.issuer.name }}
|
||||
{% if badge.program.issuer.url %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Issued to:</b> {{ user.email }}
|
||||
</p>
|
||||
|
||||
<h3>What is this badge about?</h3>
|
||||
<p>{{ badge.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<h3>Similar Badges</h3>
|
||||
<p>Cupcake <a href="#">link to Badges page</a> something about badges
|
||||
should go here.</p>
|
||||
|
||||
{% for item in similar %}
|
||||
<div class="span3">
|
||||
{% include "includes/badge-thumbnail.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
Загрузка…
Ссылка в новой задаче