Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rdmo/config/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def filter_for_project(self, project):
return (
self
.filter_for_site(project.site)
.filter(catalogs=project.catalog)
.filter(models.Q(catalogs=None) | models.Q(catalogs=project.catalog))
.filter(models.Q(groups=None) | models.Q(groups__in=project.groups))
.filter(available=True)
)
Expand Down
1 change: 1 addition & 0 deletions rdmo/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@

PLUGINS = [ # introduced in 2.5
'rdmo.projects.exports.RDMOXMLExport',
'rdmo.projects.exports.RDMOSnapshotXMLExport',
'rdmo.projects.imports.RDMOXMLImport',
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ const ProjectImport = ({ allowedTypes, handleImport, importUrls}) => {
<label className="control-label">{gettext('Import directly')}</label>
<ul className='list-unstyled mb-0'>
{importUrls.map((url) => (
<li key={url.key}>
<li key={url.url_name}>
<a href={url.href} target='_blank' rel='noopener noreferrer'>
{url.label}
{url.title}
</a>
</li>
))}
Expand Down
24 changes: 18 additions & 6 deletions rdmo/projects/exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,25 @@ def render(self):
class RDMOXMLExport(Export):

def render(self):
if self.project:
content_disposition = f'attachment; filename="{self.project.title}.xml"'
serializer = ProjectExportSerializer(self.project)
content_disposition = f'attachment; filename="{self.project.title}.xml"'
serializer = ProjectExportSerializer(self.project)

else:
content_disposition = f'attachment; filename="{self.snapshot.title}.xml"'
serializer = SnapshotExportSerializer(self.snapshot)
xmldata = XMLRenderer().render(serializer.data)
response = HttpResponse(prettify_xml(xmldata), content_type="application/xml")

if settings.EXPORT_CONTENT_DISPOSITION == 'attachment':
response['Content-Disposition'] = content_disposition

return response


class RDMOSnapshotXMLExport(Export):

plugin_type = 'project_snapshot_export'

def render(self):
content_disposition = f'attachment; filename="{self.snapshot.title}.xml"'
serializer = SnapshotExportSerializer(self.snapshot)

xmldata = XMLRenderer().render(serializer.data)
response = HttpResponse(prettify_xml(xmldata), content_type="application/xml")
Expand Down
125 changes: 74 additions & 51 deletions rdmo/projects/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,38 @@ def update_values(self, current_project, catalog, values, snapshots=None):
.get(value.set_index, {}) \
.get(value.collection_index)

def get_import_plugin(self, key, current_project=None):
plugins = Plugin.objects.for_context(
def get_plugin_by_url_name(self, url_name, project=None):
for plugin in Plugin.objects.for_context(
plugin_type=PLUGIN_TYPES.PROJECT_IMPORT,
project=current_project,
project=project,
user=self.request.user,
format=key
)
plugin = next((i for i in plugins if i.url_name == key), None)
if plugin is None:
raise Http404
):
if plugin.url_name == url_name:
return plugin

import_plugin = plugin.initialize_class()
import_plugin.request = self.request
import_plugin.current_project = current_project
def get_plugin_by_suffix(self, suffix, project=None):
for plugin in Plugin.objects.for_context(
plugin_type=PLUGIN_TYPES.PROJECT_IMPORT,
project=project,
user=self.request.user,
):
accept = plugin.plugin_meta.get('accept')
if isinstance(accept, dict):
for _mime_type, suffixes in accept.items():
if suffix in suffixes:
return plugin

def get_import_plugin(self):
plugin = self.get_plugin_by_url_name(self.kwargs.get('url_name'), self.object)
if plugin:
import_plugin = plugin.initialize_class()
import_plugin.request = self.request
import_plugin.current_project = self.object

return import_plugin

return import_plugin
# no plugin for this url_name found
raise Http404

def upload_file(self):
try:
Expand Down Expand Up @@ -106,47 +122,46 @@ def import_form(self):
'errors': [_('There has been an error with your import. No uploaded or retrieved file could be found.')]
}, status=400)

for plugin in Plugin.objects.for_context(
plugin_type=PLUGIN_TYPES.PROJECT_IMPORT, project=current_project,
user=self.request.user, format=Path(import_file_name).suffix.lstrip('.')
):
import_plugin = plugin.initialize_class()
import_plugin.current_project = current_project
import_plugin.file_name = import_file_name
import_plugin.source_title = import_source_title
import_plugin.request = self.request
suffix = Path(import_file_name).suffix
plugin = self.get_plugin_by_suffix(suffix, current_project)
if plugin is None:
return render(self.request, 'core/error.html', {
'title': _('Import error'),
'errors': [_('Files of this type cannot be imported.')]
}, status=400)

if import_plugin.check():
try:
import_plugin.process()
except ValidationError as e:
return render(self.request, 'core/error.html', {
'title': _('Import error'),
'errors': e
}, status=400)
import_plugin = plugin.initialize_class()
import_plugin.request = self.request
import_plugin.current_project = current_project
import_plugin.file_name = import_file_name
import_plugin.source_title = import_source_title

