Merge remote-tracking branch 'origin/master' into is315

This commit is contained in:
cfdv 2011-06-20 12:50:44 -05:00
commit 78c0744077
15 changed files with 160 additions and 34 deletions

View File

@ -108,8 +108,8 @@ Contributing changes
--------------------
Slartibartfast from the planet Magrathea far off in the universe has
decided that he is bored with fjords and wants to fix issue 42 and
send us the changes.
decided that he is bored with fjords and wants to fix issue 42 (the
meaning of life bug) and send us the changes.
Slartibartfast has cloned the MediaGoblin repository and his clone
lives on gitorious.
@ -125,18 +125,18 @@ Slartibartfast does the following:
git fetch --all -p
2. Creates a branch from the tip of the MediaGoblin repository (the
remote is named ``gmg``) master branch called ``issue_42``::
remote is named ``gmg``) master branch called ``bug42_meaning_of_life``::
git checkout -b issue_42 gmg/master
git checkout -b bug42_meaning_of_life gmg/master
3. Slartibartfast works hard on his changes in the ``issue_42``
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``)::
git push origin issue_42 --set-upstream
git push origin bug42_meaning_of_life --set-upstream
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
@ -155,19 +155,19 @@ He runs the unit tests and discovers there's a bug in the code!
Then he does this:
1. He checks out the ``issue_42`` branch::
1. He checks out the ``bug42_meaning_of_life`` branch::
git checkout issue_42
git checkout bug42_meaning_of_life
2. He fixes the bug and checks it into the ``issue_42`` branch.
2. He fixes the bug and checks it into the ``bug42_meaning_of_life`` branch.
3. He pushes his changes to his clone (the remote is named ``origin``)::
git push origin issue_42
git push origin bug42_meaning_of_life
4. He adds another comment to issue 42 explaining about the mistake
and how he fixed it and that he's pushed the new change to the
``issue_42`` branch of his publicly available clone.
``bug42_meaning_of_life`` branch of his publicly available clone.
What happens next
@ -180,7 +180,7 @@ request with his changes and explains what they are.
Later, someone checks out his code and finds a problem with it. He
adds a comment to the issue tracker specifying the problem and asks
Slartibartfast to fix it. Slartibartfst goes through the above steps
again, fixes the issue, pushes it to his ``issue_42`` branch and adds
again, fixes the issue, pushes it to his ``bug42_meaning_of_life`` branch and adds
another comment to the issue tracker about how he fixed it.
Later, someone checks out his code and is happy with it. Someone
@ -192,8 +192,8 @@ Slartibartfast is notified of this. Slartibartfast does a::
git fetch --all
The changes show up in the ``master`` branch of the ``gmg`` remote.
Slartibartfast now deletes his ``issue_42`` branch because he doesn't
need it anymore.
Slartibartfast now deletes his ``bug42_meaning_of_life`` branch
because he doesn't need it anymore.
How to learn git

View File

@ -136,7 +136,7 @@ This is fine in development, but if you want to actually run celery
separately for testing (or deployment purposes), you'll want to run
the server independently::
./bin/paster serve server.ini --reload
./bin/paster serve paste.ini --reload
Running celeryd
@ -158,7 +158,7 @@ Running the test suite
Run::
./bin/nosetests
./runtests.sh
Running a shell

View File

@ -27,4 +27,4 @@ else
exit 1
fi
CELERY_ALWAYS_EAGER=true $PASTER serve server.ini --reload
CELERY_ALWAYS_EAGER=true $PASTER serve paste.ini --reload

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 mediagoblin.util import cleaned_markdown_conversion
from mongokit import DocumentMigration
@ -33,5 +35,19 @@ class MediaEntryMigration(DocumentMigration):
self.collection.update(
self.target, self.update, multi=True, safe=True)
def allmigration02_add_description_html(self):
"""
Now that we can have rich descriptions via Markdown, we should
update all existing entries to record the rich description versions.
"""
self.target = {'description_html': {'$exists': False}}
if not self.status:
for doc in self.collection.find(self.target):
self.update = {
'$set': {
'description_html': cleaned_markdown_conversion(
doc['description'])}}
MIGRATE_CLASSES = ['MediaEntry']

View File

@ -75,7 +75,8 @@ class MediaEntry(Document):
'title': unicode,
'slug': unicode,
'created': datetime.datetime,
'description': unicode,
'description': unicode, # May contain markdown/up
'description_html': unicode, # May contain plaintext, or HTML
'media_type': unicode,
'media_data': dict, # extra data relevant to this media_type
'plugin_data': dict, # plugins can dump stuff here.

View File

@ -17,11 +17,13 @@
from webob import exc
from mediagoblin.util import render_to_response, redirect
from mediagoblin.util import render_to_response, redirect, clean_html
from mediagoblin.edit import forms
from mediagoblin.edit.lib import may_edit_media
from mediagoblin.decorators import require_active_login, get_user_media_entry
import markdown
@get_user_media_entry
@require_active_login
@ -47,7 +49,14 @@ def edit_media(request, media):
u'An entry with that slug already exists for this user.')
else:
media['title'] = request.POST['title']
media['description'] = request.POST['description']
media['description'] = request.POST.get('description')
md = markdown.Markdown(
safe_mode = 'escape')
media['description_html'] = clean_html(
md.convert(
media['description']))
media['slug'] = request.POST['slug']
media.save()

View File

