Merge branch 'remotes/gullydwarf-cfdv/f360_tagging' (early part) into mergetags

Conflicts:
	mediagoblin/config_spec.ini
	mediagoblin/edit/views.py
	mediagoblin/util.py
This commit is contained in:
Christopher Allan Webber 2011-07-30 13:09:01 -05:00
commit 3cdf366acf
11 changed files with 134 additions and 6 deletions

View File

@ -24,6 +24,11 @@ email_sender_address = string(default="notice@mediagoblin.example.org")
# Set to false to disable registrations # Set to false to disable registrations
allow_registration = boolean(default=True) allow_registration = boolean(default=True)
# tag parsing
tags_delimiter = string(default=",")
tags_case_sensitive = boolean(default=False)
tags_max_length = integer(default=50)
# By default not set, but you might want something like: # By default not set, but you might want something like:
# "%(here)s/user_dev/templates/" # "%(here)s/user_dev/templates/"
local_templates = string() local_templates = string()

View File

@ -90,6 +90,21 @@ MEDIAENTRY_INDEXES = {
# Indexing on uploaders and when media entries are created. # Indexing on uploaders and when media entries are created.
# Used for showing a user gallery, etc. # Used for showing a user gallery, etc.
'index': [('uploader', ASCENDING), 'index': [('uploader', ASCENDING),
('created', DESCENDING)]},
'state_uploader_tags_created': {
# Indexing on processed?, media uploader, associated tags, and timestamp
# Used for showing media items matching a tag search, most recent first.
'index': [('state', ASCENDING),
('uploader', ASCENDING),
('tags.slug', DESCENDING),
('created', DESCENDING)]},
'state_tags_created': {
# Indexing on processed?, media tags, and timestamp (across all users)
# This is used for a front page tag search.
'index': [('state', ASCENDING),
('tags.slug', DESCENDING),
('created', DESCENDING)]}} ('created', DESCENDING)]}}

View File

@ -184,7 +184,7 @@ class MediaEntry(Document):
'media_type': unicode, 'media_type': unicode,
'media_data': dict, # extra data relevant to this media_type 'media_data': dict, # extra data relevant to this media_type
'plugin_data': dict, # plugins can dump stuff here. 'plugin_data': dict, # plugins can dump stuff here.
'tags': [unicode], 'tags': [dict],
'state': unicode, 'state': unicode,
# For now let's assume there can only be one main file queued # For now let's assume there can only be one main file queued

View File

@ -16,6 +16,7 @@
import wtforms import wtforms
from mediagoblin.util import tag_length_validator, TOO_LONG_TAG_WARNING
class EditForm(wtforms.Form): class EditForm(wtforms.Form):
@ -26,6 +27,9 @@ class EditForm(wtforms.Form):
'Slug', 'Slug',
[wtforms.validators.Required(message="The slug can't be empty")]) [wtforms.validators.Required(message="The slug can't be empty")])
description = wtforms.TextAreaField('Description of this work') description = wtforms.TextAreaField('Description of this work')
tags = wtforms.TextField(
'Tags',
[tag_length_validator])
class EditProfileForm(wtforms.Form): class EditProfileForm(wtforms.Form):
bio = wtforms.TextAreaField('Bio', bio = wtforms.TextAreaField('Bio',

View File

@ -16,10 +16,13 @@
from webob import exc from webob import exc
from string import split
from mediagoblin import messages from mediagoblin import messages
from mediagoblin import mg_globals
from mediagoblin.util import ( from mediagoblin.util import (
render_to_response, redirect, cleaned_markdown_conversion) render_to_response, redirect, clean_html, convert_to_tag_list_of_dicts,
media_tags_as_string, cleaned_markdown_conversion)
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
from mediagoblin.decorators import require_active_login, get_user_media_entry from mediagoblin.decorators import require_active_login, get_user_media_entry
@ -34,7 +37,8 @@ def edit_media(request, media):
form = forms.EditForm(request.POST, form = forms.EditForm(request.POST,
title = media['title'], title = media['title'],
slug = media['slug'], slug = media['slug'],
description = media['description']) description = media['description'],
tags = media_tags_as_string(media['tags']))
if request.method == 'POST' and form.validate(): if request.method == 'POST' and form.validate():
# Make sure there isn't already a MediaEntry with such a slug # Make sure there isn't already a MediaEntry with such a slug
@ -50,7 +54,9 @@ def edit_media(request, media):
else: else:
media['title'] = request.POST['title'] media['title'] = request.POST['title']
media['description'] = request.POST.get('description') media['description'] = request.POST.get('description')
media['tags'] = convert_to_tag_list_of_dicts(
request.POST.get('tags'))
media['description_html'] = cleaned_markdown_conversion( media['description_html'] = cleaned_markdown_conversion(
media['description']) media['description'])

View File

@ -16,6 +16,7 @@
import wtforms import wtforms
from mediagoblin.util import tag_length_validator, TOO_LONG_TAG_WARNING
class SubmitStartForm(wtforms.Form): class SubmitStartForm(wtforms.Form):
@ -24,3 +25,6 @@ class SubmitStartForm(wtforms.Form):
[wtforms.validators.Length(min=0, max=500)]) [wtforms.validators.Length(min=0, max=500)])
description = wtforms.TextAreaField('Description of this work') description = wtforms.TextAreaField('Description of this work')
file = wtforms.FileField('File') file = wtforms.FileField('File')
tags = wtforms.TextField(
'Tags',
[tag_length_validator])

