Skip to content
New issue

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

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

Already on GitHub? # to your account

Community landing page and API changes #1759

Merged
merged 13 commits into from
Feb 27, 2020
Merged
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ webui/app/b2share-bundle.js.map
webui/app/lib/
webui/app/vendors/
webui/src/version.js
webui/package-lock.json
3 changes: 2 additions & 1 deletion b2share/modules/communities/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from invenio_db import db
from jsonpatch import apply_patch
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.exc import StatementError
from invenio_accounts.models import Role

from .errors import CommunityDeletedError, CommunityDoesNotExistError, \
Expand Down Expand Up @@ -89,7 +90,7 @@ def get(cls, id=None, name=None, with_deleted=False):
if metadata.deleted and not with_deleted:
raise CommunityDeletedError(id)
return cls(metadata)
except NoResultFound as e:
except (NoResultFound, StatementError) as e:
raise CommunityDoesNotExistError(id) from e

@classmethod
Expand Down
15 changes: 14 additions & 1 deletion b2share/modules/communities/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@

from flask import jsonify, url_for

from b2share.modules.schemas.api import CommunitySchema
from b2share.modules.schemas.serializers import block_schema_version_self_link, community_schema_json_schema_link
import json


def community_self_link(community, **kwargs):
"""Create self link to a given community.
Expand All @@ -44,6 +48,9 @@ def community_self_link(community, **kwargs):


def community_to_dict(community):
community_schema = CommunitySchema.get_community_schema(community.id)
community_schema_dict = json.loads(community_schema.community_schema)
props = community_schema_dict['properties']
return dict(
id=community.id,
name=community.name,
Expand All @@ -54,7 +61,13 @@ def community_to_dict(community):
publication_workflow=community.publication_workflow,
restricted_submission=community.restricted_submission,
links=dict(
self=community_self_link(community, _external=True)
self=community_self_link(community, _external=True),
schema=community_schema_json_schema_link(community_schema, _external=True),
block_schema=next(iter(props.values()))['$ref'] if props else ""
),
schema=dict(
version=community_schema.version,
block_schema_id=next(iter(props)) if props else ""
),
roles=dict(
admin=dict(id=community.admin_role.id,
Expand Down
6 changes: 5 additions & 1 deletion b2share/modules/communities/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
update_permission_factory
from .serializers import community_to_json_serializer, \
search_to_json_serializer, community_self_link
from b2share.utils import is_valid_uuid

current_communities = LocalProxy(
lambda: current_app.extensions['b2share-communities'])
Expand All @@ -61,7 +62,10 @@ def pass_community(f):
@wraps(f)
def inner(self, community_id, *args, **kwargs):
try:
community = Community.get(id=community_id)
if is_valid_uuid(community_id):
community = Community.get(id=community_id)
else:
community = Community.get(name=community_id)
except (CommunityDoesNotExistError):
abort(404)
except (CommunityDeletedError):
Expand Down
2 changes: 1 addition & 1 deletion b2share/modules/schemas/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ def get_community_schema(cls, community_id, version=None):
"""Retrieve the requested version of a community schema.

Args:
schema_id (ID): schema id.
community_id (ID): community id.
version (int): version of the schema to retrieve. If None, the last
version will be retrieved.

