Merge branch 'master' into test_submission_views_365

This commit is contained in:
Chris Moylan 2011-06-22 21:40:17 -05:00
commit dcebe4b032
24 changed files with 259 additions and 81 deletions

View File

@ -80,10 +80,10 @@ doing and why.
How to send us your changes
---------------------------
There are three ways to let us know how to get it:
There are two ways to let us know how to get it:
1. (preferred) **push changes to publicly available git clone and let
us know where to find it**
1. *(preferred)* **push changes to publicly available git clone and
let us know where to find it**
Push your feature/bugfix/issue branch to your publicly available
git clone and add a comment to the issue with the url for your
@ -93,14 +93,22 @@ There are three ways to let us know how to get it:
Run::
git format-patch -o patches <remote>/master
git format-patch --stdout <remote>/master > issue_<number>.patch
Then tar up the newly created ``patches`` directory and attach the
directory to the issue.
``format-patch`` creates a patch of all the commits that are in
your branch that aren't in ``<remote>/master``. The ``--stdout``
flag causes all this output to go to stdout where it's redirected
to a file named ``issue_<number>.patch``. That file should be
based on the issue you're working with. For example,
``issue_42.patch`` is a good filename and ``issue_42_rev2.patch``
is good if you did a revision of it.
Having said all that, the filename isn't wildly important.
Example workflow
================
Here's an example workflow.
@ -124,20 +132,30 @@ Slartibartfast does the following:
git fetch --all -p
This tells ``git fetch`` to fetch all the recent data from all of
the remotes (``--all``) and prune any branches that have been
deleted in the remotes (``-p``).
2. Creates a branch from the tip of the MediaGoblin repository (the
remote is named ``gmg``) master branch called ``bug42_meaning_of_life``::
git checkout -b bug42_meaning_of_life gmg/master
This creates a new branch (``-b``) named ``bug42_meaning_of_life`` based
on the tip of the ``master`` branch of the remote named ``gmg`` and checks
it out.
3. Slartibartfast works hard on his changes in the ``bug42_meaning_of_life``
branch. When done, he wants to notify us that he has made changes
he wants us to see.
4. Slartibartfast pushes his changes to his clone (the remote is named
``origin``)::
4. Slartibartfast pushes his changes to his clone::
git push origin bug42_meaning_of_life --set-upstream
This pushes the changes in the ``bug42_meaning_of_life`` branch to the
remote named ``origin``.
5. Slartibartfast adds a comment to issue 42 with the url for his
repository and the name of the branch he put the code in. He also
explains what he did and why it addresses the issue.

View File

@ -2,6 +2,10 @@
GNU MediaGoblin
=================
.. contents:: Sections
:local:
What is GNU MediaGoblin
=======================

View File

@ -50,5 +50,20 @@ class MediaEntryMigration(DocumentMigration):
'description_html': cleaned_markdown_conversion(
doc['description'])}}
MIGRATE_CLASSES = ['MediaEntry']
class UserMigration(DocumentMigration):
def allmigration01_add_bio_and_url_profile(self):
"""
User can elaborate profile with home page and biography
"""
self.target = {'url': {'$exists': False},
'bio': {'$exists': False}}
if not self.status:
for doc in self.collection.find(self.target):
self.update = {
'$set': {'url': '',
'bio': ''}}
self.collection.update(
self.target, self.update, multi=True, safe=True)
MIGRATE_CLASSES = ['MediaEntry', 'User']

View File