View File

@ -16,11 +16,13 @@
from os.path import splitext from os.path import splitext
from cgi import FieldStorage from cgi import FieldStorage
from string import split
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from mediagoblin.util import ( from mediagoblin.util import (
render_to_response, redirect, cleaned_markdown_conversion) render_to_response, redirect, cleaned_markdown_conversion, \
convert_to_tag_list_of_dicts)
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
@ -59,6 +61,10 @@ def submit_start(request):
entry['media_type'] = u'image' # heh entry['media_type'] = u'image' # heh
entry['uploader'] = request.user['_id'] entry['uploader'] = request.user['_id']
# Process the user's folksonomy "tags"
entry['tags'] = convert_to_tag_list_of_dicts(
request.POST.get('tags'))
# Save, just so we can get the entry id for the sake of using # Save, just so we can get the entry id for the sake of using
# it to generate the file path # it to generate the file path
entry.save(validate=False) entry.save(validate=False)

View File

@ -25,6 +25,7 @@
<div class="grid_8 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.file) }}
{{ wtforms_util.render_field_div(submit_form.tags) }}
{{ 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) }}
<div class="form_submit_buttons"> <div class="form_submit_buttons">

View File

@ -116,6 +116,9 @@
</p> </p>
{% endif %} {% endif %}
</p> </p>
{% if media.tags %}
{% include "mediagoblin/utils/tags.html" %}
{% endif %}
</div> </div>
{% else %} {% else %}
<p>Sorry, no such media found.<p/> <p>Sorry, no such media found.<p/>

View File

@ -0,0 +1,25 @@
{#
# 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 tags_content -%}
<ul class="mediaentry_tags">
{% for tag in media.tags %}
<li class="tag">{{ tag['name'] }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -25,6 +25,7 @@ import re
import urllib import urllib
from math import ceil, floor from math import ceil, floor
import copy import copy
import wtforms
from babel.localedata import exists from babel.localedata import exists
import jinja2 import jinja2
@ -384,8 +385,66 @@ def clean_html(html):
return HTML_CLEANER.clean_html(html) return HTML_CLEANER.clean_html(html)
MARKDOWN_INSTANCE = markdown.Markdown(safe_mode='escape') def convert_to_tag_list_of_dicts(tag_string):
"""
Filter input from incoming string containing user tags,
Strips trailing, leading, and internal whitespace, and also converts
the "tags" text into an array of tags
"""
taglist = []
if tag_string:
# Strip out internal, trailing, and leading whitespace
stripped_tag_string = u' '.join(tag_string.strip().split())
# Split the tag string into a list of tags
for tag in stripped_tag_string.split(
mg_globals.app_config['tags_delimiter']):
# Ignore empty or duplicate tags
if tag.strip() and tag.strip() not in [t['name'] for t in taglist]:
if mg_globals.app_config['tags_case_sensitive']:
taglist.append({'name': tag.strip(),
'slug': slugify(tag.strip())})
else:
taglist.append({'name': tag.strip().lower(),
'slug': slugify(tag.strip().lower())})
return taglist
def media_tags_as_string(media_entry_tags):
"""
Generate a string from a media item's tags, stored as a list of dicts
This is the opposite of convert_to_tag_list_of_dicts
"""
media_tag_string = ''
if media_entry_tags:
media_tag_string = mg_globals.app_config['tags_delimiter'].join(
[tag['name'] for tag in media_entry_tags])
return media_tag_string
TOO_LONG_TAG_WARNING = \
u'Tags must be shorter than %s characters. Tags that are too long: %s'
def tag_length_validator(form, field):
"""
Make sure tags do not exceed the maximum tag length.
"""
tags = convert_to_tag_list_of_dicts(field.data)
too_long_tags = [
tag['name'] for tag in tags
if len(tag['name']) > mg_globals.app_config['tags_max_length']]
if too_long_tags:
raise wtforms.ValidationError(
TOO_LONG_TAG_WARNING % (mg_globals.app_config['tags_max_length'], \
', '.join(too_long_tags)))
MARKDOWN_INSTANCE = markdown.Markdown(safe_mode='escape')
def cleaned_markdown_conversion(text): def cleaned_markdown_conversion(text):
""" """