# store information in session for ProjectCreateImportView
self.request.session['import_key'] = plugin.url_name
if import_plugin.check():
try:
import_plugin.process()
except ValidationError as e:
return render(self.request, 'core/error.html', {
'title': _('Import error'),
'errors': e
}, status=400)

# attach questions and current values
self.update_values(current_project, import_plugin.catalog,
import_plugin.values, import_plugin.snapshots)
# store information in session for ProjectCreateImportView
self.request.session['import_plugin_id'] = plugin.id

return render(self.request, 'projects/project_import.html', {
'method': 'import_file',
'current_project': current_project,
'source_title': import_plugin.source_title,
'source_project': import_plugin.project,
'values': import_plugin.values,
'snapshots': import_plugin.snapshots if not current_project else None,
'tasks': import_plugin.tasks,
'views': import_plugin.views
})
# attach questions and current values
self.update_values(current_project, import_plugin.catalog,
import_plugin.values, import_plugin.snapshots)

return render(self.request, 'core/error.html', {
'title': _('Import error'),
'errors': [_('Files of this type cannot be imported.')]
}, status=400)
return render(self.request, 'projects/project_import.html', {
'method': 'import_file',
'current_project': current_project,
'source_title': import_plugin.source_title,
'source_project': import_plugin.project,
'values': import_plugin.values,
'snapshots': import_plugin.snapshots if not current_project else None,
'tasks': import_plugin.tasks,
'views': import_plugin.views
})

def import_file(self):
current_project = self.object
Expand All @@ -159,7 +174,7 @@ def import_file(self):

