Merge branch 'master' into test_submission_views_365

This commit is contained in:
Chris Moylan 2011-07-05 21:42:18 -05:00
commit f0b497ec5a
53 changed files with 1314 additions and 649 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ mediagoblin.egg-info
*.pyo
docs/_build/
user_dev/
mediagoblin_user.ini
server-log.txt
*~
*.swp

View File

@ -48,9 +48,9 @@ copyright = u'2011, Free Software Foundation, Inc and contributors'
# built documents.
#
# The short X.Y version.
version = '0.0.2'
version = '0.0.3'
# The full version, including alpha/beta/rc tags.
release = '0.0.2'
release = '0.0.3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@ -150,7 +150,7 @@ celeryd in another window.
Run::
CELERY_CONFIG_MODULE=mediagoblin.celery_setup.from_celery ./bin/celeryd
CELERY_CONFIG_MODULE=mediagoblin.init.celery.from_celery ./bin/celeryd
Running the test suite

View File

@ -16,6 +16,22 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
if [ "$1" = "-h" ]
then
echo "$0 [-h] [-c paste.ini] ARGS_to_paster"
echo " For example:"
echo " $0 -c fcgi.ini port_number=23371"
exit 1
fi
PASTE_INI=paste.ini
if [ "$1" = "-c" ]
then
PASTE_INI="$2"
shift
shift
fi
if [ -f ./bin/paster ]; then
echo "Using ./bin/paster";
export PASTER="./bin/paster";
@ -27,4 +43,5 @@ else
exit 1
fi
CELERY_ALWAYS_EAGER=true $PASTER serve paste.ini --reload
set -x
CELERY_ALWAYS_EAGER=true $PASTER serve $PASTE_INI "$@" --reload

57
maketarball.sh Executable file
View File

@ -0,0 +1,57 @@
#!/bin/bash
# usage: maketarball
# maketarball <tag>
#
# With no arguments, this creates a source tarball from git master with a
# filename based on today's date.
#
# With a <tag> argument, this creates a tarball of the tag.
#
# Examples:
#
# ./maketarball
# ./maketarball v0.0.2
NOWDATE=`date "+%Y-%m-%d"`
if [ -z "$1" ]
then
REVISH=master
PREFIX="$NOWDATE-$REVISH"
else
REVISH=$1
PREFIX="$REVISH"
fi
# convert PREFIX to all lowercase.
# nix the v from tag names.
PREFIX=`echo "$PREFIX" | tr '[A-Z]' '[a-z]' | sed s/v//`
echo "== REVISH $REVISH"
echo "== PREFIX $PREFIX"
echo ""
echo "generating archive...."
git archive \
--format=tar \
--prefix=mediagoblin-$PREFIX/ \
$REVISH > mediagoblin-$PREFIX.tar
if [[ $? -ne 0 ]]
then
echo "git archive command failed. See above text for reason."
if [[ -e mediagoblin-$PREFIX.tar ]]
then
rm mediagoblin-$PREFIX.tar
fi
exit 1;
fi
echo "compressing...."
gzip mediagoblin-$PREFIX.tar
echo "archive at mediagoblin-$PREFIX.tar.gz"
echo "done."

View File

