Merge branch 'master' of gitorious.org:mediagoblin/mediagoblin into metadata
Conflicts: mediagoblin/gmg_commands/__init__.py
This commit is contained in:
commit
1d09e8b4f1
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,6 +7,7 @@
|
|||||||
/build/
|
/build/
|
||||||
/eggs/
|
/eggs/
|
||||||
/lib/
|
/lib/
|
||||||
|
/lib64
|
||||||
/local/
|
/local/
|
||||||
/include/
|
/include/
|
||||||
/parts/
|
/parts/
|
||||||
|
1
AUTHORS
1
AUTHORS
@ -19,6 +19,7 @@ Thank you!
|
|||||||
* Asheesh Laroia
|
* Asheesh Laroia
|
||||||
* Bassam Kurdali
|
* Bassam Kurdali
|
||||||
* Bernhard Keller
|
* Bernhard Keller
|
||||||
|
* Berker Peksag
|
||||||
* Boris Bobrov
|
* Boris Bobrov
|
||||||
* Brandon Invergo
|
* Brandon Invergo
|
||||||
* Brett Smith
|
* Brett Smith
|
||||||
|
@ -63,16 +63,16 @@ The csv file
|
|||||||
============
|
============
|
||||||
The media:location column
|
The media:location column
|
||||||
-------------------------
|
-------------------------
|
||||||
The media:location column is the one column that is absolutely necessary for
|
The media:location column is the one column that is absolutely necessary for
|
||||||
uploading your media. This gives a path to each piece of media you upload. This
|
uploading your media. This gives a path to each piece of media you upload. This
|
||||||
can either a path to a local file or a direct link to remote media (with the
|
can either a path to a local file or a direct link to remote media (with the
|
||||||
link in http format). As you can see in the example above the (fake) media was
|
link in http format). As you can see in the example above the (fake) media was
|
||||||
stored remotely on "www.example.net".
|
stored remotely on "www.example.net".
|
||||||
|
|
||||||
Other columns
|
Other columns
|
||||||
-------------
|
-------------
|
||||||
Other columns can be used to provide detailed metadata about each media entry.
|
Other columns can be used to provide detailed metadata about each media entry.
|
||||||
Our metadata system accepts any information provided for in the
|
Our metadata system accepts any information provided for in the
|
||||||
`RDFa Core Initial Context`_, and the batchupload script recognizes all of the
|
`RDFa Core Initial Context`_, and the batchupload script recognizes all of the
|
||||||
resources provided within it.
|
resources provided within it.
|
||||||
|
|
||||||
@ -89,4 +89,4 @@ information of uploaded media entries.
|
|||||||
- **dc:description** sets a description of your media entry. If this is left blank the media entry's description will not be filled in.
|
- **dc:description** sets a description of your media entry. If this is left blank the media entry's description will not be filled in.
|
||||||
- **dc:rights** will set a license for your media entry `if` the data provided is a valid URI. If this is left blank 'All Rights Reserved' will be selected.
|
- **dc:rights** will set a license for your media entry `if` the data provided is a valid URI. If this is left blank 'All Rights Reserved' will be selected.
|
||||||
|
|
||||||
You can of course, change these values later.
|
You can of course, change these values later.
|
||||||
|
@ -35,6 +35,7 @@ allow_reporting = true
|
|||||||
## If you want the terms of service displayed, you can uncomment this
|
## If you want the terms of service displayed, you can uncomment this
|
||||||
# show_tos = true
|
# show_tos = true
|
||||||
|
|
||||||
|
user_privilege_scheme = "uploader,commenter,reporter"
|
||||||
[storage:queuestore]
|
[storage:queuestore]
|
||||||
base_dir = %(here)s/user_dev/media/queue
|
base_dir = %(here)s/user_dev/media/queue
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ def create_user(register_form):
|
|||||||
results = hook_runall("auth_create_user", register_form)
|
results = hook_runall("auth_create_user", register_form)
|
||||||
return results[0]
|
return results[0]
|
||||||
|
|
||||||
|
|
||||||
def extra_validation(register_form):
|
def extra_validation(register_form):
|
||||||
from mediagoblin.auth.tools import basic_extra_validation
|
from mediagoblin.auth.tools import basic_extra_validation
|
||||||
|
|
||||||
|
@ -132,11 +132,7 @@ def register_user(request, register_form):
|
|||||||
user = auth.create_user(register_form)
|
user = auth.create_user(register_form)
|
||||||
|
|
||||||
# give the user the default privileges
|
# give the user the default privileges
|
||||||
default_privileges = [
|
user.all_privileges += get_default_privileges(user)
|
||||||
Privilege.query.filter(Privilege.privilege_name==u'commenter').first(),
|
|
||||||
Privilege.query.filter(Privilege.privilege_name==u'uploader').first(),
|
|
||||||
Privilege.query.filter(Privilege.privilege_name==u'reporter').first()]
|
|
||||||
user.all_privileges += default_privileges
|
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
# log the user in
|
# log the user in
|
||||||
@ -151,6 +147,14 @@ def register_user(request, register_form):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_default_privileges(user):
|
||||||
|
instance_privilege_scheme = mg_globals.app_config['user_privilege_scheme']
|
||||||
|
default_privileges = [Privilege.query.filter(
|
||||||
|
Privilege.privilege_name==privilege_name).first()
|
||||||
|
for privilege_name in instance_privilege_scheme.split(',')]
|
||||||
|
default_privileges = [privilege for privilege in default_privileges if not privilege == None]
|
||||||
|
|
||||||
|
return default_privileges
|
||||||
|
|
||||||
def check_login_simple(username, password):
|
def check_login_simple(username, password):
|
||||||
user = auth.get_user(username=username)
|
user = auth.get_user(username=username)
|
||||||
|
@ -89,6 +89,9 @@ upload_limit = integer(default=None)
|
|||||||
# Max file size (in Mb)
|
# Max file size (in Mb)
|
||||||
max_file_size = integer(default=None)
|
max_file_size = integer(default=None)
|
||||||
|
|
||||||
|
# Privilege scheme
|
||||||
|
user_privilege_scheme = string(default="uploader,commenter,reporter")
|
||||||
|
|
||||||
[jinja2]
|
[jinja2]
|
||||||
# Jinja2 supports more directives than the minimum required by mediagoblin.
|
# Jinja2 supports more directives than the minimum required by mediagoblin.
|
||||||
# This setting allows users creating custom templates to specify a list of
|
# This setting allows users creating custom templates to specify a list of
|
||||||
|
@ -46,6 +46,12 @@ class UserMixin(object):
|
|||||||
def bio_html(self):
|
def bio_html(self):
|
||||||
return cleaned_markdown_conversion(self.bio)
|
return cleaned_markdown_conversion(self.bio)
|
||||||
|
|
||||||
|
def url_for_self(self, urlgen, **kwargs):
|
||||||
|
"""Generate a URL for this User's home page."""
|
||||||
|
return urlgen('mediagoblin.user_pages.user_home',
|
||||||
|
user=self.username, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class GenerateSlugMixin(object):
|
class GenerateSlugMixin(object):
|
||||||
"""
|
"""
|
||||||
Mixin to add a generate_slug method to objects.
|
Mixin to add a generate_slug method to objects.
|
||||||
|
@ -53,10 +53,14 @@ SUBCOMMAND_MAP = {
|
|||||||
'setup': 'mediagoblin.gmg_commands.addmedia:parser_setup',
|
'setup': 'mediagoblin.gmg_commands.addmedia:parser_setup',
|
||||||
'func': 'mediagoblin.gmg_commands.addmedia:addmedia',
|
'func': 'mediagoblin.gmg_commands.addmedia:addmedia',
|
||||||
'help': 'Reprocess media entries'},
|
'help': 'Reprocess media entries'},
|
||||||
|
'deletemedia': {
|
||||||
|
'setup': 'mediagoblin.gmg_commands.deletemedia:parser_setup',
|
||||||
|
'func': 'mediagoblin.gmg_commands.deletemedia:deletemedia',
|
||||||
|
'help': 'Delete media entries'},
|
||||||
'batchaddmedia': {
|
'batchaddmedia': {
|
||||||
'setup': 'mediagoblin.gmg_commands.batchaddmedia:parser_setup',
|
'setup': 'mediagoblin.gmg_commands.batchaddmedia:parser_setup',
|
||||||
'func': 'mediagoblin.gmg_commands.batchaddmedia:batchaddmedia',
|
'func': 'mediagoblin.gmg_commands.batchaddmedia:batchaddmedia',
|
||||||
'help': 'Add many media entries at once'}
|
'help': 'Add many media entries at once'},
|
||||||
# 'theme': {
|
# 'theme': {
|
||||||
# 'setup': 'mediagoblin.gmg_commands.theme:theme_parser_setup',
|
# 'setup': 'mediagoblin.gmg_commands.theme:theme_parser_setup',
|
||||||
# 'func': 'mediagoblin.gmg_commands.theme:theme',
|
# 'func': 'mediagoblin.gmg_commands.theme:theme',
|
||||||
|
38
mediagoblin/gmg_commands/deletemedia.py
Normal file
38
mediagoblin/gmg_commands/deletemedia.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# 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.gmg_commands import util as commands_util
|
||||||
|
|
||||||
|
|
||||||
|
def parser_setup(subparser):
|
||||||
|
subparser.add_argument('media_ids',
|
||||||
|
help='Comma separated list of media IDs to will be deleted.')
|
||||||
|
|
||||||
|
|
||||||
|
def deletemedia(args):
|
||||||
|
app = commands_util.setup_app(args)
|
||||||
|
|
||||||
|
media_ids = set(map(int, args.media_ids.split(',')))
|
||||||
|
found_medias = set()
|
||||||
|
filter_ids = app.db.MediaEntry.id.in_(media_ids)
|
||||||
|
medias = app.db.MediaEntry.query.filter(filter_ids).all()
|
||||||
|
for media in medias:
|
||||||
|
found_medias.add(media.id)
|
||||||
|
media.delete()
|
||||||
|
print 'Media ID %d has been deleted.' % media.id
|
||||||
|
for media in media_ids - found_medias:
|
||||||
|
print 'Can\'t find a media with ID %d.' % media
|
||||||
|
print 'Done.'
|
@ -1,3 +1,3 @@
|
|||||||
<link rel="stylesheet" type="text/css"
|
<link rel="stylesheet" type="text/css"
|
||||||
href="{{ request.staticdirect('css/metadata_display.css',
|
href="{{ request.staticdirect('css/metadata_display.css',
|
||||||
'metadata_display') }}"/>
|
'metadata_display') }}"/>
|
||||||
|
@ -3,17 +3,22 @@
|
|||||||
=======================
|
=======================
|
||||||
|
|
||||||
Mediagoblin templates are written with 80 char limit for better
|
Mediagoblin templates are written with 80 char limit for better
|
||||||
readability. However that means that the html output is very verbose
|
readability. However that means that the HTML output is very verbose
|
||||||
containing LOTS of whitespace. This plugin inserts a Middleware that
|
containing *lots* of whitespace. This plugin inserts a middleware that
|
||||||
filters out whitespace from the returned HTML in the Response() objects.
|
filters out whitespace from the returned HTML in the ``Response()``
|
||||||
|
objects.
|
||||||
|
|
||||||
Simply enable this plugin by putting it somewhere where python can reach it and put it's path into the [plugins] section of your mediagoblin.ini or mediagoblin_local.ini like for example this:
|
Simply enable this plugin by putting it somewhere where Python can reach
|
||||||
|
it and put it's path into the ``[plugins]`` section of your
|
||||||
|
``mediagoblin.ini`` or ``mediagoblin_local.ini`` like for example this:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
[[mediagoblin.plugins.trim_whitespace]]
|
[[mediagoblin.plugins.trim_whitespace]]
|
||||||
|
|
||||||
There is no further configuration required. If this plugin is enabled,
|
There is no further configuration required. If this plugin is enabled,
|
||||||
all text/html documents should not have lots of whitespace in between
|
all *text/html* documents should not have lots of whitespace in between
|
||||||
elements, although it does a very naive filtering right now (just keep
|
elements, although it does a very naive filtering right now (just keep
|
||||||
the first whitespace and delete all subsequent ones).
|
the first whitespace and delete all subsequent ones).
|
||||||
|
|
||||||
|
@ -165,6 +165,7 @@
|
|||||||
<a href="{{ request.urlgen('mediagoblin.moderation.reports') }}">
|
<a href="{{ request.urlgen('mediagoblin.moderation.reports') }}">
|
||||||
{%- trans %}Report management panel{% endtrans -%}
|
{%- trans %}Report management panel{% endtrans -%}
|
||||||
</a>
|
</a>
|
||||||
|
{% template_hook("moderation_powers") %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'mediagoblin/fragments/header_notifications.html' %}
|
{% include 'mediagoblin/fragments/header_notifications.html' %}
|
||||||
|
@ -14,9 +14,50 @@
|
|||||||
# 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 pytest
|
||||||
|
|
||||||
|
from mediagoblin.db.models import User
|
||||||
|
from mediagoblin.tests.tools import fixture_add_user
|
||||||
|
from mediagoblin.tools import template
|
||||||
|
|
||||||
|
|
||||||
def setup_package():
|
def setup_package():
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
from sqlalchemy.exc import SAWarning
|
from sqlalchemy.exc import SAWarning
|
||||||
warnings.simplefilter("error", SAWarning)
|
warnings.simplefilter("error", SAWarning)
|
||||||
|
|
||||||
|
|
||||||
|
class MGClientTestCase:
|
||||||
|
|
||||||
|
usernames = None
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def setup(self, test_app):
|
||||||
|
self.test_app = test_app
|
||||||
|
|
||||||
|
if self.usernames is None:
|
||||||
|
msg = ('The usernames attribute should be overridden '
|
||||||
|
'in the subclass')
|
||||||
|
raise pytest.skip(msg)
|
||||||
|
for username, options in self.usernames:
|
||||||
|
fixture_add_user(username, **options)
|
||||||
|
|
||||||
|
def user(self, username):
|
||||||
|
return User.query.filter(User.username == username).first()
|
||||||
|
|
||||||
|
def _do_request(self, url, *context_keys, **kwargs):
|
||||||
|
template.clear_test_template_context()
|
||||||
|
response = self.test_app.request(url, **kwargs)
|
||||||
|
context_data = template.TEMPLATE_TEST_CONTEXT
|
||||||
|
for key in context_keys:
|
||||||
|
context_data = context_data[key]
|
||||||
|
return response, context_data
|
||||||
|
|
||||||
|
def do_get(self, url, *context_keys, **kwargs):
|
||||||
|
kwargs['method'] = 'GET'
|
||||||
|
return self._do_request(url, *context_keys, **kwargs)
|
||||||
|
|
||||||
|
def do_post(self, url, *context_keys, **kwargs):
|
||||||
|
kwargs['method'] = 'POST'
|
||||||
|
return self._do_request(url, *context_keys, **kwargs)
|
||||||
|
@ -20,9 +20,11 @@
|
|||||||
from mediagoblin.db.base import Session
|
from mediagoblin.db.base import Session
|
||||||
from mediagoblin.db.models import MediaEntry, User, Privilege
|
from mediagoblin.db.models import MediaEntry, User, Privilege
|
||||||
|
|
||||||
|
from mediagoblin.tests import MGClientTestCase
|
||||||
from mediagoblin.tests.tools import fixture_add_user
|
from mediagoblin.tests.tools import fixture_add_user
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
class FakeUUID(object):
|
class FakeUUID(object):
|
||||||
@ -30,6 +32,8 @@ class FakeUUID(object):
|
|||||||
|
|
||||||
UUID_MOCK = mock.Mock(return_value=FakeUUID())
|
UUID_MOCK = mock.Mock(return_value=FakeUUID())
|
||||||
|
|
||||||
|
REQUEST_CONTEXT = ['mediagoblin/root.html', 'request']
|
||||||
|
|
||||||
|
|
||||||
class TestMediaEntrySlugs(object):
|
class TestMediaEntrySlugs(object):
|
||||||
def _setup(self):
|
def _setup(self):
|
||||||
@ -204,3 +208,23 @@ def test_media_data_init(test_app):
|
|||||||
print repr(obj)
|
print repr(obj)
|
||||||
assert obj_in_session == 0
|
assert obj_in_session == 0
|
||||||
|
|
||||||
|
|
||||||
|
class TestUserUrlForSelf(MGClientTestCase):
|
||||||
|
|
||||||
|
usernames = [(u'lindsay', dict(privileges=[u'active']))]
|
||||||
|
|
||||||
|
def test_url_for_self(self):
|
||||||
|
_, request = self.do_get('/', *REQUEST_CONTEXT)
|
||||||
|
|
||||||
|
assert self.user(u'lindsay').url_for_self(request.urlgen) == '/u/lindsay/'
|
||||||
|
|
||||||
|
def test_url_for_self_not_callable(self):
|
||||||
|
_, request = self.do_get('/', *REQUEST_CONTEXT)
|
||||||
|
|
||||||
|
def fake_urlgen():
|
||||||
|
pass
|
||||||
|
|
||||||
|
with pytest.raises(TypeError) as excinfo:
|
||||||
|
self.user(u'lindsay').url_for_self(fake_urlgen())
|
||||||
|
assert excinfo.errisinstance(TypeError)
|
||||||
|
assert 'object is not callable' in str(excinfo)
|
||||||
|
@ -131,7 +131,7 @@ def load_context(url):
|
|||||||
stores internally, load them from disk.
|
stores internally, load them from disk.
|
||||||
"""
|
"""
|
||||||
if url in _CONTEXT_CACHE:
|
if url in _CONTEXT_CACHE:
|
||||||
return _CONTEXT_CACHE[url]
|
return _CONTEXT_CACHE[url]
|
||||||
|
|
||||||
# See if it's one of our basic ones
|
# See if it's one of our basic ones
|
||||||
document = BUILTIN_CONTEXTS.get(url, None)
|
document = BUILTIN_CONTEXTS.get(url, None)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user