try:
import_tmpfile_name = self.request.session.pop('import_file_name')
import_key = self.request.session.pop('import_key')
plugin_id = self.request.session.pop('import_plugin_id')
except KeyError:
return render(self.request, 'core/error.html', {
'title': _('Import error'),
Expand All @@ -168,8 +183,16 @@ def import_file(self):

checked = [key for key, value in self.request.POST.items() if 'on' in value]

if import_tmpfile_name and import_key:
import_plugin = self.get_import_plugin(import_key, current_project)
if import_tmpfile_name and plugin_id:
plugin = Plugin.objects.for_context(
plugin_type=PLUGIN_TYPES.PROJECT_IMPORT,
project=current_project,
user=self.request.user,
).get(id=plugin_id)

import_plugin = plugin.initialize_class()
import_plugin.request = self.request
import_plugin.current_project = current_project
import_plugin.file_name = import_tmpfile_name

if import_plugin.check():
Expand Down
8 changes: 2 additions & 6 deletions rdmo/projects/serializers/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,17 +151,13 @@ class Meta:

class ProjectImportPluginSerializer(serializers.ModelSerializer):

key = serializers.CharField(source='url_name')
label = serializers.CharField(source='title')
class_name = serializers.CharField(source='python_path')
href = serializers.SerializerMethodField()

class Meta:
model = Plugin
fields = (
'key',
'label',
'class_name',
'title',
'url_name',
'href',
)

Expand Down
3 changes: 2 additions & 1 deletion rdmo/projects/serializers/v1/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class OptionSetSerializer(ElementModelSerializerMixin, serializers.ModelSerializ

model = serializers.SerializerMethodField()
options = OptionSerializer(source='elements', many=True)
has_provider = serializers.BooleanField(source='has_plugins')

class Meta:
model = OptionSet
Expand All @@ -49,7 +50,7 @@ class Meta:
'uri',
'model',
'options',
'has_plugins',
'has_provider',
'has_search',
'has_refresh',
'has_conditions'
Expand Down
5 changes: 5 additions & 0 deletions rdmo/projects/static/projects/css/project.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@
width: 11px;
}
}

.snapshot-export-button {
padding: 0;
border: none;
}
28 changes: 16 additions & 12 deletions rdmo/projects/templates/projects/project_detail_sidebar.html
Original file line number Diff line number Diff line change
Expand Up @@ -123,55 +123,59 @@ <h2>{% trans 'Options' %}</h2>
</ul>

{% has_perm 'projects.export_project_object' request.user project as can_export_project %}
{% if settings.PROJECT_EXPORTS and can_export_project %}
{% if exports and can_export_project %}
<h2 id="export-project">{% trans 'Export' %}</h2>

<ul class="list-unstyled">
{% for key, label, class in settings.PROJECT_EXPORTS %}
{% for url_name, title in exports %}
<li>
<a href="{% url 'project_export' project.pk key %}" target="_blank">
{{ label }}
<a href="{% url 'project_export' project.pk url_name %}" target="_blank">
{{ title }}
</a>
</li>
{% endfor %}
</ul>
{% endif %}

{% has_perm 'projects.import_project_object' request.user project as can_import_project %}
{% if settings.PROJECT_IMPORTS and can_import_project %}
{% if can_import_project %}
{% if upload_import or ancestors_import or imports %}
<h2 id="import-project">{% trans 'Import values' %}</h2>

<ul class="list-unstyled">
{% if upload_import %}
<li>
<p>
<strong>{% trans 'Import from file' %}</strong>
</p>
{% url 'project_update_import' project.id as upload_url %}
{% include 'core/upload_form.html' with upload_url=upload_url label=True %}
</li>
{% if settings.NESTED_PROJECTS and ancestors_import %}
{% endif %}

{% if ancestors_import %}
<li>
<p>
<strong>{% trans 'Import from parent project' %}</strong>
</p>
{% include 'projects/project_detail_sidebar_parent_import.html' %}
</li>
{% endif %}
{% if settings.PROJECT_IMPORTS_LIST %}

{% if url_import %}
<li>
<p>
<strong>{% trans 'Import directly' %}</strong>
</p>
</li>
{% for key, label, class in settings.PROJECT_IMPORTS %}
{% if key in settings.PROJECT_IMPORTS_LIST %}
{% for url_name, title in url_import %}
<li>
<a href="{% url 'project_update_import' project.id key %}" target="_blank">
{{ label }}
<a href="{% url 'project_update_import' project.id url_name %}" target="_blank">
{{ title }}
</a>
</li>
{% endif %}
{% endfor %}
{% endif %}
</ul>
{% endif %}
{% endif %}
10 changes: 5 additions & 5 deletions rdmo/projects/templates/projects/project_detail_snapshots.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,17 @@ <h2>{% trans 'Snapshots' %}</h2>
{% endif %}

{% has_perm 'projects.export_project_object' request.user project as can_export_project %}
{% if settings.PROJECT_SNAPSHOT_EXPORTS and can_export_project %}
{% if snapshot_exports and can_export_project %}
<span class="dropdown">
<button type="submit" class="btn-link fa fa-download"
<button type="submit" class="btn-link fa fa-download snapshot-export-button"
title="{% trans 'Export snapshot' %}" aria-label="{% trans 'Export snapshot' %}"
data-toggle="dropdown"></button>

<ul class="dropdown-menu">
{% for key, label, class in settings.PROJECT_SNAPSHOT_EXPORTS %}
{% for url_name, title in snapshot_exports %}
<li>
<a href="{% url 'snapshot_export' project.id snapshot.id key %}" target="_blank">
{{ label }}
<a href="{% url 'snapshot_export' project.id snapshot.id url_name %}" target="_blank">
{{ title }}
</a>
</li>
{% endfor %}
Expand Down
8 changes: 4 additions & 4 deletions rdmo/projects/urls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
ProjectCancelView.as_view(), name='project_cancel'),
re_path(r'^import/$',
ProjectCreateImportView.as_view(), name='project_create_import'),
re_path(r'^import/(?P<format>[a-z-]+)/$',
re_path(r'^import/(?P<url_name>[a-z-]+)/$',
ProjectCreateImportView.as_view(), name='project_create_import'),

re_path(r'^(?P<pk>[0-9]+)/$',
Expand All @@ -78,11 +78,11 @@
ProjectDeleteView.as_view(), name='project_delete'),
re_path(r'^(?P<pk>[0-9]+)/leave/$',
ProjectLeaveView.as_view(), name='project_leave'),
re_path(r'^(?P<pk>[0-9]+)/export/(?P<format>[a-z-]+)/$',
re_path(r'^(?P<pk>[0-9]+)/export/(?P<url_name>[a-z-]+)/$',
ProjectExportView.as_view(), name='project_export'),
re_path(r'^(?P<pk>[0-9]+)/import/$',
ProjectUpdateImportView.as_view(), name='project_update_import'),
re_path(r'^(?P<pk>[0-9]+)/import/(?P<format>[a-z-]+)/$',
re_path(r'^(?P<pk>[0-9]+)/import/(?P<url_name>[a-z-]+)/$',
ProjectUpdateImportView.as_view(), name='project_update_import'),

re_path(r'^(?P<project_id>[0-9]+)/memberships/create/$',
Expand Down Expand Up @@ -116,7 +116,7 @@
SnapshotUpdateView.as_view(), name='snapshot_update'),
re_path(r'^(?P<project_id>[0-9]+)/snapshots/(?P<pk>[0-9]+)/rollback/$',
SnapshotRollbackView.as_view(), name='snapshot_rollback'),
re_path(r'^(?P<project_id>[0-9]+)/snapshots/(?P<pk>[0-9]+)/export/(?P<format>[a-z-]+)/$',
re_path(r'^(?P<project_id>[0-9]+)/snapshots/(?P<pk>[0-9]+)/export/(?P<url_name>[a-z-]+)/$',
SnapshotExportView.as_view(), name='snapshot_export'),

re_path(r'^(?P<pk>[0-9]+)/answers/$',
Expand Down
Loading
Loading