Merge branch 'master' into test_submission_views_365

This commit is contained in:
Chris Moylan 2011-07-10 22:45:52 -05:00
commit 3d073d48cb
37 changed files with 316 additions and 259 deletions

View File

@ -57,6 +57,11 @@ requirements::
sudo apt-get install mongodb git-core python python-dev \
python-lxml
On Fedora::
yum install mongodb-server python-paste-deploy python-paste-script \
git-core python python-devel
.. YouCanHelp::
If you have instructions for other GNU/Linux distributions to set

View File

@ -8,6 +8,9 @@ email_sender_address = "notice@mediagoblin.example.org"
# set to false to enable sending notices
email_debug_mode = true
# Set to false to disable registrations
allow_registration = true
## Uncomment this to put some user-overriding templates here
#local_templates = %(here)s/user_dev/templates/

View File

@ -20,18 +20,12 @@ import urllib
import routes
from webob import Request, exc
from mediagoblin import routing, util, storage, staticdirect
from mediagoblin.init.config import (
read_mediagoblin_config, generate_validation_report)
from mediagoblin import routing, util, storage
from mediagoblin.db.open import setup_connection_and_db_from_config
from mediagoblin.mg_globals import setup_globals
from mediagoblin.init.celery import setup_celery_from_config
from mediagoblin.init import get_jinja_loader
from mediagoblin.workbench import WorkbenchManager
class Error(Exception): pass
class ImproperlyConfigured(Error): pass
from mediagoblin.init import get_jinja_loader, get_staticdirector, \
setup_global_and_app_config, setup_workbench
class MediaGoblinApp(object):
@ -55,13 +49,7 @@ class MediaGoblinApp(object):
##############
# Open and setup the config
global_config, validation_result = read_mediagoblin_config(config_path)
app_config = global_config['mediagoblin']
# report errors if necessary
validation_report = generate_validation_report(
global_config, validation_result)
if validation_report:
raise ImproperlyConfigured(validation_report)
global_config, app_config = setup_global_and_app_config(config_path)
##########################################
# Setup other connections / useful objects
@ -85,19 +73,7 @@ class MediaGoblinApp(object):
self.routing = routing.get_mapper()
# set up staticdirector tool
if app_config.has_key('direct_remote_path'):
self.staticdirector = staticdirect.RemoteStaticDirect(
app_config['direct_remote_path'].strip())
elif app_config.has_key('direct_remote_paths'):
direct_remote_path_lines = app_config[
'direct_remote_paths'].strip().splitlines()
self.staticdirector = staticdirect.MultiRemoteStaticDirect(
dict([line.strip().split(' ', 1)
for line in direct_remote_path_lines]))
else:
raise ImproperlyConfigured(
"One of direct_remote_path or "
"direct_remote_paths must be provided")
self.staticdirector = get_staticdirector(app_config)
# Setup celery, if appropriate
if setup_celery and not app_config.get('celery_setup_elsewhere'):
@ -117,9 +93,6 @@ class MediaGoblinApp(object):
#######################################################
setup_globals(
app_config=app_config,
global_config=global_config,
# TODO: No need to set these two up as globals, we could
# just read them out of mg_globals.app_config
email_sender_address=app_config['email_sender_address'],
@ -130,8 +103,11 @@ class MediaGoblinApp(object):
db_connection=self.connection,
database=self.db,
public_store=self.public_store,
queue_store=self.queue_store,
workbench_manager=WorkbenchManager(app_config['workbench_path']))
queue_store=self.queue_store)
# Workbench *currently* only used by celery, so this only
# matters in always eager mode :)
setup_workbench()
def __call__(self, environ, start_response):
request = Request(environ)

View File

