在建立我們要的新模組之前,先修改一下既有的樣板
進到mediagoblin/mediagoblin/templates/mediagoblin裡
cd mediagoblin/mediagoblin/templates/mediagoblin
編輯base.html
<a class="button_action" href="{{ request.urlgen('mediagoblin.submit.start') }}">{%- trans %}Add media{% endtrans -%}</a>
<!--在這邊新增一個button給import flickr用-->
<a class="button_action" href="{{ request.urlgen('mediagoblin.import_flickr.start') }}">Import photo from Flickr</a>
<a class="button_action" href="{{ request.urlgen('mediagoblin.submit.collection') }}">{%- trans %}Create new collection{% endtrans -%}</a>
接著新增import_flickr資料夾
mkdir import_flickr
在裡面建立一個start.html
{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
{% block title -%}
{% trans %}Import Photos form Flickr!{% endtrans %} — {{ super() }}
{%- endblock %}
{% block mediagoblin_content %}
<form action="{{ request.urlgen('mediagoblin.import_flickr.start') }}" method="POST" enctype="multipart/form-data">
<div class="form_box_xl">
<h1>{% trans %}Import Photos form Flickr!{% endtrans %}</h1>
{{ wtforms_util.render_divs(submit_form) }}
<div class="form_submit_buttons">
{{ csrf_token }}
<input type="submit" value="{% trans %}Add{% endtrans %}" class="button_form" />
</div>
</div>
</form>
{% endblock %}
新增模組
先退到上兩層的mediagoblin/mediagoblin裡
建立新的資料夾叫import_flickr
mkdir import_flickr
裡面我們會需要幾個檔案,如下所示:
import_flickr/
|-__init__.py
|- forms.py
|- lib.py
|- routing.py
|- views.py
forms.py:
import wtforms
from mediagoblin.tools.text import tag_length_validator
from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
from mediagoblin.tools.licenses import licenses_as_choices
class SubmitStartForm(wtforms.Form):
file = wtforms.FileField(_('File'),description=_("""Only zip file accepted.<br>You can use <a href="http://code.google.com/p/offlickr/">Offickr</a> to download the package photo."""))
lib.py:
import logging
import uuid
from werkzeug.utils import secure_filename
from werkzeug.datastructures import FileStorage
from mediagoblin.db.models import MediaEntry
from mediagoblin.processing import mark_entry_failed
from mediagoblin.processing.task import process_media
_log = logging.getLogger(__name__)
def check_file_field(request, field_name):
"""Check if a file field meets minimal criteria"""
retval = (field_name in request.files and isinstance(request.files[field_name], FileStorage) and request.files[field_name].stream)
if not retval:
_log.debug("Form did not contain proper file field %s", field_name)
return retval
def new_upload_entry(user):
"""Create a new MediaEntry for uploading"""
entry = MediaEntry()
entry.uploader = user.id
entry.license = user.license_preference
return entry
def prepare_queue_task(app, entry, filename):
"""Prepare a MediaEntry for the processing queue and get a queue file"""
# We generate this ourselves so we know what the task id is for
# retrieval later.
# (If we got it off the task's auto-generation, there'd be
# a risk of a race condition when we'd save after sending
# off the task)
task_id = unicode(uuid.uuid4())
entry.queued_task_id = task_id
# Now store generate the queueing related filename
queue_filepath = app.queue_store.get_unique_filepath(['media_entries', task_id, secure_filename(filename)])
# queue appropriately
queue_file = app.queue_store.get_file(queue_filepath, 'wb')
# Add queued filename to the entry
entry.queued_media_file = queue_filepath
return queue_file
def run_process_media(entry, feed_url=None):
"""Process the media asynchronously
:param entry: MediaEntry() instance to be processed.
:param feed_url: A string indicating the feed_url that the PuSH servers should be notified of. This will be sth like: `request.urlgen('mediagoblin.user_pages.atom_feed',qualified=True, user=request.user.username)`"""
try:
process_media.apply_async([entry.id, feed_url], {},task_id=entry.queued_task_id)
except BaseException as exc:
# The purpose of this section is because when running in "lazy"
# or always-eager-with-exceptions-propagated celery mode that
# the failure handling won't happen on Celery end. Since we
# expect a lot of users to run things in this way we have to
# capture stuff here.
#
# ... not completely the diaper pattern because the
# exception is re-raised :)
mark_entry_failed(entry.id, exc)
# re-raise the exception
raise
routing.py:
from mediagoblin.tools.routing import add_route
add_route('mediagoblin.import_flickr.start','/import_flickr/', 'mediagoblin.import_flickr.views:submit_start')
views.py:
from mediagoblin import messages
import mediagoblin.mg_globals as mg_globals
from os.path import splitext
import logging
import uuid
import zipfile
import imghdr
from xml.etree import ElementTree
_log = logging.getLogger(__name__)
from mediagoblin.tools.text import convert_to_tag_list_of_dicts
from mediagoblin.tools.translate import pass_to_ugettext as _
from mediagoblin.tools.response import render_to_response, redirect
from mediagoblin.decorators import require_active_login
from mediagoblin.import_flickr import forms as submit_forms
from mediagoblin.messages import add_message, SUCCESS
from mediagoblin.media_types import sniff_media, \InvalidFileType, FileTypeNotSupported
from mediagoblin.import_flickr.lib import check_file_field, prepare_queue_task, \run_process_media, new_upload_entry
from mediagoblin.notifications import add_comment_subscription
@require_active_login
def submit_start(request):
"""First view for submitting a file."""
submit_form = submit_forms.SubmitStartForm(request.form, license=request.user.license_preference)
if request.method == 'POST' and submit_form.validate():
if not check_file_field(request, 'file'):
submit_form.file.errors.append(_(u'You must provide a file.'))
else:
try:
filename = request.files['file'].filename
# If the filename contains non ascii generate a unique name
if not all(ord(c) < 128 for c in filename):
filename = unicode(uuid.uuid4()) + splitext(filename)[-1]
# Sniff the submitted media to determine which
# media plugin should handle processing
# I've modified ../media_types/__init__.py so that a zip file can pass the checking system
# It will be seen as an image file
media_type, media_manager = sniff_media(request.files['file'])
# Read the zip file and processing each photo with its metadata
zf = zipfile.ZipFile(request.files['file'], 'r')
for name in zf.namelist():
try:
data = zf.read(name)
if imghdr.what(name, data):
metadata = ElementTree.ElementTree(ElementTree.fromstring(zf.read(name.split(".")[0]+'.xml')))
# Fetch the data in metadata that matches the format in mediagoblin
# Not yet finish the whole checking problem, some data may not exist
img_title = metadata.find('title').text
img_description = metadata.find('description').text
img_tags = ''
for tag in metadata.find('tags'):
img_tags = img_tags + ', ' + tag.text
upload_data = data
upload_filename = name.lstrip('dst/')
# create entry and save in database
entry = new_upload_entry(request.user)
entry.media_type = unicode(media_type)
entry.title = unicode(img_title)
entry.description = unicode(img_description)#unicode(submit_form.description.data)
entry.license = unicode('http://creativecommons.org/publicdomain/mark/1.0/')#unicode(submit_form.license.data) or None
# Process the user's folksonomy "tags"
entry.tags = convert_to_tag_list_of_dicts(img_tags)
# Generate a slug from the title
entry.generate_slug()
queue_file = prepare_queue_task(request.app, entry, upload_filename)
with queue_file:
queue_file.write(upload_data)#request.files['file'].stream.read())
# Save now so we have this data before kicking off processing
entry.save()
feed_url = request.urlgen('mediagoblin.user_pages.atom_feed', qualified=True, user=request.user.username)
run_process_media(entry, feed_url)
except KeyError:
print 'ERROR: Did not find %s in zip file' % name
# Pass off to processing
# (... don't change entry after this point to avoid race
# conditions with changes to the document via processing code)
add_message(request, SUCCESS, _('Woohoo! Submitted!'))
add_comment_subscription(request.user, entry)
return redirect(request, "mediagoblin.user_pages.user_home", user=request.user.username)
except Exception as e:
'''This section is intended to catch exceptions raised in mediagoblin.media_types'''
if isinstance(e, InvalidFileType) or \isinstance(e, FileTypeNotSupported):
submit_form.file.errors.append(e)
else:
raise
return render_to_response(request,'mediagoblin/import_flickr/start.html',{'submit_form': submit_form,'app_config': mg_globals.app_config})
最後,要回到上一層的mediagoblin資料夾裡
編輯routing.py:
import logging
from mediagoblin.tools.routing import add_route, mount, url_map
from mediagoblin.tools.pluginapi import PluginManager
from mediagoblin.admin.routing import admin_routes
from mediagoblin.auth.routing import auth_routes
_log = logging.getLogger(__name__)
def get_url_map():
add_route('index', '/', 'mediagoblin.views:root_view')
mount('/auth', auth_routes)
mount('/a', admin_routes)
import mediagoblin.submit.routing
#####在這邊插入下面這行#####
import mediagoblin.import_flickr.routing
import mediagoblin.user_pages.routing
import mediagoblin.edit.routing
import mediagoblin.webfinger.routing
import mediagoblin.listings.routing
import mediagoblin.notifications.routing
for route in PluginManager().get_routes():
add_route(*route)
return url_map
如此一來便大功告成,在mediagoblin這一端的部分結束。