Expand Down
3 changes: 1 addition & 2 deletions b2share/modules/schemas/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ def block_schema_list(verbose, community):
except BlockSchemaDoesNotExistError:
raise click.ClickException("""No block schemas found, community
parameter was: %s""" % community)
click.secho("""BLOCK SCHEMA
ID\t\t\t\tNAME\t\tMAINTAINER\tDEPRECATED\t#VERSIONS""")
click.secho("""BLOCK SCHEMA ID\t\t\t\tNAME\t\tMAINTAINER\tDEPRECATED\t#VERSIONS""")
for bs in block_schemas:
bs_comm = Community.get(id=bs.community)
click.secho("%s\t%s\t%s\t%s\t\t%d" % (
Expand Down
8 changes: 8 additions & 0 deletions b2share/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from sqlalchemy import UniqueConstraint, PrimaryKeyConstraint
from invenio_db import db
import uuid


def add_to_db(instance, skip_if_exists=False, **fields):
Expand Down Expand Up @@ -69,3 +70,10 @@ def add_to_db(instance, skip_if_exists=False, **fields):
# Add the row if it does not already exist
db.session.add(instance)
return instance

def is_valid_uuid(val):
try:
uuid.UUID(str(val))
return True
except ValueError:
return False
16 changes: 11 additions & 5 deletions webui/src/components/community_admin.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Link } from 'react-router';
import { DropdownList, Multiselect } from 'react-widgets';
import { fromJS, OrderedMap, Map } from 'immutable';
import { serverCache, notifications , browser , Error, loginURL} from '../data/server';
import { isCommunityAdmin } from './record.jsx';
import { LoginOrRegister } from './user.jsx';
import { Wait, Err } from './waiting.jsx';

Expand Down Expand Up @@ -37,8 +38,7 @@ export const CommunityAdmin = React.createClass({

render() {
const current_user = serverCache.getUser();
const communityName = this.props.params.id;
const community = serverCache.getCommunity(communityName);
const community = serverCache.getCommunity(this.props.params.id);

if (!current_user || !current_user.get || !current_user.get('name')) {
return this.renderNoUser();
Expand All @@ -50,6 +50,12 @@ export const CommunityAdmin = React.createClass({
return <Err err={community}/>;
}

if (!isCommunityAdmin(community.get('id'))) {
return <Err err={{code: 403, text: "You don't have the required role to access this page"}}/>;
}

const communityName = community.get('name');

var roles = {};
community.get('roles').forEach( role =>{
roles[role.get('name').replace(new RegExp("^com:[^:]*:"), "")] = role.get('id');
Expand Down Expand Up @@ -110,12 +116,12 @@ export const UsersTable = React.createClass({

return (
<div>
<div className="row" >
<div className="row">
<div className="col-sm-4"><h4><strong> Email Address </strong></h4></div>
<div className="col-sm-2"><h4><strong> Role </strong></h4></div>
<div className="col-sm-6"><h4><strong> Edit </strong></h4></div>
</div>
<div >{rows}</div>
<div>{rows}</div>
</div>
);
}
Expand All @@ -138,7 +144,7 @@ export const UserRow = React.createClass({
defRoleName={this.props.userRoleName}
defRoleID={this.props.userRoleID}
userEmail={this.props.userEmail}
userID={this.props.userID}/ ></div>
userID={this.props.userID}/></div>
</div>
</div>
);
Expand Down
34 changes: 26 additions & 8 deletions webui/src/components/record.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,13 @@ const Record = React.createClass({
return !community ? false :
(community instanceof Error) ? <Err err={community}/> :
(
<div key={community.get('id')} className="community">
<div className="community-small passive" title={community.get('description')}>
<p className="name">{community.get('name')}</p>
<img className="logo" src={community.get('logo')}/>
</div>
<div key={community.get('id')}>
<Link to={"/communities/"+community.get('name')}>
<div className="community-small passive" title={community.get('description')}>
<p className="name">{community.get('name')}</p>
<img className="logo" src={community.get('logo')}/>
</div>
</Link>
</div>
);
}
Expand Down Expand Up @@ -245,7 +247,7 @@ const Record = React.createClass({
);
},

renderField(id, schema, value) {
renderField(id, schema, value, vtype=null) {
function renderScalar(schema, value) {
const type = schema.get('type');
if (type === 'string' && schema.get('format') === 'date-time') {
Expand All @@ -258,13 +260,24 @@ const Record = React.createClass({
<span className={markClass} aria-hidden="true"/>
</label>
);
} else if (vtype) {
switch (vtype) {
case 'DOI':
return <Link to={"https://dx.doi.org/" + value} target="_blank">{value}</Link>
case 'Handle':
return <Link to={"https://hdl.handle.net/" + value} target="_blank">{value}</Link>
case 'URL':
return <Link to={value} target="_blank">{value}</Link>
}
}

return value;
}

if (value === undefined || value === null) {
return false;
}
//console.log(id, value);
const type = schema.get('type');
const title = schema.get('title');
let inner = null;
Expand All @@ -276,10 +289,15 @@ const Record = React.createClass({
</ul>
);
} else if (type === 'object') {
// determine if object parent has '_type' element that is a string
const mainid = schema.get('properties').keys().next().value;
const maintype = schema.get('properties').has(mainid + "_type") ? schema.get('properties').get(mainid + "_type") : null;
const vtype = (maintype && maintype.get('type') == 'string') ? value.get(mainid + "_type") : null;

inner = (
<ul className="list-unstyled">
{ schema.get('properties').entrySeq().map(
([pid, pschema]) => this.renderField(pid, pschema, value.get(pid))) }
([pid, pschema]) => this.renderField(pid, pschema, value.get(pid), pid == mainid ? vtype : null)) }
</ul>
);
} else {
Expand Down Expand Up @@ -484,7 +502,7 @@ function isRecordOwner(record) {
return record.getIn(['metadata', 'owners']).indexOf(userId) >= 0;
}

function isCommunityAdmin(communityId) {
export function isCommunityAdmin(communityId) {
if (!serverCache.getUser()) {
return false;
}
Expand Down
27 changes: 13 additions & 14 deletions webui/src/data/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const apiUrls = {
accessrequests(id) { return `${urlRoot}/api/records/${id}/accessrequests` },

communities() { return `${urlRoot}/api/communities/` },
community(id) { return `${urlRoot}/api/communities/${id}` },
communitySchema(cid, version) { return `${urlRoot}/api/communities/${cid}/schemas/${version}` },

schema(id, version) { return `${urlRoot}/api/schemas/${id}/versions/${version}` },
Expand Down Expand Up @@ -293,6 +294,7 @@ class ServerCache {
latestRecords: [], // latest records with params
searchRecords: null, // searched records view, with params
recordCache: {}, // map of record IDs to records
communityCache: {}, // map of community IDs to communities
draftCache: {}, // map of draft IDs to drafts

communities: {}, // ordered map of community IDs to communities
Expand Down Expand Up @@ -451,6 +453,13 @@ class ServerCache {
},
(xhr) => this.store.setIn(['recordCache', recordID], new Error(xhr)) ));

this.getters.community = new Pool(communityID =>
new Getter(apiUrls.community(communityID), null,
(data) => {
this.store.setIn(['communityCache', communityID], fromJS(data));
},
(xhr) => this.store.setIn(['communityCache', communityID], new Error(xhr)) ));

this.getters.draft = new Pool(draftID =>
new Getter(apiUrls.draft(draftID), null,
(data) => {
Expand Down Expand Up @@ -581,7 +590,8 @@ class ServerCache {

getLatestRecordsOfCommunity({community}) {
let q = ' community:' + community;
this.getters.latestRecordsOfCommunity.fetch({q});
let sort = 'mostrecent', page = 1, size = 10;
this.getters.latestRecordsOfCommunity.fetch({q, sort, page, size});
return this.store.getIn(['latestRecordsOfCommunity']);
}

Expand All @@ -608,19 +618,8 @@ class ServerCache {
}

getCommunity(communityIDorName) {
this.getters.communities.autofetch();
const communities = this.store.getIn(['communities']);
if (!communities) {
return null;
}
if (communities instanceof Error) {
return communities;
}
let c = communities.get(communityIDorName);
if (!c) {
c = communities.valueSeq().find(x => x.get('name') == communityIDorName);
}
return c;
this.getters.community.get(communityIDorName).fetch();
return this.store.getIn(['communityCache', communityIDorName]);
}

getRecord(id) {
Expand Down