@ -18,6 +18,8 @@ import uuid
from webob import exc
from mediagoblin import messages
from mediagoblin import mg_globals
from mediagoblin.util import render_to_response, redirect
from mediagoblin.db.util import ObjectId
from mediagoblin.auth import lib as auth_lib
@ -29,6 +31,14 @@ def register(request):
"""
Your classic registration view!
"""
# Redirects to indexpage if registrations are disabled
if not mg_globals.app_config["allow_registration"]:
messages.add_message(
request,
messages.WARNING,
('Sorry, registration is disabled on this instance.'))
return redirect(request, "index")
register_form = auth_forms.RegistrationForm(request.POST)
if request.method == 'POST' and register_form.validate():
@ -51,7 +61,7 @@ def register(request):
entry['pw_hash'] = auth_lib.bcrypt_gen_password_hash(
request.POST['password'])
entry.save(validate=True)
send_verification_email(entry, request)
return redirect(request, "mediagoblin.auth.register_success")
@ -97,13 +107,14 @@ def login(request):
'mediagoblin/auth/login.html',
{'login_form': login_form,
'next': request.GET.get('next') or request.POST.get('next'),
'login_failed': login_failed})
'login_failed': login_failed,
'allow_registration': mg_globals.app_config["allow_registration"]})
def logout(request):
# Maybe deleting the user_id parameter would be enough?
request.session.delete()
return redirect(request, "index")
@ -124,16 +135,24 @@ def verify_email(request):
if user and user['verification_key'] == unicode(request.GET['token']):
user['status'] = u'active'
user['email_verified'] = True
verification_successful = True
user.save()
verification_successful = True
messages.add_message(
request,
messages.SUCCESS,
('Your email address has been verified. '
'You may now login, edit your profile, and submit images!'))
else:
verification_successful = False
messages.add_message(request,
messages.ERROR,
'The verification key or user id is incorrect')
return render_to_response(
request,
'mediagoblin/auth/verify_email.html',
'mediagoblin/user_pages/user.html',
{'user': user,
'verification_successful': verification_successful})
'verification_successful' : verification_successful})
def resend_activation(request):

View File

@ -21,6 +21,9 @@ direct_remote_path = string(default="/mgoblin_static/")
email_debug_mode = boolean(default=True)
email_sender_address = string(default="notice@mediagoblin.example.org")
# Set to false to disable registrations
allow_registration = boolean(default=True)
# By default not set, but you might want something like:
# "%(here)s/user_dev/templates/"
local_templates = string()
@ -73,4 +76,4 @@ celeryd_eta_scheduler_precision = float()
# known lists
celery_routes = string_list()
celery_imports = string_list()
celery_imports = string_list()

View File

@ -45,11 +45,13 @@ REQUIRED READING:
To remove deprecated indexes
----------------------------
Removing deprecated indexes is easier, just do:
Removing deprecated indexes is the same, just move the index into the
deprecated indexes mapping.
INACTIVE_INDEXES = {
'collection_name': [
'deprecated_index_identifier1', 'deprecated_index_identifier2']}
DEPRECATED_INDEXES = {
'collection_name': {
'deprecated_index_identifier1': {
'index': [index_foo_goes_here]}}
... etc.

View File

@ -147,31 +147,33 @@ class MediaEntry(Document):
"""
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)
cursor = self.db.MediaEntry.find({'_id' : {"$gt": self['_id']},
'uploader': self['uploader'],
'state': 'processed'}).sort(
'_id', ASCENDING).limit(1)
if cursor.count():
return urlgen('mediagoblin.user_pages.media_home',
user=self.uploader()['username'],
media=unicode(cursor[0]['_id']))
media=unicode(cursor[0]['slug']))
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)
cursor = self.db.MediaEntry.find({'_id' : {"$lt": self['_id']},
'uploader': self['uploader'],
'state': 'processed'}).sort(
'_id', DESCENDING).limit(1)
if cursor.count():
return urlgen('mediagoblin.user_pages.media_home',
user=self.uploader()['username'],
media=unicode(cursor[0]['_id']))
media=unicode(cursor[0]['slug']))
def uploader(self):
return self.db.User.find_one({'_id': self['uploader']})
class MediaComment(Document):
__collection__ = 'media_comments'

View File

@ -81,18 +81,25 @@ def remove_deprecated_indexes(database, deprecated_indexes=DEPRECATED_INDEXES):
Args:
- database: pymongo or mongokit database instance.
- deprecated_indexes: the indexes to deprecate in the pattern of:
{'collection': ['index_identifier1', 'index_identifier2']}
{'collection_name': {
'identifier': {
'index': [index_foo_goes_here],
'unique': True}}
(... although we really only need the 'identifier' here, as the
rest of the information isn't used in this case. But it's kept
around so we can remember what it was)
Returns:
A list of indexes removed in form ('collection', 'index_name')
"""
indexes_removed = []
for collection_name, index_names in deprecated_indexes.iteritems():
for collection_name, indexes in deprecated_indexes.iteritems():
collection = database[collection_name]
collection_indexes = collection.index_information().keys()
for index_name in index_names:
for index_name, index_data in indexes.iteritems():
if index_name in collection_indexes:
collection.drop_index(index_name)

View File

@ -23,7 +23,8 @@ class EditForm(wtforms.Form):
'Title',
[wtforms.validators.Length(min=0, max=500)])
slug = wtforms.TextField(
'Slug')
'Slug',
[wtforms.validators.Required(message="The slug can't be empty")])
description = wtforms.TextAreaField('Description of this work')
class EditProfileForm(wtforms.Form):
@ -31,4 +32,5 @@ class EditProfileForm(wtforms.Form):
[wtforms.validators.Length(min=0, max=500)])
url = wtforms.TextField(
'Website',
[wtforms.validators.URL(message='Improperly formed URL')])
[wtforms.validators.Optional(),
wtforms.validators.URL(message='Improperly formed URL')])

