Skip to content

Commit 85f5ddc

Browse files
authored
Merge branch '2.x' into fix-upgrade-addpermission
2 parents a43616e + ab75336 commit 85f5ddc

8 files changed

Lines changed: 145 additions & 82 deletions

File tree

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Changelog
55
------------------
66

77
- #58 Fix ValueError: undefined property 'add_permission' on upgrade
8+
- #57 Compatibility with core#2835 (display Storage in navbar)
89
- #56 Fix UnicodeDecodeError on positions display in storage listing
910
- #55 Fix Unauthorized error when moving a container across storage
1011
- #54 Fix all storage containers reindexed when other add-ons are upgraded

src/senaite/storage/profiles/default/metadata.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
dependencies before installing this add-on own profile.
77
-->
88
<metadata>
9-
<version>2704</version>
9+
<version>2705</version>
1010
<!-- Be sure to install the following dependencies if not yet installed -->
1111
<dependencies>
1212
<dependency>profile-senaite.lims:default</dependency>

src/senaite/storage/setuphandlers.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020

2121
from Acquisition import aq_base
2222
from bika.lims import api
23-
from plone import api as ploneapi
24-
from plone.app.dexterity.behaviors.exclfromnav import IExcludeFromNavigation
2523
from Products.CMFCore.permissions import ModifyPortalContent
2624
from Products.DCWorkflow.Guard import Guard
2725
from senaite.core import permissions
@@ -36,7 +34,6 @@
3634
from senaite.storage.config import PRODUCT_NAME
3735
from senaite.storage.config import PROFILE_ID
3836

