aboutsummaryrefslogtreecommitdiffstats
path: root/mediagoblin/gmg_commands
diff options
context:
space:
mode:
Diffstat (limited to 'mediagoblin/gmg_commands')
-rw-r--r--mediagoblin/gmg_commands/__init__.py108
-rw-r--r--mediagoblin/gmg_commands/assetlink.py151
-rw-r--r--mediagoblin/gmg_commands/dbupdate.py132
-rw-r--r--mediagoblin/gmg_commands/import_export.py254
-rw-r--r--mediagoblin/gmg_commands/shell.py76
-rw-r--r--mediagoblin/gmg_commands/users.py103
-rw-r--r--mediagoblin/gmg_commands/util.py40
7 files changed, 864 insertions, 0 deletions
diff --git a/mediagoblin/gmg_commands/__init__.py b/mediagoblin/gmg_commands/__init__.py
new file mode 100644
index 00000000..d8156126
--- /dev/null
+++ b/mediagoblin/gmg_commands/__init__.py
@@ -0,0 +1,108 @@
+# 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
+import os
+
+from mediagoblin.tools.common import import_component
+
+
+SUBCOMMAND_MAP = {
+ 'shell': {
+ 'setup': 'mediagoblin.gmg_commands.shell:shell_parser_setup',
+ 'func': 'mediagoblin.gmg_commands.shell:shell',
+ 'help': 'Run a shell with some tools pre-setup'},
+ 'adduser': {
+ 'setup': 'mediagoblin.gmg_commands.users:adduser_parser_setup',
+ 'func': 'mediagoblin.gmg_commands.users:adduser',
+ 'help': 'Creates an user'},
+ 'makeadmin': {
+ 'setup': 'mediagoblin.gmg_commands.users:makeadmin_parser_setup',
+ 'func': 'mediagoblin.gmg_commands.users:makeadmin',
+ 'help': 'Makes user an admin'},
+ 'changepw': {
+ 'setup': 'mediagoblin.gmg_commands.users:changepw_parser_setup',
+ 'func': 'mediagoblin.gmg_commands.users:changepw',
+ 'help': 'Changes a user\'s password'},
+ 'dbupdate': {
+ 'setup': 'mediagoblin.gmg_commands.dbupdate:dbupdate_parse_setup',
+ 'func': 'mediagoblin.gmg_commands.dbupdate:dbupdate',
+ 'help': 'Set up or update the SQL database'},
+ 'assetlink': {
+ 'setup': 'mediagoblin.gmg_commands.assetlink:assetlink_parser_setup',
+ 'func': 'mediagoblin.gmg_commands.assetlink:assetlink',
+ 'help': 'Link assets for themes and plugins for static serving'},
+ # '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'},
+ }
+
+
+def main_cli():
+ parser = argparse.ArgumentParser(
+ description='GNU MediaGoblin utilities.')
+ parser.add_argument(
+ '-cf', '--conf_file', default=None,
+ help=(
+ "Config file used to set up environment. "
+ "Default to mediagoblin_local.ini if readable, "
+ "otherwise mediagoblin.ini"))
+
+ subparsers = parser.add_subparsers(help='sub-command help')
+ for command_name, command_struct in SUBCOMMAND_MAP.iteritems():
+ if 'help' in command_struct:
+ subparser = subparsers.add_parser(
+ command_name, help=command_struct['help'])
+ else:
+ subparser = subparsers.add_parser(command_name)
+
+ setup_func = import_component(command_struct['setup'])
+ exec_func = import_component(command_struct['func'])
+
+ setup_func(subparser)
+
+ subparser.set_defaults(func=exec_func)
+
+ args = parser.parse_args()
+ args.orig_conf_file = args.conf_file
+ if args.conf_file is None:
+ if os.path.exists('mediagoblin_local.ini') \
+ and os.access('mediagoblin_local.ini', os.R_OK):
+ args.conf_file = 'mediagoblin_local.ini'
+ else:
+ args.conf_file = 'mediagoblin.ini'
+
+ args.func(args)
+
+
+if __name__ == '__main__':
+ main_cli()
diff --git a/mediagoblin/gmg_commands/assetlink.py b/mediagoblin/gmg_commands/assetlink.py
new file mode 100644
index 00000000..148ebe9e
--- /dev/null
+++ b/mediagoblin/gmg_commands/assetlink.py
@@ -0,0 +1,151 @@
+# 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
+
+from mediagoblin import mg_globals
+from mediagoblin.init import setup_global_and_app_config
+from mediagoblin.gmg_commands import util as commands_util
+from mediagoblin.tools.theme import register_themes
+from mediagoblin.tools.translate import pass_to_ugettext as _
+from mediagoblin.tools.common import simple_printer
+from mediagoblin.tools import pluginapi
+
+
+def assetlink_parser_setup(subparser):
+ # theme_subparsers = subparser.add_subparsers(
+ # dest=u"subcommand",
+ # help=u'Assetlink options')
+
+ # # Install command
+ # install_parser = theme_subparsers.add_parser(
+ # u'install', help=u'Install a theme to this mediagoblin instance')
+ # install_parser.add_argument(
+ # u'themefile', help=u'The theme archive to be installed')
+
+ # theme_subparsers.add_parser(
+ # u'assetlink',
+ # help=(
+ # u"Link the currently installed theme's assets "
+ # u"to the served theme asset directory"))
+ pass
+
+
+###########
+# Utilities
+###########
+
+def link_theme_assets(theme, link_dir, printer=simple_printer):
+ """
+ Returns a list of string of text telling the user what we did
+ which should be printable.
+ """
+ link_dir = link_dir.rstrip(os.path.sep)
+ link_parent_dir = os.path.dirname(link_dir)
+
+ if theme is None:
+ printer(_("Cannot link theme... no theme set\n"))
+ return
+
+ def _maybe_unlink_link_dir():
+ """unlink link directory if it exists"""
+ if os.path.lexists(link_dir) \
+ and os.path.islink(link_dir):
+ os.unlink(link_dir)
+ return True
+
+ return
+
+ if theme.get('assets_dir') is None:
+ printer(_("No asset directory for this theme\n"))
+ if _maybe_unlink_link_dir():
+ printer(
+ _("However, old link directory symlink found; removed.\n"))
+ return
+
+ _maybe_unlink_link_dir()
+
+ # make the link directory parent dirs if necessary
+ if not os.path.lexists(link_parent_dir):
+ os.makedirs(link_parent_dir)
+
+ os.symlink(
+ theme['assets_dir'].rstrip(os.path.sep),
+ link_dir)
+ printer("Linked the theme's asset directory:\n %s\nto:\n %s\n" % (
+ theme['assets_dir'], link_dir))
+
+
+def link_plugin_assets(plugin_static, plugins_link_dir, printer=simple_printer):
+ """
+ Arguments:
+ - plugin_static: a mediagoblin.tools.staticdirect.PluginStatic instance
+ representing the static assets of this plugins' configuration
+ - plugins_link_dir: Base directory plugins are linked from
+ """
+ # link_dir is the final directory we'll link to, a combination of
+ # the plugin assetlink directory and plugin_static.name
+ link_dir = os.path.join(
+ plugins_link_dir.rstrip(os.path.sep), plugin_static.name)
+
+ # make the link directory parent dirs if necessary
+ if not os.path.lexists(plugins_link_dir):
+ os.makedirs(plugins_link_dir)
+
+ # See if the link_dir already exists.
+ if os.path.lexists(link_dir):
+ # if this isn't a symlink, there's something wrong... error out.
+ if not os.path.islink(link_dir):
+ printer(_('Could not link "%s": %s exists and is not a symlink\n') % (
+ plugin_static.name, link_dir))
+ return
+
+ # if this is a symlink and the path already exists, skip it.
+ if os.path.realpath(link_dir) == plugin_static.file_path:
+ # Is this comment helpful or not?
+ printer(_('Skipping "%s"; already set up.\n') % (
+ plugin_static.name))
+ return
+
+ # Otherwise, it's a link that went to something else... unlink it
+ printer(_('Old link found for "%s"; removing.\n') % (
+ plugin_static.name))
+ os.unlink(link_dir)
+
+ os.symlink(
+ plugin_static.file_path.rstrip(os.path.sep),
+ link_dir)
+ printer('Linked asset directory for plugin "%s":\n %s\nto:\n %s\n' % (
+ plugin_static.name,
+ plugin_static.file_path.rstrip(os.path.sep),
+ link_dir))
+
+
+def assetlink(args):
+ """
+ Link the asset directory of the currently installed theme and plugins
+ """
+ mgoblin_app = commands_util.setup_app(args)
+ app_config = mg_globals.app_config
+
+ # link theme
+ link_theme_assets(mgoblin_app.current_theme, app_config['theme_linked_assets_dir'])
+
+ # link plugin assets
+ ## ... probably for this we need the whole application initialized
+ for plugin_static in pluginapi.hook_runall("static_setup"):
+ link_plugin_assets(
+ plugin_static, app_config['plugin_linked_assets_dir'])
diff --git a/mediagoblin/gmg_commands/dbupdate.py b/mediagoblin/gmg_commands/dbupdate.py
new file mode 100644
index 00000000..fa25ecb2
--- /dev/null
+++ b/mediagoblin/gmg_commands/dbupdate.py
@@ -0,0 +1,132 @@
+# 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
+
+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.init import setup_global_and_app_config
+from mediagoblin.tools.common import import_component
+
+_log = logging.getLogger(__name__)
+logging.basicConfig()
+_log.setLevel(logging.DEBUG)
+
+def dbupdate_parse_setup(subparser):
+ pass
+
+
+class DatabaseData(object):
+ def __init__(self, name, models, migrations):
+ self.name = name
+ self.models = models
+ self.migrations = migrations
+
+ def make_migration_manager(self, session):
+ return MigrationManager(
+ self.name, self.models, self.migrations, session)
+
+
+def gather_database_data(media_types, plugins):
+ """
+ Gather all database data relevant to the extensions we have
+ installed so we can do migrations and table initialization.
+
+ Returns a list of DatabaseData objects.
+ """
+ managed_dbdata = []
+
+ # Add main first
+ from mediagoblin.db.models import MODELS as MAIN_MODELS
+ from mediagoblin.db.migrations import MIGRATIONS as MAIN_MIGRATIONS
+
+ managed_dbdata.append(
+ DatabaseData(
+ u'__main__', MAIN_MODELS, MAIN_MIGRATIONS))
+
+ # Then get all registered media managers (eventually, plugins)
+ for media_type in media_types:
+ models = import_component('%s.models:MODELS' % media_type)
+ migrations = import_component('%s.migrations:MIGRATIONS' % media_type)
+ managed_dbdata.append(
+ DatabaseData(media_type, models, migrations))
+
+ for plugin in plugins:
+ try:
+ models = import_component('{0}.models:MODELS'.format(plugin))
+ except ImportError as exc:
+ _log.debug('No models found for {0}: {1}'.format(
+ plugin,
+ exc))
+
+ models = []
+ except AttributeError as exc:
+ _log.warning('Could not find MODELS in {0}.models, have you \
+forgotten to add it? ({1})'.format(plugin, exc))
+ models = []
+
+ try:
+ migrations = import_component('{0}.migrations:MIGRATIONS'.format(
+ plugin))
+ except ImportError as exc:
+ _log.debug('No migrations found for {0}: {1}'.format(
+ plugin,
+ exc))
+
+ migrations = {}
+ except AttributeError as exc:
+ _log.debug('Cloud 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_dbupdate(app_config, global_config):
+ """
+ Initialize or migrate the database as specified by the config file.
+
+ Will also initialize or migrate all extensions (media types, and
+ in the future, plugins)
+ """
+
+ # Gather information from all media managers / projects
+ dbdatas = gather_database_data(
+ app_config['media_types'],
+ global_config.get('plugins', {}).keys())
+
+ # Set up the database
+ db = setup_connection_and_db_from_config(app_config, migrations=True)
+
+ Session = sessionmaker(bind=db.engine)
+
+ # 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())
+ migration_manager.init_or_migrate()
+
+
+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/import_export.py b/mediagoblin/gmg_commands/import_export.py
new file mode 100644
index 00000000..d51a1e3e
--- /dev/null
+++ b/mediagoblin/gmg_commands/import_export.py
@@ -0,0 +1,254 @@
+# 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.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.find():
+ 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.find():
+ 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
+ '''
+ 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/shell.py b/mediagoblin/gmg_commands/shell.py
new file mode 100644
index 00000000..4998acd7
--- /dev/null
+++ b/mediagoblin/gmg_commands/shell.py
@@ -0,0 +1,76 @@
+# 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 code
+
+from mediagoblin import mg_globals
+from mediagoblin.gmg_commands import util as commands_util
+
+
+def shell_parser_setup(subparser):
+ subparser.add_argument(
+ '--ipython', help='Use ipython',
+ action="store_true")
+
+
+SHELL_BANNER = """\
+GNU MediaGoblin shell!
+----------------------
+Available vars:
+ - mgoblin_app: instantiated mediagoblin application
+ - mg_globals: mediagoblin.globals
+ - db: database instance
+"""
+
+def py_shell(**user_namespace):
+ """
+ Run a shell using normal python shell.
+ """
+ code.interact(
+ banner=SHELL_BANNER,
+ local=user_namespace)
+
+
+def ipython_shell(**user_namespace):
+ """
+ Run a shell for the user using ipython. Return False if there is no IPython
+ """
+ try:
+ from IPython import embed
+ except:
+ return False
+
+ embed(
+ banner1=SHELL_BANNER,
+ 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)
diff --git a/mediagoblin/gmg_commands/users.py b/mediagoblin/gmg_commands/users.py
new file mode 100644
index 00000000..024c8498
--- /dev/null
+++ b/mediagoblin/gmg_commands/users.py
@@ -0,0 +1,103 @@
+# 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
+from mediagoblin.auth import lib as auth_lib
+from mediagoblin import mg_globals
+
+def adduser_parser_setup(subparser):
+ subparser.add_argument(
+ '--username','-u',
+ help="Username used to login")
+ subparser.add_argument(
+ '--password','-p',
+ help="Your supersecret word to login, beware of storing it in bash history")
+ subparser.add_argument(
+ '--email','-e',
+ help="Email to receive notifications")
+
+
+def adduser(args):
+ #TODO: Lets trust admins this do not validate Emails :)
+ commands_util.setup_app(args)
+
+ args.username = 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.find({
+ 'username': args.username.lower(),
+ }).count()
+
+ if users_with_username:
+ print u'Sorry, a user with that name already exists.'
+
+ else:
+ # Create the user
+ entry = db.User()
+ entry.username = unicode(args.username.lower())
+ entry.email = unicode(args.email)
+ entry.pw_hash = auth_lib.bcrypt_gen_password_hash(args.password)
+ entry.status = u'active'
+ entry.email_verified = True
+ entry.save()
+
+ print "User created (and email marked as verified)"
+
+
+def makeadmin_parser_setup(subparser):
+ subparser.add_argument(
+ 'username',
+ help="Username to give admin level")
+
+
+def makeadmin(args):
+ commands_util.setup_app(args)
+
+ db = mg_globals.database
+
+ user = db.User.one({'username': unicode(args.username.lower())})
+ if user:
+ user.is_admin = True
+ user.save()
+ print 'The user is now Admin'
+ else:
+ print 'The user doesn\'t exist'
+
+
+def changepw_parser_setup(subparser):
+ subparser.add_argument(
+ 'username',
+ help="Username used to login")
+ subparser.add_argument(
+ 'password',
+ help="Your NEW supersecret word to login")
+
+
+def changepw(args):
+ commands_util.setup_app(args)
+
+ db = mg_globals.database
+
+ user = db.User.one({'username': unicode(args.username.lower())})
+ if user:
+ user.pw_hash = auth_lib.bcrypt_gen_password_hash(args.password)
+ user.save()
+ print 'Password successfully changed'
+ else:
+ print 'The user doesn\'t exist'
diff --git a/mediagoblin/gmg_commands/util.py b/mediagoblin/gmg_commands/util.py
new file mode 100644
index 00000000..6a6853d5
--- /dev/null
+++ b/mediagoblin/gmg_commands/util.py
@@ -0,0 +1,40 @@
+# 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 app
+import getpass
+
+
+def setup_app(args):
+ """
+ Setup the application after reading the mediagoblin config files
+ """
+ mgoblin_app = app.MediaGoblinApp(args.conf_file)
+
+ return mgoblin_app
+
+def prompt_if_not_set(variable, text, password=False):
+ """
+ Checks if the variable is None and prompt for a value if it is
+ """
+ if variable is None:
+ if not password:
+ variable=raw_input(text + u' ')
+ else:
+ variable=getpass.getpass(text + u' ')
+
+ return variable