aboutsummaryrefslogtreecommitdiffstats
path: root/mediagoblin
diff options
context:
space:
mode:
Diffstat (limited to 'mediagoblin')
-rw-r--r--mediagoblin/media_types/blog/__init__.py97
-rw-r--r--mediagoblin/media_types/blog/config_spec.ini2
-rw-r--r--mediagoblin/media_types/blog/forms.py42
-rw-r--r--mediagoblin/media_types/blog/lib.py36
-rw-r--r--mediagoblin/media_types/blog/models.py60
-rw-r--r--mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html96
-rw-r--r--mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_edit_create.html41
-rw-r--r--mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_edit_create.html53
-rw-r--r--mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_listing.html48
-rw-r--r--mediagoblin/media_types/blog/templates/mediagoblin/blog/blogpost_draft_view.html42
-rw-r--r--mediagoblin/media_types/blog/views.py284
-rw-r--r--mediagoblin/media_types/image/__init__.py2
-rw-r--r--mediagoblin/processing/__init__.py2
-rw-r--r--mediagoblin/routing.py3
-rw-r--r--mediagoblin/static/css/base.css46
l---------mediagoblin/static/extlib/tinymce1
-rw-r--r--mediagoblin/static/images/media_thumbs/blogpost.jpgbin0 -> 3940 bytes
-rw-r--r--mediagoblin/templates/mediagoblin/base.html4
-rw-r--r--mediagoblin/templates/mediagoblin/extra_head.html16
-rw-r--r--mediagoblin/templates/mediagoblin/media_displays/blogpost.html33
-rw-r--r--mediagoblin/templates/mediagoblin/user_pages/blog_media.html176
-rw-r--r--mediagoblin/user_pages/views.py26
22 files changed, 1104 insertions, 6 deletions
diff --git a/mediagoblin/media_types/blog/__init__.py b/mediagoblin/media_types/blog/__init__.py
new file mode 100644
index 00000000..ec6f68ec
--- /dev/null
+++ b/mediagoblin/media_types/blog/__init__.py
@@ -0,0 +1,97 @@
+#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 os
+import logging
+_log = logging.getLogger(__name__)
+
+from mediagoblin.media_types import MediaManagerBase
+from mediagoblin.media_types.blog.models import Blog, BlogPostData
+
+from mediagoblin.tools import pluginapi
+
+PLUGIN_DIR = os.path.dirname(__file__)
+MEDIA_TYPE = 'mediagoblin.media_types.blogpost'
+
+def setup_plugin():
+ config = pluginapi.get_config(MEDIA_TYPE)
+ _log.info("setting up blog media type plugin.")
+
+ routes = [
+ #blog_create
+ ('mediagoblin.media_types.blog.create',
+ '/u/<string:user>/b/create/',
+ 'mediagoblin.media_types.blog.views:blog_edit'
+ ),
+ #blog_edit
+ ('mediagoblin.media_types.blog.edit',
+ '/u/<string:user>/b/<string:blog_slug>/edit/',
+ 'mediagoblin.media_types.blog.views:blog_edit'
+ ),
+ #blog post create
+ ('mediagoblin.media_types.blog.blogpost.create',
+ '/u/<string:user>/b/<string:blog_slug>/p/create/',
+ 'mediagoblin.media_types.blog.views:blogpost_create'
+ ),
+ #blog post edit
+ ('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'
+ ),
+ #blog admin dashboard
+ ('mediagoblin.media_types.blog.blog-dashboard',
+ '/u/<string:user>/b/<string:blog_slug>/blog_dashboard/',
+ 'mediagoblin.media_types.blog.views:blog_dashboard'
+ ),
+ #blog post listing view
+ ('mediagoblin.media_types.blog.blog_post_listing',
+ '/u/<string:user>/b/',
+ 'mediagoblin.media_types.blog.views:blog_post_listing'
+ ),
+ #blog post draft view
+ ('mediagoblin.media_types.blog.blogpost_draft_view',
+ '/u/<string:user>/b/<string:blog_slug>/p/<string:blog_post_slug>/draft/',
+ 'mediagoblin.media_types.blog.views:draft_view')
+ ]
+
+
+ pluginapi.register_routes(routes)
+ pluginapi.register_template_path(os.path.join(PLUGIN_DIR, 'templates'))
+
+
+class BlogPostMediaManager(MediaManagerBase):
+ human_readable = "Blog Post"
+ display_template = "mediagoblin/media_displays/blogpost.html"
+ default_thumb = "images/media_thumbs/blogpost.jpg"
+
+ def get_blog_by_blogpost(self):
+ blog_post_data = BlogPostData.query.filter_by(media_entry=self.entry.id).first()
+ blog = Blog.query.filter_by(id=blog_post_data.blog).first()
+ return blog
+
+def get_media_type_and_manager():
+ 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..0725b5b6
--- /dev/null
+++ b/mediagoblin/media_types/blog/lib.py
@@ -0,0 +1,36 @@
+# 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
+
+def set_blogpost_state(request, blogpost):
+ if request.form['status'] == 'Publish':
+ blogpost.state = u'processed'
+ else:
+ blogpost.state = u'failed'
+
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/templates/mediagoblin/blog/blog_admin_dashboard.html b/mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html
new file mode 100644
index 00000000..5ce838bf
--- /dev/null
+++ b/mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html
@@ -0,0 +1,96 @@
+{#
+# 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 &mdash; {{ super() }}
+{%- endblock title %}
+
+{% block mediagoblin_head%}
+<style type = "text/css">
+ td > a { text-decoration:none; font-weight: bold; }
+</style>
+{% endblock %}
+
+
+{% block mediagoblin_content %}
+<h1> {{blog.title}}</h1>
+<p>
+ {{blog.description|safe}}
+</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>
+&middot;
+ {% 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>
+ {% if blog_post.state == 'processed' %}
+ <td><a href="{{ blog_post.url_for_self(request.urlgen) }}">{{ blog_post.title }}</a></td>
+ {% else %}
+ {% set draft_url = request.urlgen('mediagoblin.media_types.blog.blogpost_draft_view',
+ blog_slug=blog.slug, user=request.user.username,
+ blog_post_slug=blog_post.slug) %}
+ <td><a href="{{ draft_url }}">{{ blog_post.title }}</a></td>
+ {% endif %}
+ <td>{{ blog_post.created.strftime("%F %R") }}</td>
+
+ {% if blog_post.state == 'processed' %}
+ <td><h6><em>Published</em></h6></td>
+ {% else %}
+ <td><h6><em>Draft</em></h6></td>
+ {% endif %}
+ {% 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) %}
+ {% set blogpost_delete_url = request.urlgen('mediagoblin.user_pages.media_confirm_delete',
+ user= blog_post.get_uploader.username,
+ media_id=blog_post.id) %}
+ <td>
+ <a class="button_action" href="{{ blogpost_edit_url }}">{% trans %}Edit{% endtrans %}</a>
+ <a class="button_action" href="{{ blogpost_delete_url }}">{% trans %}Delete{% endtrans %}</a>
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
+ {% else %}
+ {% trans %} No blog post yet. {% endtrans %}
+ {% endif %}
+{% endblock %}
+
+
+
diff --git a/mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_edit_create.html b/mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_edit_create.html
new file mode 100644
index 00000000..f4bbcb8f
--- /dev/null
+++ b/mediagoblin/media_types/blog/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|safe}}</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/media_types/blog/templates/mediagoblin/blog/blog_post_edit_create.html b/mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_edit_create.html
new file mode 100644
index 00000000..c5620c56
--- /dev/null
+++ b/mediagoblin/media_types/blog/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 %} &mdash; {{ super() }}
+{%- endblock %}
+
+{% block mediagoblin_content %}
+ <form action="" method="POST">
+ <div class="blog_form_box_xl">
+ <h1>{% trans %}Create/Edit 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="Save" class="button_form">
+ </div>
+ </div>
+ </form>
+{% endblock %}
diff --git a/mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_listing.html b/mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_listing.html
new file mode 100644
index 00000000..ea712bb0
--- /dev/null
+++ b/mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_listing.html
@@ -0,0 +1,48 @@
+{#
+# 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 %}{{ blog_owner }} 's Blog{% endtrans %} &mdash; {{ super() }}
+{%- endblock %}
+
+{% block mediagoblin_head -%}
+ <style type="text/css">
+ h3 {margin = 0; padding = 0;
+ font-size=.7 em;}
+
+ </style>
+{%- endblock %}
+
+{% block mediagoblin_content %}
+ <div class="b_list_owner"> <h1><font color="black"> {{ blog_owner }} 's Blog</font></h1></div>
+ <div>
+ {% for post in blog_posts %}
+ <div class="b_listing_title"><a href="{{ post.url_for_self(request.urlgen) }}">
+ <h2><font color="black">{{ post.title }}</font></h2></a>
+ </div>
+ <h3>{{ post.created.strftime("%d %b, %Y") }}</h3>
+
+ <div class="b_list_des"> <p>{{ post.description|safe }} </p></div>
+ </br>
+ </br>
+ {% endfor %}
+ </div>
+{% endblock %}
diff --git a/mediagoblin/media_types/blog/templates/mediagoblin/blog/blogpost_draft_view.html b/mediagoblin/media_types/blog/templates/mediagoblin/blog/blogpost_draft_view.html
new file mode 100644
index 00000000..7c634877
--- /dev/null
+++ b/mediagoblin/media_types/blog/templates/mediagoblin/blog/blogpost_draft_view.html
@@ -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/>.
+#}
+
+{% extends 'mediagoblin/base.html' %}
+
+{% block mediagoblin_head %}
+ {{ super() }}
+{% endblock %}
+
+{% block mediagoblin_content %}
+<h1> {{ blogpost.title}}</h1>
+<p>{{ blogpost.description|safe}}</p>
+
+{% set blogpost_edit_url = request.urlgen('mediagoblin.media_types.blog.blogpost.edit',
+ blog_slug=blog.slug, user=request.user.username,
+ blog_post_slug=blogpost.slug) %}
+{% set blogpost_delete_url = request.urlgen('mediagoblin.user_pages.media_confirm_delete',
+ user= blogpost.get_uploader.username,
+ media_id=blogpost.id) %}
+ <a class="button_action" href="{{ blogpost_edit_url }}">{% trans %}Edit{% endtrans %}</a>
+ <a class="button_action" href="{{ blogpost_delete_url }}">{% trans %}Delete{% endtrans %}</a>
+
+{% endblock %}
+
+
+
+
diff --git a/mediagoblin/media_types/blog/views.py b/mediagoblin/media_types/blog/views.py
new file mode 100644
index 00000000..f22d6ac1
--- /dev/null
+++ b/mediagoblin/media_types/blog/views.py
@@ -0,0 +1,284 @@
+# 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, set_blogpost_state
+
+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(cleaned_markdown_conversion((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, "Welcome! You already have created a blog.")
+ 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 = cleaned_markdown_conversion(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(cleaned_markdown_conversion((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():
+ blog_slug = request.matchdict.get('blog_slug')
+ blog = request.db.Blog.query.filter_by(slug=blog_slug,
+ author=request.user.id).first()
+ if not blog:
+ return render_404(request)
+
+ blogpost = request.db.MediaEntry()
+ blogpost.media_type = 'mediagoblin.media_types.blogpost'
+ blogpost.title = unicode(form.title.data)
+ blogpost.description = unicode(cleaned_markdown_conversion((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.generate_slug()
+
+ set_blogpost_state(request, blogpost)
+ 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 = cleaned_markdown_conversion(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():
+ blogpost.title = unicode(form.title.data)
+ blogpost.description = unicode(cleaned_markdown_conversion((form.description.data)))
+ blogpost.tags = convert_to_tag_list_of_dicts(form.tags.data)
+ blogpost.license = unicode(form.license.data)
+ set_blogpost_state(request, blogpost)
+ blogpost.generate_slug()
+ 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')
+ 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
+ })
+
+
+def draft_view(request):
+ blog_slug = request.matchdict.get('blog_slug')
+ blog_post_slug = request.matchdict.get('blog_post_slug')
+ user = request.matchdict.get('user')
+
+ blog = request.db.Blog.query.filter_by(author=request.user.id, slug=blog_slug).first()
+ blogpost = request.db.MediaEntry.query.filter_by(state = u'failed', uploader=request.user.id, slug=blog_post_slug).first()
+
+ if not blog or not blogpost:
+ return render_404(request)
+
+ return render_to_response(
+ request,
+ 'mediagoblin/blog/blogpost_draft_view.html',
+ {'blogpost':blogpost,
+ 'blog': blog
+ })
+
+
+
+
+
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/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 5961f33b..daf8a45d 100644
--- a/mediagoblin/routing.py
+++ b/mediagoblin/routing.py
@@ -36,7 +36,10 @@ def get_url_map():
import mediagoblin.webfinger.routing
import mediagoblin.listings.routing
import mediagoblin.notifications.routing
+<<<<<<< HEAD
+=======
import mediagoblin.oauth.routing
+>>>>>>> e7b8059f17c98ee88d933af52b0c4d858e882e8e
for route in PluginManager().get_routes():
add_route(*route)
diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css
index d96b9200..cadd019a 100644
--- a/mediagoblin/static/css/base.css
+++ b/mediagoblin/static/css/base.css
@@ -288,6 +288,48 @@ 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: 800px;
+ min-height: 500px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.b_listing_title {
+ height: 30px;
+ width: 100%;
+ padding: 0px;
+ background-color: #86D4B1;
+ text-transform:capitalize;
+ text-decoration: none;
+ #border-radius: 4px;
+}
+
+.b_listing_title > a {
+ text-decoration: none;
+}
+
+.b_list_owner {
+ height: 100px;
+ width: 100%;
+ padding: 0em;
+ margin-right: auto;
+ background-color: #DDA0DD;
+ #border-radius: 4px;
+ text-transform: capitalize;
+}
+
+.b_list_des {
+ text-align:justify;
+}
+
+
.edit_box {
border-top: 6px dashed #D49086
}
@@ -296,6 +338,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/static/extlib/tinymce b/mediagoblin/static/extlib/tinymce
new file mode 120000
index 00000000..debf6e2e
--- /dev/null
+++ b/mediagoblin/static/extlib/tinymce
@@ -0,0 +1 @@
+../../../extlib/tinymce \ No newline at end of file
diff --git a/mediagoblin/static/images/media_thumbs/blogpost.jpg b/mediagoblin/static/images/media_thumbs/blogpost.jpg
new file mode 100644
index 00000000..c6c68678
--- /dev/null
+++ b/mediagoblin/static/images/media_thumbs/blogpost.jpg
Binary files differ
diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html
index 88027472..b1fc658e 100644
--- a/mediagoblin/templates/mediagoblin/base.html
+++ b/mediagoblin/templates/mediagoblin/base.html
@@ -127,6 +127,10 @@
<a class="button_action" href="{{ request.urlgen('mediagoblin.submit.collection') }}">
{%- trans %}Create new collection{% endtrans -%}
</a>
+ <a class="button_action" href="{{ request.urlgen('mediagoblin.media_types.blog.create',
+ user=request.user.username) }}">
+ {%- trans %} Blog {% endtrans -%}
+ </a>
{% if request.user.is_admin %}
<p>
<span class="dropdown_title">Admin powers:</span>
diff --git a/mediagoblin/templates/mediagoblin/extra_head.html b/mediagoblin/templates/mediagoblin/extra_head.html
index 973e2b48..847cfcc7 100644
--- a/mediagoblin/templates/mediagoblin/extra_head.html
+++ b/mediagoblin/templates/mediagoblin/extra_head.html
@@ -17,3 +17,19 @@
-#}
{# Add extra head declarations here for your theme, if appropriate #}
+
+<script type="text/javascript"
+ src="{{request.staticdirect('extlib/tinymce/js/tinymce/tinymce.min.js')}}"></script>
+
+ <script type="text/javascript">
+ tinyMCE.init({
+ selector: "div.blog_form_field_input textarea",
+ height: 300,
+ width: 800,
+ plugins: [
+ "advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker",
+ "searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking",
+ "save table contextmenu directionality emoticons template paste textcolor"
+ ]
+ })
+</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..ca3441b9
--- /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/blog_media.html' %}
+
+{% block mediagoblin_head %}
+ {{ super() }}
+{% endblock %}
+
+{% block mediagoblin_media %}
+<h1> {{media.title}}</h1>
+<p>{{media.description|safe}}</p>
+
+{% endblock %}
+
+
+
+
diff --git a/mediagoblin/templates/mediagoblin/user_pages/blog_media.html b/mediagoblin/templates/mediagoblin/user_pages/blog_media.html
new file mode 100644
index 00000000..25dd4783
--- /dev/null
+++ b/mediagoblin/templates/mediagoblin/user_pages/blog_media.html
@@ -0,0 +1,176 @@
+{#
+# 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 %}
+{% from "mediagoblin/utils/pagination.html" import render_pagination %}
+
+{% block title %}{{ media.title }} &mdash; {{ super() }}{% endblock %}
+
+{% block mediagoblin_head %}
+<!--[if lte IE 8]><link rel="stylesheet"
+ href="{{ request.staticdirect('/extlib/leaflet/leaflet.ie.css') }}" /><![endif]-->
+ <script type="text/javascript"
+ src="{{ request.staticdirect('/js/comment_show.js') }}"></script>
+ <script type="text/javascript"
+ src="{{ request.staticdirect('/js/keyboard_navigation.js') }}"></script>
+
+ {% template_hook("media_head") %}
+{% endblock mediagoblin_head %}
+
+{% block mediagoblin_content %}
+ <p class="context">
+ {%- trans user_url=request.urlgen(
+ 'mediagoblin.user_pages.user_home',
+ user=media.get_uploader.username),
+ username=media.get_uploader.username -%}
+ ❖ Blog post by <a href="{{user_url}}">{{username}}</a>
+ {%- endtrans -%}
+ </p>
+ {% include "mediagoblin/utils/prev_next.html" %}
+ <div class="media_pane">
+ <div class="media_image_container">
+ {% block mediagoblin_media %}
+ {% set display_media = request.app.public_store.file_url(
+ media.get_display_media()[1]) %}
+ {# if there's a medium file size, that means the medium size
+ # isn't the original... so link to the original!
+ #}
+ {% if media.media_files.has_key('medium') %}
+ <a href="{{ request.app.public_store.file_url(
+ media.media_files['original']) }}">
+ <img class="media_image"
+ src="{{ display_media }}"
+ alt="{% trans media_title=media.title -%}
+ Image for {{ media_title }}{% endtrans %}" />
+ </a>
+ {% else %}
+ <img class="media_image"
+ src="{{ display_media }}"
+ alt="{% trans media_title=media.title -%}
+ Image for {{ media_title }}{% endtrans %}" />
+ {% endif %}
+ {% endblock %}
+ </div>
+ {% if request.user and
+ (media.uploader == request.user.id or
+ request.user.is_admin) %}
+ {% set edit_url = request.urlgen('mediagoblin.media_types.blog.blogpost.edit',
+ blog_slug=media.media_manager.get_blog_by_blogpost().slug,
+ user=request.user.username, blog_post_slug=media.slug) %}
+ <a class="button_action" href="{{ edit_url }}">{% trans %}Edit{% endtrans %}</a>
+ {% set delete_url = request.urlgen('mediagoblin.user_pages.media_confirm_delete',
+ user= media.get_uploader.username,
+ media_id=media.id) %}
+ <a class="button_action" href="{{ delete_url }}">{% trans %}Delete{% endtrans %}</a>
+
+ {% endif %}
+ </br>
+ </br>
+ {% if comments %}
+ {% if app_config['allow_comments'] %}
+ <a
+ {% if not request.user %}
+ href="{{ request.urlgen('mediagoblin.auth.login') }}"
+ {% endif %}
+ class="button_action" id="button_addcomment" title="Add a comment">
+ {% trans %}Add a comment{% endtrans %}
+ </a>
+ {% include "mediagoblin/utils/comment-subscription.html" %}
+
+ {% endif %}
+ {% if request.user %}
+ <form action="{{ request.urlgen('mediagoblin.user_pages.media_post_comment',
+ user= media.get_uploader.username,
+ media_id=media.id) }}" method="POST" id="form_comment">
+ {{ wtforms_util.render_divs(comment_form) }}
+ <div class="form_submit_buttons">
+ <input type="submit" value="{% trans %}Add this comment{% endtrans %}" class="button_action" />
+ {{ csrf_token }}
+ </div>
+ </form>
+ {% endif %}
+ <ul style="list-style:none">
+ {% for comment in comments %}
+ {% set comment_author = comment.get_author %}
+ <li id="comment-{{ comment.id }}"
+ {%- if pagination.active_id == comment.id %}
+ class="comment_wrapper comment_active">
+ <a name="comment" id="comment"></a>
+ {%- else %}
+ class="comment_wrapper">
+ {%- endif %}
+ <div class="comment_author">
+ <img src="{{ request.staticdirect('/images/icon_comment.png') }}" />
+ <a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
+ user=comment_author.username) }}"
+ class="comment_authorlink">
+ {{- comment_author.username -}}
+ </a>
+ <a href="{{ request.urlgen('mediagoblin.user_pages.media_home.view_comment',
+ comment=comment.id,
+ user=media.get_uploader.username,
+ media=media.slug_or_id) }}#comment"
+ class="comment_whenlink">
+ <span title='{{- comment.created.strftime("%I:%M%p %Y-%m-%d") -}}'>
+ {%- trans formatted_time=timesince(comment.created) -%}
+ {{ formatted_time }} ago
+ {%- endtrans -%}
+ </span></a>:
+ </div>
+ <div class="comment_content">
+ {% autoescape False -%}
+ {{ comment.content_html }}
+ {%- endautoescape %}
+ </div>
+ </li>
+ {% endfor %}
+ </ul>
+ {{ render_pagination(request, pagination,
+ media.url_for_self(request.urlgen)) }}
+ {% endif %}
+ </div>
+ <div class="media_sidebar">
+ <h3>{% trans %}Added{% endtrans %}</h3>
+ <p><span title="{{ media.created.strftime("%I:%M%p %Y-%m-%d") }}">
+ {%- trans formatted_time=timesince(media.created) -%}
+ {{ formatted_time }} ago
+ {%- endtrans -%}
+ </span></p>
+
+ {% block mediagoblin_after_added_sidebar %}
+ {% endblock %}
+
+ {% if media.tags %}
+ {% include "mediagoblin/utils/tags.html" %}
+ {% endif %}
+
+ {% include "mediagoblin/utils/collections.html" %}
+
+ {% include "mediagoblin/utils/license.html" %}
+
+ {% include "mediagoblin/utils/exif.html" %}
+
+ {% template_hook("media_sideinfo") %}
+
+ {% block mediagoblin_sidebar %}
+ {% endblock %}
+
+ </div>
+ <div class="clear"></div>
+{% endblock %}
diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py
index 9d3cb08b..87d0034e 100644
--- a/mediagoblin/user_pages/views.py
+++ b/mediagoblin/user_pages/views.py
@@ -286,11 +286,29 @@ def media_collect(request, media):
#TODO: Why does @user_may_delete_media not implicate @require_active_login?
-@get_media_entry_by_id
-@require_active_login
-@user_may_delete_media
-def media_confirm_delete(request, media):
+@require_active_login
+def media_confirm_delete(request):
+
+ allowed_state = [u'failed', u'processed']
+ media = None
+ for media_state in allowed_state:
+ media = request.db.MediaEntry.query.filter_by(id=request.matchdict['media_id'], state=media_state).first()
+ if media:
+ break
+
+ if not media:
+ return render_404(request)
+
+ given_username = request.matchdict.get('user')
+ if given_username and (given_username != media.get_uploader.username):
+ return render_404(request)
+
+ uploader_id = media.uploader
+ if not (request.user.is_admin or
+ request.user.id == uploader_id):
+ raise Forbidden()
+
form = user_forms.ConfirmDeleteForm(request.form)
if request.method == 'POST' and form.validate():