aboutsummaryrefslogtreecommitdiffstats
path: root/mediagoblin/gmg_commands
diff options
context:
space:
mode:
Diffstat (limited to 'mediagoblin/gmg_commands')
-rw-r--r--mediagoblin/gmg_commands/__init__.py54
-rw-r--r--mediagoblin/gmg_commands/addmedia.py33
-rw-r--r--mediagoblin/gmg_commands/alembic_commands.py71
-rw-r--r--mediagoblin/gmg_commands/batchaddmedia.py60
-rw-r--r--mediagoblin/gmg_commands/dbupdate.py131
-rw-r--r--mediagoblin/gmg_commands/deletemedia.py9
-rw-r--r--mediagoblin/gmg_commands/import_export.py256
-rw-r--r--mediagoblin/gmg_commands/reprocess.py43
-rw-r--r--mediagoblin/gmg_commands/serve.py65
-rw-r--r--mediagoblin/gmg_commands/shell.py58
-rw-r--r--mediagoblin/gmg_commands/users.py63
-rw-r--r--mediagoblin/gmg_commands/util.py4
12 files changed, 446 insertions, 401 deletions
diff --git a/mediagoblin/gmg_commands/__init__.py b/mediagoblin/gmg_commands/__init__.py
index 0cb239a2..98b097a6 100644
--- a/mediagoblin/gmg_commands/__init__.py
+++ b/mediagoblin/gmg_commands/__init__.py
@@ -16,9 +16,16 @@
import argparse
import os
+import shutil
+
+import six
from mediagoblin.tools.common import import_component
+import logging
+_log = logging.getLogger(__name__)
+logging.basicConfig()
+
SUBCOMMAND_MAP = {
'shell': {
@@ -61,29 +68,25 @@ SUBCOMMAND_MAP = {
'setup': 'mediagoblin.gmg_commands.deletemedia:parser_setup',
'func': 'mediagoblin.gmg_commands.deletemedia:deletemedia',
'help': 'Delete media entries'},
+ 'serve': {
+ 'setup': 'mediagoblin.gmg_commands.serve:parser_setup',
+ 'func': 'mediagoblin.gmg_commands.serve:serve',
+ 'help': 'PasteScript replacement'},
'batchaddmedia': {
'setup': 'mediagoblin.gmg_commands.batchaddmedia:parser_setup',
'func': 'mediagoblin.gmg_commands.batchaddmedia:batchaddmedia',
'help': 'Add many media entries at once'},
+ 'alembic': {
+ 'setup': 'mediagoblin.gmg_commands.alembic_commands:parser_setup',
+ 'func': 'mediagoblin.gmg_commands.alembic_commands:raw_alembic_cli',
+ 'help': (
+ 'Run raw alembic commands with our local database. '
+ '(Unless you know what you\'re doing, use dbupdate instead!)')},
# 'theme': {
# 'setup': 'mediagoblin.gmg_commands.theme:theme_parser_setup',
# 'func': 'mediagoblin.gmg_commands.theme:theme',
# 'help': 'Theming commands',
# }
-
- ## These might be useful, mayyyybe, but don't really work anymore
- ## due to mongo change and the "versatility" of sql options.
- ##
- ## For now, commenting out. Might re-enable soonish?
- #
- # 'env_export': {
- # 'setup': 'mediagoblin.gmg_commands.import_export:import_export_parse_setup',
- # 'func': 'mediagoblin.gmg_commands.import_export:env_export',
- # 'help': 'Exports the data for this MediaGoblin instance'},
- # 'env_import': {
- # 'setup': 'mediagoblin.gmg_commands.import_export:import_export_parse_setup',
- # 'func': 'mediagoblin.gmg_commands.import_export:env_import',
- # 'help': 'Imports the data for this MediaGoblin instance'},
}
@@ -98,7 +101,7 @@ def main_cli():
"otherwise mediagoblin.ini"))
subparsers = parser.add_subparsers(help='sub-command help')
- for command_name, command_struct in SUBCOMMAND_MAP.iteritems():
+ for command_name, command_struct in six.iteritems(SUBCOMMAND_MAP):
if 'help' in command_struct:
subparser = subparsers.add_parser(
command_name, help=command_struct['help'])
@@ -121,6 +124,27 @@ def main_cli():
else:
args.conf_file = 'mediagoblin.ini'
+ # This is a hopefully TEMPORARY hack for adding a mediagoblin.ini
+ # if none exists, to make up for a deficiency as we are migrating
+ # our docs to the new "no mediagoblin.ini by default" workflow.
+ # Normally, the docs should provide instructions for this, but
+ # since 0.7.1 docs say "install from master!" and yet we removed
+ # mediagoblin.ini, we need to cover our bases...
+
+ parent_directory = os.path.split(os.path.abspath(args.conf_file))[0]
+ if os.path.split(args.conf_file)[1] == 'mediagoblin.ini' \
+ and not os.path.exists(args.conf_file) \
+ and os.path.exists(
+ os.path.join(
+ parent_directory, 'mediagoblin.example.ini')):
+ # Do the copy-over of the mediagoblin config for the user
+ _log.warning(
+ "No mediagoblin.ini found and no other path given, "
+ "so making one for you.")
+ shutil.copy(
+ os.path.join(parent_directory, "mediagoblin.example.ini"),
+ os.path.join(parent_directory, "mediagoblin.ini"))
+
args.func(args)
diff --git a/mediagoblin/gmg_commands/addmedia.py b/mediagoblin/gmg_commands/addmedia.py
index 2f095760..babc6ed7 100644
--- a/mediagoblin/gmg_commands/addmedia.py
+++ b/mediagoblin/gmg_commands/addmedia.py
@@ -14,8 +14,13 @@
# 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 __future__ import print_function
+
import os
+import six
+
+from mediagoblin.db.models import LocalUser
from mediagoblin.gmg_commands import util as commands_util
from mediagoblin.submit.lib import (
submit_media, get_upload_file_limits,
@@ -71,41 +76,41 @@ def addmedia(args):
app = commands_util.setup_app(args)
# get the user
- user = app.db.User.query.filter_by(username=args.username.lower()).first()
+ user = app.db.LocalUser.query.filter(
+ LocalUser.username==args.username.lower()
+ ).first()
if user is None:
- print "Sorry, no user by username '%s'" % args.username
+ print("Sorry, no user by username '%s'" % args.username)
return
-
+
# check for the file, if it exists...
filename = os.path.split(args.filename)[-1]
abs_filename = os.path.abspath(args.filename)
if not os.path.exists(abs_filename):
- print "Can't find a file with filename '%s'" % args.filename
+ print("Can't find a file with filename '%s'" % args.filename)
return
- upload_limit, max_file_size = get_upload_file_limits(user)
-
def maybe_unicodeify(some_string):
# this is kinda terrible
if some_string is None:
return None
- else:
- return unicode(some_string)
+ if six.PY2:
+ return six.text_type(some_string, 'utf-8')
+ return some_string
try:
submit_media(
mg_app=app,
user=user,
- submitted_file=file(abs_filename, 'r'), filename=filename,
+ submitted_file=open(abs_filename, 'rb'), filename=filename,
title=maybe_unicodeify(args.title),
description=maybe_unicodeify(args.description),
collection_slug=args.collection_slug,
license=maybe_unicodeify(args.license),
- tags_string=maybe_unicodeify(args.tags) or u"",
- upload_limit=upload_limit, max_file_size=max_file_size)
+ tags_string=maybe_unicodeify(args.tags) or u"")
except FileUploadLimit:
- print "This file is larger than the upload limits for this site."
+ print("This file is larger than the upload limits for this site.")
except UserUploadLimit:
- print "This file will put this user past their upload limits."
+ print("This file will put this user past their upload limits.")
except UserPastUploadLimit:
- print "This user is already past their upload limits."
+ print("This user is already past their upload limits.")
diff --git a/mediagoblin/gmg_commands/alembic_commands.py b/mediagoblin/gmg_commands/alembic_commands.py
new file mode 100644
index 00000000..4e73ec13
--- /dev/null
+++ b/mediagoblin/gmg_commands/alembic_commands.py
@@ -0,0 +1,71 @@
+# 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 argparse
+
+from alembic import config
+from sqlalchemy.orm import sessionmaker
+
+from mediagoblin.db.open import setup_connection_and_db_from_config
+from mediagoblin.init import setup_global_and_app_config
+from mediagoblin.db.migration_tools import build_alembic_config
+from mediagoblin.tools.common import import_component
+
+
+class FudgedCommandLine(config.CommandLine):
+ def main(self, args, db, global_config):
+ options = self.parser.parse_args(args.args_for_alembic)
+
+ # This is useful for people running commands involving plugins, such
+ # as building autogenerated revisions.
+ if args.with_plugins:
+ plugins = global_config.get('plugins', {}).keys()
+ for plugin in plugins:
+ try:
+ import_component('{0}.models:MODELS'.format(plugin))
+ except ImportError:
+ # It doesn't really matter if there's no models to import
+ # here.
+ pass
+
+ # This code is inspired by a hack in Alembic, but isn't the same really.
+ # Regardless, Alembic is Expat licensed.
+ if not hasattr(options, "cmd"):
+ print(
+ "* Only use this command if you know what you are doing! *\n"
+ "If not, use the 'gmg dbupdate' command instead.\n\n"
+ "(You can also pass --with-plugins here.)"
+ "Alembic help:\n")
+ self.parser.print_help()
+ return
+ else:
+ Session = sessionmaker(bind=db.engine)
+ session = Session()
+
+ cfg = build_alembic_config(global_config, options, session)
+
+ self.run_cmd(cfg, options)
+
+def parser_setup(subparser):
+ subparser.add_argument(
+ "--with-plugins", action="store_true",
+ help="Import all plugins' models before running alembic commands.")
+ subparser.add_argument("args_for_alembic", nargs=argparse.REMAINDER)
+
+def raw_alembic_cli(args):
+ global_config, app_config = setup_global_and_app_config(args.conf_file)
+ db = setup_connection_and_db_from_config(app_config, migrations=False)
+ FudgedCommandLine().main(args, db, global_config)
diff --git a/mediagoblin/gmg_commands/batchaddmedia.py b/mediagoblin/gmg_commands/batchaddmedia.py
index 4931bda2..274d72bc 100644
--- a/mediagoblin/gmg_commands/batchaddmedia.py
+++ b/mediagoblin/gmg_commands/batchaddmedia.py
@@ -14,11 +14,18 @@
# 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 requests, codecs
+from __future__ import print_function
+
+import codecs
import csv
-from urlparse import urlparse
+import os
+import requests
+import six
+
+from six.moves.urllib.parse import urlparse
+
+from mediagoblin.db.models import LocalUser
from mediagoblin.gmg_commands import util as commands_util
from mediagoblin.submit.lib import (
submit_media, get_upload_file_limits,
@@ -58,13 +65,14 @@ def batchaddmedia(args):
files_uploaded, files_attempted = 0, 0
# get the user
- user = app.db.User.query.filter_by(username=args.username.lower()).first()
+ user = app.db.LocalUser.query.filter(
+ LocalUser.username==args.username.lower()
+ ).first()
if user is None:
- print _(u"Sorry, no user by username '{username}' exists".format(
- username=args.username))
+ print(_(u"Sorry, no user by username '{username}' exists".format(
+ username=args.username)))
return
- upload_limit, max_file_size = get_upload_file_limits(user)
temp_files = []
if os.path.isfile(args.metadata_path):
@@ -73,19 +81,18 @@ def batchaddmedia(args):
else:
error = _(u'File at {path} not found, use -h flag for help'.format(
path=args.metadata_path))
- print error
+ print(error)
return
abs_metadata_filename = os.path.abspath(metadata_path)
abs_metadata_dir = os.path.dirname(abs_metadata_filename)
- upload_limit, max_file_size = get_upload_file_limits(user)
def maybe_unicodeify(some_string):
# this is kinda terrible
if some_string is None:
return None
else:
- return unicode(some_string)
+ return six.text_type(some_string)
with codecs.open(
abs_metadata_filename, 'r', encoding='utf-8') as all_metadata:
@@ -110,13 +117,13 @@ def batchaddmedia(args):
license = file_metadata.get('license')
try:
json_ld_metadata = compact_and_validate(file_metadata)
- except ValidationError, exc:
+ except ValidationError as exc:
error = _(u"""Error with media '{media_id}' value '{error_path}': {error_msg}
Metadata was not uploaded.""".format(
media_id=media_id,
error_path=exc.path[0],
error_msg=exc.message))
- print error
+ print(error)
continue
url = urlparse(original_location)
@@ -136,9 +143,9 @@ Metadata was not uploaded.""".format(
try:
media_file = file(file_abs_path, 'r')
except IOError:
- print _(u"""\
+ print(_(u"""\
FAIL: Local file {filename} could not be accessed.
-{filename} will not be uploaded.""".format(filename=filename))
+{filename} will not be uploaded.""".format(filename=filename)))
continue
try:
submit_media(
@@ -150,33 +157,33 @@ FAIL: Local file {filename} could not be accessed.
description=maybe_unicodeify(description),
license=maybe_unicodeify(license),
metadata=json_ld_metadata,
- tags_string=u"",
- upload_limit=upload_limit, max_file_size=max_file_size)
- print _(u"""Successfully submitted {filename}!
+ tags_string=u"")
+ print(_(u"""Successfully submitted {filename}!
Be sure to look at the Media Processing Panel on your website to be sure it
-uploaded successfully.""".format(filename=filename))
+uploaded successfully.""".format(filename=filename)))
files_uploaded += 1
except FileUploadLimit:
- print _(
-u"FAIL: This file is larger than the upload limits for this site.")
+ print(_(
+u"FAIL: This file is larger than the upload limits for this site."))
except UserUploadLimit:
- print _(
-"FAIL: This file will put this user past their upload limits.")
+ print(_(
+"FAIL: This file will put this user past their upload limits."))
except UserPastUploadLimit:
- print _("FAIL: This user is already past their upload limits.")
- print _(
+ print(_("FAIL: This user is already past their upload limits."))
+ print(_(
"{files_uploaded} out of {files_attempted} files successfully submitted".format(
files_uploaded=files_uploaded,
- files_attempted=files_attempted))
+ files_attempted=files_attempted)))
def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
# csv.py doesn't do Unicode; encode temporarily as UTF-8:
+ # TODO: this probably won't be necessary in Python 3
csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
dialect=dialect, **kwargs)
for row in csv_reader:
# decode UTF-8 back to Unicode, cell by cell:
- yield [unicode(cell, 'utf-8') for cell in row]
+ yield [six.text_type(cell, 'utf-8') for cell in row]
def utf_8_encoder(unicode_csv_data):
for line in unicode_csv_data:
@@ -203,4 +210,3 @@ def parse_csv_file(file_contents):
objects_dict[media_id] = (line_dict)
return objects_dict
-
diff --git a/mediagoblin/gmg_commands/dbupdate.py b/mediagoblin/gmg_commands/dbupdate.py
index 27283a20..bafe76bb 100644
--- a/mediagoblin/gmg_commands/dbupdate.py
+++ b/mediagoblin/gmg_commands/dbupdate.py
@@ -16,10 +16,13 @@
import logging
+import six
+from alembic import command
from sqlalchemy.orm import sessionmaker
from mediagoblin.db.open import setup_connection_and_db_from_config
-from mediagoblin.db.migration_tools import MigrationManager
+from mediagoblin.db.migration_tools import (
+ MigrationManager, build_alembic_config, populate_table_foundations)
from mediagoblin.init import setup_global_and_app_config
from mediagoblin.tools.common import import_component
@@ -34,19 +37,20 @@ def dbupdate_parse_setup(subparser):
class DatabaseData(object):
- def __init__(self, name, models, foundations, migrations):
+ def __init__(self, name, models, migrations):
self.name = name
self.models = models
- self.foundations = foundations
self.migrations = migrations
def make_migration_manager(self, session):
return MigrationManager(
- self.name, self.models, self.foundations, self.migrations, session)
+ self.name, self.models, self.migrations, session)
def gather_database_data(plugins):
"""
+ Gather all database data relevant to the extensions installed.
+
Gather all database data relevant to the extensions we have
installed so we can do migrations and table initialization.
@@ -57,11 +61,10 @@ def gather_database_data(plugins):
# Add main first
from mediagoblin.db.models import MODELS as MAIN_MODELS
from mediagoblin.db.migrations import MIGRATIONS as MAIN_MIGRATIONS
- from mediagoblin.db.models import FOUNDATIONS as MAIN_FOUNDATIONS
managed_dbdata.append(
DatabaseData(
- u'__main__', MAIN_MODELS, MAIN_FOUNDATIONS, MAIN_MIGRATIONS))
+ u'__main__', MAIN_MODELS, MAIN_MIGRATIONS))
for plugin in plugins:
try:
@@ -73,8 +76,8 @@ def gather_database_data(plugins):
models = []
except AttributeError as exc:
- _log.warning('Could not find MODELS in {0}.models, have you \
-forgotten to add it? ({1})'.format(plugin, exc))
+ _log.warning('Could not find MODELS in {0}.models, have you '
+ 'forgotten to add it? ({1})'.format(plugin, exc))
models = []
try:
@@ -87,23 +90,50 @@ forgotten to add it? ({1})'.format(plugin, exc))
migrations = {}
except AttributeError as exc:
- _log.debug('Could not find MIGRATIONS in {0}.migrations, have you \
-forgotten to add it? ({1})'.format(plugin, exc))
+ _log.debug('Could not find MIGRATIONS in {0}.migrations, have you'
+ 'forgotten to add it? ({1})'.format(plugin, exc))
migrations = {}
+ if models:
+ managed_dbdata.append(
+ DatabaseData(plugin, models, migrations))
+
+ return managed_dbdata
+
+
+def run_foundations(db, global_config):
+ """
+ Gather foundations data and run it.
+ """
+ from mediagoblin.db.models import FOUNDATIONS as MAIN_FOUNDATIONS
+ all_foundations = [(u"__main__", MAIN_FOUNDATIONS)]
+
+ Session = sessionmaker(bind=db.engine)
+ session = Session()
+
+ plugins = global_config.get('plugins', {})
+
+ for plugin in plugins:
try:
- foundations = import_component('{0}.models:FOUNDATIONS'.format(plugin))
+ foundations = import_component(
+ '{0}.models:FOUNDATIONS'.format(plugin))
+ all_foundations.append((plugin, foundations))
except ImportError as exc:
- foundations = {}
+ continue
except AttributeError as exc:
- foundations = {}
+ continue
- if models:
- managed_dbdata.append(
- DatabaseData(plugin, models, foundations, migrations))
+ for name, foundations in all_foundations:
+ populate_table_foundations(session, foundations, name)
- return managed_dbdata
+def run_alembic_migrations(db, app_config, global_config):
+ """Initialize a database and runs all Alembic migrations."""
+ Session = sessionmaker(bind=db.engine)
+ session = Session()
+ cfg = build_alembic_config(global_config, None, session)
+
+ return command.upgrade(cfg, 'heads')
def run_dbupdate(app_config, global_config):
@@ -113,15 +143,36 @@ def run_dbupdate(app_config, global_config):
Will also initialize or migrate all extensions (media types, and
in the future, plugins)
"""
-
# Set up the database
db = setup_connection_and_db_from_config(app_config, migrations=True)
- #Run the migrations
- run_all_migrations(db, app_config, global_config)
+
+ # Do we have migrations
+ should_run_sqam_migrations = db.engine.has_table("core__migrations") and \
+ sqam_migrations_to_run(db, app_config,
+ global_config)
+
+ # Looks like a fresh database!
+ # (We set up this variable here because doing "run_all_migrations" below
+ # will change things.)
+ fresh_database = (
+ not db.engine.has_table("core__migrations") and
+ not db.engine.has_table("alembic_version"))
+
+ # Run the migrations
+ if should_run_sqam_migrations:
+ run_all_migrations(db, app_config, global_config)
+
+ run_alembic_migrations(db, app_config, global_config)
+
+ # If this was our first time initializing the database,
+ # we must lay down the foundations
+ if fresh_database:
+ run_foundations(db, global_config)
def run_all_migrations(db, app_config, global_config):
- """
+ """Initialize or migrates a database.
+
Initializes or migrates a database that already has a
connection setup and also initializes or migrates all
extensions based on the config files.
@@ -131,7 +182,7 @@ def run_all_migrations(db, app_config, global_config):
"""
# Gather information from all media managers / projects
dbdatas = gather_database_data(
- global_config.get('plugins', {}).keys())
+ list(global_config.get('plugins', {}).keys()))
Session = sessionmaker(bind=db.engine)
@@ -142,6 +193,42 @@ def run_all_migrations(db, app_config, global_config):
migration_manager.init_or_migrate()
+def sqam_migrations_to_run(db, app_config, global_config):
+ """
+ Check whether any plugins have sqlalchemy-migrate migrations left to run.
+
+ This is a kludge so we can transition away from sqlalchemy-migrate
+ except where necessary.
+ """
+ # @@: This shares a lot of code with run_all_migrations, but both
+ # are legacy and will be removed at some point.
+
+ # Gather information from all media managers / projects
+ dbdatas = gather_database_data(
+ list(global_config.get('plugins', {}).keys()))
+
+ Session = sessionmaker(bind=db.engine)
+
+ # We can bail out early if it turns out that sqlalchemy-migrate
+ # was never installed with any migrations
+ from mediagoblin.db.models import MigrationData
+ if Session().query(MigrationData).filter_by(
+ name=u"__main__").first() is None:
+ return False
+
+ # Setup media managers for all dbdata, run init/migrate and print info
+ # For each component, create/migrate tables
+ for dbdata in dbdatas:
+ migration_manager = dbdata.make_migration_manager(Session())
+ if migration_manager.migrations_to_run():
+ # If *any* migration managers have migrations to run,
+ # we'll have to run them.
+ return True
+
+ # Otherwise, scot free!
+ return False
+
+
def dbupdate(args):
global_config, app_config = setup_global_and_app_config(args.conf_file)
run_dbupdate(app_config, global_config)
diff --git a/mediagoblin/gmg_commands/deletemedia.py b/mediagoblin/gmg_commands/deletemedia.py
index ab5a81f6..bf50abde 100644
--- a/mediagoblin/gmg_commands/deletemedia.py
+++ b/mediagoblin/gmg_commands/deletemedia.py
@@ -14,6 +14,7 @@
# 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 __future__ import print_function
import sys
from mediagoblin.gmg_commands import util as commands_util
@@ -29,7 +30,7 @@ def deletemedia(args):
media_ids = set([int(mid) for mid in args.media_ids.split(',') if mid.isdigit()])
if not media_ids:
- print 'Can\'t find any valid media ID(s).'
+ print('Can\'t find any valid media ID(s).')
sys.exit(1)
found_medias = set()
filter_ids = app.db.MediaEntry.id.in_(media_ids)
@@ -37,8 +38,8 @@ def deletemedia(args):
for media in medias:
found_medias.add(media.id)
media.delete()
- print 'Media ID %d has been deleted.' % media.id
+ 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.'
+ print('Can\'t find a media with ID %d.' % media)
+ print('Done.')
sys.exit(0)
diff --git a/mediagoblin/gmg_commands/import_export.py b/mediagoblin/gmg_commands/import_export.py
deleted file mode 100644
index fbac09f6..00000000
--- a/mediagoblin/gmg_commands/import_export.py
+++ /dev/null
@@ -1,256 +0,0 @@
-# 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 import mg_globals
-from mediagoblin.db.open import setup_connection_and_db_from_config
-from mediagoblin.gmg_commands import util as commands_util
-from mediagoblin.storage.filestorage import BasicFileStorage
-from mediagoblin.init import setup_storage, setup_global_and_app_config
-
-import shutil
-import tarfile
-import tempfile
-import subprocess
-import os.path
-import os
-import sys
-import logging
-from contextlib import closing
-
-_log = logging.getLogger('gmg.import_export')
-logging.basicConfig()
-_log.setLevel(logging.INFO)
-
-
-def import_export_parse_setup(subparser):
- # TODO: Add default
- subparser.add_argument(
- 'tar_file')
- subparser.add_argument(
- '--mongodump_path', default='mongodump',
- help='mongodump binary')
- subparser.add_argument(
- '--mongorestore_path', default='mongorestore',
- help='mongorestore binary')
- subparser.add_argument(
- '--cache_path',
- help='Temporary directory where files will be temporarily dumped')
-
-
-def _import_media(db, args):
- '''
- Import media files
-
- Must be called after _import_database()
- '''
- _log.info('-> Importing media...')
-
- media_cache = BasicFileStorage(
- args._cache_path['media'])
-
- # TODO: Add import of queue files
- queue_cache = BasicFileStorage(args._cache_path['queue'])
-
- for entry in db.MediaEntry.query.filter_by():
- for name, path in entry.media_files.items():
- _log.info('Importing: {0} - {1}'.format(
- entry.title.encode('ascii', 'replace'),
- name))
-
- media_file = mg_globals.public_store.get_file(path, mode='wb')
- media_file.write(
- media_cache.get_file(path, mode='rb').read())
-
- _log.info('...Media imported')
-
-
-def _import_database(db, args):
- '''
- Restore mongo database from ___.bson files
- '''
- _log.info('-> Importing database...')
-
- p = subprocess.Popen([
- args.mongorestore_path,
- '-d', db.name,
- os.path.join(args._cache_path['database'], db.name)])
-
- p.wait()
-
- _log.info('...Database imported')
-
-
-def env_import(args):
- '''
- Restore mongo database and media files from a tar archive
- '''
- if not args.cache_path:
- args.cache_path = tempfile.mkdtemp()
-
- setup_global_and_app_config(args.conf_file)
-
- # Creates mg_globals.public_store and mg_globals.queue_store
- setup_storage()
-
- global_config, app_config = setup_global_and_app_config(args.conf_file)
- db = setup_connection_and_db_from_config(
- app_config)
-
- tf = tarfile.open(
- args.tar_file,
- mode='r|gz')
-
- tf.extractall(args.cache_path)
-
- args.cache_path = os.path.join(
- args.cache_path, 'mediagoblin-data')
- args = _setup_paths(args)
-
- # Import database from extracted data
- _import_database(db, args)
-
- _import_media(db, args)
-
- _clean(args)
-
-
-def _setup_paths(args):
- '''
- Populate ``args`` variable with cache subpaths
- '''
- args._cache_path = dict()
- PATH_MAP = {
- 'media': 'media',
- 'queue': 'queue',
- 'database': 'database'}
-
- for key, val in PATH_MAP.items():
- args._cache_path[key] = os.path.join(args.cache_path, val)
-
- return args
-
-
-def _create_archive(args):
- '''
- Create the tar archive
- '''
- _log.info('-> Compressing to archive')
-
- tf = tarfile.open(
- args.tar_file,
- mode='w|gz')
-
- with closing(tf):
- tf.add(args.cache_path, 'mediagoblin-data/')
-
- _log.info('...Archiving done')
-
-
-def _clean(args):
- '''
- Remove cache directory
- '''
- shutil.rmtree(args.cache_path)
-
-
-def _export_check(args):
- '''
- Run security checks for export command
- '''
- if os.path.exists(args.tar_file):
- overwrite = raw_input(
- 'The output file already exists. '
- 'Are you **SURE** you want to overwrite it? '
- '(yes/no)> ')
- if not overwrite == 'yes':
- print 'Aborting.'
-
- return False
-
- return True
-
-
-def _export_database(db, args):
- _log.info('-> Exporting database...')
-
- p = subprocess.Popen([
- args.mongodump_path,
- '-d', db.name,
- '-o', args._cache_path['database']])
-
- p.wait()
-
- _log.info('...Database exported')
-
-
-def _export_media(db, args):
- _log.info('-> Exporting media...')
-
- media_cache = BasicFileStorage(
- args._cache_path['media'])
-
- # TODO: Add export of queue files
- queue_cache = BasicFileStorage(args._cache_path['queue'])
-
- for entry in db.MediaEntry.query.filter_by():
- for name, path in entry.media_files.items():
- _log.info(u'Exporting {0} - {1}'.format(
- entry.title,
- name))
- try:
- mc_file = media_cache.get_file(path, mode='wb')
- mc_file.write(
- mg_globals.public_store.get_file(path, mode='rb').read())
- except Exception as e:
- _log.error('Failed: {0}'.format(e))
-
- _log.info('...Media exported')
-
-
-def env_export(args):
- '''
- Export database and media files to a tar archive
- '''
- commands_util.check_unrecognized_args(args)
- if args.cache_path:
- if os.path.exists(args.cache_path):
- _log.error('The cache directory must not exist '
- 'before you run this script')
- _log.error('Cache directory: {0}'.format(args.cache_path))
-
- return False
- else:
- args.cache_path = tempfile.mkdtemp()
-
- args = _setup_paths(args)
-
- if not _export_check(args):
- _log.error('Checks did not pass, exiting')
- sys.exit(0)
-
- globa_config, app_config = setup_global_and_app_config(args.conf_file)
-
- setup_storage()
-
- db = setup_connection_and_db_from_config(app_config)
-
- _export_database(db, args)
-
- _export_media(db, args)
-
- _create_archive(args)
-
- _clean(args)
diff --git a/mediagoblin/gmg_commands/reprocess.py b/mediagoblin/gmg_commands/reprocess.py
index e2f19ea3..85cae6df 100644
--- a/mediagoblin/gmg_commands/reprocess.py
+++ b/mediagoblin/gmg_commands/reprocess.py
@@ -13,6 +13,9 @@
#
# 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 __future__ import print_function
+
import argparse
import os
@@ -143,7 +146,7 @@ def available(args):
manager = get_processing_manager_for_type(media_type)
except ProcessingManagerDoesNotExist:
entry = MediaEntry.query.filter_by(id=args.id_or_type).first()
- print 'No such processing manager for {0}'.format(entry.media_type)
+ print('No such processing manager for {0}'.format(entry.media_type))
if args.state:
processors = manager.list_all_processors_by_state(args.state)
@@ -152,25 +155,25 @@ def available(args):
else:
processors = manager.list_eligible_processors(media_entry)
- print "Available processors:"
- print "====================="
- print ""
+ print("Available processors:")
+ print("=====================")
+ print("")
if args.action_help:
for processor in processors:
- print processor.name
- print "-" * len(processor.name)
+ print(processor.name)
+ print("-" * len(processor.name))
parser = processor.generate_parser()
parser.print_help()
- print ""
+ print("")
else:
for processor in processors:
if processor.description:
- print " - %s: %s" % (processor.name, processor.description)
+ print(" - %s: %s" % (processor.name, processor.description))
else:
- print " - %s" % processor.name
+ print(" - %s" % processor.name)
def run(args, media_id=None):
@@ -185,12 +188,12 @@ def run(args, media_id=None):
processor_class = manager.get_processor(
args.reprocess_command, media_entry)
except ProcessorDoesNotExist:
- print 'No such processor "%s" for media with id "%s"' % (
- args.reprocess_command, media_entry.id)
+ print('No such processor "%s" for media with id "%s"' % (
+ args.reprocess_command, media_entry.id))
return
except ProcessorNotEligible:
- print 'Processor "%s" exists but media "%s" is not eligible' % (
- args.reprocess_command, media_entry.id)
+ print('Processor "%s" exists but media "%s" is not eligible' % (
+ args.reprocess_command, media_entry.id))
return
reprocess_parser = processor_class.generate_parser()
@@ -203,7 +206,7 @@ def run(args, media_id=None):
except ProcessingManagerDoesNotExist:
entry = MediaEntry.query.filter_by(id=media_id).first()
- print 'No such processing manager for {0}'.format(entry.media_type)
+ print('No such processing manager for {0}'.format(entry.media_type))
def bulk_run(args):
@@ -233,12 +236,12 @@ def thumbs(args):
processor_class = manager.get_processor(
'resize', media_entry)
except ProcessorDoesNotExist:
- print 'No such processor "%s" for media with id "%s"' % (
- 'resize', media_entry.id)
+ print('No such processor "%s" for media with id "%s"' % (
+ 'resize', media_entry.id))
return
except ProcessorNotEligible:
- print 'Processor "%s" exists but media "%s" is not eligible' % (
- 'resize', media_entry.id)
+ print('Processor "%s" exists but media "%s" is not eligible' % (
+ 'resize', media_entry.id))
return
reprocess_parser = processor_class.generate_parser()
@@ -260,7 +263,7 @@ def thumbs(args):
reprocess_info=reprocess_request)
except ProcessingManagerDoesNotExist:
- print 'No such processing manager for {0}'.format(entry.media_type)
+ print('No such processing manager for {0}'.format(entry.media_type))
def initial(args):
@@ -276,7 +279,7 @@ def initial(args):
media_entry,
reprocess_action='initial')
except ProcessingManagerDoesNotExist:
- print 'No such processing manager for {0}'.format(entry.media_type)
+ print('No such processing manager for {0}'.format(entry.media_type))
def reprocess(args):
diff --git a/mediagoblin/gmg_commands/serve.py b/mediagoblin/gmg_commands/serve.py
new file mode 100644
index 00000000..6ded1cfc
--- /dev/null
+++ b/mediagoblin/gmg_commands/serve.py
@@ -0,0 +1,65 @@
+# 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 __future__ import print_function
+
+from paste.deploy import loadapp, loadserver
+
+
+class ServeCommand(object):
+
+ def loadserver(self, server_spec, name, relative_to, **kwargs):
+ return loadserver(server_spec, name=name, relative_to=relative_to,
+ **kwargs)
+
+ def loadapp(self, app_spec, name, relative_to, **kwargs):
+ return loadapp(app_spec, name=name, relative_to=relative_to, **kwargs)
+
+ def daemonize(self):
+ pass
+
+ def restart_with_reloader(self):
+ pass
+
+ def restart_with_monitor(self, reloader=False):
+ pass
+
+ def run(self):
+ print('Running...')
+
+
+def parser_setup(subparser):
+ subparser.add_argument('config', metavar='CONFIG_FILE')
+ subparser.add_argument('command',
+ choices=['start', 'stop', 'restart', 'status'],
+ nargs='?', default='start')
+ subparser.add_argument('-n', '--app-name',
+ dest='app_name',
+ metavar='NAME',
+ help="Load the named application (default main)")
+ subparser.add_argument('-s', '--server',
+ dest='server',
+ metavar='SERVER_TYPE',
+ help="Use the named server.")
+ subparser.add_argument('--reload',
+ dest='reload',
+ action='store_true',
+ help="Use auto-restart file monitor")
+
+
+def serve(args):
+ serve_cmd = ServeCommand() # TODO: pass args to it
+ serve_cmd.run()
diff --git a/mediagoblin/gmg_commands/shell.py b/mediagoblin/gmg_commands/shell.py
index 4998acd7..4d3ec241 100644
--- a/mediagoblin/gmg_commands/shell.py
+++ b/mediagoblin/gmg_commands/shell.py
@@ -20,6 +20,8 @@ import code
from mediagoblin import mg_globals
from mediagoblin.gmg_commands import util as commands_util
+from mediagoblin.tools.transition import DISABLE_GLOBALS
+
def shell_parser_setup(subparser):
subparser.add_argument(
@@ -27,14 +29,23 @@ def shell_parser_setup(subparser):
action="store_true")
-SHELL_BANNER = """\
-GNU MediaGoblin shell!
-----------------------
-Available vars:
- - mgoblin_app: instantiated mediagoblin application
- - mg_globals: mediagoblin.globals
- - db: database instance
-"""
+if DISABLE_GLOBALS:
+ SHELL_BANNER = (
+ "GNU MediaGoblin shell!\n"
+ "----------------------\n"
+ "Available vars:\n"
+ " - app: instantiated mediagoblin application\n"
+ " - db: database session\n"
+ " - ctx: context object\n")
+else:
+ SHELL_BANNER = (
+ "GNU MediaGoblin shell!\n"
+ "----------------------\n"
+ "Available vars:\n"
+ " - app: instantiated mediagoblin application\n"
+ " - mg_globals: mediagoblin.globals\n"
+ " - db: database instance\n"
+ " - ctx: context object\n")
def py_shell(**user_namespace):
"""
@@ -59,18 +70,27 @@ def ipython_shell(**user_namespace):
user_ns=user_namespace)
return True
+
def shell(args):
"""
Setup a shell for the user either a normal Python shell or an IPython one
"""
- user_namespace = {
- 'mg_globals': mg_globals,
- 'mgoblin_app': commands_util.setup_app(args),
- 'db': mg_globals.database}
-
- if args.ipython:
- ipython_shell(**user_namespace)
- else:
- # Try ipython_shell first and fall back if not available
- if not ipython_shell(**user_namespace):
- py_shell(**user_namespace)
+ app = commands_util.setup_app(args)
+
+ def run_shell(db, ctx):
+ user_namespace = {
+ 'mg_globals': mg_globals,
+ 'app': app,
+ 'db': db,
+ "ctx": ctx}
+
+ if args.ipython:
+ ipython_shell(**user_namespace)
+ else:
+ # Try ipython_shell first and fall back if not available
+ if not ipython_shell(**user_namespace):
+ py_shell(**user_namespace)
+
+ with app.gen_context() as ctx:
+ db = ctx.db
+ run_shell(db, ctx)
diff --git a/mediagoblin/gmg_commands/users.py b/mediagoblin/gmg_commands/users.py
index 71149497..d1a8b72d 100644
--- a/mediagoblin/gmg_commands/users.py
+++ b/mediagoblin/gmg_commands/users.py
@@ -14,6 +14,13 @@
# 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 __future__ import print_function
+
+import sys
+
+import six
+
+from mediagoblin.db.models import LocalUser
from mediagoblin.gmg_commands import util as commands_util
from mediagoblin import auth
from mediagoblin import mg_globals
@@ -34,24 +41,25 @@ def adduser(args):
#TODO: Lets trust admins this do not validate Emails :)
commands_util.setup_app(args)
- args.username = unicode(commands_util.prompt_if_not_set(args.username, "Username:"))
+ args.username = six.text_type(commands_util.prompt_if_not_set(args.username, "Username:"))
args.password = commands_util.prompt_if_not_set(args.password, "Password:",True)
args.email = commands_util.prompt_if_not_set(args.email, "Email:")
db = mg_globals.database
users_with_username = \
- db.User.query.filter_by(
- username=args.username.lower()
+ db.LocalUser.query.filter(
+ LocalUser.username==args.username.lower()
).count()
if users_with_username:
- print u'Sorry, a user with that name already exists.'
+ print(u'Sorry, a user with that name already exists.')
+ sys.exit(1)
else:
# Create the user
- entry = db.User()
- entry.username = args.username.lower()
- entry.email = unicode(args.email)
+ entry = db.LocalUser()
+ entry.username = six.text_type(args.username.lower())
+ entry.email = six.text_type(args.email)
entry.pw_hash = auth.gen_password_hash(args.password)
default_privileges = [
db.Privilege.query.filter(
@@ -66,13 +74,14 @@ def adduser(args):
entry.all_privileges = default_privileges
entry.save()
- print "User created (and email marked as verified)"
+ print(u"User created (and email marked as verified).")
def makeadmin_parser_setup(subparser):
subparser.add_argument(
'username',
- help="Username to give admin level")
+ help="Username to give admin level",
+ type=six.text_type)
def makeadmin(args):
@@ -80,23 +89,26 @@ def makeadmin(args):
db = mg_globals.database
- user = db.User.query.filter_by(
- username=unicode(args.username.lower())).one()
+ user = db.LocalUser.query.filter(
+ LocalUser.username==args.username.lower()
+ ).first()
if user:
user.all_privileges.append(
db.Privilege.query.filter(
db.Privilege.privilege_name==u'admin').one()
)
user.save()
- print 'The user is now Admin'
+ print(u'The user %s is now an admin.' % args.username)
else:
- print 'The user doesn\'t exist'
+ print(u'The user %s doesn\'t exist.' % args.username)
+ sys.exit(1)
def changepw_parser_setup(subparser):
subparser.add_argument(
'username',
- help="Username used to login")
+ help="Username used to login",
+ type=six.text_type)
subparser.add_argument(
'password',
help="Your NEW supersecret word to login")
@@ -107,20 +119,23 @@ def changepw(args):
db = mg_globals.database
- user = db.User.query.filter_by(
- username=unicode(args.username.lower())).one()
+ user = db.LocalUser.query.filter(
+ LocalUser.username==args.username.lower()
+ ).first()
if user:
user.pw_hash = auth.gen_password_hash(args.password)
user.save()
- print 'Password successfully changed'
+ print(u'Password successfully changed for user %s.' % args.username)
else:
- print 'The user doesn\'t exist'
+ print(u'The user %s doesn\'t exist.' % args.username)
+ sys.exit(1)
def deleteuser_parser_setup(subparser):
subparser.add_argument(
'username',
- help="Username to delete")
+ help="Username to delete",
+ type=six.text_type)
def deleteuser(args):
@@ -128,10 +143,12 @@ def deleteuser(args):
db = mg_globals.database
- user = db.User.query.filter_by(
- username=unicode(args.username.lower())).one()
+ user = db.LocalUser.query.filter(
+ LocalUser.username==args.username.lower()
+ ).first()
if user:
user.delete()
- print 'The user %s has been deleted' % args.username
+ print('The user %s has been deleted.' % args.username)
else:
- print 'The user %s doesn\'t exist' % args.username
+ print('The user %s doesn\'t exist.' % args.username)
+ sys.exit(1)
diff --git a/mediagoblin/gmg_commands/util.py b/mediagoblin/gmg_commands/util.py
index 63e39ca9..4a4a9a74 100644
--- a/mediagoblin/gmg_commands/util.py
+++ b/mediagoblin/gmg_commands/util.py
@@ -18,6 +18,8 @@
from mediagoblin import app
import getpass
+import six
+
def setup_app(args):
"""
@@ -33,7 +35,7 @@ def prompt_if_not_set(variable, text, password=False):
"""
if variable is None:
if not password:
- variable=raw_input(text + u' ')
+ variable = six.moves.input(text + u' ')
else:
variable=getpass.getpass(text + u' ')