Merge branch 'master' into test_submission_views_365
This commit is contained in:
commit
4ce57ac85d
@ -40,10 +40,6 @@ def setup_celery_from_config(app_config, global_config,
|
||||
- set_environ: if set, this will CELERY_CONFIG_MODULE to the
|
||||
settings_module
|
||||
"""
|
||||
if app_config.get('celery_setup_elsewhere') == True:
|
||||
# Don't setup celery based on our config file.
|
||||
return
|
||||
|
||||
if global_config.has_key('celery'):
|
||||
celery_conf = global_config['celery']
|
||||
else:
|
||||
|
@ -23,7 +23,8 @@ from mediagoblin.celery_setup import setup_celery_from_config
|
||||
OUR_MODULENAME = __name__
|
||||
|
||||
|
||||
def setup_self(check_environ_for_conf=True, module_name=OUR_MODULENAME):
|
||||
def setup_self(check_environ_for_conf=True, module_name=OUR_MODULENAME,
|
||||
default_conf_file='mediagoblin.ini'):
|
||||
"""
|
||||
Transform this module into a celery config module by reading the
|
||||
mediagoblin config file. Set the environment variable
|
||||
@ -36,9 +37,9 @@ def setup_self(check_environ_for_conf=True, module_name=OUR_MODULENAME):
|
||||
"""
|
||||
if check_environ_for_conf:
|
||||
mgoblin_conf_file = os.path.abspath(
|
||||
os.environ.get('MEDIAGOBLIN_CONFIG', 'mediagoblin.ini'))
|
||||
os.environ.get('MEDIAGOBLIN_CONFIG', default_conf_file))
|
||||
else:
|
||||
mgoblin_conf_file = 'mediagoblin.ini'
|
||||
mgoblin_conf_file = default_conf_file
|
||||
|
||||
if not os.path.exists(mgoblin_conf_file):
|
||||
raise IOError(
|
||||
@ -48,6 +49,7 @@ def setup_self(check_environ_for_conf=True, module_name=OUR_MODULENAME):
|
||||
# this is the module that gets set up.
|
||||
os.environ['CELERY_CONFIG_MODULE'] = module_name
|
||||
app.MediaGoblinApp(mgoblin_conf_file, setup_celery=False)
|
||||
|
||||
setup_celery_from_config(
|
||||
mg_globals.app_config, mg_globals.global_config,
|
||||
settings_module=module_name,
|
||||
|
@ -16,11 +16,15 @@
|
||||
|
||||
import os
|
||||
|
||||
from mediagoblin.tests.tools import TEST_APP_CONFIG
|
||||
from mediagoblin.celery_setup.from_celery import setup_self
|
||||
|
||||
|
||||
OUR_MODULENAME = __name__
|
||||
CELERY_SETUP = False
|
||||
|
||||
|
||||
if os.environ.get('CELERY_CONFIG_MODULE') == OUR_MODULENAME:
|
||||
setup_self(check_environ_for_conf=False, module_name=OUR_MODULENAME)
|
||||
setup_self(check_environ_for_conf=False, module_name=OUR_MODULENAME,
|
||||
default_conf_file=TEST_APP_CONFIG)
|
||||
CELERY_SETUP = True
|
||||
|
118
mediagoblin/db/indexes.py
Normal file
118
mediagoblin/db/indexes.py
Normal file
@ -0,0 +1,118 @@
|
||||
# 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/>.
|
||||
|
||||
"""
|
||||
Indexes for the local database.
|
||||
|
||||
To add new indexes
|
||||
------------------
|
||||
|
||||
Indexes are recorded in the following format:
|
||||
|
||||
ACTIVE_INDEXES = {
|
||||
'collection_name': {
|
||||
'identifier': { # key identifier used for possibly deprecating later
|
||||
'index': [index_foo_goes_here]}}
|
||||
|
||||
... and anything else being parameters to the create_index function
|
||||
(including unique=True, etc)
|
||||
|
||||
Current indexes must be registered in ACTIVE_INDEXES... deprecated
|
||||
indexes should be marked in DEPRECATED_INDEXES.
|
||||
|
||||
Remember, ordering of compound indexes MATTERS. Read below for more.
|
||||
|
||||
REQUIRED READING:
|
||||
- http://kylebanker.com/blog/2010/09/21/the-joy-of-mongodb-indexes/
|
||||
|
||||
- http://www.mongodb.org/display/DOCS/Indexes
|
||||
- http://www.mongodb.org/display/DOCS/Indexing+Advice+and+FAQ
|
||||
|
||||
|
||||
To remove deprecated indexes
|
||||
----------------------------
|
||||
|
||||
Removing deprecated indexes is easier, just do:
|
||||
|
||||
INACTIVE_INDEXES = {
|
||||
'collection_name': [
|
||||
'deprecated_index_identifier1', 'deprecated_index_identifier2']}
|
||||
|
||||
... etc.
|
||||
|
||||
If an index has been deprecated that identifier should NEVER BE USED
|
||||
AGAIN. Eg, if you previously had 'awesomepants_unique', you shouldn't
|
||||
use 'awesomepants_unique' again, you should create a totally new name
|
||||
or at worst use 'awesomepants_unique2'.
|
||||
"""
|
||||
|
||||
from pymongo import ASCENDING, DESCENDING
|
||||
|
||||
|
||||
################
|
||||
# Active indexes
|
||||
################
|
||||
ACTIVE_INDEXES = {}
|
||||
|
||||
# MediaEntry indexes
|
||||
# ------------------
|
||||
|
||||
MEDIAENTRY_INDEXES = {
|
||||
'uploader_slug_unique': {
|
||||
# Matching an object to an uploader + slug.
|
||||
# MediaEntries are unique on these two combined, eg:
|
||||
# /u/${myuser}/m/${myslugname}/
|
||||
'index': [('uploader', ASCENDING),
|
||||
('slug', ASCENDING)],
|
||||
'unique': True},
|
||||
|
||||
'created': {
|
||||
# A global index for all media entries created, in descending
|
||||
# order. This is used for the site's frontpage.
|
||||
'index': [('created', DESCENDING)]},
|
||||
|
||||
'uploader_created': {
|
||||
# Indexing on uploaders and when media entries are created.
|
||||
# Used for showing a user gallery, etc.
|
||||
'index': [('uploader', ASCENDING),
|
||||
('created', DESCENDING)]}}
|
||||
|
||||
|
||||
ACTIVE_INDEXES['media_entries'] = MEDIAENTRY_INDEXES
|
||||
|
||||
|
||||
# User indexes
|
||||
# ------------
|
||||
|
||||
USER_INDEXES = {
|
||||
'username_unique': {
|
||||
# Index usernames, and make sure they're unique.
|
||||
# ... I guess we might need to adjust this once we're federated :)
|
||||
'index': 'username',
|
||||
'unique': True},
|
||||
'created': {
|
||||
# All most recently created users
|
||||
'index': 'created'}}
|
||||
|
||||
|
||||
ACTIVE_INDEXES['users'] = USER_INDEXES
|
||||
|
||||
|
||||
####################
|
||||
# Deprecated indexes
|
||||
####################
|
||||
|
||||
DEPRECATED_INDEXES = {}
|
@ -108,11 +108,6 @@ class MediaEntry(Document):
|
||||
|
||||
migration_handler = migrations.MediaEntryMigration
|
||||
|
||||
indexes = [
|
||||
# Referene uniqueness of slugs by uploader
|
||||
{'fields': ['uploader', 'slug'],
|
||||
'unique': True}]
|
||||
|
||||
def main_mediafile(self):
|
||||
pass
|
||||
|
||||
|
@ -14,8 +14,88 @@
|
||||
# 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/>.
|
||||
|
||||
"""
|
||||
Utilities for database operations.
|
||||
|
||||
Some note on migration and indexing tools:
|
||||
|
||||
We store information about what the state of the database is in the
|
||||
'mediagoblin' document of the 'app_metadata' collection. Keys in that
|
||||
document relevant to here:
|
||||
|
||||
- 'migration_number': The integer representing the current state of
|
||||
the migrations
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
# Imports that other modules might use
|
||||
from pymongo import DESCENDING
|
||||
from pymongo.errors import InvalidId
|
||||
from mongokit import ObjectId
|
||||
|
||||
from mediagoblin.db.indexes import ACTIVE_INDEXES, DEPRECATED_INDEXES
|
||||
|
||||
|
||||
def add_new_indexes(database, active_indexes=ACTIVE_INDEXES):
|
||||
"""
|
||||
Add any new indexes to the database.
|
||||
|
||||
Args:
|
||||
- database: pymongo or mongokit database instance.
|
||||
- active_indexes: indexes to possibly add in the pattern of:
|
||||
{'collection_name': {
|
||||
'identifier': {
|
||||
'index': [index_foo_goes_here],
|
||||
'unique': True}}
|
||||
where 'index' is the index to add and all other options are
|
||||
arguments for collection.create_index.
|
||||
|
||||
Returns:
|
||||
A list of indexes added in form ('collection', 'index_name')
|
||||
"""
|
||||
indexes_added = []
|
||||
|
||||
for collection_name, indexes in active_indexes.iteritems():
|
||||
collection = database[collection_name]
|
||||
collection_indexes = collection.index_information().keys()
|
||||
|
||||
for index_name, index_data in indexes.iteritems():
|
||||
if not index_name in collection_indexes:
|
||||
# Get a copy actually so we don't modify the actual
|
||||
# structure
|
||||
index_data = copy.copy(index_data)
|
||||
index = index_data.pop('index')
|
||||
collection.create_index(
|
||||
index, name=index_name, **index_data)
|
||||
|
||||
indexes_added.append((collection_name, index_name))
|
||||
|
||||
return indexes_added
|
||||
|
||||
|
||||
def remove_deprecated_indexes(database, deprecated_indexes=DEPRECATED_INDEXES):
|
||||
"""
|
||||
Remove any deprecated indexes from the database.
|
||||
|
||||
Args:
|
||||
- database: pymongo or mongokit database instance.
|
||||
- deprecated_indexes: the indexes to deprecate in the pattern of:
|
||||
{'collection': ['index_identifier1', 'index_identifier2']}
|
||||
|
||||
Returns:
|
||||
A list of indexes removed in form ('collection', 'index_name')
|
||||
"""
|
||||
indexes_removed = []
|
||||
|
||||
for collection_name, index_names in deprecated_indexes.iteritems():
|
||||
collection = database[collection_name]
|
||||
collection_indexes = collection.index_information().keys()
|
||||
|
||||
for index_name in index_names:
|
||||
if index_name in collection_indexes:
|
||||
collection.drop_index(index_name)
|
||||
|
||||
indexes_removed.append((collection_name, index_name))
|
||||
|
||||
return indexes_removed
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
|
||||
from mediagoblin.db import migrations
|
||||
from mediagoblin.db import util as db_util
|
||||
from mediagoblin.gmg_commands import util as commands_util
|
||||
|
||||
|
||||
@ -27,8 +28,17 @@ def migrate_parser_setup(subparser):
|
||||
|
||||
def migrate(args):
|
||||
mgoblin_app = commands_util.setup_app(args)
|
||||
print "Applying migrations..."
|
||||
|
||||
# Clear old indexes
|
||||
print "== Clearing old indexes... =="
|
||||
removed_indexes = db_util.remove_deprecated_indexes(mgoblin_app.db)
|
||||
|
||||
for collection, index_name in removed_indexes:
|
||||
print "Removed index '%s' in collection '%s'" % (
|
||||
index_name, collection)
|
||||
|
||||
# Migrate
|
||||
print "== Applying migrations... =="
|
||||
for model_name in migrations.MIGRATE_CLASSES:
|
||||
model = getattr(mgoblin_app.db, model_name)
|
||||
|
||||
@ -38,4 +48,10 @@ def migrate(args):
|
||||
migration = model.migration_handler(model)
|
||||
migration.migrate_all(collection=model.collection)
|
||||
|
||||
print "... done."
|
||||
# Add new indexes
|
||||
print "== Adding new indexes... =="
|
||||
new_indexes = db_util.add_new_indexes(mgoblin_app.db)
|
||||
|
||||
for collection, index_name in new_indexes:
|
||||
print "Added index '%s' to collection '%s'" % (
|
||||
index_name, collection)
|
||||
|
34
mediagoblin/messages.py
Normal file
34
mediagoblin/messages.py
Normal file
@ -0,0 +1,34 @@
|
||||
# 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/>.
|
||||
|
||||
DEBUG = 'debug'
|
||||
INFO = 'info'
|
||||
SUCCESS = 'success'
|
||||
WARNING = 'warning'
|
||||
ERROR = 'error'
|
||||
|
||||
def add_message(request, level, text):
|
||||
messages = request.session.setdefault('messages', [])
|
||||
messages.append({'level': level, 'text': text})
|
||||
request.session.save()
|
||||
|
||||
def fetch_messages(request, clear_from_session=True):
|
||||
messages = request.session.get('messages')
|
||||
if messages and clear_from_session:
|
||||
# Save that we removed the messages from the session
|
||||
request.session['messages'] = []
|
||||
request.session.save()
|
||||
return messages
|
@ -4,6 +4,12 @@ body {
|
||||
font-family: sans-serif;
|
||||
padding:none;
|
||||
margin:0px;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
form {
|
||||
margin:0px;
|
||||
padding:0px;
|
||||
}
|
||||
|
||||
/* Carter One font */
|
||||
@ -38,6 +44,11 @@ label {
|
||||
|
||||
/* website structure */
|
||||
|
||||
.mediagoblin_body {
|
||||
position:relative;
|
||||
min-height:100%;
|
||||
}
|
||||
|
||||
.mediagoblin_header {
|
||||
width:100%;
|
||||
height:36px;
|
||||
@ -46,6 +57,63 @@ label {
|
||||
margin-bottom:40px;
|
||||
}
|
||||
|
||||
.mediagoblin_footer {
|
||||
width:100%;
|
||||
height:26px;
|
||||
background-color:#393939;
|
||||
bottom:0px;
|
||||
padding-top:8px;
|
||||
position:absolute;
|
||||
text-align:center;
|
||||
font-size:14px;
|
||||
color:#999;
|
||||
}
|
||||
|
||||
.mediagoblin_content {
|
||||
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;
|
||||
@ -119,8 +187,8 @@ a.mediagoblin_logo:hover {
|
||||
font-size:28px;
|
||||
}
|
||||
|
||||
.form_field_input input {
|
||||
width:300px;
|
||||
.form_field_input input, .form_field_input textarea {
|
||||
width:100%;
|
||||
font-size:18px;
|
||||
}
|
||||
|
||||
@ -159,11 +227,12 @@ ul.media_thumbnail {
|
||||
|
||||
li.media_thumbnail {
|
||||
width:200px;
|
||||
height:133px;
|
||||
height:200px;
|
||||
display:-moz-inline-stack;
|
||||
display:inline-block;
|
||||
vertical-align:top;
|
||||
margin:0px 10px 10px 0px;
|
||||
text-align:center;
|
||||
zoom:1;
|
||||
. *display:inline;
|
||||
}
|
||||
|
@ -28,34 +28,49 @@
|
||||
|
||||
<body>
|
||||
{% block mediagoblin_body %}
|
||||
<div class="mediagoblin_body">
|
||||
{% block mediagoblin_header %}
|
||||
<div class="mediagoblin_header">
|
||||
<div class="container_12">
|
||||
<div class="grid_12">
|
||||
{% block mediagoblin_logo %}
|
||||
<a class="mediagoblin_logo" href="{{ request.urlgen('index') }}"></a>
|
||||
{% endblock %}{% block mediagoblin_header_title %}{% endblock %}
|
||||
<div class="mediagoblin_header_right">
|
||||
{% if request.user %}
|
||||
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
|
||||
user= request.user['username']) }}">
|
||||
{{ request.user['username'] }}</a>'s account
|
||||
(<a href="{{ request.urlgen('mediagoblin.auth.logout') }}">logout</a>)
|
||||
{% else %}
|
||||
<a href="{{ request.urlgen('mediagoblin.auth.login') }}">
|
||||
Login</a>
|
||||
{% endif %}
|
||||
<div class="mediagoblin_header">
|
||||
<div class="container_12">
|
||||
<div class="grid_12">
|
||||
{% block mediagoblin_logo %}
|
||||
<a class="mediagoblin_logo" href="{{ request.urlgen('index') }}"></a>
|
||||
{% endblock %}{% block mediagoblin_header_title %}{% endblock %}
|
||||
<div class="mediagoblin_header_right">
|
||||
{% if request.user %}
|
||||
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
|
||||
user= request.user['username']) }}">
|
||||
{{ request.user['username'] }}</a>'s account
|
||||
(<a href="{{ request.urlgen('mediagoblin.auth.logout') }}">logout</a>)
|
||||
{% else %}
|
||||
<a href="{{ request.urlgen('mediagoblin.auth.login') }}">
|
||||
Login</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
<div class="container_12">
|
||||
|
||||
{% include "mediagoblin/utils/messages.html" %}
|
||||
|
||||
<div class="container_12 mediagoblin_content">
|
||||
<div class="grid_12">
|
||||
{% block mediagoblin_content %}
|
||||
{% endblock mediagoblin_content %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% block mediagoblin_footer %}
|
||||
<div class="mediagoblin_footer">
|
||||
<div class="container_12">
|
||||
<div class="grid_12">
|
||||
Powered by <a href="http://mediagoblin.org">MediaGoblin</a>, a <a href="http://gnu.org/">GNU project</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock mediagoblin_body %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -27,13 +27,15 @@
|
||||
method="POST" enctype="multipart/form-data">
|
||||
<div class="grid_6 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(
|
||||
media['media_files']['thumb']) }}" />
|
||||
</div>
|
||||
{{ wtforms_util.render_divs(form) }}
|
||||
<div class="form_submit_buttons">
|
||||
<a href="{{ media.url_for_self(request.urlgen) }}">Cancel</a>
|
||||
<input type="submit" value="Save changes" class="button" />
|
||||
</div>
|
||||
<img src="{{ request.app.public_store.file_url(
|
||||
media['media_files']['thumb']) }}" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
@ -25,7 +25,9 @@
|
||||
method="POST" enctype="multipart/form-data">
|
||||
<div class="grid_6 prefix_1 suffix_1 form_box">
|
||||
<h1>Submit yer media</h1>
|
||||
{{ wtforms_util.render_divs(submit_form) }}
|
||||
{{ 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>
|
||||
|
@ -20,15 +20,16 @@
|
||||
{% 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><a href="{{ request.urlgen(
|
||||
'mediagoblin.user_pages.user_home',
|
||||
user=user.username) }}">{{ user.username }}</a>'s media</h1>
|
||||
<h1>
|
||||
<a href="{{ request.urlgen(
|
||||
'mediagoblin.user_pages.user_home',
|
||||
user=user.username) }}">{{ user.username }}</a>'s media</h1>
|
||||
|
||||
{% include "mediagoblin/utils/object_gallery.html" %}
|
||||
|
||||
|
@ -25,16 +25,18 @@
|
||||
</h1>
|
||||
<img class="media_image" src="{{ request.app.public_store.file_url(
|
||||
media.media_files.main) }}" />
|
||||
<p>
|
||||
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>
|
||||
{% autoescape False %}
|
||||
<p>{{ media.description_html }}</p>
|
||||
<p>{{ media.description_html }}</p>
|
||||
{% endautoescape %}
|
||||
<p>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>
|
||||
{% if media['uploader'] == request.user['_id'] %}
|
||||
<p><a href="{{ request.urlgen('mediagoblin.edit.edit_media',
|
||||
user= media.uploader().username,
|
||||
|
32
mediagoblin/templates/mediagoblin/utils/messages.html
Normal file
32
mediagoblin/templates/mediagoblin/utils/messages.html
Normal file
@ -0,0 +1,32 @@
|
||||
{#
|
||||
# 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/>.
|
||||
#}
|
||||
|
||||
{# Display any queued messages #}
|
||||
{% set messages = fetch_messages(request) %}
|
||||
{% if messages %}
|
||||
<div class="container_12 mediagoblin_messages">
|
||||
<div class="grid_12">
|
||||
<ul class="mediagoblin_messages">
|
||||
{% for msg in messages %}
|
||||
<li class="message_{{ msg.level }}">{{ msg.text }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -16,23 +16,47 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{# Generically render a field #}
|
||||
{% macro render_field_div(field) %}
|
||||
<div class="form_field_box">
|
||||
<div class="form_field_label">{{ field.label }}</div>
|
||||
{% if field.description -%}
|
||||
<div class="form_field_description">{{ field.description }}</div>
|
||||
{%- endif %}
|
||||
<div class="form_field_input">{{ field }}</div>
|
||||
{%- if field.errors -%}
|
||||
{% for error in field.errors %}
|
||||
<div class="form_field_error">
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
{# Generically render a textarea
|
||||
# ... mostly the same thing except it includes rows and cols #}
|
||||
{% macro render_textarea_div(field, rows=8, cols=20) %}
|
||||
<div class="form_field_box">
|
||||
<div class="form_field_label">{{ field.label }}</div>
|
||||
{% if field.description -%}
|
||||
<div class="form_field_description">{{ field.description }}</div>
|
||||
{%- endif %}
|
||||
<div class="form_field_input">{{ field(rows=rows, cols=cols) }}</div>
|
||||
{%- if field.errors -%}
|
||||
{% for error in field.errors %}
|
||||
<div class="form_field_error">
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
{# Auto-render a form as a series of divs #}
|
||||
{% macro render_divs(form) -%}
|
||||
{% for field in form %}
|
||||
<div class="form_field_box">
|
||||
<div class="form_field_label">{{ field.label }}</div>
|
||||
{% if field.description -%}
|
||||
<div class="form_field_description">{{ field.description }}</div>
|
||||
{%- endif %}
|
||||
<div class="form_field_input">{{ field }}</div>
|
||||
{%- if field.errors -%}
|
||||
{% for error in field.errors %}
|
||||
<div class="form_field_error">
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
</div>
|
||||
{{ render_field_div(field) }}
|
||||
{% endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
|
44
mediagoblin/tests/test_messages.py
Normal file
44
mediagoblin/tests/test_messages.py
Normal file
@ -0,0 +1,44 @@
|
||||
# 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/>.
|
||||
|
||||
from mediagoblin.messages import fetch_messages, add_message
|
||||
from mediagoblin.tests.tools import setup_fresh_app
|
||||
from mediagoblin import util
|
||||
|
||||
|
||||
@setup_fresh_app
|
||||
def test_messages(test_app):
|
||||
"""
|
||||
Added messages should show up in the request.session,
|
||||
fetched messages should be the same as the added ones,
|
||||
and fetching should clear the message list.
|
||||
"""
|
||||
# Aquire a request object
|
||||
test_app.get('/')
|
||||
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
|
||||
request = context['request']
|
||||
|
||||
# The message queue should be empty
|
||||
assert request.session.get('messages', []) == []
|
||||
|
||||
# Adding a message should modify the session accordingly
|
||||
add_message(request, 'herp_derp', 'First!')
|
||||
test_msg_queue = [{'text': 'First!', 'level': 'herp_derp'}]
|
||||
assert request.session['messages'] == test_msg_queue
|
||||
|
||||
# fetch_messages should return and empty the queue
|
||||
assert fetch_messages(request) == test_msg_queue
|
||||
assert request.session.get('messages') == []
|
@ -7,6 +7,9 @@ email_sender_address = "notice@mediagoblin.example.org"
|
||||
email_debug_mode = true
|
||||
db_name = __mediagoblin_tests__
|
||||
|
||||
# Celery shouldn't be set up by the paste app factory as it's set up
|
||||
# elsewhere
|
||||
# Celery shouldn't be set up by the application as it's setup via
|
||||
# mediagoblin.celery_setup.from_celery
|
||||
celery_setup_elsewhere = true
|
||||
|
||||
[celery]
|
||||
celery_always_eager = true
|
||||
|
@ -21,9 +21,8 @@ import os, shutil
|
||||
from paste.deploy import loadapp
|
||||
from webtest import TestApp
|
||||
|
||||
from mediagoblin import util, mg_globals
|
||||
from mediagoblin import util
|
||||
from mediagoblin.config import read_mediagoblin_config
|
||||
from mediagoblin.celery_setup import setup_celery_from_config
|
||||
from mediagoblin.decorators import _make_safe
|
||||
from mediagoblin.db.open import setup_connection_and_db_from_config
|
||||
|
||||
@ -36,7 +35,6 @@ TEST_APP_CONFIG = pkg_resources.resource_filename(
|
||||
TEST_USER_DEV = pkg_resources.resource_filename(
|
||||
'mediagoblin.tests', 'test_user_dev')
|
||||
MGOBLIN_APP = None
|
||||
CELERY_SETUP = False
|
||||
|
||||
USER_DEV_DIRECTORIES_TO_SETUP = [
|
||||
'media/public', 'media/queue',
|
||||
@ -60,8 +58,10 @@ def suicide_if_bad_celery_environ():
|
||||
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
|
||||
|
||||
global MGOBLIN_APP
|
||||
global CELERY_SETUP
|
||||
|
||||
# Just return the old app if that exists and it's okay to set up
|
||||
# and return
|
||||
@ -103,13 +103,6 @@ def get_test_app(dump_old_app=True):
|
||||
app = TestApp(test_app)
|
||||
MGOBLIN_APP = app
|
||||
|
||||
# setup celery
|
||||
if not CELERY_SETUP:
|
||||
setup_celery_from_config(
|
||||
mg_globals.app_config, mg_globals.global_config,
|
||||
set_environ=True)
|
||||
CELERY_SETUP = True
|
||||
|
||||
return app
|
||||
|
||||
|
||||
|
@ -32,6 +32,7 @@ from lxml.html.clean import Cleaner
|
||||
import markdown
|
||||
|
||||
from mediagoblin import mg_globals
|
||||
from mediagoblin import messages
|
||||
from mediagoblin.db.util import ObjectId
|
||||
|
||||
TESTS_ENABLED = False
|
||||
@ -104,6 +105,10 @@ def get_jinja_env(template_loader, locale):
|
||||
mg_globals.translations.gettext,
|
||||
mg_globals.translations.ngettext)
|
||||
|
||||
# All templates will know how to ...
|
||||
# ... fetch all waiting messages and remove them from the queue
|
||||
template_env.globals['fetch_messages'] = messages.fetch_messages
|
||||
|
||||
if exists(locale):
|
||||
SETUP_JINJA_ENVS[locale] = template_env
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user