Skip to content

Commit

Permalink
organisations: show organisation information
Browse files Browse the repository at this point in the history
This commit shows information about the current organisation, as the title, the description and optionally a logo. Users can search for records that belong to this organisation only.

* Enables REST endpoints for organisations files.
* Enables UI endpoints for organisations files.
* Associates un bucket when an organisation is created.
* Adds `description`, `_bucket` and `_files` properties to JSON schema.
* Adds `description` property to elasticsearch mapping.
* Serializes `description`, `_bucket` and `_files` properties when organisation is retrieved by REST API.
* Adds a Jinja filter to get an image URL for a record.
* Displays organisation's information in front page and inner pages.
* Searches only for global records in main search input.
* Closes #268.

Co-Authored-by: Sébastien Délèze <[email protected]>
  • Loading branch information
Sébastien Délèze committed Sep 7, 2020
1 parent 86bb6fb commit 775302d
Show file tree
Hide file tree
Showing 15 changed files with 219 additions and 32 deletions.
20 changes: 17 additions & 3 deletions sonar/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,19 @@ def _(x):
'route': '/deposits/<pid_value>/files/<filename>',
'view_imp': 'invenio_records_files.utils:file_download_ui',
'record_class': 'invenio_records_files.api:Record'
}
},
'org_previewer': {
'pid_type': 'org',
'route': '/organisations/<pid_value>/preview/<filename>',
'view_imp': 'invenio_previewer.views:preview',
'record_class': 'sonar.modules.organisations.api:OrganisationRecord'
},
'org_files': {
'pid_type': 'org',
'route': '/organisations/<pid_value>/files/<filename>',
'view_imp': 'invenio_records_files.utils:file_download_ui',
'record_class': 'invenio_records_files.api:Record'
},
}
"""Records UI for sonar."""

Expand Down Expand Up @@ -335,7 +347,8 @@ def _(x):
':json_v1'),
},
list_route='/organisations/',
item_route='/organisations/<pid(org):pid_value>',
item_route='/organisations/<pid(org, record_class="sonar.modules.'
'organisations.api:OrganisationRecord"):pid_value>',
default_media_type='application/json',
max_result_window=10000,
search_factory_imp='sonar.modules.organisations.query:search_factory',
Expand Down Expand Up @@ -505,7 +518,8 @@ def _(x):
RECORDS_FILES_REST_ENDPOINTS = {
'RECORDS_REST_ENDPOINTS': {
'doc': '/files',
'depo': '/files'
'depo': '/files',
'org': '/files'
}
}

Expand Down
18 changes: 12 additions & 6 deletions sonar/modules/organisations/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

"""Organisation Api."""


from functools import partial

from werkzeug.local import LocalProxy
Expand All @@ -33,11 +32,8 @@
lambda: OrganisationRecord.get_organisation_by_user(current_user_record))

# provider
OrganisationProvider = type(
'OrganisationProvider',
(Provider,),
dict(pid_type='org')
)
OrganisationProvider = type('OrganisationProvider', (Provider, ),
dict(pid_type='org'))
# minter
organisation_pid_minter = partial(id_minter, provider=OrganisationProvider)
# fetcher
Expand All @@ -62,6 +58,16 @@ class OrganisationRecord(SonarRecord):
provider = OrganisationProvider
schema = 'organisations/organisation-v1.0.0.json'

@classmethod
def create(cls, data, id_=None, dbcommit=False, with_bucket=True,
**kwargs):
"""Create organisation record."""
return super(OrganisationRecord, cls).create(data,
id_=id_,
dbcommit=dbcommit,
with_bucket=with_bucket,
**kwargs)

