diff options
25 files changed, 810 insertions, 2 deletions
diff --git a/README.md b/README.md new file mode 100644 index 00000000..d6b023a9 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +mediagoblin_blog +================ diff --git a/mediagoblin.ini b/mediagoblin.ini index 30dacadf..cb908dd2 100644 --- a/mediagoblin.ini +++ b/mediagoblin.ini @@ -45,3 +45,4 @@ base_url = /mgoblin_media/ [[mediagoblin.plugins.geolocation]] [[mediagoblin.plugins.basic_auth]] [[mediagoblin.media_types.image]] +[[mediagoblin.media_types.blog]] diff --git a/mediagoblin/media_types/blog/__init__.py b/mediagoblin/media_types/blog/__init__.py new file mode 100644 index 00000000..a579a179 --- /dev/null +++ b/mediagoblin/media_types/blog/__init__.py @@ -0,0 +1,44 @@ +#GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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/>. + +from mediagoblin.media_types import MediaManagerBase + +from mediagoblin.tools import pluginapi + +MEDIA_TYPE = 'mediagoblin.media_types.blogpost' + +def setup_plugin(): + config = pluginapi.get_config(MEDIA_TYPE) + +class BlogPostMediaManager(MediaManagerBase): + human_readable = "Blog Post" + display_template = "mediagoblin/media_displays/blogpost.html" + default_thumb = "images/media_thumbs/blogpost.jpg" + +def get_media_type_and_manager(ext): + return MEDIA_TYPE, BlogPostMediaManager + + +hooks = { + 'setup': setup_plugin, + 'get_media_type_and_manager': get_media_type_and_manager, + ('media_manager', MEDIA_TYPE): lambda: BlogPostMediaManager, +} + + + + + diff --git a/mediagoblin/media_types/blog/config_spec.ini b/mediagoblin/media_types/blog/config_spec.ini new file mode 100644 index 00000000..e0a6844e --- /dev/null +++ b/mediagoblin/media_types/blog/config_spec.ini @@ -0,0 +1,2 @@ +[plugin_spec] +max_blog_count = integer(default=1) diff --git a/mediagoblin/media_types/blog/forms.py b/mediagoblin/media_types/blog/forms.py new file mode 100644 index 00000000..9f595fcf --- /dev/null +++ b/mediagoblin/media_types/blog/forms.py @@ -0,0 +1,42 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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/>. + +import wtforms + +from mediagoblin.tools.text import tag_length_validator, TOO_LONG_TAG_WARNING +from mediagoblin.tools.translate import lazy_pass_to_ugettext as _ +from mediagoblin.tools.licenses import licenses_as_choices + +class BlogPostEditForm(wtforms.Form): + title = wtforms.TextField(_('Title'), + [wtforms.validators.Length(min=0, max=500)]) + description = wtforms.TextAreaField(_('Description')) + tags = wtforms.TextField(_('Tags'), [tag_length_validator], + description="Seperate tags by commas.") + license = wtforms.SelectField(_('License'), + [wtforms.validators.Optional(),], choices=licenses_as_choices()) + +class BlogEditForm(wtforms.Form): + title = wtforms.TextField(_('Title'), + [wtforms.validators.Length(min=0, max=500)]) + description = wtforms.TextAreaField(_('Description')) + + + + + + + diff --git a/mediagoblin/media_types/blog/lib.py b/mediagoblin/media_types/blog/lib.py new file mode 100644 index 00000000..f95ecca5 --- /dev/null +++ b/mediagoblin/media_types/blog/lib.py @@ -0,0 +1,30 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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/>. + + +def check_blog_slug_used(author_id, slug, ignore_b_id=None): + from mediagoblin.media_types.blog.models import Blog + query = Blog.query.filter_by(author=author_id, slug=slug) + if ignore_b_id: + query = query.filter(Blog.id != ignore_b_id) + does_exist = query.first() is not None + return does_exist + +def may_edit_blogpost(request, blog): + if request.user.is_admin or request.user.id == blog.author: + return True + return False + diff --git a/mediagoblin/media_types/blog/models.py b/mediagoblin/media_types/blog/models.py new file mode 100644 index 00000000..a9288c00 --- /dev/null +++ b/mediagoblin/media_types/blog/models.py @@ -0,0 +1,60 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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/>. + +import datetime + +from mediagoblin.db.base import Base +from mediagoblin.db.models import Collection, User, MediaEntry +from mediagoblin.db.mixin import GenerateSlugMixin + +from mediagoblin.media_types.blog.lib import check_blog_slug_used + +from mediagoblin.tools.text import cleaned_markdown_conversion + +from sqlalchemy import ( + Column, Integer, ForeignKey, Unicode, UnicodeText, DateTime) +from sqlalchemy.orm import relationship, backref + + +class BlogMixin(GenerateSlugMixin): + def check_slug_used(self, slug): + return check_blog_slug_used(self.author, slug, self.id) + +class Blog(Base, BlogMixin): + __tablename__ = "mediatype__blogs" + id = Column(Integer, primary_key=True) + title = Column(Unicode) + description = Column(UnicodeText) + author = Column(Integer, ForeignKey(User.id), nullable=False, index=True) #similar to uploader + created = Column(DateTime, nullable=False, default=datetime.datetime.now, index=True) + slug = Column(Unicode) + + +BACKREF_NAME = "blogpost__media_data" + +class BlogPostData(Base): + __tablename__ = "blogpost__mediadata" + + # The primary key *and* reference to the main media_entry + media_entry = Column(Integer, ForeignKey('core__media_entries.id'), primary_key=True) + blog = Column(Integer, ForeignKey('mediatype__blogs.id'), nullable=False) + get_media_entry = relationship("MediaEntry", + backref=backref(BACKREF_NAME, uselist=False, + cascade="all, delete-orphan")) + + +DATA_MODEL = BlogPostData +MODELS = [BlogPostData, Blog] diff --git a/mediagoblin/media_types/blog/routing.py b/mediagoblin/media_types/blog/routing.py new file mode 100644 index 00000000..b34c8280 --- /dev/null +++ b/mediagoblin/media_types/blog/routing.py @@ -0,0 +1,57 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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/>. + +from mediagoblin.tools.routing import add_route + +#URL mapping for blog-admin, where all the blog posts of a particular blog +#are listed. providing the facility of edit, delete and view a particular blog post." +add_route('mediagoblin.media_types.blog.blog-dashboard', \ + '/u/<string:user>/b/<string:blog_slug>/blog_dashboard/', 'mediagoblin.media_types.blog.views:blog_dashboard') + +#URL mapping for creating a new blog post. (add blo_slug in the url at "blogpost" place later" +add_route('mediagoblin.media_types.blog.blogpost.create', + '/u/<string:user>/b/<string:blog_slug>/p/create/', + 'mediagoblin.media_types.blog.views:blogpost_create') + +#URL mapping for editing an existing blog post. +add_route('mediagoblin.media_types.blog.blogpost.edit', + '/u/<string:user>/b/<string:blog_slug>/p/<string:blog_post_slug>/edit/', + 'mediagoblin.media_types.blog.views:blogpost_edit') + +#URL mapping for blog-collection-admin, where all the blogs of the user +#are listed. providing the facility of edit, delete and view a blog. +#view facility redirects to blog-admin page of that particular blog. +add_route('mediagoblin.media_types.blog.blog-collection-admin', \ + '/u/<string:user>/blog-collection-admin/', 'mediagoblin.media_types.blog.views:blog-collection-admin') + +#URL mapping for creating a new blog. +add_route('mediagoblin.media_types.blog.create', '/u/<string:user>/b/create/', + 'mediagoblin.media_types.blog.views:blog_edit') + + +#URL mapping for editing an existing blog. +add_route('mediagoblin.media_types.blog.edit', '/u/<string:user>/b/<string:blog_slug>/edit/', + 'mediagoblin.media_types.blog.views:blog_edit') + +#URL mapping for blog_post_listing view. +add_route('mediagoblin.media_types.blog.blog_post_listing', '/u/<string:user>/b/', + 'mediagoblin.media_types.blog.views:blog_post_listing') + + + + + + diff --git a/mediagoblin/media_types/blog/views.py b/mediagoblin/media_types/blog/views.py new file mode 100644 index 00000000..44f61f0f --- /dev/null +++ b/mediagoblin/media_types/blog/views.py @@ -0,0 +1,277 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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/>. + +import logging + +_log = logging.getLogger(__name__) + +from datetime import datetime + +from werkzeug.exceptions import Forbidden + +from mediagoblin import mg_globals + +from mediagoblin.media_types.blog import forms as blog_forms +from mediagoblin.media_types.blog.models import Blog, BlogPostData +from mediagoblin.media_types.blog.lib import may_edit_blogpost + +from mediagoblin.messages import add_message, SUCCESS, ERROR +from mediagoblin.decorators import (require_active_login, active_user_from_url, + get_media_entry_by_id, user_may_alter_collection, + get_user_collection) +from mediagoblin.tools.response import (render_to_response, + redirect, redirect_obj, render_404) +from mediagoblin.tools.translate import pass_to_ugettext as _ +from mediagoblin.tools.template import render_template +from mediagoblin.tools.text import ( + convert_to_tag_list_of_dicts, media_tags_as_string, clean_html, + cleaned_markdown_conversion) +from mediagoblin.tools.url import slugify + +from mediagoblin.db.util import check_media_slug_used, check_collection_slug_used +from mediagoblin.db.models import User, Collection, MediaEntry + +from mediagoblin.notifications import add_comment_subscription + + +@require_active_login +def blog_edit(request): + """ + View for editing the existing blog or automatically + creating a new blog if user does not have any yet. + """ + url_user = request.matchdict.get('user', None) + blog_slug = request.matchdict.get('blog_slug', None) + + max_blog_count = 1 + form = blog_forms.BlogEditForm(request.form) + # the blog doesn't exists yet + if not blog_slug: + if Blog.query.filter_by(author=request.user.id).count()<max_blog_count: + if request.method=='GET': + return render_to_response( + request, + 'mediagoblin/blog/blog_edit_create.html', + {'form': form, + 'user' : request.user, + 'app_config': mg_globals.app_config}) + + if request.method=='POST' and form.validate(): + _log.info("Here") + blog = Blog() + blog.title = unicode(form.title.data) + blog.description = unicode(form.description.data) #remember clean html data. + blog.author = request.user.id + blog.generate_slug() + + blog.save() + return redirect(request, "mediagoblin.media_types.blog.blog-dashboard", + user=request.user.username, + blog_slug=blog.slug) + else: + #the case when max blog count is one. + blog = request.db.Blog.query.filter_by(author=request.user.id).first() + add_message(request, ERROR, "You can not create any more blogs") + return redirect(request, "mediagoblin.media_types.blog.blog-dashboard", + user=request.user.username, + blog_slug=blog.slug) + + + #Blog already exists. + else: + blog = Blog.query.filter_by(slug=blog_slug).first() + if request.method == 'GET': + defaults = dict( + title = blog.title, + description = blog.description, + author = request.user.id) + + form = blog_forms.BlogEditForm(**defaults) + + return render_to_response( + request, + 'mediagoblin/blog/blog_edit_create.html', + {'form': form, + 'user': request.user, + 'app_config': mg_globals.app_config}) + else: + if request.method == 'POST' and form.validate(): + blog.title = unicode(form.title.data) + blog.description = unicode(form.description.data) + blog.author = request.user.id + blog.generate_slug() + + blog.save() + add_message(request, SUCCESS, "Your blog is updated.") + return redirect(request, "mediagoblin.media_types.blog.blog-dashboard", + user=request.user.username, + blog_slug=blog.slug) + +@require_active_login +def blogpost_create(request): + + + form = blog_forms.BlogPostEditForm(request.form, license=request.user.license_preference) + + if request.method == 'POST' and form.validate(): + + state_value = request.form['status'] + if state_value == u'Publish': + state_value = u'processed' + + blog_slug = request.matchdict.get('blog_slug') + blog = request.db.Blog.query.filter_by(slug=blog_slug, + author=request.user.id).first() + + + blogpost = request.db.MediaEntry() + blogpost.media_type = 'mediagoblin.media_types.blogpost' + blogpost.title = unicode(form.title.data) + blogpost.description = unicode(form.description.data) + blogpost.tags = convert_to_tag_list_of_dicts(form.tags.data) + blogpost.license = unicode(form.license.data) or None + blogpost.uploader = request.user.id + blogpost.state = state_value + + blogpost.generate_slug() + + blogpost.save() + + # connect this blogpost to its blog + blog_post_data = request.db.BlogPostData() + blog_post_data.blog = blog.id + blog_post_data.media_entry = blogpost.id + blog_post_data.save() + + add_message(request, SUCCESS, _('Woohoo! Submitted!')) + add_comment_subscription(request.user, blogpost) + return redirect(request, "mediagoblin.media_types.blog.blog-dashboard", + user=request.user.username, + blog_slug=blog.slug) + + return render_to_response( + request, + 'mediagoblin/blog/blog_post_edit_create.html', + {'form': form, + 'app_config': mg_globals.app_config, + 'user': request.user.username}) + + +@require_active_login +def blogpost_edit(request): + blog_slug = request.matchdict.get('blog_slug', None) + blog_post_slug = request.matchdict.get('blog_post_slug', None) + + blogpost = request.db.MediaEntry.query.filter_by(slug=blog_post_slug, uploader=request.user.id).first() + blog = request.db.Blog.query.filter_by(slug=blog_slug, author=request.user.id).first() + + if not blogpost or not blog: + return render_404(request) + + defaults = dict( + title = blogpost.title, + description = blogpost.description, + tags=media_tags_as_string(blogpost.tags), + license=blogpost.license) + + form = blog_forms.BlogPostEditForm(request.form, **defaults) + if request.method == 'POST' and form.validate(): + + state_value = request.form['status'] + if state_value == u'Publish': + state_value = u'processed' + blogpost.title = unicode(form.title.data) + blogpost.description = unicode(form.description.data) + blogpost.tags = convert_to_tag_list_of_dicts(form.tags.data) + blogpost.license = unicode(form.license.data) + + blogpost.generate_slug() + blogpost.state = state_value + blogpost.save() + + add_message(request, SUCCESS, _('Woohoo! edited blogpost is submitted')) + return redirect(request, "mediagoblin.media_types.blog.blog-dashboard", + user=request.user.username, + blog_slug=blog.slug) + + return render_to_response( + request, + 'mediagoblin/blog/blog_post_edit_create.html', + {'form': form, + 'app_config': mg_globals.app_config, + 'user': request.user.username, + 'blog_post_slug': blog_post_slug + }) + +@require_active_login +def blog_dashboard(request): + + url_user = request.matchdict.get('user') + blog_posts_list = [] + blog_slug = request.matchdict.get('blog_slug') + _log.info(blog_slug) + + blog = request.db.Blog.query.filter_by(slug=blog_slug).first() + + if not blog: + return render_404(request) + + blog_post_data = request.db.BlogPostData.query.filter_by(blog=blog.id).all() + + for each_blog_post_data in blog_post_data: + blog_post = each_blog_post_data.get_media_entry + if blog_post: + blog_posts_list.append(blog_post) + blog_posts_list.reverse() + blog_post_count = len(blog_posts_list) + + if may_edit_blogpost(request, blog): + return render_to_response( + request, + 'mediagoblin/blog/blog_admin_dashboard.html', + {'blog_posts_list': blog_posts_list, + 'blog_slug':blog_slug, + 'blog':blog, + 'blog_post_count':blog_post_count + }) + +#supposed to list all the blog posts not just belonging to a particular post. +def blog_post_listing(request): + + blog_owner = request.matchdict.get('user') + _log.info("Username is %s"%(blog_owner)) + owner_user = User.query.filter_by(username=blog_owner).one() + + if not owner_user: + return render_404(request) + + all_blog_posts = MediaEntry.query.filter_by( + uploader=owner_user.id, media_type='mediagoblin.media_types.blogpost', + state=u'processed').all() + all_blog_posts.reverse() + _log.info(len(all_blog_posts)) + + return render_to_response( + request, + 'mediagoblin/blog/blog_post_listing.html', + {'blog_posts': all_blog_posts, + 'blog_owner': blog_owner + }) + + + + + diff --git a/mediagoblin/media_types/image/__init__.py b/mediagoblin/media_types/image/__init__.py index 1bb9c6f3..bf42e0b3 100644 --- a/mediagoblin/media_types/image/__init__.py +++ b/mediagoblin/media_types/image/__init__.py @@ -26,7 +26,7 @@ MEDIA_TYPE = 'mediagoblin.media_types.image' def setup_plugin(): - config = pluginapi.get_config('mediagoblin.media_types.image') + config = pluginapi.get_config(MEDIA_TYPE) class ImageMediaManager(MediaManagerBase): diff --git a/mediagoblin/plugins/blog/LICENCE b/mediagoblin/plugins/blog/LICENCE new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/mediagoblin/plugins/blog/LICENCE diff --git a/mediagoblin/plugins/blog/README.srt b/mediagoblin/plugins/blog/README.srt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/mediagoblin/plugins/blog/README.srt diff --git a/mediagoblin/plugins/blog/__init__.py b/mediagoblin/plugins/blog/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/mediagoblin/plugins/blog/__init__.py diff --git a/mediagoblin/plugins/blog/forms.py b/mediagoblin/plugins/blog/forms.py new file mode 100644 index 00000000..52af1cf1 --- /dev/null +++ b/mediagoblin/plugins/blog/forms.py @@ -0,0 +1,17 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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/>. + +import wtforms diff --git a/mediagoblin/plugins/blog/setup.py b/mediagoblin/plugins/blog/setup.py new file mode 100644 index 00000000..c4e0ee97 --- /dev/null +++ b/mediagoblin/plugins/blog/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup, find_packages + +setup( + name = 'blog', + version = '1.0', + packages = find_packages(); + include_package_data = True, + install_requires = [], + license = 'AGPLv3', + ) diff --git a/mediagoblin/plugins/blog/templates/blogpost_edit.html b/mediagoblin/plugins/blog/templates/blogpost_edit.html new file mode 100644 index 00000000..fec7a9c2 --- /dev/null +++ b/mediagoblin/plugins/blog/templates/blogpost_edit.html @@ -0,0 +1,19 @@ +{# +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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" %} + diff --git a/mediagoblin/plugins/blog/views.py b/mediagoblin/plugins/blog/views.py new file mode 100644 index 00000000..c4e8601d --- /dev/null +++ b/mediagoblin/plugins/blog/views.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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/>. diff --git a/mediagoblin/processing/__init__.py b/mediagoblin/processing/__init__.py index f3a85940..27d89895 100644 --- a/mediagoblin/processing/__init__.py +++ b/mediagoblin/processing/__init__.py @@ -171,7 +171,7 @@ class BaseProcessingFail(Exception): subclass from. You shouldn't call this itself; instead you should subclass it - and provid the exception_path and general_message applicable to + and provide the exception_path and general_message applicable to this error. """ general_message = u'' diff --git a/mediagoblin/routing.py b/mediagoblin/routing.py index 986eb2ed..4228d29b 100644 --- a/mediagoblin/routing.py +++ b/mediagoblin/routing.py @@ -36,6 +36,7 @@ def get_url_map(): import mediagoblin.webfinger.routing import mediagoblin.listings.routing import mediagoblin.notifications.routing + import mediagoblin.media_types.blog.routing for route in PluginManager().get_routes(): add_route(*route) diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css index 84d274d1..85027d77 100644 --- a/mediagoblin/static/css/base.css +++ b/mediagoblin/static/css/base.css @@ -288,6 +288,20 @@ text-align: center; max-width: 460px; } +.blog_form_box_xl { + background-color: #222; + border-top: 6px solid #D49086; + padding: 3% 5%; + display: block; + float: none; + width: 90%; + max-width: 700px; + min-height: 500px; + margin-left: auto; + margin-right: auto; +} + + .edit_box { border-top: 6px dashed #D49086 } @@ -296,6 +310,10 @@ text-align: center; width: 100%; } +.blog_form_field_input input, .blog_form_field_input textarea { + width: 100%; +} + .form_field_input { margin-bottom: 10px; } diff --git a/mediagoblin/templates/mediagoblin/blog/blog_admin_dashboard.html b/mediagoblin/templates/mediagoblin/blog/blog_admin_dashboard.html new file mode 100644 index 00000000..c45f0e30 --- /dev/null +++ b/mediagoblin/templates/mediagoblin/blog/blog_admin_dashboard.html @@ -0,0 +1,74 @@ +{# +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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" %} + +{% block title -%} +{{blog.title}} Dashboard — {{ super() }} +{%- endblock title %} + +{% block mediagoblin_content %} +<h1> {{blog.title}}</h1> +<p> + {{blog.description}} +</p> +<p> + {% set blogpost_create_url = request.urlgen('mediagoblin.media_types.blog.blogpost.create', + blog_slug=blog.slug, + user=request.user.username) %} +<a class="button_action" href="{{ blogpost_create_url }}"> +{%- trans %}Add Blog Post{% endtrans -%} +</a> +· + {% set blog_edit_url = request.urlgen('mediagoblin.media_types.blog.edit', + blog_slug=blog.slug, + user=request.user.username) %} +<a class="button_action" href="{{ blog_edit_url }}"> +{%- trans %}Edit Blog{% endtrans -%} +</a> +</p> +<h2> Blog Post Entries </h2> + {% if blog_post_count!=0 %} + <table class="media_panel processing"> + <tr> + <th>Title</th> + <th>submitted</th> + <th></th> + </tr> + {% for blog_post in blog_posts_list %} + <tr> + <td><a href="{{ blog_post.url_for_self(request.urlgen) }}">{{ blog_post.title }}</a></td> + <td>{{ blog_post.created.strftime("%F %R") }}</td> + <td><h6><em>{{ blog_post.state }}</em></h6></td> + {% set blogpost_edit_url = request.urlgen('mediagoblin.media_types.blog.blogpost.edit', + blog_slug=blog.slug, user=request.user.username, + blog_post_slug=blog_post.slug) %} + <td><a class="button_action" href="{{ blogpost_edit_url }}">Edit</a> + <a class="button_action" >Delete</a> + </td> + + </tr> + {% endfor %} + </table> + {% else %} + {% trans %} No blog post yet. {% endtrans %} + {% endif %} +{% endblock %} + + + diff --git a/mediagoblin/templates/mediagoblin/blog/blog_edit_create.html b/mediagoblin/templates/mediagoblin/blog/blog_edit_create.html new file mode 100644 index 00000000..fbed93db --- /dev/null +++ b/mediagoblin/templates/mediagoblin/blog/blog_edit_create.html @@ -0,0 +1,41 @@ +{# +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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="" + method="POST" enctype="multipart/form-data"> + <div class="blog_form_box_xl"> + <h1>{% trans %}Create/Edit a Blog{% endtrans %}</h1> + <b>Title</b> + <div class="blog_form_field_input input"> + <h3>{{ form.title}}</h3> + </div> + <b>Description</b> + <div class="blog_form_field_input textarea"> + <h3>{{form.description}}</h3> + </div> + <div class="form_submit_buttons"> + {{ csrf_token }} + <input type="submit" value="{% trans %}Add{% endtrans %}" class="button_form" /> + </div> + </div> + </form> +{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/blog/blog_post_edit_create.html b/mediagoblin/templates/mediagoblin/blog/blog_post_edit_create.html new file mode 100644 index 00000000..b380d44c --- /dev/null +++ b/mediagoblin/templates/mediagoblin/blog/blog_post_edit_create.html @@ -0,0 +1,53 @@ +{# +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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 title -%} + {% trans %}Create/Edit a blog post.{% endtrans %} — {{ super() }} +{%- endblock %} + +{% block mediagoblin_content %} + <form action="" method="POST"> + <div class="blog_form_box_xl"> + <h1>{% trans %}Create a Blog Post.{% endtrans %}</h1> + <b>Title</b> + <div class="blog_form_field_input input"> + <h3>{{ form.title}}</h3> + </div> + <b>Description</b> + <div class="blog_form_field_input textarea"> + <h3>{{form.description|safe}}</h3> + </div> + <b>Tags</b> + <div class="blog_form_field_input input"> + <h3>{{form.tags}}</h3> + </div> + <b>License</b> + <div class="blog_form_field_input input"> + <h3>{{form.license}}</h3> + </div> + <div class="form_submit_buttons"> + {{ csrf_token }} + <input type="submit" name="status" value="Publish" class="button_form"> + <input type="submit" name="status" value="Draft" class="button_form"> + </div> + </div> + </form> +{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/extra_head.html b/mediagoblin/templates/mediagoblin/extra_head.html index 973e2b48..176a77da 100644 --- a/mediagoblin/templates/mediagoblin/extra_head.html +++ b/mediagoblin/templates/mediagoblin/extra_head.html @@ -17,3 +17,14 @@ -#} {# Add extra head declarations here for your theme, if appropriate #} + +<script type="text/javascript" + src="{{request.staticdirect('/tinymce/js/tinymce/tinymce.min.js')}}"></script> + <script type="text/javascript"> + tinyMCE.init({ + selector: "div.blog_form_field_input textarea", + height: "100%", + width:"100%" + + }) +</script> diff --git a/mediagoblin/templates/mediagoblin/media_displays/blogpost.html b/mediagoblin/templates/mediagoblin/media_displays/blogpost.html new file mode 100644 index 00000000..b96ab34b --- /dev/null +++ b/mediagoblin/templates/mediagoblin/media_displays/blogpost.html @@ -0,0 +1,33 @@ +{# +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# 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/user_pages/media.html' %} + +{% block mediagoblin_head %} + {{ super() }} +{% endblock %} + +{% block mediagoblin_media %} +<h1> {{media.title}}</h1> +<p>{{media.description}}</p> + +{% endblock %} + + + + |