Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add several CLI commands & bug fixes #1826

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions b2share/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from flask import request
from invenio_records_rest.utils import deny_all, allow_all
from b2share.modules.oauthclient.b2access import make_b2access_remote_app
from b2share.modules.roles import B2ShareRoles
from b2share.modules.records.search import B2ShareRecordsSearch
from b2share.modules.records.permissions import (
UpdateRecordPermission, DeleteRecordPermission
Expand Down
31 changes: 30 additions & 1 deletion b2share/modules/communities/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@
from invenio_accounts.models import Role

from .errors import CommunityDeletedError, CommunityDoesNotExistError, \
InvalidCommunityError
InvalidCommunityError, CommunityPolicyDoesNotExistError, CommunityPolicyInvalidValueError
from .signals import after_community_delete, after_community_insert, \
after_community_update, before_community_delete, before_community_insert, \
before_community_update
from .models import Community as CommunityMetadata, _community_admin_role_name, \
_community_member_role_name
from .policies import PolicyValuesMapping


class Community(object):
Expand Down Expand Up @@ -225,6 +226,34 @@ def patch(self, patch):
self.update(data)
return self

def policy(self, name, value):
"""Update the community's policy with value.

Args:
policy (string): the name of the policy

Returns:
:class:`Community`: self

Raises:
jsonpatch.JsonPatchConflict: the json patch conflicts on the
community.

jsonpatch.InvalidJsonPatch: the json patch is invalid.

b2share.modules.communities.errors.InvalidCommunityError: The
community patch failed because the resulting community is
not valid.
"""

if not name in PolicyValuesMapping.keys():
raise CommunityPolicyDoesNotExistError()
if not value in PolicyValuesMapping[name]:
raise CommunityPolicyInvalidValueError()

return self.update({name: value})


# TODO: add properties for getting schemas and admins

def delete(self):
Expand Down
129 changes: 121 additions & 8 deletions b2share/modules/communities/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@

from invenio_db import db

from .api import Community, CommunityDoesNotExistError

from .api import Community, CommunityDoesNotExistError, \
CommunityPolicyDoesNotExistError, CommunityPolicyInvalidValueError

@click.group()
def communities():
Expand Down Expand Up @@ -95,8 +95,12 @@ def create(verbose, name, description, logo):
def list(verbose):
"""List all communities in this instances' database"""
communities = Community.get_all()
for c in communities:
click.echo("%s\t%s\t%s\t%s" % (c.name[0:15], c.id, c.description[0:31], c.logo))

if communities:
for c in communities:
click.echo("%s\t%s\t%s\t%s" % (c.name[0:15], c.id, c.description[0:31], c.logo))
else:
raise click.ClickException("there are no communities defined")


@communities.command()
Expand Down Expand Up @@ -145,7 +149,116 @@ def edit(verbose, id, name, description, logo, clear_fields):
@communities.command()
@with_appcontext
@click.argument('community')
@click.argument('json_file')
def set_schema(community, json_file):
from b2share.modules.schemas.cli import update_or_set_community_schema
update_or_set_community_schema(community, json_file)
@click.argument('json_file', required=False, default=None)
@click.option('--no-block', required=False, is_flag=True)
@click.option('--root-schema', required=False, default=None, type=int)
def set_schema(community, json_file, root_schema, no_block):
"""Set the community block schema and/or root schema version.
If a root schema version is given, but no JSON file, the latest known block schema will be used (if present)."""
from b2share.modules.schemas.cli import update_or_set_community_schema, update_or_set_community_root_schema
if json_file or no_block:
update_or_set_community_schema(community, json_file, root_schema, no_block)
elif root_schema:
update_or_set_community_root_schema(community, root_schema)
else:
raise click.BadParameter("Need at least a JSON file, block flag or root schema version")


@communities.group()
@with_appcontext
def roles():
"""Manage community roles"""

@roles.command('list')
@with_appcontext
@click.argument('community', required=False)
def community_roles_list(community=None):
"""List all communities' roles"""

def list_item(comm):
click.secho("%s\t%s\t\t%s\t%s" % (
comm.id,
comm.name,
comm.admin_role,
comm.member_role
))

click.secho("ID\t\t\t\t\tNAME\t\tROLES")
if not community:
communities = Community.get_all()
if communities:
for c in communities:
list_item(c)
else:
raise click.ClickException("there are no communities defined")
else:
list_item(Community.get(community))


@communities.group()
@with_appcontext
def policies():
"""Manage community policies"""

@policies.command('list')
@with_appcontext
@click.argument('community', required=False)
def community_policies_list(community=None):
"""List all communities' policy values"""

def list_item(comm):
click.secho("%s\t%s\t\t%s\t%s" % (
comm.id,
comm.name,
comm.publication_workflow,
comm.restricted_submission
))

click.secho("ID\t\t\t\t\tNAME\t\tWORKFLOW\tMEMBERS-ONLY")
if not community:
communities = Community.get_all()
if communities:
for c in communities:
list_item(c)
else:
raise click.ClickException("there are no communities defined")
else:
list_item(Community.get(community))


@policies.command('set')
@with_appcontext
@click.option('-v', '--verbose', is_flag=True, default=False)
@click.argument('community')
@click.argument('policy')
@click.argument('value', required=False)
@click.option('--enable', 'enable', flag_value=True)
@click.option('--disable', 'enable', flag_value=False, default=True)
def community_policies_set(verbose, community, policy, enable=None, value=None):
"""Enable/disable/set the value for a given community and policy."""

from .policies import PolicyValuesMapping, PolicyToggleValues

# if a value is omitted, use the enable flag and use its value
if value is None:
if not enable is None:
value = enable
else:
raise click.BadParameter("No value given or use --enable or --disable flag")

try:
comm = Community.get(id=community)
comm.policy(policy, value)
except CommunityDoesNotExistError:
raise click.BadParameter("No such community with ID %s" % community)
except CommunityPolicyDoesNotExistError:
raise click.BadParameter("No such community policy '%s', choose from: '%s'" % (policy, "', '".join(PolicyValuesMapping.keys())))
except CommunityPolicyInvalidValueError:
if PolicyValuesMapping[policy] == PolicyToggleValues:
raise click.BadParameter("Invalid value '%s' for policy '%s', use --enable or --disable flag" % (value, policy))
else:
raise click.BadParameter("Invalid value '%s' for policy '%s', choose from: '%s'" % (value, policy, "', '".join([str(x) for x in PolicyValuesMapping[policy]])))

db.session.commit()
if verbose:
click.echo("Community policy '%s' updated" % policy)
8 changes: 8 additions & 0 deletions b2share/modules/communities/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,11 @@ class NotACommunityRoleError(RESTException):

code = 400
description = 'This role doesn\'t belong to any community.'

class CommunityPolicyDoesNotExistError(Exception):
"""Exception raised when a requested community policy does not exist."""
pass

class CommunityPolicyInvalidValueError(Exception):
"""Exception raised when a community policy value is invalid."""
pass
36 changes: 36 additions & 0 deletions b2share/modules/communities/policies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
#
# This file is part of EUDAT B2Share.
# Copyright (C) 2016 University of Tuebingen, CERN
# Copyright (C) 2015 University of Tuebingen.
#
# B2Share is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# B2Share 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with B2Share; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
#
# In applying this license, CERN does not
# waive the privileges and immunities granted to it by virtue of its status
# as an Intergovernmental Organization or submit itself to any jurisdiction.

"""Communities interface and API."""

from __future__ import absolute_import

from .workflows import publication_workflows

PolicyToggleValues = [True, False]

PolicyValuesMapping = {
'restricted_submission': PolicyToggleValues,
'publication_workflow': publication_workflows.keys()
}
16 changes: 8 additions & 8 deletions b2share/modules/records/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from invenio_pidstore.models import PIDStatus
from invenio_pidstore.providers.datacite import DataCiteProvider
from invenio_records_files.api import Record
from invenio_records.cli import records

from b2share.modules.deposit.api import create_file_pids
from b2share.modules.records.serializers import datacite_v31
Expand All @@ -46,20 +47,19 @@
from b2share.modules.handle.proxies import current_handle


@click.group()
def b2records():
"""B2SHARE Records commands."""
@records.group()
def manage():
"""B2SHARE record management commands."""


@b2records.command()
@manage.command()
@with_appcontext
def update_expired_embargoes():
"""Updates all records with expired embargoes to open access."""
update_expired_embargoes_task.delay()
click.secho('Expiring embargoes...', fg='green')


@b2records.command()
@manage.command()
@with_appcontext
@click.option('-u', '--update', is_flag=True, default=False,
help='updates if necessary')
Expand Down Expand Up @@ -104,7 +104,7 @@ def check_and_update_handle_records(update, verbose):
click.secho(' file PID ok: {}'.format(pid))


@b2records.command()
@manage.command()
@with_appcontext
@click.option('-u', '--update', is_flag=True, default=False)
@click.argument('record_pid', required=True)
Expand Down Expand Up @@ -147,7 +147,7 @@ def check_handles(update, record_pid):
db.session.commit()


@b2records.command()
@manage.command()
@with_appcontext
@click.option('-r', '--record', default=None)
@click.option('-a', '--allrecords', is_flag=True, default=False)
Expand Down
4 changes: 2 additions & 2 deletions b2share/modules/records/ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from .errors import register_error_handlers
from .views import create_blueprint
from .indexer import indexer_receiver
from .cli import b2records
from .cli import records as records_cmd


class B2ShareRecords(object):
Expand All @@ -48,7 +48,7 @@ def __init__(self, app=None):
def init_app(self, app):
"""Flask application initialization."""
self.init_config(app)
app.cli.add_command(b2records)
app.cli.add_command(records_cmd)
app.extensions['b2share-records'] = self
register_triggers(app)
register_error_handlers(app)
Expand Down
48 changes: 48 additions & 0 deletions b2share/modules/roles/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
#
# This file is part of EUDAT B2Share.
# Copyright (C) 2016 University of Tuebingen, CERN.
# Copyright (C) 2015 University of Tuebingen.
#
# B2Share is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# B2Share 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with B2Share; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.

"""B2SHARE module providing access to user accounts


B2SHARE is accessible to anonymous users (not authenticated). However some
actions can only be performed by authenticated users.

Invenio provides the ``invenio-accounts`` module which stores in the database
user account information.

The B2SHARE module ``b2share.modules.users`` adds some features on top of
``invenio-accounts``.

* A REST API enabling to read user information and to create **REST API Access
Tokens**. See ``b2share.modules.users.views``.

* Permission classes limiting the access to the REST API. See the
``b2share.modules.users.permissions`` module.

REST API Access Tokens enable a user to send authenticated requests via the
REST API. Example: ``GET /api/user/?access_token=<ACCESS_TOKEN>``. See
``invenio-oauth2server`` for more information on Access Tokens.
"""

from __future__ import absolute_import, print_function

from .ext import B2ShareRoles

__all__ = ('B2ShareRoles')
Loading