Merge branch 'master' of gitorious.org:mediagoblin/mediagoblin
authorChristopher Allan Webber <cwebber@dustycloud.org>
Tue, 10 Jan 2012 14:12:30 +0000 (08:12 -0600)
committerChristopher Allan Webber <cwebber@dustycloud.org>
Tue, 10 Jan 2012 14:12:30 +0000 (08:12 -0600)
Conflicts:
mediagoblin/db/mongo/models.py

mediagoblin/db/mixin.py [new file with mode: 0644]
mediagoblin/db/mongo/models.py
mediagoblin/db/sql/models.py
mediagoblin/staticdirect.py
mediagoblin/templates/mediagoblin/base.html
mediagoblin/templates/mediagoblin/user_pages/media.html
mediagoblin/tests/test_mgoblin_app.ini
mediagoblin/tests/test_submission.py
paste.ini

diff --git a/mediagoblin/db/mixin.py b/mediagoblin/db/mixin.py
new file mode 100644 (file)
index 0000000..4fb325d
--- /dev/null
@@ -0,0 +1,90 @@
+# 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/>.
+
+"""
+This module contains some Mixin classes for the db objects.
+
+A bunch of functions on the db objects are really more like
+"utility functions": They could live outside the classes
+and be called "by hand" passing the appropiate reference.
+They usually only use the public API of the object and
+rarely use database related stuff.
+
+These functions now live here and get "mixed in" into the
+real objects.
+"""
+
+from mediagoblin.auth import lib as auth_lib
+from mediagoblin.tools import common
+
+
+class UserMixin(object):
+    def check_login(self, password):
+        """
+        See if a user can login with this password
+        """
+        return auth_lib.bcrypt_check_password(
+            password, self.pw_hash)
+
+
+class MediaEntryMixin(object):
+    def get_display_media(self, media_map,
+                          fetch_order=common.DISPLAY_IMAGE_FETCHING_ORDER):
+        """
+        Find the best media for display.
+
+        Args:
+        - media_map: a dict like
+          {u'image_size': [u'dir1', u'dir2', u'image.jpg']}
+        - fetch_order: the order we should try fetching images in
+
+        Returns:
+        (media_size, media_path)
+        """
+        media_sizes = media_map.keys()
+
+        for media_size in common.DISPLAY_IMAGE_FETCHING_ORDER:
+            if media_size in media_sizes:
+                return media_map[media_size]
+
+    def main_mediafile(self):
+        pass
+
+    def url_for_self(self, urlgen):
+        """
+        Generate an appropriate url for ourselves
+
+        Use a slug if we have one, else use our '_id'.
+        """
+        uploader = self.get_uploader
+
+        if self.get('slug'):
+            return urlgen(
+                'mediagoblin.user_pages.media_home',
+                user=uploader.username,
+                media=self.slug)
+        else:
+            return urlgen(
+                'mediagoblin.user_pages.media_home',
+                user=uploader.username,
+                media=unicode(self._id))
+
+    def get_fail_exception(self):
+        """
+        Get the exception that's appropriate for this error
+        """
+        if self['fail_error']:
+            return common.import_component(self['fail_error'])
index f1e8eae626c0e55c0e706373dac601538f0d1fe4..d9b5a57039f69479877979154ddf7b59a08c6241 100644 (file)
@@ -18,12 +18,12 @@ import datetime
 
 from mongokit import Document
 
-from mediagoblin.auth import lib as auth_lib
 from mediagoblin import mg_globals
 from mediagoblin.db.mongo import migrations
 from mediagoblin.db.mongo.util import ASCENDING, DESCENDING, ObjectId
 from mediagoblin.tools.pagination import Pagination
-from mediagoblin.tools import url, common
+from mediagoblin.tools import url
+from mediagoblin.db.mixin import UserMixin, MediaEntryMixin
 
 ###################
 # Custom validators
@@ -34,7 +34,7 @@ from mediagoblin.tools import url, common
 ########
 
 
-class User(Document):
+class User(Document, UserMixin):
     """
     A user of MediaGoblin.
 
@@ -89,15 +89,8 @@ class User(Document):
         'status': u'needs_email_verification',
         'is_admin': False}
 
-    def check_login(self, password):
-        """
-        See if a user can login with this password
-        """
-        return auth_lib.bcrypt_check_password(
-            password, self.pw_hash)
 
-
-class MediaEntry(Document):
+class MediaEntry(Document, MediaEntryMixin):
     """
     Record of a piece of media.
 
@@ -224,28 +217,6 @@ class MediaEntry(Document):
         return self.db.MediaComment.find({
                 'media_entry': self._id}).sort('created', order)
 
-    def get_display_media(self, media_map,
-                          fetch_order=common.DISPLAY_IMAGE_FETCHING_ORDER):
-        """
-        Find the best media for display.
-
-        Args:
-        - media_map: a dict like
-          {u'image_size': [u'dir1', u'dir2', u'image.jpg']}
-        - fetch_order: the order we should try fetching images in
-
-        Returns:
-        (media_size, media_path)
-        """
-        media_sizes = media_map.keys()
-
-        for media_size in common.DISPLAY_IMAGE_FETCHING_ORDER:
-            if media_size in media_sizes:
-                return media_map[media_size]
-
-    def main_mediafile(self):
-        pass
-
     def generate_slug(self):
         self.slug = url.slugify(self.title)
 
@@ -303,13 +274,6 @@ class MediaEntry(Document):
     def get_uploader(self):
         return self.db.User.find_one({'_id': self.uploader})
 
