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 *.pyo
docs/_build/ docs/_build/
user_dev/ user_dev/
mediagoblin_user.ini
server-log.txt server-log.txt
*~ *~
*.swp *.swp

View File

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

View File

@ -150,7 +150,7 @@ celeryd in another window.
Run:: 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 Running the test suite

View File

@ -16,6 +16,22 @@
# You should have received a copy of the GNU Affero General Public License # 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/>. # 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 if [ -f ./bin/paster ]; then
echo "Using ./bin/paster"; echo "Using ./bin/paster";
export PASTER="./bin/paster"; export PASTER="./bin/paster";
@ -27,4 +43,5 @@ else
exit 1 exit 1
fi 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 webob import Request, exc
from mediagoblin import routing, util, storage, staticdirect from mediagoblin import routing, util, storage, staticdirect
from mediagoblin.config import ( from mediagoblin.init.config import (
read_mediagoblin_config, generate_validation_report) read_mediagoblin_config, generate_validation_report)
from mediagoblin.db.open import setup_connection_and_db_from_config from mediagoblin.db.open import setup_connection_and_db_from_config
from mediagoblin.mg_globals import setup_globals 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 from mediagoblin.workbench import WorkbenchManager
@ -71,7 +72,7 @@ class MediaGoblinApp(object):
app_config) app_config)
# Get the template environment # Get the template environment
self.template_loader = util.get_jinja_loader( self.template_loader = get_jinja_loader(
app_config.get('user_template_path')) app_config.get('user_template_path'))
# Set up storage systems # 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 # 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/>. # 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.auth import lib as auth_lib
from mediagoblin import mg_globals from mediagoblin import mg_globals
from mediagoblin.db import migrations from mediagoblin.db import migrations
from mediagoblin.db.util import DESCENDING, ObjectId from mediagoblin.db.util import ASCENDING, DESCENDING, ObjectId
from mediagoblin.util import Pagination
################### ###################
# Custom validators # Custom validators
@ -109,24 +108,13 @@ class MediaEntry(Document):
migration_handler = migrations.MediaEntryMigration migration_handler = migrations.MediaEntryMigration
def get_comments(self):
return self.db.MediaComment.find({
'media_entry': self['_id']}).sort('created', DESCENDING)
def main_mediafile(self): def main_mediafile(self):
pass 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): def generate_slug(self):
self['slug'] = util.slugify(self['title']) self['slug'] = util.slugify(self['title'])
@ -154,6 +142,32 @@ class MediaEntry(Document):
'mediagoblin.user_pages.media_home', 'mediagoblin.user_pages.media_home',
user=uploader['username'], user=uploader['username'],
media=unicode(self['_id'])) 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): def uploader(self):
return self.db.User.find_one({'_id': self['uploader']}) return self.db.User.find_one({'_id': self['uploader']})

View File

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

View File