View File

@ -106,9 +106,9 @@ def edit_profile(request):
messages.add_message(request,
messages.SUCCESS,
'Profile edited!')
return redirect(request,
"mediagoblin.edit.profile",
username=edit_username)
return redirect(request,
'mediagoblin.user_pages.user_home',
user=edit_username)
return render_to_response(
request,

View File

@ -15,8 +15,33 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import jinja2
from mediagoblin import staticdirect
from mediagoblin.init.config import (
read_mediagoblin_config, generate_validation_report)
from mediagoblin import mg_globals
from mediagoblin.mg_globals import setup_globals
from mediagoblin.workbench import WorkbenchManager
class Error(Exception): pass
class ImproperlyConfigured(Error): pass
def setup_global_and_app_config(config_path):
global_config, validation_result = read_mediagoblin_config(config_path)
app_config = global_config['mediagoblin']
# report errors if necessary
validation_report = generate_validation_report(
global_config, validation_result)
if validation_report:
raise ImproperlyConfigured(validation_report)
setup_globals(
app_config=app_config,
global_config=global_config)
return global_config, app_config
def get_jinja_loader(user_template_path=None):
"""
Set up the Jinja template loaders, possibly allowing for user
@ -31,3 +56,27 @@ def get_jinja_loader(user_template_path=None):
jinja2.PackageLoader('mediagoblin', 'templates')])
else:
return jinja2.PackageLoader('mediagoblin', 'templates')
def get_staticdirector(app_config):
if app_config.has_key('direct_remote_path'):
return staticdirect.RemoteStaticDirect(
app_config['direct_remote_path'].strip())
elif app_config.has_key('direct_remote_paths'):
direct_remote_path_lines = app_config[
'direct_remote_paths'].strip().splitlines()
return staticdirect.MultiRemoteStaticDirect(
dict([line.strip().split(' ', 1)
for line in direct_remote_path_lines]))
else:
raise ImproperlyConfigured(
"One of direct_remote_path or "
"direct_remote_paths must be provided")
def setup_workbench():
app_config = mg_globals.app_config
workbench_manager = WorkbenchManager(app_config['workbench_path'])
setup_globals(workbench_manager = workbench_manager)

View File

@ -1,6 +1,6 @@
body {
background-color: #272727;
color: #f7f7f7;
background-color: #1F1F1F;
color: #aaa;
font-family: sans-serif;
padding:none;
margin:0px;
@ -18,7 +18,7 @@ form {
font-family: 'Carter One';
font-style: normal;
font-weight: normal;
src: local('CarterOne'), url('http://themes.googleusercontent.com/font?kit=VjW2qt1pkqVtO22ObxgEBRsxEYwM7FgeyaSgU71cLG0') format('woff');
src: local('CarterOne'), url('http://themes.googleusercontent.com/font?kit=FWNn6ITYqL6or7ZTmBxRhq3fkYX5z1QtDUdIWoaaD_k') format('woff');
}
/* text styles */
@ -33,13 +33,8 @@ h2{
margin-top:20px;
}
p {
font-family: sans-serif;
font-size:16px;
}
a {
color: #86D4B1;
color: #fff;
}
label {
@ -56,15 +51,33 @@ label {
.mediagoblin_header {
width:100%;
height:36px;
background-color:#393939;
background-color:#2F2F2F;
padding-top:14px;
margin-bottom:40px;
}
.header_submit{
color:#272727;
background-color:#aaa;
background-image: -webkit-gradient(linear, left top, left bottom, from(##D2D2D2), to(#aaa));
background-image: -webkit-linear-gradient(top, #D2D2D2, #aaa);
background-image: -moz-linear-gradient(top, #D2D2D2, #aaa);
background-image: -ms-linear-gradient(top, #D2D2D2, #aaa);
background-image: -o-linear-gradient(top, #D2D2D2, #aaa);
background-image: linear-gradient(top, #D2D2D2, #aaa);
box-shadow:0px 0px 4px #000;
border-radius:5px 5px 5px 5px;
margin:8px;
padding:3px 8px;
text-decoration:none;
border:medium none;
font-family:'Carter One',arial,serif;
}
.mediagoblin_footer {
width:100%;
height:26px;
background-color:#393939;
height:30px;
background-color:#2F2F2F;
bottom:0px;
padding-top:8px;
position:absolute;
@ -77,19 +90,6 @@ label {
padding-bottom:74px;
}
a.mediagoblin_logo {
width:34px;
height:25px;
margin-right:10px;
background-image:url('../images/icon.png');
background-position:0px 0px;
display:inline-block;
}
a.mediagoblin_logo:hover {
background-position:0px -28px;
}
.mediagoblin_header_right {
float:right;
}
@ -122,6 +122,10 @@ a.mediagoblin_logo:hover {
text-align:center;
}
.pagination_arrow{
margin:5px;
}
/* forms */
.form_box {
@ -130,7 +134,7 @@ text-align:center;
background-repeat:repeat-x;
font-size:18px;
padding-bottom:30px;
padding-top:1px;
padding-top:30px;
margin-left:auto;
margin-right:auto;
display:block;
@ -160,6 +164,7 @@ text-align:center;
.form_field_error {
background-color:#87453b;
color:#fff;
border:none;
font-size:16px;
padding:9px;
@ -204,14 +209,14 @@ img.media_icon{
/* navigation */
.navigation_button{
width: 139px;
width:139px;
display:block;
float:left;
text-align: center;
background-color: #393939;
text-decoration: none;
padding: 6px 0pt;
font-family: 'Carter One', arial, serif;
text-align:center;
background-color:#393939;
text-decoration:none;
padding:12px 0pt;
font-family:'Carter One', arial, serif;
font-size:2em;
margin:0 0 20px
}
@ -257,11 +262,3 @@ ul.mediagoblin_messages {
background-color: #f7f7f7;
color:#272727;
}
/* profile stuff */
.profile_content {
padding: 6px;
background-color: #393939;
margin-bottom: 10px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

View File

@ -18,7 +18,4 @@ from routes.route import Route
submit_routes = [
Route('mediagoblin.submit.start', '/',
controller='mediagoblin.submit.views:submit_start'),
Route('mediagoblin.submit.success', '/success/',
template='mediagoblin/submit/success.html',
controller='mediagoblin.views:simple_template_render')]
controller='mediagoblin.submit.views:submit_start')]

View File

@ -95,8 +95,3 @@ def submit_start(request):
request,
'mediagoblin/submit/start.html',
{'submit_form': submit_form})
def submit_success(request):
return render_to_response(
request, 'mediagoblin/submit/success.html', {})

View File

@ -35,7 +35,9 @@
<input type="hidden" name="next" value="{{ next }}" class="button"
style="display: none;"/>
{% endif %}
<p>Don't have an account yet?<br /><a href="{{ request.urlgen('mediagoblin.auth.register') }}">Create one here!</a></p>
{% if allow_registration %}
<p>Don't have an account yet?<br /><a href="{{ request.urlgen('mediagoblin.auth.register') }}">Create one here!</a></p>
{% endif %}
</div>
</form>
{% endblock %}

View File

@ -1,28 +0,0 @@
{#
# 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/>.
#}
{% extends "mediagoblin/base.html" %}
{% block mediagoblin_content %}
<p>
{% if verification_successful %}
Your email address has been verified!
{% else %}
The verification key or user id is incorrect
{% endif %}
</p>
{% endblock %}

View File

@ -38,8 +38,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 %}
<a class="mediagoblin_logo" href="{{ request.urlgen('index') }}"><img src="{{ request.staticdirect('/images/logo.png') }}" alt="Mediagoblin logo" /></a>
{% endblock %}
{% if request.user %}
<a class="header_submit" href="{{ request.urlgen('mediagoblin.submit.start') }}">Submit media</a>
{% endif %}
{% block mediagoblin_header_title %}{% endblock %}
<div class="mediagoblin_header_right">
{% if request.user %}
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',

View File

@ -29,10 +29,12 @@
If you have an account, you can
<a href="{{ request.urlgen('mediagoblin.auth.login') }}">Login</a>.
</p>
<p>
If you don't have an account, please
<a href="{{ request.urlgen('mediagoblin.auth.register') }}">Register</a>.
</p>
{% if allow_registration %}
<p>
If you don't have an account, please
<a href="{{ request.urlgen('mediagoblin.auth.register') }}">Register</a>.
</p>
{% endif %}
{% endif %}
{# temporarily, an "image gallery" that isn't one really ;) #}

View File

@ -1,22 +0,0 @@
{#
# 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/>.
#}
{% extends "mediagoblin/base.html" %}
{% block mediagoblin_content %}
Woohoo! Submitted!
{% endblock %}

View File

@ -55,7 +55,7 @@
<form action="{{ request.urlgen('mediagoblin.user_pages.media_post_comment',
user= media.uploader().username,
media=media._id) }}" method="POST">
{{ wtforms_util.render_field_div(comment_form.comment) }}
{{ wtforms_util.render_field_div(comment_form.comment_content) }}
<div class="form_submit_buttons">
<input type="submit" value="Post comment!" class="button" />
</div>
@ -65,7 +65,12 @@
{% if comments %}
{% for comment in comments %}
{% set comment_author = comment.author() %}
<div class="comment_wrapper" id="comment-{{ comment['_id'] }}">
{% if pagination.active_id == comment._id %}
<div class="comment_wrapper comment_active" id="comment-{{ comment['_id'] }}">
<a name="comment" id="comment"></a>
{% else %}
<div class="comment_wrapper" id="comment-{{ comment['_id'] }}">
{% endif %}
<div class="comment_content">
{% autoescape False %}
{{ comment.content_html }}
@ -77,7 +82,10 @@
{{ comment_author['username'] }}</a> at
<!--</div>
<div class="comment_datetime">-->
<a href="#comment-{{ comment['_id'] }}">
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home.view_comment',
comment = comment['_id'],
user = media.uploader().username,
media = media._id) }}#comment">
{{ "%4d-%02d-%02d %02d:%02d"|format(comment.created.year,
comment.created.month,
comment.created.day,
@ -88,7 +96,10 @@
</div>
{% endfor %}
{{ render_pagination(request, pagination) }}
{{ render_pagination(request, pagination,
request.urlgen('mediagoblin.user_pages.media_home',
user = media.uploader().username,
media = media._id)) }}
</div>
{% endif %}
<div class="grid_5 omega">

View File

@ -26,33 +26,25 @@
{% 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" %}
<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>
{% else %}
{# This *should* not occur as the view makes sure we pass in a user. #}
<p>Sorry, no such user found.<p/>
<h1>{{ user.username }}'s profile</h1>
<div class="grid_6 alpha">
{% 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 %}
</div>
<div class="grid_10 omega">
{% set pagination_base_url = user_gallery_url %}
{% include "mediagoblin/utils/object_gallery.html" %}
<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>
{% else %}
{# This *should* not occur as the view makes sure we pass in a user. #}
<p>Sorry, no such user found.<p/>
</div>
{% endif %}
{% endblock %}

View File

@ -34,9 +34,16 @@
{% if pagination.has_prev %}
<a href="{{ pagination.get_page_url_explicit(
base_url, get_params,
pagination.page - 1) }}">&laquo; Prev</a>
pagination.page - 1) }}"><img class="pagination_arrow" src="/mgoblin_static/images/pagination_left.png" alt="Previous page" />Newer</a>
{% endif %}
{% if pagination.has_next %}
<a href="{{ pagination.get_page_url_explicit(
base_url, get_params,
pagination.page + 1) }}">Older<img class="pagination_arrow" src="/mgoblin_static/images/pagination_right.png" alt="Next page" />
</a>
{% endif %}
<br />
Go to page:
{%- for page in pagination.iter_pages() %}
{% if page %}
{% if page != pagination.page %}
@ -50,12 +57,6 @@
<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 %}

View File

@ -24,20 +24,23 @@
{# 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;
<img src="/mgoblin_static/images/navigation_left.png" alt="Previous image" />
</a>
{% else %}
{# This is the first entry. display greyed-out 'previous' image #}
<p class="navigation_button">X</p>
<p class="navigation_button navigation_left">
<img src="/mgoblin_static/images/navigation_end.png" alt="No previous images" />
</p>
{% endif %}
{# Likewise, this could be the very last media entry #}
{% if next_entry_url %}
<a class="navigation_button" href="{{ next_entry_url }}">
&gt;
<img src="/mgoblin_static/images/navigation_right.png" alt="Next image" />
</a>
{% else %}
{# This is the last entry. display greyed-out 'next' image #}
<p class="navigation_button">X</p>
<p class="navigation_button">
<img src="/mgoblin_static/images/navigation_end.png" alt="No following images" />
</p>
{% endif %}
</div>

View File

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

View File

@ -189,7 +189,7 @@ def test_register_views(test_app):
"/auth/verify_email/?userid=%s&token=total_bs" % unicode(
new_user['_id']))
context = util.TEMPLATE_TEST_CONTEXT[
'mediagoblin/auth/verify_email.html']
'mediagoblin/user_pages/user.html']
assert context['verification_successful'] == False
new_user = mg_globals.database.User.find_one(
{'username': 'happygirl'})
@ -201,7 +201,7 @@ def test_register_views(test_app):
util.clear_test_template_context()
test_app.get("%s?%s" % (path, get_params))
context = util.TEMPLATE_TEST_CONTEXT[
'mediagoblin/auth/verify_email.html']
'mediagoblin/user_pages/user.html']
assert context['verification_successful'] == True
new_user = mg_globals.database.User.find_one(
{'username': 'happygirl'})

View File

@ -1,21 +1,22 @@
# 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/>.
import wtforms
class MediaCommentForm(wtforms.Form):
comment = wtforms.TextAreaField('Comment',
[wtforms.validators.Required()])
# 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/>.
import wtforms
class MediaCommentForm(wtforms.Form):
comment_content = wtforms.TextAreaField(
'Comment',
[wtforms.validators.Required()])

View File

@ -24,6 +24,9 @@ user_routes = [
Route('mediagoblin.user_pages.media_home', '/{user}/m/{media}/',
requirements=dict(m_id="[0-9a-fA-F]{24}"),
controller="mediagoblin.user_pages.views:media_home"),
Route('mediagoblin.user_pages.media_home.view_comment',
'/{user}/m/{media}/c/{comment}/',
controller="mediagoblin.user_pages.views:media_home"),
Route('mediagoblin.edit.edit_media', "/{user}/m/{media}/edit/",
controller="mediagoblin.edit.views:edit_media"),
Route('mediagoblin.user_pages.atom_feed', '/{user}/atom/',

View File

@ -95,8 +95,14 @@ def media_home(request, media, page, **kwargs):
"""
'Homepage' of a MediaEntry()
"""
if ObjectId(request.matchdict.get('comment')):
pagination = Pagination(
page, media.get_comments(), MEDIA_COMMENTS_PER_PAGE,
ObjectId(request.matchdict.get('comment')))
else:
pagination = Pagination(
page, media.get_comments(), MEDIA_COMMENTS_PER_PAGE)
pagination = Pagination(page, media.get_comments(), MEDIA_COMMENTS_PER_PAGE)
comments = pagination()
comment_form = user_forms.MediaCommentForm(request.POST)
@ -118,7 +124,7 @@ def media_post_comment(request):
comment = request.db.MediaComment()
comment['media_entry'] = ObjectId(request.matchdict['media'])
comment['author'] = request.user['_id']
comment['content'] = request.POST['comment']
comment['content'] = request.POST['comment_content']
comment['content_html'] = cleaned_markdown_conversion(comment['content'])

View File

@ -14,6 +14,8 @@
# 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/>.
from __future__ import division
from email.MIMEText import MIMEText
import gettext
import pkg_resources
@ -21,7 +23,7 @@ import smtplib
import sys
import re
import urllib
from math import ceil
from math import ceil, floor
import copy
from babel.localedata import exists
@ -35,6 +37,8 @@ from mediagoblin import mg_globals
from mediagoblin import messages
from mediagoblin.db.util import ObjectId
from itertools import izip, count
TESTS_ENABLED = False
def _activate_testing():
"""
@ -133,7 +137,16 @@ def render_to_response(request, template, context):
def redirect(request, *args, **kwargs):
"""Returns a HTTPFound(), takes a request and then urlgen params"""
return exc.HTTPFound(location=request.urlgen(*args, **kwargs))
querystring = None
if kwargs.get('querystring'):
querystring = kwargs.get('querystring')
del kwargs['querystring']
return exc.HTTPFound(
location=''.join([
request.urlgen(*args, **kwargs),
querystring if querystring else '']))
def setup_user_in_request(request):
@ -418,7 +431,8 @@ class Pagination(object):
get actual data slice through __call__().
"""
def __init__(self, page, cursor, per_page=PAGINATION_DEFAULT_PER_PAGE):
def __init__(self, page, cursor, per_page=PAGINATION_DEFAULT_PER_PAGE,
jump_to_id=False):
"""
Initializes Pagination
@ -426,11 +440,25 @@ class Pagination(object):
- page: requested page
- per_page: number of objects per page
- cursor: db cursor
- jump_to_id: ObjectId, sets the page to the page containing the object
with _id == jump_to_id.
"""
self.page = page
self.page = page
self.per_page = per_page
self.cursor = cursor
self.total_count = self.cursor.count()
self.active_id = None
if jump_to_id:
cursor = copy.copy(self.cursor)
for (doc, increment) in izip(cursor, count(0)):
if doc['_id'] == jump_to_id:
self.page = 1 + int(floor(increment / self.per_page))
self.active_id = jump_to_id
break
def __call__(self):
"""

View File

@ -14,6 +14,7 @@
# 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/>.
from mediagoblin import mg_globals
from mediagoblin.util import render_to_response
from mediagoblin.db.util import DESCENDING
@ -23,7 +24,8 @@ def root_view(request):
return render_to_response(
request, 'mediagoblin/root.html',
{'media_entries': media_entries})
{'media_entries': media_entries,
'allow_registration': mg_globals.app_config["allow_registration"]})
def simple_template_render(request):