Merge branch 'master' of gitorious.org:mediagoblin/mediagoblin
Conflicts: mediagoblin/db/mongo/models.py
This commit is contained in:
commit
73079ac4b8
90
mediagoblin/db/mixin.py
Normal file
90
mediagoblin/db/mixin.py
Normal file
@ -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'])
|
@ -18,12 +18,12 @@ import datetime
|
|||||||
|
|
||||||
from mongokit import Document
|
from mongokit import Document
|
||||||
|
|
||||||
from mediagoblin.auth import lib as auth_lib
|
|
||||||
from mediagoblin import mg_globals
|
from mediagoblin import mg_globals
|
||||||
from mediagoblin.db.mongo import migrations
|
from mediagoblin.db.mongo import migrations
|
||||||
from mediagoblin.db.mongo.util import ASCENDING, DESCENDING, ObjectId
|
from mediagoblin.db.mongo.util import ASCENDING, DESCENDING, ObjectId
|
||||||
from mediagoblin.tools.pagination import Pagination
|
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
|
# Custom validators
|
||||||
@ -34,7 +34,7 @@ from mediagoblin.tools import url, common
|
|||||||
########
|
########
|
||||||
|
|
||||||
|
|
||||||
class User(Document):
|
class User(Document, UserMixin):
|
||||||
"""
|
"""
|
||||||
A user of MediaGoblin.
|
A user of MediaGoblin.
|
||||||
|
|
||||||
@ -89,15 +89,8 @@ class User(Document):
|
|||||||
'status': u'needs_email_verification',
|
'status': u'needs_email_verification',
|
||||||
'is_admin': False}
|
'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, MediaEntryMixin):
|
||||||
class MediaEntry(Document):
|
|
||||||
"""
|
"""
|
||||||
Record of a piece of media.
|
Record of a piece of media.
|
||||||
|
|
||||||
@ -224,28 +217,6 @@ class MediaEntry(Document):
|
|||||||
return self.db.MediaComment.find({
|
return self.db.MediaComment.find({
|
||||||
'media_entry': self._id}).sort('created', order)
|
'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):
|
def generate_slug(self):
|
||||||
self.slug = url.slugify(self.title)
|
self.slug = url.slugify(self.title)
|
||||||
|
|
||||||
@ -303,13 +274,6 @@ class MediaEntry(Document):
|
|||||||
def get_uploader(self):
|
def get_uploader(self):
|
||||||
return self.db.User.find_one({'_id': self.uploader})
|
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):
|
class MediaComment(Document):
|
||||||
"""
|
"""
|
||||||
|
@ -7,6 +7,7 @@ from sqlalchemy import (
|
|||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
from mediagoblin.db.sql.base import GMGTableBase
|
from mediagoblin.db.sql.base import GMGTableBase
|
||||||
|
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin
|
||||||
|
|
||||||
|
|
||||||
Base = declarative_base(cls=GMGTableBase)
|
Base = declarative_base(cls=GMGTableBase)
|
||||||
@ -24,7 +25,7 @@ class SimpleFieldAlias(object):
|
|||||||
setattr(instance, self.fieldname, val)
|
setattr(instance, self.fieldname, val)
|
||||||
|
|
||||||
|
|
||||||
class User(Base):
|
class User(Base, UserMixin):
|
||||||
__tablename__ = "users"
|
__tablename__ = "users"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
@ -48,7 +49,7 @@ class User(Base):
|
|||||||
_id = SimpleFieldAlias("id")
|
_id = SimpleFieldAlias("id")
|
||||||
|
|
||||||
|
|
||||||
class MediaEntry(Base):
|
class MediaEntry(Base, MediaEntryMixin):
|
||||||
__tablename__ = "media_entries"
|
__tablename__ = "media_entries"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
|
@ -14,9 +14,6 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import pkg_resources
|
|
||||||
import urlparse
|
|
||||||
|
|
||||||
####################################
|
####################################
|
||||||
# Staticdirect infrastructure.
|
# Staticdirect infrastructure.
|
||||||
# Borrowed largely from cc.engine
|
# Borrowed largely from cc.engine
|
||||||
@ -26,7 +23,9 @@ import urlparse
|
|||||||
####################################
|
####################################
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
import urlparse
|
import logging
|
||||||
|
|
||||||
|
_log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class StaticDirect(object):
|
class StaticDirect(object):
|
||||||
@ -37,6 +36,10 @@ class StaticDirect(object):
|
|||||||
if filepath in self.cache:
|
if filepath in self.cache:
|
||||||
return self.cache[filepath]
|
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)
|
static_direction = self.cache[filepath] = self.get(filepath)
|
||||||
return static_direction
|
return static_direction
|
||||||
|
|
||||||
|
@ -22,11 +22,9 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
<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>
|
<title>{% block title %}{{ app_config['html_title'] }}{% endblock %}</title>
|
||||||
<link rel="stylesheet" type="text/css"
|
<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"
|
<link rel="stylesheet" type="text/css"
|
||||||
href="{{ request.staticdirect('/css/base.css') }}"/>
|
href="{{ request.staticdirect('/css/base.css') }}"/>
|
||||||
<link rel="stylesheet" type="text/css"
|
|
||||||
href="{{ request.staticdirect('/css/video-js.css') }}"/>
|
|
||||||
<link rel="shortcut icon"
|
<link rel="shortcut icon"
|
||||||
href="{{ request.staticdirect('/images/goblin.ico') }}" />
|
href="{{ request.staticdirect('/images/goblin.ico') }}" />
|
||||||
<script src="{{ request.staticdirect('/js/extlib/jquery.js') }}"></script>
|
<script src="{{ request.staticdirect('/js/extlib/jquery.js') }}"></script>
|
||||||
|
@ -158,6 +158,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if app_config['allow_attachments']
|
{% if app_config['allow_attachments']
|
||||||
|
and request.user
|
||||||
and (media.uploader == request.user._id
|
and (media.uploader == request.user._id
|
||||||
or request.user.is_admin) %}
|
or request.user.is_admin) %}
|
||||||
<p>
|
<p>
|
||||||
|
@ -7,6 +7,9 @@ db_name = __mediagoblin_tests__
|
|||||||
# tag parsing
|
# tag parsing
|
||||||
tags_max_length = 50
|
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
|
# Celery shouldn't be set up by the application as it's setup via
|
||||||
# mediagoblin.init.celery.from_celery
|
# mediagoblin.init.celery.from_celery
|
||||||
celery_setup_elsewhere = true
|
celery_setup_elsewhere = true
|
||||||
|
@ -51,11 +51,17 @@ class TestSubmission:
|
|||||||
|
|
||||||
self.test_user = test_user
|
self.test_user = test_user
|
||||||
|
|
||||||
|
self.login()
|
||||||
|
|
||||||
|
def login(self):
|
||||||
self.test_app.post(
|
self.test_app.post(
|
||||||
'/auth/login/', {
|
'/auth/login/', {
|
||||||
'username': u'chris',
|
'username': u'chris',
|
||||||
'password': 'toast'})
|
'password': 'toast'})
|
||||||
|
|
||||||
|
def logout(self):
|
||||||
|
self.test_app.get('/auth/logout/')
|
||||||
|
|
||||||
def test_missing_fields(self):
|
def test_missing_fields(self):
|
||||||
# Test blank form
|
# Test blank form
|
||||||
# ---------------
|
# ---------------
|
||||||
@ -95,6 +101,14 @@ class TestSubmission:
|
|||||||
assert template.TEMPLATE_TEST_CONTEXT.has_key(
|
assert template.TEMPLATE_TEST_CONTEXT.has_key(
|
||||||
'mediagoblin/user_pages/user.html')
|
'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
|
# Test PNG
|
||||||
# --------
|
# --------
|
||||||
template.clear_test_template_context()
|
template.clear_test_template_context()
|
||||||
|
22
paste.ini
22
paste.ini
@ -19,6 +19,28 @@ use = egg:mediagoblin#app
|
|||||||
filter-with = beaker
|
filter-with = beaker
|
||||||
config = %(here)s/mediagoblin_local.ini %(here)s/mediagoblin.ini
|
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]
|
[app:publicstore_serve]
|
||||||
use = egg:Paste#static
|
use = egg:Paste#static
|
||||||
document_root = %(here)s/user_dev/media/public/
|
document_root = %(here)s/user_dev/media/public/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user