@ -21,11 +21,12 @@ import routes
from webob import Request, exc
from mediagoblin import routing, util, storage, staticdirect
from mediagoblin.config import (
from mediagoblin.init.config import (
read_mediagoblin_config, generate_validation_report)
from mediagoblin.db.open import setup_connection_and_db_from_config
from mediagoblin.mg_globals import setup_globals
from mediagoblin.celery_setup import setup_celery_from_config
from mediagoblin.init.celery import setup_celery_from_config
from mediagoblin.init import get_jinja_loader
from mediagoblin.workbench import WorkbenchManager
@ -71,7 +72,7 @@ class MediaGoblinApp(object):
app_config)
# Get the template environment
self.template_loader = util.get_jinja_loader(
self.template_loader = get_jinja_loader(
app_config.get('user_template_path'))
# Set up storage systems

View File

@ -1,357 +0,0 @@
/*
960 Grid System ~ Core CSS.
Learn more ~ http://960.gs/
Licensed under GPL and MIT.
*/
/*
Forces backgrounds to span full width,
even if there is horizontal scrolling.
Increase this if your layout is wider.
Note: IE6 works fine without this fix.
*/
body {
min-width: 960px;
}
/* `Container
----------------------------------------------------------------------------------------------------*/
.container_12 {
margin-left: auto;
margin-right: auto;
width: 960px;
}
/* `Grid >> Global
----------------------------------------------------------------------------------------------------*/
.grid_1,
.grid_2,
.grid_3,
.grid_4,
.grid_5,
.grid_6,
.grid_7,
.grid_8,
.grid_9,
.grid_10,
.grid_11,
.grid_12 {
display: inline;
float: left;
margin-left: 10px;
margin-right: 10px;
}
.push_1, .pull_1,
.push_2, .pull_2,
.push_3, .pull_3,
.push_4, .pull_4,
.push_5, .pull_5,
.push_6, .pull_6,
.push_7, .pull_7,
.push_8, .pull_8,
.push_9, .pull_9,
.push_10, .pull_10,
.push_11, .pull_11 {
position: relative;
}
/* `Grid >> Children (Alpha ~ First, Omega ~ Last)
----------------------------------------------------------------------------------------------------*/
.alpha {
margin-left: 0;
}
.omega {
margin-right: 0;
}
/* `Grid >> 12 Columns
----------------------------------------------------------------------------------------------------*/
.container_12 .grid_1 {
width: 60px;
}
.container_12 .grid_2 {
width: 140px;
}
.container_12 .grid_3 {
width: 220px;
}
.container_12 .grid_4 {
width: 300px;
}
.container_12 .grid_5 {
width: 380px;
}
.container_12 .grid_6 {
width: 460px;
}
.container_12 .grid_7 {
width: 540px;
}
.container_12 .grid_8 {
width: 620px;
}
.container_12 .grid_9 {
width: 700px;
}
.container_12 .grid_10 {
width: 780px;
}
.container_12 .grid_11 {
width: 860px;
}
.container_12 .grid_12 {
width: 940px;
}
/* `Prefix Extra Space >> 12 Columns
----------------------------------------------------------------------------------------------------*/
.container_12 .prefix_1 {
padding-left: 80px;
}
.container_12 .prefix_2 {
padding-left: 160px;
}
.container_12 .prefix_3 {
padding-left: 240px;
}
.container_12 .prefix_4 {
padding-left: 320px;
}
.container_12 .prefix_5 {
padding-left: 400px;
}
.container_12 .prefix_6 {
padding-left: 480px;
}
.container_12 .prefix_7 {
padding-left: 560px;
}
.container_12 .prefix_8 {
padding-left: 640px;
}
.container_12 .prefix_9 {
padding-left: 720px;
}
.container_12 .prefix_10 {
padding-left: 800px;
}
.container_12 .prefix_11 {
padding-left: 880px;
}
/* `Suffix Extra Space >> 12 Columns
----------------------------------------------------------------------------------------------------*/
.container_12 .suffix_1 {
padding-right: 80px;
}
.container_12 .suffix_2 {
padding-right: 160px;
}
.container_12 .suffix_3 {
padding-right: 240px;
}
.container_12 .suffix_4 {
padding-right: 320px;
}
.container_12 .suffix_5 {
padding-right: 400px;
}
.container_12 .suffix_6 {
padding-right: 480px;
}
.container_12 .suffix_7 {
padding-right: 560px;
}
.container_12 .suffix_8 {
padding-right: 640px;
}
.container_12 .suffix_9 {
padding-right: 720px;
}
.container_12 .suffix_10 {
padding-right: 800px;
}
.container_12 .suffix_11 {
padding-right: 880px;
}
/* `Push Space >> 12 Columns
----------------------------------------------------------------------------------------------------*/
.container_12 .push_1 {
left: 80px;
}
.container_12 .push_2 {
left: 160px;
}
.container_12 .push_3 {
left: 240px;
}
.container_12 .push_4 {
left: 320px;
}
.container_12 .push_5 {
left: 400px;
}
.container_12 .push_6 {
left: 480px;
}
.container_12 .push_7 {
left: 560px;
}
.container_12 .push_8 {
left: 640px;
}
.container_12 .push_9 {
left: 720px;
}
.container_12 .push_10 {
left: 800px;
}
.container_12 .push_11 {
left: 880px;
}
/* `Pull Space >> 12 Columns
----------------------------------------------------------------------------------------------------*/
.container_12 .pull_1 {
left: -80px;
}
.container_12 .pull_2 {
left: -160px;
}
.container_12 .pull_3 {
left: -240px;
}
.container_12 .pull_4 {
left: -320px;
}
.container_12 .pull_5 {
left: -400px;
}
.container_12 .pull_6 {
left: -480px;
}
.container_12 .pull_7 {
left: -560px;
}
.container_12 .pull_8 {
left: -640px;
}
.container_12 .pull_9 {
left: -720px;
}
.container_12 .pull_10 {
left: -800px;
}
.container_12 .pull_11 {
left: -880px;
}
/* `Clear Floated Elements
----------------------------------------------------------------------------------------------------*/
/* http://sonspring.com/journal/clearing-floats */
.clear {
clear: both;
display: block;
overflow: hidden;
visibility: hidden;
width: 0;
height: 0;
}
/* http://www.yuiblog.com/blog/2010/09/27/clearfix-reloaded-overflowhidden-demystified */
.clearfix:before,
.clearfix:after,
.container_12:before,
.container_12:after {
content: '.';
display: block;
overflow: hidden;
visibility: hidden;
font-size: 0;
line-height: 0;
width: 0;
height: 0;
}
.clearfix:after,
.container_12:after {
clear: both;
}
/*
The following zoom:1 rule is specifically for IE6 + IE7.
Move to separate stylesheet if invalid CSS is a problem.
*/
.clearfix,
.container_12 {
zoom: 1;
}

View File

@ -0,0 +1,447 @@
/*
960 Grid System ~ Core CSS.
Learn more ~ http://960.gs/
Licensed under GPL and MIT.
*/
/*
Forces backgrounds to span full width,
even if there is horizontal scrolling.
Increase this if your layout is wider.
Note: IE6 works fine without this fix.
*/
body {
min-width: 960px;
}
/* Container
----------------------------------------------------------------------------------------------------*/
.container_16 {
margin-left: auto;
margin-right: auto;
width: 960px;
}
/* Grid >> Global
----------------------------------------------------------------------------------------------------*/
.grid_1,
.grid_2,
.grid_3,
.grid_4,
.grid_5,
.grid_6,
.grid_7,
.grid_8,
.grid_9,
.grid_10,
.grid_11,
.grid_12,
.grid_13,
.grid_14,
.grid_15,
.grid_16 {
display: inline;
float: left;
position: relative;
margin-left: 10px;
margin-right: 10px;
}
.push_1, .pull_1,
.push_2, .pull_2,
.push_3, .pull_3,
.push_4, .pull_4,
.push_5, .pull_5,
.push_6, .pull_6,
.push_7, .pull_7,
.push_8, .pull_8,
.push_9, .pull_9,
.push_10, .pull_10,
.push_11, .pull_11,
.push_12, .pull_12,
.push_13, .pull_13,
.push_14, .pull_14,
.push_15, .pull_15,
.push_16, .pull_16 {
position: relative;
}
/* Grid >> Children (Alpha ~ First, Omega ~ Last)
----------------------------------------------------------------------------------------------------*/
.alpha {
margin-left: 0;
}
.omega {
margin-right: 0;
}
/* Grid >> 16 Columns
----------------------------------------------------------------------------------------------------*/
.container_16 .grid_1 {
width: 40px;
}
.container_16 .grid_2 {
width: 100px;
}
.container_16 .grid_3 {
width: 160px;
}
.container_16 .grid_4 {
width: 220px;
}
.container_16 .grid_5 {
width: 280px;
}
.container_16 .grid_6 {
width: 340px;
}
.container_16 .grid_7 {
width: 400px;
}
.container_16 .grid_8 {
width: 460px;
}
.container_16 .grid_9 {
width: 520px;
}
.container_16 .grid_10 {
width: 580px;
}
.container_16 .grid_11 {
width: 640px;
}
.container_16 .grid_12 {
width: 700px;
}
.container_16 .grid_13 {
width: 760px;
}
.container_16 .grid_14 {
width: 820px;
}
.container_16 .grid_15 {
width: 880px;
}
.container_16 .grid_16 {
width: 940px;
}
/* Prefix Extra Space >> 16 Columns
----------------------------------------------------------------------------------------------------*/
.container_16 .prefix_1 {
padding-left: 60px;
}
.container_16 .prefix_2 {
padding-left: 120px;
}
.container_16 .prefix_3 {
padding-left: 180px;
}
.container_16 .prefix_4 {
padding-left: 240px;
}
.container_16 .prefix_5 {
padding-left: 300px;
}
.container_16 .prefix_6 {
padding-left: 360px;
}
.container_16 .prefix_7 {
padding-left: 420px;
}
.container_16 .prefix_8 {
padding-left: 480px;
}
.container_16 .prefix_9 {
padding-left: 540px;
}
.container_16 .prefix_10 {
padding-left: 600px;
}
.container_16 .prefix_11 {
padding-left: 660px;
}
.container_16 .prefix_12 {
padding-left: 720px;
}
.container_16 .prefix_13 {
padding-left: 780px;
}
.container_16 .prefix_14 {
padding-left: 840px;
}
.container_16 .prefix_15 {
padding-left: 900px;
}
/* Suffix Extra Space >> 16 Columns
----------------------------------------------------------------------------------------------------*/
.container_16 .suffix_1 {
padding-right: 60px;
}
.container_16 .suffix_2 {
padding-right: 120px;
}
.container_16 .suffix_3 {
padding-right: 180px;
}
.container_16 .suffix_4 {
padding-right: 240px;
}
.container_16 .suffix_5 {
padding-right: 300px;
}
.container_16 .suffix_6 {
padding-right: 360px;
}
.container_16 .suffix_7 {
padding-right: 420px;
}
.container_16 .suffix_8 {
padding-right: 480px;
}
.container_16 .suffix_9 {
padding-right: 540px;
}
.container_16 .suffix_10 {
padding-right: 600px;
}
.container_16 .suffix_11 {
padding-right: 660px;
}
.container_16 .suffix_12 {
padding-right: 720px;
}
.container_16 .suffix_13 {
padding-right: 780px;
}
.container_16 .suffix_14 {
padding-right: 840px;
}
.container_16 .suffix_15 {
padding-right: 900px;
}
/* Push Space >> 16 Columns
----------------------------------------------------------------------------------------------------*/
.container_16 .push_1 {
left: 60px;
}
.container_16 .push_2 {
left: 120px;
}
.container_16 .push_3 {
left: 180px;
}
.container_16 .push_4 {
left: 240px;
}
.container_16 .push_5 {
left: 300px;
}
.container_16 .push_6 {
left: 360px;
}
.container_16 .push_7 {
left: 420px;
}
.container_16 .push_8 {
left: 480px;
}
.container_16 .push_9 {
left: 540px;
}
.container_16 .push_10 {
left: 600px;
}
.container_16 .push_11 {
left: 660px;
}
.container_16 .push_12 {
left: 720px;
}
.container_16 .push_13 {
left: 780px;
}
.container_16 .push_14 {
left: 840px;
}
.container_16 .push_15 {
left: 900px;
}
/* Pull Space >> 16 Columns
----------------------------------------------------------------------------------------------------*/
.container_16 .pull_1 {
left: -60px;
}
.container_16 .pull_2 {
left: -120px;
}
.container_16 .pull_3 {
left: -180px;
}
.container_16 .pull_4 {
left: -240px;
}
.container_16 .pull_5 {
left: -300px;
}
.container_16 .pull_6 {
left: -360px;
}
.container_16 .pull_7 {
left: -420px;
}
.container_16 .pull_8 {
left: -480px;
}
.container_16 .pull_9 {
left: -540px;
}
.container_16 .pull_10 {
left: -600px;
}
.container_16 .pull_11 {
left: -660px;
}
.container_16 .pull_12 {
left: -720px;
}
.container_16 .pull_13 {
left: -780px;
}
.container_16 .pull_14 {
left: -840px;
}
.container_16 .pull_15 {
left: -900px;
}
/* `Clear Floated Elements
----------------------------------------------------------------------------------------------------*/
/* http://sonspring.com/journal/clearing-floats */
.clear {
clear: both;
display: block;
overflow: hidden;
visibility: hidden;
width: 0;
height: 0;
}
/* http://www.yuiblog.com/blog/2010/09/27/clearfix-reloaded-overflowhidden-demystified */
.clearfix:before,
.clearfix:after,
.container_16:before,
.container_16:after {
content: '.';
display: block;
overflow: hidden;
visibility: hidden;
font-size: 0;
line-height: 0;
width: 0;
height: 0;
}
.clearfix:after,
.container_16:after {
clear: both;
}
/*
The following zoom:1 rule is specifically for IE6 + IE7.
Move to separate stylesheet if invalid CSS is a problem.
*/
.clearfix,
.container_16 {
zoom: 1;
}

View File

@ -0,0 +1,202 @@
/* `XHTML, HTML4, HTML5 Reset
----------------------------------------------------------------------------------------------------*/
a,
abbr,
acronym,
address,
applet,
article,
aside,
audio,
b,
big,
blockquote,
body,
canvas,
caption,
center,
cite,
code,
dd,
del,
details,
dfn,
dialog,
div,
dl,
dt,
em,
embed,
fieldset,
figcaption,
figure,
font,
footer,
form,
h1,
h2,
h3,
h4,
h5,
h6,
header,
hgroup,
hr,
html,
i,
iframe,
img,
ins,
kbd,
label,
legend,
li,
mark,
menu,
meter,
nav,
object,
ol,
output,
p,
pre,
progress,
q,
rp,
rt,
ruby,
s,
samp,
section,
small,
span,
strike,
strong,
sub,
summary,
sup,
table,
tbody,
td,
tfoot,
th,
thead,
time,
tr,
tt,
u,
ul,
var,
video,
xmp {
border: 0;
margin: 0;
padding: 0;
font-size: 100%;
}
html,
body {
height: 100%;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
/*
Override the default (display: inline) for
browsers that do not recognize HTML5 tags.
IE8 (and lower) requires a shiv:
http://ejohn.org/blog/html5-shiv
*/
display: block;
}
b,
strong {
/*
Makes browsers agree.
IE + Opera = font-weight: bold.
Gecko + WebKit = font-weight: bolder.
*/
font-weight: bold;
}
img {
color: transparent;
font-size: 0;
vertical-align: middle;
/*
For IE.
http://css-tricks.com/ie-fix-bicubic-scaling-for-images
*/
-ms-interpolation-mode: bicubic;
}
li {
/*
For IE6 + IE7.
*/
display: list-item;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
th,
td,
caption {
font-weight: normal;
vertical-align: top;
text-align: left;
}
q {
quotes: none;
}
q:before,
q:after {
content: '';
content: none;
}
sub,
sup,
small {
font-size: 75%;
}
sub,
sup {
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
svg {
/*
For IE9.
*/
overflow: hidden;
}

View File

@ -0,0 +1,86 @@
/*
960 Grid System ~ Text CSS.
Learn more ~ http://960.gs/
Licensed under GPL and MIT.
*/
/* `Basic HTML
----------------------------------------------------------------------------------------------------*/
body {
font: 13px/1.5 'Helvetica Neue', Arial, 'Liberation Sans', FreeSans, sans-serif;
}
pre,
code {
font-family: 'DejaVu Sans Mono', Monaco, Consolas, monospace;
}
hr {
border: 0 #ccc solid;
border-top-width: 1px;
clear: both;
height: 0;
}
/* `Headings
----------------------------------------------------------------------------------------------------*/
h1 {
font-size: 25px;
}
h2 {
font-size: 23px;
}
h3 {
font-size: 21px;
}
h4 {
font-size: 19px;
}
h5 {
font-size: 17px;
}
h6 {
font-size: 15px;
}
/* `Spacing
----------------------------------------------------------------------------------------------------*/
ol {
list-style: decimal;
}
ul {
list-style: disc;
}
li {
margin-left: 30px;
}
p,
dl,
hr,
h1,
h2,
h3,
h4,
h5,
h6,
ol,
ul,
pre,
table,
address,
fieldset,
figure {
margin-bottom: 20px;
}

View File

@ -13,3 +13,49 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Database Abstraction/Wrapper Layer
==================================
**NOTE from Chris Webber:** I asked Elrond to explain why he put
ASCENDING and DESCENDING in db/util.py when we could just import from
pymongo. Read beow for why, but note that nobody is actually doing
this and there's no proof that we'll ever support more than
MongoDB... it would be a huge amount of work to do so.
If you really want to prove that possible, jump on IRC and talk to
us about making such a branch. In the meanwhile, it doesn't hurt to
have things as they are... if it ever makes it hard for us to
actually do things, we might revisit or remove this. But for more
information, read below.
This submodule is for most of the db specific stuff.
There are two main ideas here:
1. Open up a small possibility to replace mongo by another
db. This means, that all direct mongo accesses should
happen in the db submodule. While all the rest uses an
API defined by this submodule.
Currently this API happens to be basicly mongo.
Which means, that the abstraction/wrapper layer is
extremely thin.
2. Give the rest of the app a simple and easy way to get most of
their db needs. Which often means some simple import
from db.util.
What does that mean?
* Never import mongo directly outside of this submodule.
* Inside this submodule you can do whatever is needed. The
API border is exactly at the submodule layer. Nowhere
else.
* helper functions can be moved in here. They become part
of the db.* API
"""

View File

@ -22,8 +22,7 @@ from mediagoblin import util
from mediagoblin.auth import lib as auth_lib
from mediagoblin import mg_globals
from mediagoblin.db import migrations
from mediagoblin.db.util import DESCENDING, ObjectId
from mediagoblin.util import Pagination
from mediagoblin.db.util import ASCENDING, DESCENDING, ObjectId
###################
# Custom validators
@ -109,24 +108,13 @@ class MediaEntry(Document):
migration_handler = migrations.MediaEntryMigration
def get_comments(self):
return self.db.MediaComment.find({
'media_entry': self['_id']}).sort('created', DESCENDING)
def main_mediafile(self):
pass
def get_comments(self, page):
cursor = self.db.MediaComment.find({
'media_entry': self['_id']}).sort('created', DESCENDING)
pagination = Pagination(page, cursor)
comments = pagination()
data = list()
for comment in comments:
comment['author'] = self.db.User.find_one({
'_id': comment['author']})
data.append(comment)
return (data, pagination)
def generate_slug(self):
self['slug'] = util.slugify(self['title'])
@ -154,6 +142,32 @@ class MediaEntry(Document):
'mediagoblin.user_pages.media_home',
user=uploader['username'],
media=unicode(self['_id']))
def url_to_prev(self, urlgen):
"""
Provide a url to the previous entry from this user, if there is one
"""
cursor = self.db.MediaEntry.find({'_id' : {"$lt": self['_id']},
'uploader': self['uploader']}).sort(
'_id', DESCENDING).limit(1)
if cursor.count():
return urlgen('mediagoblin.user_pages.media_home',
user=self.uploader()['username'],
media=unicode(cursor[0]['_id']))
def url_to_next(self, urlgen):
"""
Provide a url to the next entry from this user, if there is one
"""
cursor = self.db.MediaEntry.find({'_id' : {"$gt": self['_id']},
'uploader': self['uploader']}).sort(
'_id', ASCENDING).limit(1)
if cursor.count():
return urlgen('mediagoblin.user_pages.media_home',
user=self.uploader()['username'],
media=unicode(cursor[0]['_id']))
def uploader(self):
return self.db.User.find_one({'_id': self['uploader']})

View File

@ -30,7 +30,7 @@ document relevant to here:
import copy
# Imports that other modules might use
from pymongo import DESCENDING
from pymongo import ASCENDING, DESCENDING
from pymongo.errors import InvalidId
from mongokit import ObjectId

View File

@ -17,6 +17,7 @@
from webob import exc
from mediagoblin import messages
from mediagoblin.util import render_to_response, redirect, clean_html
from mediagoblin.edit import forms
from mediagoblin.edit.lib import may_edit_media
@ -63,6 +64,14 @@ def edit_media(request, media):
return redirect(request, "mediagoblin.user_pages.media_home",
user=media.uploader()['username'], media=media['slug'])
if request.user['is_admin'] \
and media['uploader'] != request.user['_id'] \
and request.method != 'POST':
messages.add_message(
request, messages.WARNING,
"You are editing another user's media. Proceed with caution.")
return render_to_response(
request,
'mediagoblin/edit/edit.html',
@ -73,7 +82,18 @@ def edit_media(request, media):
@require_active_login
def edit_profile(request):
user = request.user
# admins may edit any user profile given a username in the querystring
edit_username = request.GET.get('username')
if request.user['is_admin'] and request.user['username'] != edit_username:
user = request.db.User.find_one({'username': edit_username})
# No need to warn again if admin just submitted an edited profile
if request.method != 'POST':
messages.add_message(
request, messages.WARNING,
"You are editing a user's profile. Proceed with caution.")
else:
user = request.user
form = forms.EditProfileForm(request.POST,
url = user.get('url'),
bio = user.get('bio'))
@ -83,7 +103,12 @@ def edit_profile(request):
user['bio'] = request.POST['bio']
user.save()
return redirect(request, "index", user=user['username'])
messages.add_message(request,
messages.SUCCESS,
'Profile edited!')
return redirect(request,
"mediagoblin.edit.profile",
username=edit_username)
return render_to_response(
request,

View File

@ -1,4 +1,3 @@
{#
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011 Free Software Foundation, Inc
#
@ -14,24 +13,21 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% extends "mediagoblin/base.html" %}
{% block mediagoblin_content %}
{# temporarily, an "image gallery" that isn't one really ;) #}
{% if media %}
<div class="grid_8 alpha media_image">
<img src="{{ request.app.public_store.file_url(
media.media_files.main) }}" />
<h1>Media details for {{media.title}}</h1>
<p>
<br/>Uploaded: {{ media.created}}
<br/>Description: {{media.description}}
</p>
</div>
<div class="grid_4 omega sidebar">
<p>Uploaded: {{ media.created}}</p>
</div>
{% else %}
<p>Sorry, no such media found.<p/>
{% endif %}
{% endblock %}
import jinja2
def get_jinja_loader(user_template_path=None):
"""
Set up the Jinja template loaders, possibly allowing for user
overridden templates.
(In the future we may have another system for providing theming;
for now this is good enough.)
"""
if user_template_path:
return jinja2.ChoiceLoader(
[jinja2.FileSystemLoader(user_template_path),
jinja2.PackageLoader('mediagoblin', 'templates')])
else:
return jinja2.PackageLoader('mediagoblin', 'templates')

View File

@ -20,7 +20,7 @@ import sys
MANDATORY_CELERY_IMPORTS = ['mediagoblin.process_media']
DEFAULT_SETTINGS_MODULE = 'mediagoblin.celery_setup.dummy_settings_module'
DEFAULT_SETTINGS_MODULE = 'mediagoblin.init.celery.dummy_settings_module'
def setup_celery_from_config(app_config, global_config,

View File

@ -17,7 +17,7 @@
import os
from mediagoblin import app, mg_globals
from mediagoblin.celery_setup import setup_celery_from_config
from mediagoblin.init.celery import setup_celery_from_config
OUR_MODULENAME = __name__

View File

@ -17,7 +17,7 @@
import os
from mediagoblin.tests.tools import TEST_APP_CONFIG
from mediagoblin.celery_setup.from_celery import setup_self
from mediagoblin.init.celery.from_celery import setup_self
OUR_MODULENAME = __name__

View File

@ -21,7 +21,8 @@ from celery.task import task
from mediagoblin import mg_globals as mgg
THUMB_SIZE = 200, 200
THUMB_SIZE = 180, 180
MEDIUM_SIZE = 640, 640
def create_pub_filepath(entry, filename):
@ -43,20 +44,32 @@ def process_media_initial(media_id):
mgg.queue_store, queued_filepath,
'source')
queued_file = file(queued_filename, 'r')
thumb = Image.open(queued_filename)
thumb.thumbnail(THUMB_SIZE, Image.ANTIALIAS)
# ensure color mode is compatible with jpg
if thumb.mode != "RGB":
thumb = thumb.convert("RGB")
with queued_file:
thumb = Image.open(queued_file)
thumb.thumbnail(THUMB_SIZE, Image.ANTIALIAS)
# ensure color mode is compatible with jpg
if thumb.mode != "RGB":
thumb = thumb.convert("RGB")
thumb_filepath = create_pub_filepath(entry, 'thumbnail.jpg')
thumb_filepath = create_pub_filepath(entry, 'thumbnail.jpg')
thumb_file = mgg.public_store.get_file(thumb_filepath, 'w')
with thumb_file:
thumb.save(thumb_file, "JPEG", quality=90)
thumb_file = mgg.public_store.get_file(thumb_filepath, 'w')
with thumb_file:
thumb.save(thumb_file, "JPEG")
"""
Create medium file, used in `media.html`
"""
medium = Image.open(queued_filename)
medium.thumbnail(MEDIUM_SIZE, Image.ANTIALIAS)
if medium.mode != "RGB":
medium = medium.convert("RGB")
medium_filepath = create_pub_filepath(entry, 'medium.jpg')
medium_file = mgg.public_store.get_file(medium_filepath, 'w')
with medium_file:
medium.save(medium_file, "JPEG", quality=90)
# we have to re-read because unlike PIL, not everything reads
# things in string representation :)
@ -73,6 +86,7 @@ def process_media_initial(media_id):
media_files_dict = entry.setdefault('media_files', {})
media_files_dict['thumb'] = thumb_filepath
media_files_dict['main'] = main_filepath
media_files_dict['medium'] = medium_filepath
entry['state'] = u'processed'
entry.save()

View File

@ -23,10 +23,14 @@ form {
/* text styles */
h1 {
h1{
font-family: 'Carter One', arial, serif;
margin-bottom: 20px;
margin-top:40px;
margin-bottom: 15px;
margin-top:15px;
}
h2{
margin-top:20px;
}
p {
@ -73,47 +77,6 @@ label {
padding-bottom:74px;
}
ul.mediagoblin_messages {
list-style:none inside;
color:#393932;
margin:2px;
padding:2px;
}
ul.mediagoblin_messages li {
background-color:#d4d4d4;
border-style:solid;
border-width:3px;
border-color:#959595;
margin:5px;
padding:8px;
}
ul.mediagoblin_messages li.message_success {
background-color: #88d486;
border-color: #5bba59;
}
ul.mediagoblin_messages li.message_warning {
background-color: #d4c686;
border-color: #baa959;
}
ul.mediagoblin_messages li.message_error {
background-color: #d48686;
border-color: #ba5959;
}
ul.mediagoblin_messages li.message_info {
background-color: #86b9d4;
border-color: #5998ba;
}
ul.mediagoblin_messages li.message_debug {
background-color: #aa86d4;
border-color: #8659ba;
}
a.mediagoblin_logo {
width:34px;
height:25px;
@ -133,15 +96,6 @@ a.mediagoblin_logo:hover {
/* common website elements */
.dotted_line {
width:100%;
height:0px;
border-bottom: dotted 1px #5f5f5f;
position:absolute;
left:0px;
margin-top:-20px;
}
.button {
font-family:'Carter One', arial, serif;
height:32px;
@ -164,6 +118,10 @@ a.mediagoblin_logo:hover {
padding-right:11px;
}
.pagination{
text-align:center;
}
/* forms */
.form_box {
@ -213,30 +171,97 @@ a.mediagoblin_logo:hover {
text-align:right;
}
/* media pages */
/* comments */
.media_image{
width:640px;
.comment_author {
margin-bottom:40px;
padding-top:4px;
}
.media_sidebar{
width:280px;
.comment_content p {
margin-bottom:4px;
}
/* media galleries */
ul.media_thumbnail {
.media_thumbnail {
padding:0px;
width:180px;
height:180px;
overflow:hidden;
float:left;
margin:0px 4px 10px 4px;
text-align:center;
}
li.media_thumbnail {
width:200px;
height:200px;
display:-moz-inline-stack;
display:inline-block;
vertical-align:top;
margin:0px 10px 10px 0px;
text-align:center;
zoom:1;
. *display:inline;
/* icons */
img.media_icon{
margin:0 4px;
vertical-align:sub;
}
/* navigation */
.navigation_button{
width: 139px;
display:block;
float:left;
text-align: center;
background-color: #393939;
text-decoration: none;
padding: 6px 0pt;
font-family: 'Carter One', arial, serif;
font-size:2em;
margin:0 0 20px
}
p.navigation_button{
color:#272727;
}
.navigation_left{
margin-right:2px;
}
/* messages */
ul.mediagoblin_messages {
list-style:none inside;
color:#f7f7f7;
}
.mediagoblin_messages li {
margin:5px 0;
padding:8px;
text-align:center;
}
.message_success {
background-color: #378566;
}
.message_warning {
background-color: #87453b;
}
.message_error {
background-color: #87453b;
}
.message_info {
background-color: #378566;
}
.message_debug {
background-color: #f7f7f7;
color:#272727;
}
/* profile stuff */
.profile_content {
padding: 6px;
background-color: #393939;
margin-bottom: 10px;
}

View File

@ -1 +0,0 @@
../../../contrib/960_12_col.css

View File

@ -0,0 +1 @@
../../../contrib/960_16_col.css

View File

@ -0,0 +1 @@
../../../contrib/reset.css

View File

@ -0,0 +1 @@
../../../contrib/text.css

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

View File

@ -24,6 +24,7 @@ from mediagoblin.util import (
from mediagoblin.decorators import require_active_login
from mediagoblin.submit import forms as submit_forms, security
from mediagoblin.process_media import process_media_initial
from mediagoblin.messages import add_message, SUCCESS
@require_active_login
@ -85,7 +86,10 @@ def submit_start(request):
# queue it for processing
process_media_initial.delay(unicode(entry['_id']))
return redirect(request, "mediagoblin.submit.success")
add_message(request, SUCCESS, 'Woohoo! Submitted!')
return redirect(request, "mediagoblin.user_pages.user_home",
user = request.user['username'])
return render_to_response(
request,

View File

@ -20,10 +20,9 @@
{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
{% block mediagoblin_content %}
<form action="{{ request.urlgen('mediagoblin.auth.login') }}"
method="POST" enctype="multipart/form-data">
<div class="grid_4 prefix_1 suffix_1 form_box">
<div class="grid_6 prefix_1 suffix_1 form_box">
<h1>Log in</h1>
{% if login_failed %}
<div class="form_field_error">Login failed!</div>

View File

@ -23,7 +23,7 @@
<form action="{{ request.urlgen('mediagoblin.auth.register') }}"
method="POST" enctype="multipart/form-data">
<div class="grid_4 prefix_1 suffix_1 form_box">
<div class="grid_6 prefix_1 suffix_1 form_box">
<h1>Create an account!</h1>
{{ wtforms_util.render_divs(register_form) }}
<div class="form_submit_buttons">

View File

@ -19,7 +19,11 @@
<head>
<title>{% block title %}GNU MediaGoblin{% endblock title %}</title>
<link rel="stylesheet" type="text/css"
href="{{ request.staticdirect('/css/contrib/960_12_col.css') }}"/>
href="{{ request.staticdirect('/css/contrib/reset.css') }}"/>
<link rel="stylesheet" type="text/css"
href="{{ request.staticdirect('/css/contrib/text.css') }}"/>
<link rel="stylesheet" type="text/css"
href="{{ request.staticdirect('/css/contrib/960_16_col.css') }}"/>
<link rel="stylesheet" type="text/css"
href="{{ request.staticdirect('/css/base.css') }}"/>
{% block mediagoblin_head %}
@ -31,8 +35,8 @@
<div class="mediagoblin_body">
{% block mediagoblin_header %}
<div class="mediagoblin_header">
<div class="container_12">
<div class="grid_12">
<div class="container_16">
<div class="grid_16">
{% block mediagoblin_logo %}
<a class="mediagoblin_logo" href="{{ request.urlgen('index') }}"></a>
{% endblock %}{% block mediagoblin_header_title %}{% endblock %}
@ -51,8 +55,8 @@
</div>
</div>
{% endblock %}
<div class="container_12 mediagoblin_content">
<div class="grid_12">
<div class="container_16 mediagoblin_content">
<div class="grid_16">
{% include "mediagoblin/utils/messages.html" %}
{% block mediagoblin_content %}
{% endblock mediagoblin_content %}
@ -60,8 +64,8 @@
</div>
{% block mediagoblin_footer %}
<div class="mediagoblin_footer">
<div class="container_12">
<div class="grid_12">
<div class="container_16">
<div class="grid_16">
Powered by <a href="http://mediagoblin.org">MediaGoblin</a>, a <a href="http://gnu.org/">GNU project</a>
</div>
</div>

View File

@ -25,7 +25,7 @@
user= media.uploader().username,
media= media._id) }}"
method="POST" enctype="multipart/form-data">
<div class="grid_6 prefix_1 suffix_1 edit_box form_box">
<div class="grid_8 prefix_1 suffix_1 edit_box form_box">
<h1>Editing {{ media.title }}</h1>
<div style="text-align: center;" >
<img src="{{ request.app.public_store.file_url(

View File

@ -21,10 +21,10 @@
{% block mediagoblin_content %}
<form action="{{ request.urlgen('mediagoblin.edit.profile',
user=user.username) }}"
<form action="{{ request.urlgen('mediagoblin.edit.profile') }}?username={{
user['username'] }}"
method="POST" enctype="multipart/form-data">
<div class="grid_6 prefix_1 suffix_1 edit_box form_box">
<div class="grid_8 prefix_1 suffix_1 edit_box form_box">
<h1>Editing {{ user['username'] }}'s profile</h1>
{{ wtforms_util.render_divs(form) }}
<div class="form_submit_buttons">

View File

@ -23,7 +23,6 @@
{% if request.user %}
<p>
<a href="{{ request.urlgen('mediagoblin.submit.start') }}">Submit an item</a>
<a href="{{ request.urlgen('mediagoblin.edit.profile') }}">Edit profile</a>
</p>
{% else %}
<p>

View File

@ -20,17 +20,16 @@
{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
{% block mediagoblin_content %}
<form action="{{ request.urlgen('mediagoblin.submit.start') }}"
method="POST" enctype="multipart/form-data">
<div class="grid_6 prefix_1 suffix_1 form_box">
<div class="grid_8 prefix_1 suffix_1 form_box">
<h1>Submit yer media</h1>
{{ wtforms_util.render_field_div(submit_form.file) }}
{{ wtforms_util.render_field_div(submit_form.title) }}
{{ wtforms_util.render_textarea_div(submit_form.description) }}
{{ wtforms_util.render_field_div(submit_form.file) }}
<div class="form_submit_buttons">
<input type="submit" value="Submit" class="button" />
</div>
</div>
</form>
{% endblock %}
{% endblock %}

View File

@ -31,7 +31,11 @@
'mediagoblin.user_pages.user_home',
user=user.username) }}">{{ user.username }}</a>'s media</h1>
{% include "mediagoblin/utils/object_gallery.html" %}
</div>
<div class="container_16 media_gallery">
{% include "mediagoblin/utils/object_gallery.html" %}
</div>
<div class="grid_16">
<a href={{ request.urlgen(
'mediagoblin.user_pages.atom_feed',

View File

@ -18,86 +18,100 @@
{% extends "mediagoblin/base.html" %}
{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
{% from "mediagoblin/utils/pagination.html" import render_pagination %}
{% block mediagoblin_content %}
{# temporarily, an "image gallery" that isn't one really ;) #}
{% if media %}
<div class="grid_8 alpha media_image">
<h1>
<div class="grid_11 alpha">
{% if media.media_files.medium %}
<img src="{{ request.app.public_store.file_url(
media.media_files.medium) }}" />
{% else %}
<img src="{{ request.app.public_store.file_url(
media.media_files.main) }}" />
{% endif %}
<h2>
{{media.title}}
</h1>
<img class="media_image" src="{{ request.app.public_store.file_url(
media.media_files.main) }}" />
</h2>
{% autoescape False %}
<p>{{ media.description_html }}</p>
{% endautoescape %}
<p>
Uploaded on
&mdash;&nbsp;uploaded on
{{ "%4d-%02d-%02d"|format(media.created.year,
media.created.month, media.created.day) }}
by
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
user= media.uploader().username) }}">
{{- media.uploader().username }}</a>
</p>
</p>
<br />
{% autoescape False %}
<p>{{ media.description_html }}</p>
{% endautoescape %}
{% if media['uploader'] == request.user['_id'] %}
<p><a href="{{ request.urlgen('mediagoblin.edit.edit_media',
user= media.uploader().username,
media= media._id) }}">Edit</a></p>
{% endif %}
<h3>Comments</h3>
{% if request.user %}
<form action="{{ request.urlgen('mediagoblin.user_pages.media_post_comment',
user= media.uploader().username,
media=media._id) }}" method="POST">
<h3>Post a comment!</h3>
{{ wtforms_util.render_field_div(comment_form.comment) }}
<div class="form_submit_buttons">
<input type="submit" value="Submit" class="button" />
<input type="submit" value="Post comment!" class="button" />
</div>
</form>
{% endif %}
{#
{{ wtforms_util.render_textarea_div(submit_form.description) }}
{{ wtforms_util.render_field_div(submit_form.file) }}
#}
{% if comments %}
<h3>Comments</h3>
{% for comment in comments %}
{% set comment_author = comment.author() %}
<div class="comment_wrapper" id="comment-{{ comment['_id'] }}">
<div class="comment_author">By:
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
user = comment['author']['username']) }}">
{{ comment['author']['username'] }}
</a>
</div>
<div class="comment_datetime">
<a href="#comment-{{ comment['_id'] }}">
{{ "%4d-%02d-%02d %02d:%02d"|format(comment.created.year,
comment.created.month,
comment.created.day,
comment.created.hour,
comment.created.minute) }}
</a>
</div>
<div class="comment_content">
{% autoescape False %}
{{ comment.content_html }}
{% endautoescape %}
</div>
</div>
</div>
<div class="comment_author">&mdash;
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
user = comment_author['username']) }}">
{{ comment_author['username'] }}</a> at
<!--</div>
<div class="comment_datetime">-->
<a href="#comment-{{ comment['_id'] }}">
{{ "%4d-%02d-%02d %02d:%02d"|format(comment.created.year,
comment.created.month,
comment.created.day,
comment.created.hour,
comment.created.minute) }}
</a>
</div>
</div>
{% endfor %}
{% include "mediagoblin/utils/pagination.html" %}
{{ render_pagination(request, pagination) }}
</div>
{% endif %}
<div class="grid_4 omega media_sidebar">
<p>This is a sidebar! Yay!</p>
<div class="grid_5 omega">
{% include "mediagoblin/utils/prev_next.html" %}
<h3>Sidebar content here!</h3>
<p>
{% if media['uploader'] == request.user['_id'] or
request.user['is_admin'] %}
<p>
<a href="{{ request.urlgen('mediagoblin.edit.edit_media',
user= media.uploader().username,
media= media._id) }}"
><img src="{{ request.staticdirect('/images/icon_edit.png') }}"
class="media_icon" />edit</a>
</p>
<p>
<img src="{{ request.staticdirect('/images/icon_delete.png') }}"
class="media_icon" />delete
</p>
{% endif %}
</p>
</div>
{% else %}
<p>Sorry, no such media found.<p/>
<p>Sorry, no such media found.<p/>
{% endif %}
{% endblock %}

View File

@ -20,27 +20,39 @@
{% block mediagoblin_head %}
<link rel="alternate" type="application/atom+xml"
href="{{ request.urlgen(
'mediagoblin.user_pages.atom_feed',
'mediagoblin.user_pages.atom_feed',
user=user.username) }}">
{% endblock mediagoblin_head %}
{% block mediagoblin_content -%}
{% if user %}
<h1>{{ user.username }}'s profile</h1>
{% include "mediagoblin/utils/profile.html" %}
{% if request.user['_id'] == user['_id'] or request.user['is_admin'] %}
<a href="{{ request.urlgen('mediagoblin.edit.profile') }}?username={{
user.username }}">Edit profile</a>
{% endif %}
{% if request.user['_id'] == user['_id'] %}
<p>
<a href="{{ request.urlgen('mediagoblin.submit.start') }}">Submit an item</a>
</p>
{% endif %}
{% set pagination_base_url = user_gallery_url %}
{% include "mediagoblin/utils/object_gallery.html" %}
<p><a href="{{ request.urlgen('mediagoblin.user_pages.user_gallery',
user= request.user['username']) }}">View all of {{ user.username }}'s media</a></p>
<div class="clear"></div>
<p><a href="{{ user_gallery_url }}">View all of {{ user.username }}'s media</a></p>
<a href={{ request.urlgen(
'mediagoblin.user_pages.atom_feed',
user=user.username) }}> atom feed</a>
user=user.username) }}>atom feed</a>
{% else %}
{# This *should* not occur as the view makes sure we pass in a user. #}
<p>Sorry, no such user found.<p/>
{% endif %}
{% endblock %}
{% endblock %}

View File

@ -16,20 +16,22 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% block object_gallery_content -%}
<div>
{% if media_entries %}
<ul class="media_thumbnail">
{% for entry in media_entries %}
<li class="media_thumbnail">
<a href="{{ entry.url_for_self(request.urlgen) }}">
<img src="{{ request.app.public_store.file_url(
entry['media_files']['thumb']) }}" /></a>
</li>
{% endfor %}
</ul>
{% include "mediagoblin/utils/pagination.html" %}
{% endif %}
{% from "mediagoblin/utils/pagination.html" import render_pagination %}
</div>
{% endblock %}
{% block object_gallery_content -%}
{% if media_entries %}
{% for entry in media_entries %}
<div class="media_thumbnail">
<a href="{{ entry.url_for_self(request.urlgen) }}">
<img src="{{ request.app.public_store.file_url(
entry['media_files']['thumb']) }}" /></a>
</div>
{% endfor %}
{% if pagination_base_url %}
{# different url, so set that and don't keep the get params #}
{{ render_pagination(request, pagination, pagination_base_url, False) }}
{% else %}
{{ render_pagination(request, pagination) }}
{% endif %}
{% endif %}
{% endblock %}

View File

@ -15,30 +15,48 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{# only display if {{pagination}} is defined #}
{% macro render_pagination(request, pagination,
base_url=None, preserve_get_params=True) %}
{# only display if {{pagination}} is defined #}
{% if pagination and pagination.pages > 1 %}
{% if not base_url %}
{% set base_url = request.path_info %}
{% endif %}
{% if pagination %}
<div class="pagination">
{% if preserve_get_params %}
{% set get_params = request.GET %}
{% else %}
{% set get_params = {} %}
{% endif %}
{% if pagination.has_prev %}
<a href="{{ pagination.get_page_url(request, pagination.page-1) }}">&laquo; Prev</>
{% endif %}
{%- for page in pagination.iter_pages() %}
{% if page %}
{% if page != pagination.page %}
<a href="{{ pagination.get_page_url(request, page) }}">{{ page }}</a>
{% else %}
<strong>{{ page }}</strong>
{% endif %}
{% else %}
<span class="ellipsis"></span>
<div class="pagination">
<p>
{% if pagination.has_prev %}
<a href="{{ pagination.get_page_url_explicit(
base_url, get_params,
pagination.page - 1) }}">&laquo; Prev</a>
{% endif %}
{%- endfor %}
{% if pagination.has_next %}
<a href="{{ pagination.get_page_url(request, pagination.page + 1) }}">Next &raquo;</a>
{% endif %}
</div>
{% endif %}
{%- for page in pagination.iter_pages() %}
{% if page %}
{% if page != pagination.page %}
<a href="{{ pagination.get_page_url_explicit(
base_url, get_params,
page) }}">{{ page }}</a>
{% else %}
{{ page }}
{% endif %}
{% else %}
<span class="ellipsis"></span>
{% endif %}
{%- endfor %}
{% if pagination.has_next %}
<a href="{{ pagination.get_page_url_explicit(
base_url, get_params,
pagination.page + 1) }}">Next &raquo;</a>
{% endif %}
</p>
</div>
{% endif %}
{% endmacro %}

View File

@ -0,0 +1,43 @@
{#
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011 Free Software Foundation, Inc
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{# Provide navigation links to neighboring media entries, if possible #}
{% set prev_entry_url = media.url_to_prev(request.urlgen) %}
{% set next_entry_url = media.url_to_next(request.urlgen) %}
<div>
{# There are no previous entries for the very first media entry #}
{% if prev_entry_url %}
<a class="navigation_button navigation_left" href="{{ prev_entry_url }}">
&lt;
</a>
{% else %}
{# This is the first entry. display greyed-out 'previous' image #}
<p class="navigation_button">X</p>
{% endif %}
{# Likewise, this could be the very last media entry #}
{% if next_entry_url %}
<a class="navigation_button" href="{{ next_entry_url }}">
&gt;
</a>
{% else %}
{# This is the last entry. display greyed-out 'next' image #}
<p class="navigation_button">X</p>
{% endif %}
</div>

View File

@ -17,19 +17,19 @@
#}
{% block profile_content -%}
<div>
<ul>
{% if user.url or user.bio %}
<div class="profile_content">
{% if user.url %}
<li>
<a href="{{ user.url }}">homepage</a>
</li>
{% endif %}
<div class="profile_homepage">
<a href="{{ user.url }}">{{ user.url }}</a>
</div>
{% endif %}
{% if user.bio %}
<li>
{{ user.bio }}
</li>
{% endif %}
</ul>
</div>
<div class="profile_bio">
{{ user.bio }}
</div>
{% endif %}
</div>
{% endif %}
{% endblock %}

View File

@ -16,8 +16,8 @@
import pkg_resources
from mediagoblin import celery_setup
from mediagoblin.config import read_mediagoblin_config
from mediagoblin.init import celery as celery_setup
from mediagoblin.init.config import read_mediagoblin_config
TEST_CELERY_CONF_NOSPECIALDB = pkg_resources.resource_filename(

View File

@ -16,7 +16,7 @@
import pkg_resources
from mediagoblin import config
from mediagoblin.init import config
CARROT_CONF_GOOD = pkg_resources.resource_filename(

View File

@ -8,7 +8,7 @@ email_debug_mode = true
db_name = __mediagoblin_tests__
# Celery shouldn't be set up by the application as it's setup via
# mediagoblin.celery_setup.from_celery
# mediagoblin.init.celery.from_celery
celery_setup_elsewhere = true
[celery]

View File

@ -22,7 +22,7 @@ from paste.deploy import loadapp
from webtest import TestApp
from mediagoblin import util
from mediagoblin.config import read_mediagoblin_config
from mediagoblin.init.config import read_mediagoblin_config
from mediagoblin.decorators import _make_safe
from mediagoblin.db.open import setup_connection_and_db_from_config
@ -42,8 +42,8 @@ USER_DEV_DIRECTORIES_TO_SETUP = [
BAD_CELERY_MESSAGE = """\
Sorry, you *absolutely* must run nosetests with the
mediagoblin.celery_setup.from_tests module. Like so:
$ CELERY_CONFIG_MODULE=mediagoblin.celery_setup.from_tests ./bin/nosetests"""
mediagoblin.init.celery.from_tests module. Like so:
$ CELERY_CONFIG_MODULE=mediagoblin.init.celery.from_tests ./bin/nosetests"""
class BadCeleryEnviron(Exception): pass
@ -51,7 +51,7 @@ class BadCeleryEnviron(Exception): pass
def suicide_if_bad_celery_environ():
if not os.environ.get('CELERY_CONFIG_MODULE') == \
'mediagoblin.celery_setup.from_tests':
'mediagoblin.init.celery.from_tests':
raise BadCeleryEnviron(BAD_CELERY_MESSAGE)
@ -59,7 +59,7 @@ def get_test_app(dump_old_app=True):
suicide_if_bad_celery_environ()
# Leave this imported as it sets up celery.
from mediagoblin.celery_setup import from_tests
from mediagoblin.init.celery import from_tests
global MGOBLIN_APP

View File

@ -48,10 +48,15 @@ def user_home(request, page):
if media_entries == None:
return exc.HTTPNotFound()
user_gallery_url = request.urlgen(
'mediagoblin.user_pages.user_gallery',
user=user['username'])
return render_to_response(
request,
'mediagoblin/user_pages/user.html',
{'user': user,
'user_gallery_url': user_gallery_url,
'media_entries': media_entries,
'pagination': pagination})
@ -82,17 +87,19 @@ def user_gallery(request, page):
'media_entries': media_entries,
'pagination': pagination})
MEDIA_COMMENTS_PER_PAGE = 50
@get_user_media_entry
@uses_pagination
def media_home(request, media, **kwargs):
def media_home(request, media, page, **kwargs):
"""
'Homepage' of a MediaEntry()
"""
comment_form = user_forms.MediaCommentForm(request.POST)
pagination = Pagination(page, media.get_comments(), MEDIA_COMMENTS_PER_PAGE)
comments = pagination()
(comments, pagination) = media.get_comments(kwargs.get('page'))
comment_form = user_forms.MediaCommentForm(request.POST)
return render_to_response(
request,

View File

@ -64,22 +64,6 @@ def clear_test_buckets():
clear_test_template_context()
def get_jinja_loader(user_template_path=None):
"""
Set up the Jinja template loaders, possibly allowing for user
overridden templates.
(In the future we may have another system for providing theming;
for now this is good enough.)
"""
if user_template_path:
return jinja2.ChoiceLoader(
[jinja2.FileSystemLoader(user_template_path),
jinja2.PackageLoader('mediagoblin', 'templates')])
else:
return jinja2.PackageLoader('mediagoblin', 'templates')
SETUP_JINJA_ENVS = {}

View File

@ -27,4 +27,4 @@ else
exit 1
fi
CELERY_CONFIG_MODULE=mediagoblin.celery_setup.from_tests $NOSETESTS $@
CELERY_CONFIG_MODULE=mediagoblin.init.celery.from_tests $NOSETESTS $@

View File

@ -18,7 +18,7 @@ from setuptools import setup, find_packages
setup(
name = "mediagoblin",
version = "0.0.2",
version = "0.0.3",
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
zip_safe=False,
# scripts and dependencies

0
test
View File