@ -17,6 +17,7 @@
from webob import exc from webob import exc
from mediagoblin import messages
from mediagoblin.util import render_to_response, redirect, clean_html from mediagoblin.util import render_to_response, redirect, clean_html
from mediagoblin.edit import forms from mediagoblin.edit import forms
from mediagoblin.edit.lib import may_edit_media 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", return redirect(request, "mediagoblin.user_pages.media_home",
user=media.uploader()['username'], media=media['slug']) 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( return render_to_response(
request, request,
'mediagoblin/edit/edit.html', 'mediagoblin/edit/edit.html',
@ -73,7 +82,18 @@ def edit_media(request, media):
@require_active_login @require_active_login
def edit_profile(request): 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, form = forms.EditProfileForm(request.POST,
url = user.get('url'), url = user.get('url'),
bio = user.get('bio')) bio = user.get('bio'))
@ -83,7 +103,12 @@ def edit_profile(request):
user['bio'] = request.POST['bio'] user['bio'] = request.POST['bio']
user.save() 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( return render_to_response(
request, request,

View File

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

View File

@ -20,7 +20,7 @@ import sys
MANDATORY_CELERY_IMPORTS = ['mediagoblin.process_media'] 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, def setup_celery_from_config(app_config, global_config,

View File

@ -17,7 +17,7 @@
import os import os
from mediagoblin import app, mg_globals 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__ OUR_MODULENAME = __name__

View File

@ -17,7 +17,7 @@
import os import os
from mediagoblin.tests.tools import TEST_APP_CONFIG 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__ OUR_MODULENAME = __name__

View File

@ -21,7 +21,8 @@ from celery.task import task
from mediagoblin import mg_globals as mgg 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): def create_pub_filepath(entry, filename):
@ -43,20 +44,32 @@ def process_media_initial(media_id):
mgg.queue_store, queued_filepath, mgg.queue_store, queued_filepath,
'source') '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_filepath = create_pub_filepath(entry, 'thumbnail.jpg')
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_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: Create medium file, used in `media.html`
thumb.save(thumb_file, "JPEG") """
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 # we have to re-read because unlike PIL, not everything reads
# things in string representation :) # things in string representation :)
@ -73,6 +86,7 @@ def process_media_initial(media_id):
media_files_dict = entry.setdefault('media_files', {}) media_files_dict = entry.setdefault('media_files', {})
media_files_dict['thumb'] = thumb_filepath media_files_dict['thumb'] = thumb_filepath
media_files_dict['main'] = main_filepath media_files_dict['main'] = main_filepath
media_files_dict['medium'] = medium_filepath
entry['state'] = u'processed' entry['state'] = u'processed'
entry.save() entry.save()

View File

@ -23,10 +23,14 @@ form {
/* text styles */ /* text styles */
h1 { h1{
font-family: 'Carter One', arial, serif; font-family: 'Carter One', arial, serif;
margin-bottom: 20px; margin-bottom: 15px;
margin-top:40px; margin-top:15px;
}
h2{
margin-top:20px;
} }
p { p {
@ -73,47 +77,6 @@ label {
padding-bottom:74px; 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 { a.mediagoblin_logo {
width:34px; width:34px;
height:25px; height:25px;
@ -133,15 +96,6 @@ a.mediagoblin_logo:hover {
/* common website elements */ /* common website elements */
.dotted_line {
width:100%;
height:0px;
border-bottom: dotted 1px #5f5f5f;
position:absolute;
left:0px;
margin-top:-20px;
}
.button { .button {
font-family:'Carter One', arial, serif; font-family:'Carter One', arial, serif;
height:32px; height:32px;
@ -164,6 +118,10 @@ a.mediagoblin_logo:hover {
padding-right:11px; padding-right:11px;
} }
.pagination{
text-align:center;
}
/* forms */ /* forms */
.form_box { .form_box {
@ -213,30 +171,97 @@ a.mediagoblin_logo:hover {
text-align:right; text-align:right;
} }
/* media pages */ /* comments */
.media_image{ .comment_author {
width:640px; margin-bottom:40px;
padding-top:4px;
} }
.media_sidebar{ .comment_content p {
width:280px; margin-bottom:4px;
} }
/* media galleries */ /* media galleries */
ul.media_thumbnail { .media_thumbnail {
padding:0px; padding:0px;
width:180px;
height:180px;
overflow:hidden;
float:left;
margin:0px 4px 10px 4px;
text-align:center;
} }
li.media_thumbnail { /* icons */
width:200px;
height:200px; img.media_icon{
display:-moz-inline-stack; margin:0 4px;
display:inline-block; vertical-align:sub;
vertical-align:top; }
margin:0px 10px 10px 0px;
text-align:center; /* navigation */
zoom:1;
. *display:inline; .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.decorators import require_active_login
from mediagoblin.submit import forms as submit_forms, security from mediagoblin.submit import forms as submit_forms, security
from mediagoblin.process_media import process_media_initial from mediagoblin.process_media import process_media_initial
from mediagoblin.messages import add_message, SUCCESS
@require_active_login @require_active_login
@ -85,7 +86,10 @@ def submit_start(request):
# queue it for processing # queue it for processing
process_media_initial.delay(unicode(entry['_id'])) 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( return render_to_response(
request, request,

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@
user= media.uploader().username, user= media.uploader().username,
media= media._id) }}" media= media._id) }}"
method="POST" enctype="multipart/form-data"> 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> <h1>Editing {{ media.title }}</h1>
<div style="text-align: center;" > <div style="text-align: center;" >
<img src="{{ request.app.public_store.file_url( <img src="{{ request.app.public_store.file_url(

View File

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

View File

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

View File

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

View File

@ -31,7 +31,11 @@
'mediagoblin.user_pages.user_home', 'mediagoblin.user_pages.user_home',
user=user.username) }}">{{ user.username }}</a>'s media</h1> 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( <a href={{ request.urlgen(
'mediagoblin.user_pages.atom_feed', 'mediagoblin.user_pages.atom_feed',

View File

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

View File

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

View File

@ -16,20 +16,22 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
#} #}
{% block object_gallery_content -%} {% from "mediagoblin/utils/pagination.html" import render_pagination %}
<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 %}
</div> {% block object_gallery_content -%}
{% endblock %} {% 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/>. # 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 %} {% if preserve_get_params %}
<div class="pagination"> {% set get_params = request.GET %}
{% else %}
{% set get_params = {} %}
{% endif %}
{% if pagination.has_prev %} <div class="pagination">
<a href="{{ pagination.get_page_url(request, pagination.page-1) }}">&laquo; Prev</> <p>
{% endif %} {% if pagination.has_prev %}
<a href="{{ pagination.get_page_url_explicit(
{%- for page in pagination.iter_pages() %} base_url, get_params,
{% if page %} pagination.page - 1) }}">&laquo; Prev</a>
{% if page != pagination.page %}
<a href="{{ pagination.get_page_url(request, page) }}">{{ page }}</a>
{% else %}
<strong>{{ page }}</strong>
{% endif %}
{% else %}
<span class="ellipsis"></span>
{% endif %} {% endif %}
{%- endfor %}
{%- for page in pagination.iter_pages() %}
{% if pagination.has_next %} {% if page %}
<a href="{{ pagination.get_page_url(request, pagination.page + 1) }}">Next &raquo;</a> {% if page != pagination.page %}
{% endif %} <a href="{{ pagination.get_page_url_explicit(
</div> base_url, get_params,
{% endif %} 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 -%} {% block profile_content -%}
<div> {% if user.url or user.bio %}
<ul> <div class="profile_content">
{% if user.url %} {% if user.url %}
<li> <div class="profile_homepage">
<a href="{{ user.url }}">homepage</a> <a href="{{ user.url }}">{{ user.url }}</a>
</li> </div>
{% endif %} {% endif %}
{% if user.bio %} {% if user.bio %}
<li> <div class="profile_bio">
{{ user.bio }} {{ user.bio }}
</li> </div>
{% endif %} {% endif %}
</ul> </div>
</div> {% endif %}
{% endblock %} {% endblock %}

View File

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

View File

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

View File

@ -8,7 +8,7 @@ email_debug_mode = true
db_name = __mediagoblin_tests__ db_name = __mediagoblin_tests__
# Celery shouldn't be set up by the application as it's setup via # 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_setup_elsewhere = true
[celery] [celery]

View File

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

View File

@ -48,10 +48,15 @@ def user_home(request, page):
if media_entries == None: if media_entries == None:
return exc.HTTPNotFound() return exc.HTTPNotFound()
user_gallery_url = request.urlgen(
'mediagoblin.user_pages.user_gallery',
user=user['username'])
return render_to_response( return render_to_response(
request, request,
'mediagoblin/user_pages/user.html', 'mediagoblin/user_pages/user.html',
{'user': user, {'user': user,
'user_gallery_url': user_gallery_url,
'media_entries': media_entries, 'media_entries': media_entries,
'pagination': pagination}) 'pagination': pagination})
@ -82,17 +87,19 @@ def user_gallery(request, page):
'media_entries': media_entries, 'media_entries': media_entries,
'pagination': pagination}) 'pagination': pagination})
MEDIA_COMMENTS_PER_PAGE = 50
@get_user_media_entry @get_user_media_entry
@uses_pagination @uses_pagination
def media_home(request, media, **kwargs): def media_home(request, media, page, **kwargs):
""" """
'Homepage' of a MediaEntry() '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( return render_to_response(
request, request,

View File

@ -64,22 +64,6 @@ def clear_test_buckets():
clear_test_template_context() 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 = {} SETUP_JINJA_ENVS = {}

View File

@ -27,4 +27,4 @@ else
exit 1 exit 1
fi 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( setup(
name = "mediagoblin", name = "mediagoblin",
version = "0.0.2", version = "0.0.3",
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
zip_safe=False, zip_safe=False,
# scripts and dependencies # scripts and dependencies

0
test
View File