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:
commit
3cdf366acf
@ -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()
|
||||||
|
@ -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)]}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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',
|
||||||
|
@ -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,6 +54,8 @@ 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'])
|
||||||
|
@ -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])
|
||||||
|
@ -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)
|
||||||
|
@ -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">
|
||||||
|
@ -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/>
|
||||||
|
25
mediagoblin/templates/mediagoblin/utils/tags.html
Normal file
25
mediagoblin/templates/mediagoblin/utils/tags.html
Normal 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 %}
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user