-    def get_fail_exception(self):
-        """
-        Get the exception that's appropriate for this error
-        """
-        if self['fail_error']:
-            return common.import_component(self['fail_error'])
-
 
 class MediaComment(Document):
     """
index 31a6ed3b3962c5b1812bff6fec1b7608ae4c6123..95821b4f9f9044e1015d0b14712b706eb7471a7e 100644 (file)
@@ -7,6 +7,7 @@ from sqlalchemy import (
 from sqlalchemy.orm import relationship
 
 from mediagoblin.db.sql.base import GMGTableBase
+from mediagoblin.db.mixin import UserMixin, MediaEntryMixin
 
 
 Base = declarative_base(cls=GMGTableBase)
@@ -24,7 +25,7 @@ class SimpleFieldAlias(object):
         setattr(instance, self.fieldname, val)
 
 
-class User(Base):
+class User(Base, UserMixin):
     __tablename__ = "users"
 
     id = Column(Integer, primary_key=True)
@@ -48,7 +49,7 @@ class User(Base):
     _id = SimpleFieldAlias("id")
 
 
-class MediaEntry(Base):
+class MediaEntry(Base, MediaEntryMixin):
     __tablename__ = "media_entries"
 
     id = Column(Integer, primary_key=True)
index c6d2b3745b35c6bb1721731d0b2f0900958aeb6b..2bddb1607d5578553a030af299cbb708bccf79cb 100644 (file)
@@ -14,9 +14,6 @@
 # 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 pkg_resources
-import urlparse
-
 ####################################
 # Staticdirect infrastructure.
 # Borrowed largely from cc.engine
@@ -26,7 +23,9 @@ import urlparse
 ####################################
 
 import pkg_resources
-import urlparse
+import logging
+
+_log = logging.getLogger(__name__)
 
 
 class StaticDirect(object):
@@ -37,6 +36,10 @@ class StaticDirect(object):
         if filepath in self.cache:
             return self.cache[filepath]
 
+        if not pkg_resources.resource_exists('mediagoblin',
+                'static' + filepath):
+            _log.info("StaticDirect resource %r not found locally",
+                filepath)
         static_direction = self.cache[filepath] = self.get(filepath)
         return static_direction
 
index 82ee41b794e4dc5f30430385a78703f4ec496ff0..5335ebe3f808f89765d1a9fb61f7505d89f3219e 100644 (file)
     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
     <title>{% block title %}{{ app_config['html_title'] }}{% endblock %}</title>
     <link rel="stylesheet" type="text/css"
-          href="{{ request.staticdirect('/css/reset.css') }}"/>
+          href="{{ request.staticdirect('/css/extlib/reset.css') }}"/>
     <link rel="stylesheet" type="text/css"
           href="{{ request.staticdirect('/css/base.css') }}"/>
-    <link rel="stylesheet" type="text/css"
-          href="{{ request.staticdirect('/css/video-js.css') }}"/>
     <link rel="shortcut icon"
           href="{{ request.staticdirect('/images/goblin.ico') }}" />
     <script src="{{ request.staticdirect('/js/extlib/jquery.js') }}"></script>
index 10525f4c4166b8179c0be7505d92a5817c578c44..583e4ebd6d2f9eecd1691ef5d014bf7700ee38cc 100644 (file)
       </ul>
     {% endif %}
     {% if app_config['allow_attachments']
+          and request.user
           and (media.uploader == request.user._id
                or request.user.is_admin) %}
       <p>
index 2525a4f90b390e6b4fbfaef7259e2719d92c0ebd..c91ed92bb4ba7f83f74dd31786f44ef19a09f2ed 100644 (file)
@@ -7,6 +7,9 @@ db_name = __mediagoblin_tests__
 # tag parsing
 tags_max_length = 50
 
+# So we can start to test attachments:
+allow_attachments = True
+
 # Celery shouldn't be set up by the application as it's setup via
 # mediagoblin.init.celery.from_celery
 celery_setup_elsewhere = true
index 2b17c5159dc323b6cc236f539209feed36749ad8..b3c112497d41c93a208640db8ba29824f36100ca 100644 (file)
@@ -51,11 +51,17 @@ class TestSubmission:
 
         self.test_user = test_user
 
+        self.login()
+
+    def login(self):
         self.test_app.post(
             '/auth/login/', {
                 'username': u'chris',
                 'password': 'toast'})
 
+    def logout(self):
+        self.test_app.get('/auth/logout/')
+
     def test_missing_fields(self):
         # Test blank form
         # ---------------
@@ -95,6 +101,14 @@ class TestSubmission:
         assert template.TEMPLATE_TEST_CONTEXT.has_key(
             'mediagoblin/user_pages/user.html')
 
+        # Make sure the media view is at least reachable, logged in...
+        self.test_app.get('/u/chris/m/normal-upload-1/')
+        # ... and logged out too.
+        self.logout()
+        self.test_app.get('/u/chris/m/normal-upload-1/')
+        # Log back in for the remaining tests.
+        self.login()
+
         # Test PNG
         # --------
         template.clear_test_template_context()
index c729e41d6d78dcdbf2f5064cdb9deeb7782e63b2..13c152094898d66a29edad64754be1cd7bfa3c85 100644 (file)
--- a/paste.ini
+++ b/paste.ini
@@ -19,6 +19,28 @@ use = egg:mediagoblin#app
 filter-with = beaker
 config = %(here)s/mediagoblin_local.ini %(here)s/mediagoblin.ini
 
+[loggers]
+keys = root
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-7.7s [%(name)s] %(message)s
+
 [app:publicstore_serve]
 use = egg:Paste#static
 document_root = %(here)s/user_dev/media/public/