@ -46,6 +46,8 @@ class User(Document):
'status': unicode,
'verification_key': unicode,
'is_admin': bool,
'url' : unicode,
'bio' : unicode
}
required_fields = ['username', 'created', 'pw_hash', 'email']
@ -56,6 +58,8 @@ class User(Document):
'status': u'needs_email_verification',
'verification_key': lambda: unicode(uuid.uuid4()),
'is_admin': False}
migration_handler = migrations.UserMigration
def check_login(self, password):
"""

View File

@ -17,4 +17,5 @@
# Imports that other modules might use
from pymongo import DESCENDING
from pymongo.errors import InvalidId
from mongokit import ObjectId

View File

@ -15,11 +15,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from bson.errors import InvalidId
from webob import exc
from mediagoblin.util import redirect
from mediagoblin.db.util import ObjectId
from mediagoblin.db.util import ObjectId, InvalidId
def _make_safe(decorator, original):

View File

@ -25,3 +25,10 @@ class EditForm(wtforms.Form):
slug = wtforms.TextField(
'Slug')
description = wtforms.TextAreaField('Description of this work')
class EditProfileForm(wtforms.Form):
bio = wtforms.TextAreaField('Bio',
[wtforms.validators.Length(min=0, max=500)])
url = wtforms.TextField(
'Website',
[wtforms.validators.URL(message='Improperly formed URL')])

View File

@ -19,4 +19,5 @@ from routes.route import Route
edit_routes = [
# Media editing view handled in user_pages/routing.py
]
Route('mediagoblin.edit.profile', '/profile/',
controller="mediagoblin.edit.views:edit_profile")]

View File

@ -68,3 +68,25 @@ def edit_media(request, media):
'mediagoblin/edit/edit.html',
{'media': media,
'form': form})
@require_active_login
def edit_profile(request):
user = request.user
form = forms.EditProfileForm(request.POST,
url = user.get('url'),
bio = user.get('bio'))
if request.method == 'POST' and form.validate():
user['url'] = request.POST['url']
user['bio'] = request.POST['bio']
user.save()
return redirect(request, "index", user=user['username'])
return render_to_response(
request,
'mediagoblin/edit/edit_profile.html',
{'user': user,
'form': form})

View File

@ -69,23 +69,6 @@ a.mediagoblin_logo:hover {
float:right;
}
.button {
font-family:'Carter One', arial, serif;
height:32px;
min-width:99px;
background-color:#86d4b1;
box-shadow:0px 0px 4px #000;
border-radius:5px;
border:none;
color:#272727;
margin:10px;
font-size:1em;
display:block;
text-align:center;
padding-left:11px;
padding-right:11px;
}
/* common website elements */
.dotted_line {
@ -97,6 +80,28 @@ a.mediagoblin_logo:hover {
margin-top:-20px;
}
.button {
font-family:'Carter One', arial, serif;
height:32px;
min-width:99px;
background-color:#86d4b1;
background-image: -webkit-gradient(linear, left top, left bottom, from(#86d4b1), to(#62caa2));
background-image: -webkit-linear-gradient(top, #86d4b1, #62caa2);
background-image: -moz-linear-gradient(top, #86d4b1, #62caa2);
background-image: -ms-linear-gradient(top, #86d4b1, #62caa2);
background-image: -o-linear-gradient(top, #86d4b1, #62caa2);
background-image: linear-gradient(top, #86d4b1, #62caa2);
box-shadow:0px 0px 4px #000;
border-radius:5px;
border:none;
color:#272727;
margin:10px 0px 10px 15px;
font-size:1em;
text-align:center;
padding-left:11px;
padding-right:11px;
}
/* forms */
.form_box {
@ -104,8 +109,9 @@ a.mediagoblin_logo:hover {
margin-left:auto;
margin-right:auto;
background-color:#393939;
padding:0px 83px 30px 83px;
border-top:5px solid #d49086;
background-image:url("../images/background_lines.png");
background-repeat:repeat-x;
padding:1px 83px 30px 83px;
font-size:18px;
}
@ -113,6 +119,11 @@ a.mediagoblin_logo:hover {
width:600px;
}
.edit_box {
width:600px;
background-image:url("../images/background_edit.png");
}
.form_box h1 {
font-size:28px;
}
@ -139,6 +150,10 @@ a.mediagoblin_logo:hover {
margin-bottom:8px;
}
.form_submit_buttons {
text-align:right;
}
/* media pages */
img.media_image {
@ -147,14 +162,17 @@ img.media_image {
margin-right:auto;
}
li.media_thumbnail {
width: 200px;
min-height: 250px;
display: -moz-inline-stack;
display: inline-block;
vertical-align: top;
margin: 5px;
zoom: 1;
*display: inline;
_height: 250px;
ul.media_thumbnail {
padding:0px;
}
li.media_thumbnail {
width:200px;
height:133px;
display:-moz-inline-stack;
display:inline-block;
vertical-align:top;
margin:0px 10px 10px 0px;
zoom:1;
. *display:inline;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

View File

@ -35,6 +35,8 @@
<div class="mediagoblin_header_right">
{% if request.user %}
{{ request.user['username'] }}'s account
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
user= request.user['username']) }}">home</a>
<a href="{{ request.urlgen('mediagoblin.user_pages.user_gallery',
user= request.user['username']) }}">gallery</a>
(<a href="{{ request.urlgen('mediagoblin.auth.logout') }}">logout</a>)

View File

@ -20,19 +20,21 @@
{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
{% block mediagoblin_content %}
<h1>Edit details for {{ media.title }}</h1>
<form action="{{ request.urlgen('mediagoblin.edit.edit_media',
user= media.uploader().username,
media= media._id) }}"
method="POST" enctype="multipart/form-data">
<div class="submit_box form_box">
<div class="edit_box form_box">
<h1>Editing {{ media.title }}</h1>
{{ wtforms_util.render_divs(form) }}
<div class="form_submit_buttons">
<input type="submit" value="submit" class="button" />
<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>
<img src="{{ request.app.public_store.file_url(
media['media_files']['thumb']) }}" />
{% endblock %}

View File

@ -0,0 +1,35 @@
{#
# 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" %}
{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
{% block mediagoblin_content %}
<form action="{{ request.urlgen('mediagoblin.edit.profile',
user=user.username) }}"
method="POST" enctype="multipart/form-data">
<div class="edit_box form_box">
<h1>Editing {{ user['username'] }}'s profile</h1>
{{ wtforms_util.render_divs(form) }}
<div class="form_submit_buttons">
<input type="submit" value="submit" class="button" />
</div>
</div>
</form>
{% endblock %}

View File

@ -22,20 +22,19 @@
<h1>{% trans %}Welcome to GNU MediaGoblin!{% endtrans %}</h1>
{% if request.user %}
<p>
<a href="{{ request.urlgen('mediagoblin.submit.start') }}">Submit an item</a>.
</p>
<p>
<a href="{{ request.urlgen('mediagoblin.submit.start') }}">Submit an item</a>
<a href="{{ request.urlgen('mediagoblin.edit.profile') }}">Edit profile</a>
</p>
{% else %}
<p>
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>
<p>
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>
{% endif %}
{# temporarily, an "image gallery" that isn't one really ;) #}

View File

@ -27,7 +27,7 @@
<h1>Submit yer media</h1>
{{ wtforms_util.render_divs(submit_form) }}
<div class="form_submit_buttons">
<input type="submit" value="submit" class="button" />
<input type="submit" value="Submit" class="button" />
</div>
</div>
</form>

View File

@ -34,10 +34,12 @@
by
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
user= media.uploader().username) }}">
{{- media.uploader().username }}</a></p>
<p><a href="{{ request.urlgen('mediagoblin.edit.edit_media',
user= media.uploader().username,
media= media._id) }}">Edit</a></p>
{{- media.uploader().username }}</a></p>
{% 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 %}
{% else %}
<p>Sorry, no such media found.<p/>
{% endif %}

View File

@ -27,6 +27,8 @@
{% block mediagoblin_content -%}
{% if user %}
<h1>User page for '{{ user.username }}'</h1>
{% include "mediagoblin/utils/profile.html" %}
{% include "mediagoblin/utils/object_gallery.html" %}

View File

@ -19,7 +19,7 @@
{% block object_gallery_content -%}
<div>
{% if media_entries %}
<ul>
<ul class="media_thumbnail">
{% for entry in media_entries %}
<li class="media_thumbnail">
<a href="{{ entry.url_for_self(request.urlgen) }}">

View File

@ -0,0 +1,35 @@
{#
# 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/>.
#}
{% block profile_content -%}
<div>
<ul>
{% if user.url %}
<li>
<a href="{{ user.url }}">homepage</a>
</li>
{% endif %}
{% if user.bio %}
<li>
{{ user.bio }}
</li>
{% endif %}
</ul>
</div>
{% endblock %}

View File

@ -16,12 +16,17 @@
from mediagoblin import mg_globals
from mediagoblin.tests.tools import (
MEDIAGOBLIN_TEST_DB_NAME, suicide_if_bad_celery_environ)
def setup_package():
pass
suicide_if_bad_celery_environ()
def teardown_package():
if mg_globals.db_connection:
print "Killing db ..."
mg_globals.db_connection.drop_database(mg_globals.database.name)
print "... done"
if ((mg_globals.db_connection
and mg_globals.database.name == MEDIAGOBLIN_TEST_DB_NAME)):
print "Killing db ..."
mg_globals.db_connection.drop_database(MEDIAGOBLIN_TEST_DB_NAME)
print "... done"

View File

@ -28,7 +28,7 @@ from mediagoblin.decorators import _make_safe
from mediagoblin.db.open import setup_connection_and_db_from_config
MEDIAGOBLIN_TEST_DB_NAME = '__mediagoblinunittests__'
MEDIAGOBLIN_TEST_DB_NAME = u'__mediagoblin_tests__'
TEST_SERVER_CONFIG = pkg_resources.resource_filename(
'mediagoblin.tests', 'test_paste.ini')
TEST_APP_CONFIG = pkg_resources.resource_filename(
@ -42,17 +42,23 @@ USER_DEV_DIRECTORIES_TO_SETUP = [
'media/public', 'media/queue',
'beaker/sessions/data', 'beaker/sessions/lock']
BAD_CELERY_MESSAGE = """\
Sorry, you *absolutely* must run nosetests with the
mediagoblin.celery_setup.from_tests module. Like so:
$ CELERY_CONFIG_MODULE=mediagoblin.celery_setup.from_tests ./bin/nosetests"""
class BadCeleryEnviron(Exception): pass
def get_test_app(dump_old_app=True):
def suicide_if_bad_celery_environ():
if not os.environ.get('CELERY_CONFIG_MODULE') == \
'mediagoblin.celery_setup.from_tests':
raise BadCeleryEnviron(
u"Sorry, you *absolutely* must run nosetests with the\n"
u"mediagoblin.celery_setup.from_tests module. Like so:\n"
u"$ CELERY_CONFIG_MODULE=mediagoblin.celery_setup.from_tests ./bin/nosetests")
raise BadCeleryEnviron(BAD_CELERY_MESSAGE)
def get_test_app(dump_old_app=True):
suicide_if_bad_celery_environ()
global MGOBLIN_APP
global CELERY_SETUP
@ -78,6 +84,7 @@ def get_test_app(dump_old_app=True):
# @@: For now we're dropping collections, but we could also just
# collection.remove() ?
connection, db = setup_connection_and_db_from_config(app_config)
assert db.name == MEDIAGOBLIN_TEST_DB_NAME
collections_to_wipe = [
collection
@ -87,10 +94,6 @@ def get_test_app(dump_old_app=True):
for collection in collections_to_wipe:
db.drop_collection(collection)
# Don't need these anymore...
del(connection)
del(db)
# TODO: Drop and recreate indexes
# setup app and return

View File

@ -373,6 +373,10 @@ HTML_CLEANER = Cleaner(
def clean_html(html):
# clean_html barfs on an empty string
if not html:
return u''
return HTML_CLEANER.clean_html(html)