@ -19,7 +19,8 @@ from cgi import FieldStorage
from werkzeug.utils import secure_filename
from mediagoblin.util import render_to_response, redirect
from mediagoblin.util import (
render_to_response, redirect, cleaned_markdown_conversion)
from mediagoblin.decorators import require_active_login
from mediagoblin.submit import forms as submit_forms, security
from mediagoblin.process_media import process_media_initial
@ -46,8 +47,14 @@ def submit_start(request):
# create entry and save in database
entry = request.db.MediaEntry()
entry['title'] = request.POST['title'] or unicode(splitext(filename)[0])
entry['title'] = (
request.POST['title']
or unicode(splitext(filename)[0]))
entry['description'] = request.POST.get('description')
entry['description_html'] = cleaned_markdown_conversion(
entry['description'])
entry['media_type'] = u'image' # heh
entry['uploader'] = request.user['_id']

View File

@ -25,7 +25,9 @@
</h1>
<img class="media_image" src="{{ request.app.public_store.file_url(
media.media_files.main) }}" />
<p>{{ media.description }}</p>
{% autoescape False %}
<p>{{ media.description_html }}</p>
{% endautoescape %}
<p>Uploaded on
{{ "%4d-%02d-%02d"|format(media.created.year,
media.created.month, media.created.day) }}

View File

@ -242,17 +242,69 @@ def test_authentication_views(test_app):
test_user.save()
# Get login
# ---------
test_app.get('/auth/login/')
# Make sure it rendered with the appropriate template
assert util.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/auth/login.html')
# Log in as that user
# Failed login - blank form
# -------------------------
util.clear_test_template_context()
response = test_app.post('/auth/login/')
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
form = context['login_form']
assert form.username.errors == [u'This field is required.']
assert form.password.errors == [u'This field is required.']
# Failed login - blank user
# -------------------------
util.clear_test_template_context()
response = test_app.post(
'/auth/login/', {
'password': u'toast'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
form = context['login_form']
assert form.username.errors == [u'This field is required.']
# Failed login - blank password
# -----------------------------
util.clear_test_template_context()
response = test_app.post(
'/auth/login/', {
'username': u'chris'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
form = context['login_form']
assert form.password.errors == [u'This field is required.']
# Failed login - bad user
# -----------------------
util.clear_test_template_context()
response = test_app.post(
'/auth/login/', {
'username': u'steve',
'password': 'toast'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
assert context['login_failed']
# Failed login - bad password
# ---------------------------
util.clear_test_template_context()
response = test_app.post(
'/auth/login/', {
'username': u'chris',
'password': 'jam'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
assert context['login_failed']
# Successful login
# ----------------
util.clear_test_template_context()
response = test_app.post(
'/auth/login/', {
'username': u'chris',
'password': 'toast'})
# User should be redirected
response.follow()
assert_equal(
urlparse.urlsplit(response.location)[2],
@ -260,10 +312,38 @@ def test_authentication_views(test_app):
assert util.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/root.html')
# Make sure we're in the session or something
session = util.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']['request'].session
# Make sure user is in the session
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
session = context['request'].session
assert session['user_id'] == unicode(test_user['_id'])
# Log out as that user
# Make sure we're not in the session
# Successful logout
# -----------------
util.clear_test_template_context()
response = test_app.get('/auth/logout/')
# Should be redirected to index page
response.follow()
assert_equal(
urlparse.urlsplit(response.location)[2],
'/')
assert util.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/root.html')
# Make sure the user is not in the session
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
session = context['request'].session
assert session.has_key('user_id') == False
# User is redirected to custom URL if POST['next'] is set
# -------------------------------------------------------
util.clear_test_template_context()
response = test_app.post(
'/auth/login/', {
'username': u'chris',
'password': 'toast',
'next' : '/u/chris/'})
assert_equal(
urlparse.urlsplit(response.location)[2],
'/u/chris/')

View File

@ -30,7 +30,7 @@ from mediagoblin.db.open import setup_connection_and_db_from_config
MEDIAGOBLIN_TEST_DB_NAME = '__mediagoblinunittests__'
TEST_SERVER_CONFIG = pkg_resources.resource_filename(
'mediagoblin.tests', 'test_server.ini')
'mediagoblin.tests', 'test_paste.ini')
TEST_APP_CONFIG = pkg_resources.resource_filename(
'mediagoblin.tests', 'test_mgoblin_app.ini')
TEST_USER_DEV = pkg_resources.resource_filename(

View File

@ -108,10 +108,10 @@ def atom_feed(request):
feed = AtomFeed(request.matchdict['user'],
feed_url=request.url,
url=request.host_url)
for entry in cursor:
feed.add(entry.get('title'),
entry.get('description'),
entry.get('description_html'),
content_type='html',
author=request.matchdict['user'],
updated=entry.get('created'),

View File

@ -29,11 +29,11 @@ import jinja2
import translitcodec
from webob import Response, exc
from lxml.html.clean import Cleaner
import markdown
from mediagoblin import mg_globals
from mediagoblin.db.util import ObjectId
TESTS_ENABLED = False
def _activate_testing():
"""
@ -98,7 +98,7 @@ def get_jinja_env(template_loader, locale):
template_env = jinja2.Environment(
loader=template_loader, autoescape=True,
extensions=['jinja2.ext.i18n'])
extensions=['jinja2.ext.i18n', 'jinja2.ext.autoescape'])
template_env.install_gettext_callables(
mg_globals.translations.gettext,
@ -376,6 +376,16 @@ def clean_html(html):
return HTML_CLEANER.clean_html(html)
MARKDOWN_INSTANCE = markdown.Markdown(safe_mode='escape')
def cleaned_markdown_conversion(text):
"""
Take a block of text, run it through MarkDown, and clean its HTML.
"""
return clean_html(MARKDOWN_INSTANCE.convert(text))
SETUP_GETTEXTS = {}
def setup_gettext(locale):

View File

@ -43,6 +43,7 @@ setup(
'argparse',
'webtest',
'ConfigObj',
'Markdown',
## For now we're expecting that users will install this from
## their package managers.
# 'lxml',