Web UI for freezer
This changes are the first implementation of the freezer web ui. It's a read only version that permit to view the backups in swift made by the user. Change-Id: I79fd98ce3fc364c7a82a8b764e1733febb16b647
This commit is contained in:
parent
978a30ec3a
commit
fb27cb7b85
|
@ -13,9 +13,9 @@ To install the horizon web ui you need to do the following::
|
|||
|
||||
# cd freezer/horizon_web_ui
|
||||
|
||||
# cp -r devfreezer /opt/stack/horizon/openstack_dashboard/dashboards/
|
||||
# cp -r freezer /opt/stack/horizon/openstack_dashboard/dashboards/
|
||||
|
||||
# cp _50_devfreezer.py /opt/stack/horizon/openstack_dashboard/enabled/
|
||||
# cp _50_freezer.py /opt/stack/horizon/openstack_dashboard/enabled/
|
||||
# cd /opt/stack/horizon/
|
||||
|
||||
# ./run_tests.sh --runserver 0.0.0.0:8878
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# The name of the dashboard to be added to HORIZON['dashboards']. Required.
|
||||
DASHBOARD = 'devfreezer'
|
||||
DASHBOARD = 'freezer'
|
||||
|
||||
# If set to True, this dashboard will not be added to the settings.
|
||||
DISABLED = False
|
||||
|
||||
# A list of applications to be added to INSTALLED_APPS.
|
||||
ADD_INSTALLED_APPS = [
|
||||
'openstack_dashboard.dashboards.devfreezer',
|
||||
'openstack_dashboard.dashboards.freezer',
|
||||
]
|
|
@ -1,13 +0,0 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class Devfreezer(horizon.Dashboard):
|
||||
name = _("Backup as a Service")
|
||||
slug = "devfreezer"
|
||||
panels = ('freezerweb',) # Add your panels here.
|
||||
default_panel = 'freezerweb' # Specify the slug of the dashboard's default panel.
|
||||
|
||||
|
||||
horizon.register(Devfreezer)
|
|
@ -1,13 +0,0 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.dashboards.devfreezer import dashboard
|
||||
|
||||
|
||||
class Freezerweb(horizon.Panel):
|
||||
name = _("Freezer")
|
||||
slug = "freezerweb"
|
||||
|
||||
|
||||
dashboard.Devfreezer.register(Freezerweb)
|
|
@ -1,15 +0,0 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
||||
|
||||
class InstancesTable(tables.DataTable):
|
||||
name = tables.Column("name", verbose_name=_("Name"))
|
||||
status = tables.Column("status", verbose_name=_("Status"))
|
||||
zone = tables.Column('availability_zone',
|
||||
verbose_name=_("Availability Zone"))
|
||||
image_name = tables.Column('image_name', verbose_name=_("Image Name"))
|
||||
|
||||
class Meta:
|
||||
name = "instances"
|
||||
verbose_name = _("Instances")
|
|
@ -1,17 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Freezer Web Interface" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Freezer Web Interface") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block devfreezer_main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import IndexView
|
||||
from openstack_dashboard.dashboards.devfreezer.freezerweb import views
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^\?tab=mypanel_tabs_tab$',
|
||||
views.IndexView.as_view(), name='mypanel_tabs')
|
||||
)
|
|
@ -1,16 +0,0 @@
|
|||
from horizon import views
|
||||
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard.dashboards.devfreezer.freezerweb \
|
||||
import tabs as mydashboard_tabs
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
tab_group_class = mydashboard_tabs.MypanelTabs
|
||||
# A very simple class-based view...
|
||||
template_name = 'devfreezer/freezerweb/index.html'
|
||||
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
# Add data to the context here...
|
||||
return context
|
||||
|
|
@ -1 +0,0 @@
|
|||
/* Additional CSS for devfreezer. */
|
|
@ -1 +0,0 @@
|
|||
/* Additional JavaScript for devfreezer. */
|
|
@ -0,0 +1,19 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class Mygroup(horizon.PanelGroup):
|
||||
slug = "mygroup"
|
||||
name = _("Freezer")
|
||||
panels = ('freezerpanel',)
|
||||
|
||||
|
||||
class Freezer(horizon.Dashboard):
|
||||
name = _("Backup-aaS")
|
||||
slug = "freezer"
|
||||
panels = (Mygroup,) # Add your panels here.
|
||||
default_panel = 'freezerpanel' # Specify the slug of the default panel.
|
||||
|
||||
|
||||
horizon.register(Freezer)
|
|
@ -0,0 +1,31 @@
|
|||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import browsers
|
||||
|
||||
from openstack_dashboard.dashboards.freezer.freezerpanel import tables
|
||||
|
||||
|
||||
class ContainerBrowser(browsers.ResourceBrowser):
|
||||
name = "swift"
|
||||
verbose_name = _("Swift")
|
||||
navigation_table_class = tables.ContainersTable
|
||||
content_table_class = tables.ObjectsTable
|
||||
navigable_item_name = _("Container")
|
||||
navigation_kwarg_name = "container_name"
|
||||
content_kwarg_name = "subfolder_path"
|
||||
has_breadcrumb = True
|
||||
breadcrumb_url = "horizon:freezer:freezerpanel:index"
|
|
@ -0,0 +1,13 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.dashboards.freezer import dashboard
|
||||
|
||||
|
||||
class Freezerpanel(horizon.Panel):
|
||||
name = _("Admin")
|
||||
slug = "freezerpanel"
|
||||
|
||||
|
||||
dashboard.Freezer.register(Freezerpanel)
|
|
@ -0,0 +1,220 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
from horizon.utils.urlresolvers import reverse # noqa
|
||||
from openstack_dashboard import api
|
||||
from django.template import defaultfilters as filters
|
||||
from django.utils import http
|
||||
from django.utils import safestring
|
||||
|
||||
from django import template
|
||||
|
||||
from horizon import tables
|
||||
|
||||
LOADING_IMAGE = safestring.mark_safe('<img src="/static/dashboard/img/loading.gif" />')
|
||||
|
||||
def get_metadata(container):
|
||||
# If the metadata has not been loading, display a loading image
|
||||
if not get_metadata_loaded(container):
|
||||
return LOADING_IMAGE
|
||||
template_name = 'freezer/freezerpanel/_container_metadata.html'
|
||||
context = {"container": container}
|
||||
return template.loader.render_to_string(template_name, context)
|
||||
|
||||
def wrap_delimiter(name):
|
||||
if name and not name.endswith(api.swift.FOLDER_DELIMITER):
|
||||
return name + api.swift.FOLDER_DELIMITER
|
||||
return name
|
||||
|
||||
def get_container_link(container):
|
||||
return reverse("horizon:freezer:freezerpanel:index",
|
||||
args=(wrap_delimiter(container.name),))
|
||||
|
||||
def get_metadata_loaded(container):
|
||||
# Determine if metadata has been loaded if the attribute is already set.
|
||||
return hasattr(container, 'is_public') and container.is_public is not None
|
||||
|
||||
class ContainerAjaxUpdateRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
def get_data(self, request, container_name):
|
||||
container = api.swift.swift_get_container(request,
|
||||
container_name,
|
||||
with_data=False)
|
||||
return container
|
||||
|
||||
|
||||
class ContainersTable(tables.DataTable):
|
||||
METADATA_LOADED_CHOICES = (
|
||||
(False, None),
|
||||
(True, True),
|
||||
)
|
||||
name = tables.Column("name", link=get_container_link,
|
||||
verbose_name=_("Name"))
|
||||
bytes = tables.Column(lambda x: x.container_bytes_used if get_metadata_loaded(x) else LOADING_IMAGE
|
||||
, verbose_name=_("Size"))
|
||||
count = tables.Column(lambda x: x.container_object_count if get_metadata_loaded(x) else LOADING_IMAGE
|
||||
, verbose_name=_("Object count"))
|
||||
metadata = tables.Column(get_metadata,
|
||||
verbose_name=_("Container Details"),
|
||||
classes=('nowrap-col', ),)
|
||||
|
||||
metadata_loaded = tables.Column(get_metadata_loaded,
|
||||
status=True,
|
||||
status_choices=METADATA_LOADED_CHOICES,
|
||||
hidden=True)
|
||||
def get_object_id(self, container):
|
||||
return container.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
url = super(ContainersTable, self).get_absolute_url()
|
||||
return http.urlquote(url)
|
||||
|
||||
def get_full_url(self):
|
||||
"""Returns the encoded absolute URL path with its query string."""
|
||||
url = super(ContainersTable, self).get_full_url()
|
||||
return http.urlquote(url)
|
||||
|
||||
class Meta:
|
||||
name = "containers"
|
||||
verbose_name = _("Backups")
|
||||
row_class = ContainerAjaxUpdateRow
|
||||
status_columns = ['metadata_loaded', ]
|
||||
|
||||
|
||||
class ObjectFilterAction(tables.FilterAction):
|
||||
def _filtered_data(self, table, filter_string):
|
||||
request = table.request
|
||||
container = self.table.kwargs['container_name']
|
||||
subfolder = self.table.kwargs['subfolder_path']
|
||||
prefix = wrap_delimiter(subfolder) if subfolder else ''
|
||||
self.filtered_data = api.swift.swift_filter_objects(request,
|
||||
filter_string,
|
||||
container,
|
||||
prefix=prefix)
|
||||
return self.filtered_data
|
||||
|
||||
def filter_subfolders_data(self, table, objects, filter_string):
|
||||
data = self._filtered_data(table, filter_string)
|
||||
return [datum for datum in data if
|
||||
datum.content_type == "application/pseudo-folder"]
|
||||
|
||||
def filter_objects_data(self, table, objects, filter_string):
|
||||
data = self._filtered_data(table, filter_string)
|
||||
return [datum for datum in data if
|
||||
datum.content_type != "application/pseudo-folder"]
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
if self.table.kwargs.get('container_name', None):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_link_subfolder(subfolder):
|
||||
container_name = subfolder.container_name
|
||||
return reverse("horizon:freezer:freezerpanel:index",
|
||||
args=(wrap_delimiter(container_name),
|
||||
wrap_delimiter(subfolder.name)))
|
||||
|
||||
def sanitize_name(name):
|
||||
return name.split(api.swift.FOLDER_DELIMITER)[-1]
|
||||
|
||||
|
||||
def get_size(obj):
|
||||
if obj.bytes is None:
|
||||
return _("pseudo-folder")
|
||||
return filters.filesizeformat(obj.bytes)
|
||||
|
||||
class DeleteObject(tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete Object",
|
||||
u"Delete Objects",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Deleted Object",
|
||||
u"Deleted Objects",
|
||||
count
|
||||
)
|
||||
|
||||
name = "delete_object"
|
||||
allowed_data_types = ("objects", "subfolders",)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
obj = self.table.get_object_by_id(obj_id)
|
||||
container_name = obj.container_name
|
||||
datum_type = getattr(obj, self.table._meta.data_type_name, None)
|
||||
if datum_type == 'subfolders':
|
||||
obj_id = obj_id[(len(container_name) + 1):] + "/"
|
||||
api.swift.swift_delete_object(request, container_name, obj_id)
|
||||
|
||||
def get_success_url(self, request):
|
||||
url = super(DeleteObject, self).get_success_url(request)
|
||||
return http.urlquote(url)
|
||||
|
||||
|
||||
class DeleteMultipleObjects(DeleteObject):
|
||||
name = "delete_multiple_objects"
|
||||
|
||||
class CreatePseudoFolder(tables.FilterAction):
|
||||
def _filtered_data(self, table, filter_string):
|
||||
request = table.request
|
||||
container = self.table.kwargs['container_name']
|
||||
subfolder = self.table.kwargs['subfolder_path']
|
||||
prefix = wrap_delimiter(subfolder) if subfolder else ''
|
||||
self.filtered_data = api.swift.swift_filter_objects(request,
|
||||
filter_string,
|
||||
container,
|
||||
prefix=prefix)
|
||||
return self.filtered_data
|
||||
|
||||
def filter_subfolders_data(self, table, objects, filter_string):
|
||||
data = self._filtered_data(table, filter_string)
|
||||
return [datum for datum in data if
|
||||
datum.content_type == "application/pseudo-folder"]
|
||||
|
||||
def filter_objects_data(self, table, objects, filter_string):
|
||||
data = self._filtered_data(table, filter_string)
|
||||
return [datum for datum in data if
|
||||
datum.content_type != "application/pseudo-folder"]
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
if self.table.kwargs.get('container_name', None):
|
||||
return True
|
||||
return False
|
||||
|
||||
class ObjectsTable(tables.DataTable):
|
||||
name = tables.Column("name",
|
||||
link=get_link_subfolder,
|
||||
allowed_data_types=("subfolders",),
|
||||
verbose_name=_("Object Name"),
|
||||
filters=(sanitize_name,))
|
||||
|
||||
size = tables.Column(get_size, verbose_name=_('Size'))
|
||||
|
||||
class Meta:
|
||||
name = "objects"
|
||||
verbose_name = _("Objects")
|
||||
table_actions = (ObjectFilterAction,
|
||||
DeleteMultipleObjects)
|
||||
data_types = ("subfolders", "objects")
|
||||
browser_table = "content"
|
||||
footer = False
|
||||
|
||||
def get_absolute_url(self):
|
||||
url = super(ObjectsTable, self).get_absolute_url()
|
||||
return http.urlquote(url)
|
||||
|
||||
def get_full_url(self):
|
||||
"""Returns the encoded absolute URL path with its query string.
|
||||
|
||||
This is used for the POST action attribute on the form element
|
||||
wrapping the table. We use this method to persist the
|
||||
pagination marker.
|
||||
|
||||
"""
|
||||
url = super(ObjectsTable, self).get_full_url()
|
||||
return http.urlquote(url)
|
|
@ -4,36 +4,34 @@ from horizon import exceptions
|
|||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.dashboards.devfreezer.freezerweb import tables
|
||||
from openstack_dashboard.dashboards.freezer.freezerpanel import tables
|
||||
|
||||
|
||||
class InstanceTab(tabs.TableTab):
|
||||
name = _("Instances Tab")
|
||||
class ContainerTab(tabs.TableTab):
|
||||
name = _("Backups Tab")
|
||||
slug = "instances_tab"
|
||||
table_classes = (tables.InstancesTable,)
|
||||
table_classes = (tables.ContainersTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_instances_data(self):
|
||||
def get_containers_data(self):
|
||||
try:
|
||||
marker = self.request.GET.get(
|
||||
tables.InstancesTable._meta.pagination_param, None)
|
||||
|
||||
instances, self._has_more = api.nova.server_list(
|
||||
self.request,
|
||||
search_opts={'marker': marker, 'paginate': True})
|
||||
|
||||
return instances
|
||||
tables.ContainersTable._meta.pagination_param, None)
|
||||
containers, self._has_more = api.swift.swift_get_containers(self.request, marker)
|
||||
print '{}'.format(containers)
|
||||
return containers
|
||||
except Exception:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to get instances')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
class MypanelTabs(tabs.TabGroup):
|
||||
slug = "mypanel_tabs"
|
||||
tabs = (InstanceTab,)
|
||||
sticky = True
|
||||
tabs = (ContainerTab,)
|
||||
sticky = True
|
|
@ -0,0 +1,12 @@
|
|||
{% load i18n %}
|
||||
<ul>
|
||||
<li>{% trans "Object Count: " %}{{ container.container_object_count }}</li>
|
||||
<li>{% trans "Size: " %}{{ container.container_bytes_used|filesizeformat }}</li>
|
||||
<li>{% trans "Access: " %}
|
||||
{% if container.public_url %}
|
||||
<a href="{{ container.public_url }}">{% trans "Public" %}</a>
|
||||
{% else %}
|
||||
{% trans "Private" %}
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
|
@ -0,0 +1,15 @@
|
|||
{% extends 'freezer/base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Backups" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Freezer") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block mydashboard_main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ swift_browser.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,15 @@
|
|||
{% extends 'freezer/base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Backups" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Freezer") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block mydashboard_main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,7 +1,7 @@
|
|||
from horizon.test import helpers as test
|
||||
|
||||
|
||||
class FreezerwebTests(test.TestCase):
|
||||
# Unit tests for freezerweb.
|
||||
class MypanelTests(test.TestCase):
|
||||
# Unit tests for mypanel.
|
||||
def test_me(self):
|
||||
self.assertTrue(1 + 1 == 2)
|
|
@ -0,0 +1,15 @@
|
|||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from openstack_dashboard.dashboards.freezer.freezerpanel import views
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
|
||||
url(r'^((?P<container_name>.+?)/)?(?P<subfolder_path>(.+/)+)?$',
|
||||
views.BackupView.as_view(), name='index'),
|
||||
|
||||
url(r'^\?tab=mypanel_tabs_tab$',
|
||||
views.IndexView.as_view(), name='mypanel_tabs'),
|
||||
)
|
|
@ -0,0 +1,98 @@
|
|||
from horizon import browsers
|
||||
from horizon import tabs
|
||||
from horizon import exceptions
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.functional import cached_property # noqa
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.dashboards.freezer.freezerpanel \
|
||||
import tabs as freezer_tabs
|
||||
from openstack_dashboard.dashboards.freezer.freezerpanel \
|
||||
import browsers as freezer_browsers
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
tab_group_class = freezer_tabs.MypanelTabs
|
||||
template_name = 'freezer/freezerpanel/index.html'
|
||||
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
# Add data to the context here...
|
||||
return context
|
||||
|
||||
class BackupView(browsers.ResourceBrowserView):
|
||||
browser_class = freezer_browsers.ContainerBrowser
|
||||
template_name = "freezer/freezerpanel/container.html"
|
||||
|
||||
def get_containers_data(self):
|
||||
containers = []
|
||||
self._more = None
|
||||
marker = self.request.GET.get('marker', None)
|
||||
try:
|
||||
containers, self._more = api.swift.swift_get_containers(
|
||||
self.request, marker=marker)
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve container list.')
|
||||
exceptions.handle(self.request, msg)
|
||||
return containers
|
||||
|
||||
@cached_property
|
||||
def objects(self):
|
||||
"""Returns a list of objects given the subfolder's path.
|
||||
|
||||
The path is from the kwargs of the request.
|
||||
"""
|
||||
objects = []
|
||||
self._more = None
|
||||
marker = self.request.GET.get('marker', None)
|
||||
container_name = self.kwargs['container_name']
|
||||
subfolder = self.kwargs['subfolder_path']
|
||||
prefix = None
|
||||
if container_name:
|
||||
self.navigation_selection = True
|
||||
if subfolder:
|
||||
prefix = subfolder
|
||||
try:
|
||||
objects, self._more = api.swift.swift_get_objects(
|
||||
self.request,
|
||||
container_name,
|
||||
marker=marker,
|
||||
prefix=prefix)
|
||||
except Exception:
|
||||
self._more = None
|
||||
objects = []
|
||||
msg = _('Unable to retrieve object list.')
|
||||
exceptions.handle(self.request, msg)
|
||||
return objects
|
||||
|
||||
def is_subdir(self, item):
|
||||
content_type = "application/pseudo-folder"
|
||||
return getattr(item, "content_type", None) == content_type
|
||||
|
||||
def is_placeholder(self, item):
|
||||
object_name = getattr(item, "name", "")
|
||||
return object_name.endswith(api.swift.FOLDER_DELIMITER)
|
||||
|
||||
def get_objects_data(self):
|
||||
"""Returns a list of objects within the current folder."""
|
||||
filtered_objects = [item for item in self.objects
|
||||
if (not self.is_subdir(item) and
|
||||
not self.is_placeholder(item))]
|
||||
return filtered_objects
|
||||
|
||||
def get_subfolders_data(self):
|
||||
"""Returns a list of subfolders within the current folder."""
|
||||
filtered_objects = [item for item in self.objects
|
||||
if self.is_subdir(item)]
|
||||
return filtered_objects
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(BackupView, self).get_context_data(**kwargs)
|
||||
context['container_name'] = self.kwargs["container_name"]
|
||||
context['subfolders'] = []
|
||||
if self.kwargs["subfolder_path"]:
|
||||
(parent, slash, folder) = self.kwargs["subfolder_path"] \
|
||||
.strip('/').rpartition('/')
|
||||
while folder:
|
||||
path = "%s%s%s/" % (parent, slash, folder)
|
||||
context['subfolders'].insert(0, (folder, path))
|
||||
(parent, slash, folder) = parent.rpartition('/')
|
||||
return context
|
|
@ -0,0 +1 @@
|
|||
/* Additional CSS for mydashboard. */
|
|
@ -0,0 +1 @@
|
|||
/* Additional JavaScript for mydashboard. */
|
|
@ -6,6 +6,6 @@
|
|||
|
||||
{% block main %}
|
||||
{% include "horizon/_messages.html" %}
|
||||
{% block devfreezer_main %}{% endblock %}
|
||||
{% block mydashboard_main %}{% endblock %}
|
||||
{% endblock %}
|
||||
|
Loading…
Reference in New Issue