|
@@ -17,7 +17,7 @@ from jinja2.exceptions import TemplateError
|
|
|
|
|
|
|
|
from circuits.models import Circuit, CircuitTermination
|
|
from circuits.models import Circuit, CircuitTermination
|
|
|
from extras.views import ObjectConfigContextView
|
|
from extras.views import ObjectConfigContextView
|
|
|
-from ipam.models import ASN, IPAddress, Prefix, VLAN, VLANGroup
|
|
|
|
|
|
|
+from ipam.models import ASN, IPAddress, VLANGroup
|
|
|
from ipam.tables import InterfaceVLANTable
|
|
from ipam.tables import InterfaceVLANTable
|
|
|
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
|
|
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
|
|
|
from netbox.views import generic
|
|
from netbox.views import generic
|
|
@@ -27,7 +27,9 @@ from utilities.paginator import EnhancedPaginator, get_paginate_count
|
|
|
from utilities.permissions import get_permission_for_model
|
|
from utilities.permissions import get_permission_for_model
|
|
|
from utilities.query import count_related
|
|
from utilities.query import count_related
|
|
|
from utilities.query_functions import CollateAsChar
|
|
from utilities.query_functions import CollateAsChar
|
|
|
-from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
|
|
|
|
|
|
|
+from utilities.views import (
|
|
|
|
|
+ GetRelatedModelsMixin, GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
|
|
|
|
|
+)
|
|
|
from virtualization.filtersets import VirtualMachineFilterSet
|
|
from virtualization.filtersets import VirtualMachineFilterSet
|
|
|
from virtualization.models import VirtualMachine
|
|
from virtualization.models import VirtualMachine
|
|
|
from virtualization.tables import VirtualMachineTable
|
|
from virtualization.tables import VirtualMachineTable
|
|
@@ -226,19 +228,21 @@ class RegionListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(Region)
|
|
@register_model_view(Region)
|
|
|
-class RegionView(generic.ObjectView):
|
|
|
|
|
|
|
+class RegionView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = Region.objects.all()
|
|
queryset = Region.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
regions = instance.get_descendants(include_self=True)
|
|
regions = instance.get_descendants(include_self=True)
|
|
|
- related_models = (
|
|
|
|
|
- (Site.objects.restrict(request.user, 'view').filter(region__in=regions), 'region_id'),
|
|
|
|
|
- (Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
|
|
|
|
|
- (Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
|
|
|
|
|
- )
|
|
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(
|
|
|
|
|
+ request,
|
|
|
|
|
+ regions,
|
|
|
|
|
+ extra=(
|
|
|
|
|
+ (Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
|
|
|
|
|
+ (Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -306,19 +310,21 @@ class SiteGroupListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(SiteGroup)
|
|
@register_model_view(SiteGroup)
|
|
|
-class SiteGroupView(generic.ObjectView):
|
|
|
|
|
|
|
+class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = SiteGroup.objects.all()
|
|
queryset = SiteGroup.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
groups = instance.get_descendants(include_self=True)
|
|
groups = instance.get_descendants(include_self=True)
|
|
|
- related_models = (
|
|
|
|
|
- (Site.objects.restrict(request.user, 'view').filter(group__in=groups), 'group_id'),
|
|
|
|
|
- (Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
|
|
|
|
|
- (Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
|
|
|
|
|
- )
|
|
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(
|
|
|
|
|
+ request,
|
|
|
|
|
+ groups,
|
|
|
|
|
+ extra=(
|
|
|
|
|
+ (Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
|
|
|
|
|
+ (Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -380,31 +386,25 @@ class SiteListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(Site)
|
|
@register_model_view(Site)
|
|
|
-class SiteView(generic.ObjectView):
|
|
|
|
|
|
|
+class SiteView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = Site.objects.prefetch_related('tenant__group')
|
|
queryset = Site.objects.prefetch_related('tenant__group')
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
- related_models = (
|
|
|
|
|
- # DCIM
|
|
|
|
|
- (Location.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
|
|
|
|
|
- (Rack.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
|
|
|
|
|
- (Device.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
|
|
|
|
|
- # Virtualization
|
|
|
|
|
- (VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance), 'site_id'),
|
|
|
|
|
- # IPAM
|
|
|
|
|
- (Prefix.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
|
|
|
|
|
- (ASN.objects.restrict(request.user, 'view').filter(sites=instance), 'site_id'),
|
|
|
|
|
- (VLANGroup.objects.restrict(request.user, 'view').filter(
|
|
|
|
|
- scope_type=ContentType.objects.get_for_model(Site),
|
|
|
|
|
- scope_id=instance.pk
|
|
|
|
|
- ), 'site'),
|
|
|
|
|
- (VLAN.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
|
|
|
|
|
- # Circuits
|
|
|
|
|
- (Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(), 'site_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(
|
|
|
|
|
+ request,
|
|
|
|
|
+ instance,
|
|
|
|
|
+ [CableTermination, CircuitTermination],
|
|
|
|
|
+ (
|
|
|
|
|
+ (VLANGroup.objects.restrict(request.user, 'view').filter(
|
|
|
|
|
+ scope_type=ContentType.objects.get_for_model(Site),
|
|
|
|
|
+ scope_id=instance.pk
|
|
|
|
|
+ ), 'site'),
|
|
|
|
|
+ (ASN.objects.restrict(request.user, 'view').filter(sites=instance), 'site_id'),
|
|
|
|
|
+ (Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(),
|
|
|
|
|
+ 'site_id'),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -466,18 +466,13 @@ class LocationListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(Location)
|
|
@register_model_view(Location)
|
|
|
-class LocationView(generic.ObjectView):
|
|
|
|
|
|
|
+class LocationView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = Location.objects.all()
|
|
queryset = Location.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
locations = instance.get_descendants(include_self=True)
|
|
locations = instance.get_descendants(include_self=True)
|
|
|
- related_models = (
|
|
|
|
|
- (Rack.objects.restrict(request.user, 'view').filter(location__in=locations), 'location_id'),
|
|
|
|
|
- (Device.objects.restrict(request.user, 'view').filter(location__in=locations), 'location_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(request, locations, [CableTermination]),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -541,16 +536,12 @@ class RackRoleListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(RackRole)
|
|
@register_model_view(RackRole)
|
|
|
-class RackRoleView(generic.ObjectView):
|
|
|
|
|
|
|
+class RackRoleView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = RackRole.objects.all()
|
|
queryset = RackRole.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
- related_models = (
|
|
|
|
|
- (Rack.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(request, instance),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -655,15 +646,10 @@ class RackElevationListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(Rack)
|
|
@register_model_view(Rack)
|
|
|
-class RackView(generic.ObjectView):
|
|
|
|
|
|
|
+class RackView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
|
|
queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
- related_models = (
|
|
|
|
|
- (Device.objects.restrict(request.user, 'view').filter(rack=instance), 'rack_id'),
|
|
|
|
|
- (PowerFeed.objects.restrict(request.user).filter(rack=instance), 'rack_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
|
|
peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
|
|
|
|
|
|
|
|
if instance.location:
|
|
if instance.location:
|
|
@@ -679,7 +665,7 @@ class RackView(generic.ObjectView):
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(request, instance, [CableTermination]),
|
|
|
'next_rack': next_rack,
|
|
'next_rack': next_rack,
|
|
|
'prev_rack': prev_rack,
|
|
'prev_rack': prev_rack,
|
|
|
'svg_extra': svg_extra,
|
|
'svg_extra': svg_extra,
|
|
@@ -838,19 +824,12 @@ class ManufacturerListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(Manufacturer)
|
|
@register_model_view(Manufacturer)
|
|
|
-class ManufacturerView(generic.ObjectView):
|
|
|
|
|
|
|
+class ManufacturerView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = Manufacturer.objects.all()
|
|
queryset = Manufacturer.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
- related_models = (
|
|
|
|
|
- (DeviceType.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
|
|
|
|
|
- (ModuleType.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
|
|
|
|
|
- (InventoryItem.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
|
|
|
|
|
- (Platform.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(request, instance, [InventoryItemTemplate]),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -912,16 +891,16 @@ class DeviceTypeListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(DeviceType)
|
|
@register_model_view(DeviceType)
|
|
|
-class DeviceTypeView(generic.ObjectView):
|
|
|
|
|
|
|
+class DeviceTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = DeviceType.objects.all()
|
|
queryset = DeviceType.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
- related_models = (
|
|
|
|
|
- (Device.objects.restrict(request.user).filter(device_type=instance), 'device_type_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(request, instance, omit=[
|
|
|
|
|
+ ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate,
|
|
|
|
|
+ InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate,
|
|
|
|
|
+ RearPortTemplate,
|
|
|
|
|
+ ]),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1151,16 +1130,16 @@ class ModuleTypeListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(ModuleType)
|
|
@register_model_view(ModuleType)
|
|
|
-class ModuleTypeView(generic.ObjectView):
|
|
|
|
|
|
|
+class ModuleTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = ModuleType.objects.all()
|
|
queryset = ModuleType.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
- related_models = (
|
|
|
|
|
- (Module.objects.restrict(request.user).filter(module_type=instance), 'module_type_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(request, instance, omit=[
|
|
|
|
|
+ ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate,
|
|
|
|
|
+ InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate,
|
|
|
|
|
+ RearPortTemplate,
|
|
|
|
|
+ ]),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1711,17 +1690,12 @@ class DeviceRoleListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(DeviceRole)
|
|
@register_model_view(DeviceRole)
|
|
|
-class DeviceRoleView(generic.ObjectView):
|
|
|
|
|
|
|
+class DeviceRoleView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = DeviceRole.objects.all()
|
|
queryset = DeviceRole.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
- related_models = (
|
|
|
|
|
- (Device.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
|
|
|
|
|
- (VirtualMachine.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(request, instance),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1775,17 +1749,12 @@ class PlatformListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(Platform)
|
|
@register_model_view(Platform)
|
|
|
-class PlatformView(generic.ObjectView):
|
|
|
|
|
|
|
+class PlatformView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = Platform.objects.all()
|
|
queryset = Platform.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
- related_models = (
|
|
|
|
|
- (Device.objects.restrict(request.user, 'view').filter(platform=instance), 'platform_id'),
|
|
|
|
|
- (VirtualMachine.objects.restrict(request.user, 'view').filter(platform=instance), 'platform_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(request, instance),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2157,22 +2126,12 @@ class ModuleListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(Module)
|
|
@register_model_view(Module)
|
|
|
-class ModuleView(generic.ObjectView):
|
|
|
|
|
|
|
+class ModuleView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = Module.objects.all()
|
|
queryset = Module.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
- related_models = (
|
|
|
|
|
- (Interface.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
|
|
|
- (ConsolePort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
|
|
|
- (ConsoleServerPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
|
|
|
- (PowerPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
|
|
|
- (PowerOutlet.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
|
|
|
- (FrontPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
|
|
|
- (RearPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(request, instance),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -3552,16 +3511,12 @@ class PowerPanelListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(PowerPanel)
|
|
@register_model_view(PowerPanel)
|
|
|
-class PowerPanelView(generic.ObjectView):
|
|
|
|
|
|
|
+class PowerPanelView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = PowerPanel.objects.all()
|
|
queryset = PowerPanel.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
- related_models = (
|
|
|
|
|
- (PowerFeed.objects.restrict(request.user).filter(power_panel=instance), 'power_panel_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(request, instance),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -3665,16 +3620,18 @@ class VirtualDeviceContextListView(generic.ObjectListView):
|
|
|
|
|
|
|
|
|
|
|
|
|
@register_model_view(VirtualDeviceContext)
|
|
@register_model_view(VirtualDeviceContext)
|
|
|
-class VirtualDeviceContextView(generic.ObjectView):
|
|
|
|
|
|
|
+class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView):
|
|
|
queryset = VirtualDeviceContext.objects.all()
|
|
queryset = VirtualDeviceContext.objects.all()
|
|
|
|
|
|
|
|
def get_extra_context(self, request, instance):
|
|
def get_extra_context(self, request, instance):
|
|
|
- related_models = (
|
|
|
|
|
- (Interface.objects.restrict(request.user, 'view').filter(vdcs__in=[instance]), 'vdc_id'),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
return {
|
|
return {
|
|
|
- 'related_models': related_models,
|
|
|
|
|
|
|
+ 'related_models': self.get_related_models(
|
|
|
|
|
+ request,
|
|
|
|
|
+ instance,
|
|
|
|
|
+ extra=(
|
|
|
|
|
+ (Interface.objects.restrict(request.user, 'view').filter(vdcs__in=[instance]), 'vdc_id'),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|