39-
4037
SITE_STRUCTURE = [
4138
# Tuples of (portal_type, obj_id, obj_title, parent_path, display_type)
4239
# If parent_path is None, assume folder_id is portal
@@ -429,21 +426,27 @@ def resolve_parent(parent_path):
429426

430427

431428
def display_in_nav(obj):
432-
"""Makes an object to be displayed in the navigation bar
429+
"""Makes an object and/or objects from the given portal type to be
430+
displayed in the navigation bar
433431
"""
434-
# Display in navigation
435-
registry_id = "plone.displayed_types"
436432
portal_type = api.get_portal_type(obj)
437-
to_display = ploneapi.portal.get_registry_record(registry_id, default=())
438-
if portal_type not in to_display:
439-
to_display = to_display + (portal_type, )
440-
ploneapi.portal.set_registry_record(registry_id, to_display)
441-
442-
nav_exclude = IExcludeFromNavigation(obj, None)
443-
if nav_exclude:
444-
nav_exclude.exclude_from_nav = False
445-
obj.reindexObject(idxs=["exclude_from_nav"])
446433

434+
# remove from senaite setup's sidebar_skip_types
435+
setup = api.get_senaite_setup()
436+
skip = setup.getSidebarSkipTypes()
437+
if skip and portal_type in skip:
438+
skip = tuple(pt for pt in skip if pt != portal_type)
439+
setup.setSidebarSkipTypes(skip)
440+
441+
# if a root folder, add to senaite setup's sidebar_folders
442+
setup = api.get_senaite_setup()
443+
portal = api.get_portal()
444+
if api.get_parent(obj) == portal:
445+
obj_id = api.get_id(obj)
446+
folders = setup.getSidebarFolders()
447+
if obj_id not in folders:
448+
folders += (obj_id, )
449+
setup.setSidebarFolders(folders)
447450

448451
def reindex_storage_structure(portal):
449452
"""Reindex storage structure

src/senaite/storage/tests/base.py

Lines changed: 10 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -19,88 +19,42 @@
1919
# Some rights reserved, see README and LICENSE.
2020

2121
import transaction
22-
import unittest2 as unittest
23-
from plone.app.testing import PLONE_FIXTURE
24-
from plone.app.testing import TEST_USER_ID
25-
from plone.app.testing import FunctionalTesting
26-
from plone.app.testing import PloneSandboxLayer
2722
from plone.app.testing import applyProfile
28-
from plone.app.testing import setRoles
23+
from plone.app.testing import FunctionalTesting
2924
from plone.testing import zope
25+
from senaite.core.tests.base import BaseTestCase
26+
from senaite.core.tests.layers import BaseLayer
3027

3128

32-
class SimpleTestLayer(PloneSandboxLayer):
33-
"""Setup Plone with installed AddOn only
34-
"""
35-
defaultBases = (PLONE_FIXTURE,)
29+
class SimpleTestLayer(BaseLayer):
3630

3731
def setUpZope(self, app, configurationContext):
3832
super(SimpleTestLayer, self).setUpZope(app, configurationContext)
3933

40-
import bika.lims
41-
import senaite.app.listing
42-
import senaite.app.spotlight
43-
import senaite.core
44-
import senaite.impress
45-
import senaite.lims
46-
import senaite.storage
47-
4834
# Load ZCML
49-
self.loadZCML(package=bika.lims)
50-
self.loadZCML(package=senaite.lims)
51-
self.loadZCML(package=senaite.core)
52-
self.loadZCML(package=senaite.app.listing)
53-
self.loadZCML(package=senaite.impress)
54-
self.loadZCML(package=senaite.app.spotlight)
35+
import senaite.storage
5536
self.loadZCML(package=senaite.storage)
5637

5738
# Install product and call its initialize() function
58-
zope.installProduct(app, "bika.lims")
59-
zope.installProduct(app, "senaite.lims")
60-
zope.installProduct(app, "senaite.core")
61-
zope.installProduct(app, "senaite.app.listing")
62-
zope.installProduct(app, "senaite.impress")
63-
zope.installProduct(app, "senaite.app.spotlight")
6439
zope.installProduct(app, "senaite.storage")
6540

6641
def setUpPloneSite(self, portal):
6742
super(SimpleTestLayer, self).setUpPloneSite(portal)
68-
applyProfile(portal, "senaite.core:default")
6943
applyProfile(portal, "senaite.storage:default")
7044
transaction.commit()
7145

7246

73-
###
74-
# Use for simple tests (w/o contents)
75-
###
76-
SIMPLE_FIXTURE = SimpleTestLayer()
47+
SIMPLE_TEST_LAYER_FIXTURE = SimpleTestLayer()
7748
SIMPLE_TESTING = FunctionalTesting(
78-
bases=(SIMPLE_FIXTURE, ),
49+
bases=(SIMPLE_TEST_LAYER_FIXTURE,),
7950
name="senaite.storage:SimpleTesting"
8051
)
8152

8253

83-
class SimpleTestCase(unittest.TestCase):
54+
class SimpleTestCase(BaseTestCase):
55+
"""Use for test cases which do not rely on demo data
56+
"""
8457
layer = SIMPLE_TESTING
8558

8659
def setUp(self):
8760
super(SimpleTestCase, self).setUp()
88-
89-
self.app = self.layer["app"]
90-
self.portal = self.layer["portal"]
91-
self.request = self.layer["request"]
92-
self.request["ACTUAL_URL"] = self.portal.absolute_url()
93-
setRoles(self.portal, TEST_USER_ID, ["LabManager", "Manager"])
94-
95-
96-
class FunctionalTestCase(unittest.TestCase):
97-
layer = SIMPLE_TESTING
98-
99-
def setUp(self):
100-
super(FunctionalTestCase, self).setUp()
101-
102-
self.app = self.layer["app"]
103-
self.portal = self.layer["portal"]
104-
self.request = self.layer["request"]
105-
self.request["ACTUAL_URL"] = self.portal.absolute_url()
106-
setRoles(self.portal, TEST_USER_ID, ["LabManager", "Member"])
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
Storage Navigation Bar Visibility
2+
==================================
3+
4+
This test ensures that the Storage folder is visible in the navigation bar
5+
after installation, in accordance with the new sidebar navigation system
6+
introduced in senaite.core.
7+
8+
Running this test from the buildout directory:
9+
10+
bin/test -m senaite.storage -t StorageNavigation
11+
12+
13+
Test Setup
14+
..........
15+
16+
Needed Imports:
17+
18+
>>> from bika.lims import api
19+
>>> from plone.app.testing import setRoles
20+
>>> from plone.app.testing import TEST_USER_ID
21+
22+
Variables:
23+
24+
>>> portal = self.portal
25+
>>> request = self.request
26+
>>> setup = api.get_senaite_setup()
27+
>>> storage = portal.senaite_storage
28+
29+
Assign default roles for the user to test with:
30+
31+
>>> setRoles(portal, TEST_USER_ID, ['LabManager',])
32+
33+
34+
Storage Folder Navigation Visibility
35+
-------------------------------------
36+
37+
After installation, the Storage folder should be visible in the navigation bar.
38+
This is configured through two mechanisms:
39+
40+
1. The portal type "StorageRootFolder" should NOT be in SENAITE Setup's sidebar_skip_types
41+
(types in this list are excluded from the sidebar):
42+
43+
>>> sidebar_skip_types = setup.getSidebarSkipTypes()
44+
>>> sidebar_skip_types is not None
45+
True
46+
>>> "StorageRootFolder" not in sidebar_skip_types
47+
True
48+
49+
2. Since the storage folder is a root folder (direct child of portal), its ID
50+
should be in SENAITE Setup's sidebar_folders:
51+
52+
>>> sidebar_folders = setup.getSidebarFolders()
53+
>>> sidebar_folders is not None
54+
True
55+
>>> "senaite_storage" in sidebar_folders
56+
True
57+
58+
59+
Verify Storage Folder Properties
60+
---------------------------------
61+
62+
The storage folder should exist and be properly configured:
63+
64+
>>> storage is not None
65+
True
66+
67+
>>> api.get_portal_type(storage)
68+
'StorageRootFolder'
69+
70+
>>> api.get_id(storage)
71+
'senaite_storage'
72+
73+
>>> storage.Title()
74+
'Sample storage'
75+
76+
The storage folder should be a direct child of the portal:
77+
78+
>>> api.get_parent(storage) == portal
79+
True

src/senaite/storage/tests/test_textual_doctests.py renamed to src/senaite/storage/tests/test_doctests.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,33 @@
2121
import doctest
2222
from os.path import join
2323

24-
from pkg_resources import resource_listdir
25-
2624
import unittest2 as unittest
27-
from senaite.storage.config import PRODUCT_NAME
25+
from pkg_resources import resource_listdir
26+
from senaite.storage import PRODUCT_NAME
2827
from senaite.storage.tests.base import SimpleTestCase
2928
from Testing import ZopeTestCase as ztc
3029

31-
rst_filenames = [f for f in resource_listdir(PRODUCT_NAME, "tests/doctests")
32-
if f.endswith(".rst")]
33-
34-
doctests = [join("doctests", filename) for filename in rst_filenames]
35-
30+
# Option flags for doctests
3631
flags = doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_NDIFF
3732

3833

3934
def test_suite():
4035
suite = unittest.TestSuite()
41-
for doctestfile in doctests:
36+
for doctest_file in get_doctest_files():
4237
suite.addTests([
4338
ztc.ZopeDocFileSuite(
44-
doctestfile,
39+
doctest_file,
4540
test_class=SimpleTestCase,
4641
optionflags=flags
4742
)
4843
])
4944
return suite
45+
46+
47+
def get_doctest_files():
48+
"""
49+
Return the available doctest files for this package.
50+
"""
51+
files = resource_listdir(PRODUCT_NAME, "tests/doctests")
52+
files = filter(lambda name: name.endswith(".rst"), files)
53+
return map(lambda name: join("doctests", name), files)

src/senaite/storage/upgrade/v02_07_000.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from senaite.storage import PRODUCT_NAME
3030
from senaite.storage import logger
3131
from senaite.storage.catalog import STORAGE_CATALOG
32+
from senaite.storage.setuphandlers import display_in_nav
3233
from zope.component import getMultiAdapter
3334

3435
version = "2.7.0"
@@ -415,3 +416,12 @@ def migrate_storage_root_folder_to_dx(tool):
415416
migrator.copy_id(src, target)
416417

417418
logger.info("Convert Storage Root Folder to Dexterity [DONE]")
419+
420+
421+
def display_storage_navbar(tool):
422+
"""Displays the storage's root folder in the navigation bar
423+
"""
424+
logger.info("Display storage in navigation bar ...")
425+
portal = api.get_portal()
426+
display_in_nav(portal.senaite_storage)
427+
logger.info("Display storage in navigation bar [DONE]")

src/senaite/storage/upgrade/v02_07_000.zcml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22
xmlns="http://namespaces.zope.org/zope"
33
xmlns:genericsetup="http://namespaces.zope.org/genericsetup">
44

5+
<genericsetup:upgradeStep
6+
title="Display Storage in navigation bar"
7+
description="
8+
This upgrade step makes the product compatible with senaite.core#2835
9+
by adding the Storage folder to the setup field “Sidebar displayed
10+
portal types”, thereby displaying the Storage link in the left-hand
11+
navigation bar."
12+
source="2704"
13+
destination="2705"
14+
handler=".v02_07_000.display_storage_navbar"
15+
profile="senaite.storage:default"/>
16+
517
<genericsetup:upgradeStep
618
title="SENAITE.STORAGE 2.7.0: Migrate Storage Root Folder to DX"
719
description="Migrate the Storage Root Folder to Dexterity"

0 commit comments

Comments
 (0)