@classmethod
def get_organisation_by_user(cls, user):
"""Return organisation associated with user.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@
"type": "string",
"minLength": 1
},
"description": {
"title": "Description",
"description": "HTML markup admitted.",
"type": "string",
"minLength": 1,
"form": {
"type": "textarea",
"templateOptions": {
"rows": 5
}
}
},
"isShared": {
"title": "Is shared",
"description": "Organisation records can be accessed by a specific URL.",
Expand All @@ -48,11 +60,67 @@
"form": {
"hideExpression": "!field.model.isShared"
}
},
"_bucket": {
"title": "Bucket UUID",
"type": "string",
"minLength": 1
},
"_files": {
"title": "Files",
"description": "List of files attached to the record.",
"type": "array",
"items": {
"title": "File item",
"description": "Describes the information of a single file in the record.",
"additionalProperties": false,
"type": "object",
"properties": {
"bucket": {
"title": "Bucket UUID",
"type": "string",
"minLength": 1
},
"file_id": {
"title": "File UUID",
"type": "string",
"minLength": 1
},
"version_id": {
"title": "Version UUID",
"type": "string",
"minLength": 1
},
"key": {
"title": "Key",
"type": "string",
"minLength": 1
},
"checksum": {
"title": "Checksum",
"description": "MD5 checksum of the file.",
"type": "string",
"minLength": 1
},
"size": {
"title": "Size",
"description": "Size of the file in bytes.",
"type": "integer"
}
},
"required": [
"bucket",
"file_id",
"version_id",
"key"
]
}
}
},
"propertiesOrder": [
"code",
"name",
"description",
"isShared",
"isDedicated"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"pid": {
"type": "keyword"
},
"description": {
"type": "text"
},
"code": {
"type": "keyword"
},
Expand Down
3 changes: 3 additions & 0 deletions sonar/modules/organisations/marshmallow/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class OrganisationMetadataSchemaV1(StrictKeysMixin):
pid = PersistentIdentifier()
code = SanitizedUnicode(required=True)
name = SanitizedUnicode(required=True)
description = SanitizedUnicode()
isShared = fields.Boolean()
isDedicated = fields.Boolean()
# When loading, if $schema is not provided, it's retrieved by
Expand All @@ -50,6 +51,8 @@ class OrganisationMetadataSchemaV1(StrictKeysMixin):
data_key="$schema",
deserialize=schema_from_organisation)
permissions = fields.Dict(dump_only=True)
_files = fields.List(fields.Dict())
_bucket = SanitizedUnicode()

@pre_dump
def add_permissions(self, item):
Expand Down
5 changes: 3 additions & 2 deletions sonar/theme/templates/sonar/frontpage.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<div class="container">
<div class="row justify-content-center">
<div class="col-8 col-xs-6 col-lg-4 py-3 py-sm-5">
<a href="{{ url_for('documents.index', view=view_code) }}">
<a href="{{ url_for('documents.index', view=view_code if g.get('organisation', {}).get('isDedicated') else config.SONAR_APP_DEFAULT_ORGANISATION) }}">
{%- if config.THEME_LOGO %}
{% set logo_code = view_code if g.get('organisation', {}).get('isDedicated') else config.SONAR_APP_DEFAULT_ORGANISATION %}
<img src="{{ url_for('static', filename='images/' ~ logo_code ~ '-logo.svg')}}" alt="{{_(config.THEME_SITENAME)}}" class="logo"/>
Expand All @@ -36,7 +36,7 @@
</div>
<div class="row justify-content-center">
<div class="col-sm-12 col-lg-8 text-right my-4">
<form class="justify-content-end" action="{{ url_for('documents.search', view=view_code) }}" role="search">
<form class="justify-content-end" action="{{ url_for('documents.search', view=view_code if g.get('organisation', {}).get('isDedicated') else config.SONAR_APP_DEFAULT_ORGANISATION) }}" role="search">
<div class="row">
<div class="col-12 col-sm-11">
<input class="form-control form-control-lg mr-2" type="search" placeholder="{{_('Search publications, authors, projects, ...')}}" aria-label="Search" name="q">
Expand All @@ -56,6 +56,7 @@
<div class="bg-secondary text-light text-center py-2">
<h6 class="m-0">{{ _('Software under development!') }}</h6>
</div>
{% include 'sonar/partial/organisation.html' %}
{% endblock %}

{%- block body %}
Expand Down
4 changes: 3 additions & 1 deletion sonar/theme/templates/sonar/page.html
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@
{%- block body_inner %}
<header>
{% include 'sonar/partial/navbar.html' %}
{%- block header %}{%- endblock header %}
{%- block header %}
{% include 'sonar/partial/organisation.html' %}
{%- endblock header %}
</header>

<div class="container my-5">
Expand Down
17 changes: 1 addition & 16 deletions sonar/theme/templates/sonar/partial/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,6 @@
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/>.
#}

{% if g.get('organisation') and not g.organisation['isDedicated'] %}
<div class="bg-secondary text-light p-1">
<div class="container">
<div class="row">
<div class="col">{{ g.organisation.name }}</div>
<div class="col text-right">
<a class="text-light" href="{{url_for('documents.index', view=config.SONAR_APP_DEFAULT_ORGANISATION)}}">
{{_('Back to SONAR')}}
</a>
</div>
</div>
</div>
</div>
{% endif %}
<nav class="navbar navbar-expand-lg navbar-dark bg-{{ 'header' if page == 'home' else 'organisation' }}">
<div class="container">
{%- if not current_user.is_authenticated %}
Expand Down Expand Up @@ -58,7 +43,7 @@
class="d-inline-block align-top mr-3 my-4" alt="">
</a>
{% if not admin %}
<form action="{{ url_for('documents.search', view=view_code) }}"
<form action="{{ url_for('documents.search', view=view_code if g.get('organisation', {}).get('isDedicated') else config.SONAR_APP_DEFAULT_ORGANISATION) }}"
class="form-inline my-2 my-lg-0 ml-lg-5">
<input name="q" class="form-control mr-sm-2" type="search" placeholder="{{_('Search')}}"
aria-label="{{_('Search')}}" value="{{ request.args.get('q', '') }}">
Expand Down
41 changes: 41 additions & 0 deletions sonar/theme/templates/sonar/partial/organisation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{% if g.get('organisation') and not g.organisation['isDedicated'] %}
<div class="bg-light py-4">
<div class="container">
<div class="row justify-content-center">
{% set thumbnail = g.organisation | record_image_url %}
{% if thumbnail %}
<div class="col-6 col-lg-2">
<img src="{{ thumbnail }}" alt="{{ g.organisation.name }}" class="img-fluid">
</div>
{% endif %}
<div class="col-12 col-lg-{{ '10' if thumbnail else '12' }}">
<h1 class="mb-2">{{ g.organisation.name }}</h1>
{% if g.organisation.get('description') %}
<p class="mb-3 text-justify">{{ g.organisation.description | nl2br | safe }}</p>
{% endif %}
<div class="row">
<div class="col-12 col-lg-4">
<form action="{{ url_for('documents.search', view=view_code) }}" role="search">
<div class="input-group mb-2">
<input type="text" name="q" value="{{ request.args.get('q', '') }}" class="form-control"
id="inlineFormInputGroup" placeholder="{{ _('Search') }}">
<div class="input-group-append">
<button type="submit" class="btn btn-primary mb-2">
<i class="fa fa-search"></i>
</button>
</div>
</div>
</form>
</div>
<div class="col text-right">
<a href="{{url_for('documents.index', view=config.SONAR_APP_DEFAULT_ORGANISATION)}}"
class="btn btn-outline-primary">
{{_('Back to SONAR')}}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
{% endif %}
30 changes: 30 additions & 0 deletions sonar/theme/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,36 @@ def schemas(record_type):
abort(404)


@blueprint.app_template_filter()
def record_image_url(record, key=None):
"""Get image URL for a record.
:param files: Liste of files of the record.
:param key: The key of the file to be rendered, if no key, takes the first.
:returns: Image url corresponding to key, or the first one.
"""
if not record.get('_files'):
return None

def image_url(file):
"""Return image URL for a file.
:param file: File to get the URL from.
:returns: URL of the file.
"""
return '/api/files/{bucket}/{key}'.format(key=file['key'],
bucket=file['bucket'])

for file in record['_files']:
if re.match(r'^.*\.(jpe?g|png|gif|svg)$',
file['key'],
flags=re.IGNORECASE):
if not key or file['key'] == key:
return image_url(file)

return None


def prepare_schema(schema):
"""Prepare schema before sending it."""
# Recursively translate properties in schema
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def app_config(app_config):


@pytest.fixture
def make_organisation(app, db):
def make_organisation(app, db, bucket_location):
"""Factory for creating organisation."""

def _make_organisation(code):
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/documents/dojson/rerodoc/test_rerodoc_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from sonar.modules.documents.dojson.rerodoc.model import marc21tojson


def test_marc21_to_type_and_organisation(app):
def test_marc21_to_type_and_organisation(app, bucket_location):
"""Test type and organisation."""

# Type only
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/documents/dojson/rerodoc/test_rerodoc_overdo.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from sonar.modules.organisations.api import OrganisationRecord


def test_create_organisation(app):
def test_create_organisation(app, bucket_location):
"""Test create organisation."""
Overdo.create_organisation('test')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from sonar.modules.organisations.api import OrganisationRecord


def test_import_organisations(app, script_info):
def test_import_organisations(app, script_info, bucket_location):
"""Test import organisations."""
runner = CliRunner()

Expand Down
Loading

0 comments on commit 775302d

Please sign in to comment.