Skip to content

Commit

Permalink
Allow use of environment variables for create-site script (Fixes #55)
Browse files Browse the repository at this point in the history
  • Loading branch information
ericof committed Jan 20, 2025
1 parent 5df801d commit e81264b
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 73 deletions.
1 change: 1 addition & 0 deletions backend/news/55.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow use of environment variables for create-site script [@ericof]
105 changes: 49 additions & 56 deletions backend/scripts/create_site.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from AccessControl.SecurityManagement import newSecurityManager
from pathlib import Path
from kitconcept.intranet.utils.scripts import asbool
from kitconcept.intranet.utils.scripts import parse_answers
from plone.distribution.api import site as site_api
from Testing.makerequest import makerequest

import json
import logging
import os
import transaction
Expand All @@ -23,21 +24,6 @@

SCRIPT_DIR = Path().cwd() / "scripts"

truthy = frozenset(("t", "true", "y", "yes", "on", "1"))


def asbool(s):
"""Return the boolean value ``True`` if the case-lowered value of string
input ``s`` is a :term:`truthy string`. If ``s`` is already one of the
boolean values ``True`` or ``False``, return it."""
if s is None:
return False
if isinstance(s, bool):
return s
s = str(s).strip()
return s.lower() in truthy


app = makerequest(globals()["app"])

request = app.REQUEST
Expand All @@ -47,53 +33,60 @@ def asbool(s):
newSecurityManager(None, admin)


def get_answers_file() -> Path:
filename = f"{ANSWERS}.json"
def get_answers_file(filename: str) -> Path:
return SCRIPT_DIR / filename


def parse_answers(answers_file: Path, site_id: str = "") -> dict:
answers = json.loads(answers_file.read_text())
if "distribution" not in answers:
# This is a bug in plone.distribution and should be fixed there
answers["distribution"] = DISTRIBUTION
if site_id:
answers["site_id"] = site_id
return answers


# VARS
DISTRIBUTION = os.getenv("DISTRIBUTION", "kitconcept-intranet")
SITE_ID = os.getenv("SITE_ID") # if set, this overrides the value in ANSWERS
ANSWERS = os.getenv("ANSWERS", "default")
ANSWERS_FILE = os.getenv("ANSWERS", "default.json")
DELETE_EXISTING = asbool(os.getenv("DELETE_EXISTING"))
# ANSWERS OVERRIDE
ANSWERS = {
"site_id": os.getenv("SITE_ID"),
"title": os.getenv("SITE_TITLE"),
"description": os.getenv("SITE_DESCRIPTION"),
"default_language": os.getenv("SITE_DEFAULT_LANGUAGE"),
"portal_timezone": os.getenv("SITE_PORTAL_TIMEZONE"),
"workflow": os.getenv("SITE_WORKFLOW"),
"setup_content": os.getenv("SITE_SETUP_CONTENT", "true"),
}


def main():
# Load site creation parameters
answers_file = get_answers_file(ANSWERS_FILE)
answers = parse_answers(answers_file, ANSWERS)
if "distribution" not in answers:
answers["distribution"] = DISTRIBUTION
site_id = answers["site_id"]

# Load site creation parameters
answers_file = get_answers_file()
answers = parse_answers(answers_file, SITE_ID)
site_id = answers["site_id"]


logger.info(f"Creating a new Plone site @ {site_id}")
logger.info(f" - Using the {DISTRIBUTION} distribution and answers from {answers_file}")


if site_id in app.objectIds() and DELETE_EXISTING:
app.manage_delObjects([site_id])
transaction.commit()
app._p_jar.sync()
logger.info(f" - Deleted existing site with id {site_id}")
else:
logger.info(f"Creating a new Plone site @ {site_id}")
logger.info(
f" - Stopping site creation, as there is already a site with id {site_id} "
"at the instance. Set DELETE_EXISTING=1 to delete the existing site before "
"creating a new one."
f" - Using the {DISTRIBUTION} distribution and answers from {answers_file}"
)

if site_id not in app.objectIds():
site = site_api._create_site(
context=app, distribution_name=DISTRIBUTION, answers=answers
)
transaction.commit()
app._p_jar.sync()
logger.info(" - Site created!")
if site_id in app.objectIds():
if DELETE_EXISTING:
app.manage_delObjects([site_id])
transaction.commit()
app._p_jar.sync()
logger.info(f" - Deleted existing site with id {site_id}")
else:
logger.info(
" - Stopping site creation, as there is already a site with id "
f"{site_id} at the instance. Set DELETE_EXISTING=1 to delete "
"the existing site before creating a new one."
)

if site_id not in app.objectIds():
site = site_api._create_site(
context=app, distribution_name=DISTRIBUTION, answers=answers
)
transaction.commit()
app._p_jar.sync()
logger.info(" - Site created!")


if __name__ == "__main__":
main()
30 changes: 13 additions & 17 deletions backend/src/kitconcept/intranet/behaviors/theming.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,19 @@
"blocks_layout": {},
}

BLOCKS_SCHEMA = json.dumps(
{
"type": "object",
"properties": {
"blocks": {"type": "object"},
"blocks_layout": {"type": "object"},
},
}
)

FONT_VOCABULARY = SimpleVocabulary(
[
SimpleTerm(value="default", title=_("Default FZJ font")),
SimpleTerm(value="impact-arialNarrow", title=_("Impact / Arial Narrow")),
SimpleTerm(value="georgia-lucidaSans", title=_("Georgia / Lucida Sans")),
]
)
BLOCKS_SCHEMA = json.dumps({
"type": "object",
"properties": {
"blocks": {"type": "object"},
"blocks_layout": {"type": "object"},
},
})

FONT_VOCABULARY = SimpleVocabulary([
SimpleTerm(value="default", title=_("Default FZJ font")),
SimpleTerm(value="impact-arialNarrow", title=_("Impact / Arial Narrow")),
SimpleTerm(value="georgia-lucidaSans", title=_("Georgia / Lucida Sans")),
])


@provider(IFormFieldProvider)
Expand Down
Empty file.
31 changes: 31 additions & 0 deletions backend/src/kitconcept/intranet/utils/scripts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from pathlib import Path

import json


truthy = frozenset(("t", "true", "y", "yes", "on", "1"))


def asbool(s):
"""Return the boolean value ``True`` if the case-lowered value of string
input ``s`` is a :term:`truthy string`. If ``s`` is already one of the
boolean values ``True`` or ``False``, return it."""
if s is None:
return False
if isinstance(s, bool):
return s
s = str(s).strip()
return s.lower() in truthy


def parse_answers(answers_file: Path, answers_env: dict) -> dict:
answers = json.loads(answers_file.read_text())
for key in answers:
env_value = answers_env.get(key, "")
if key == "setup_content" and env_value.strip():
env_value = asbool(env_value)
elif not env_value:
continue
# Override answers_file value
answers[key] = env_value
return answers
9 changes: 9 additions & 0 deletions backend/tests/utils/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"site_id": "Plone",
"title": "Plone Intranet by kitconcept",
"description": "A Plone Intranet distribution provided by kitconcept GmbH",
"default_language": "en",
"portal_timezone": "Europe/Berlin",
"workflow": "public",
"setup_content": true
}
38 changes: 38 additions & 0 deletions backend/tests/utils/test_scripts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from kitconcept.intranet.utils.scripts import parse_answers
from pathlib import Path

import pytest


@pytest.fixture
def answers_file():
path = Path(__file__).parent / "default.json"
return path


@pytest.mark.parametrize(
"key,value,expected",
(
("site_id", "", "Plone"),
("site_id", "Site", "Site"),
("title", "Foo Bar", "Foo Bar"),
("description", "A new site", "A new site"),
(
"description",
"",
"A Plone Intranet distribution provided by kitconcept GmbH",
),
("default_language", "", "en"),
("default_language", "de", "de"),
("portal_timezone", "", "Europe/Berlin"),
("portal_timezone", "UTC", "UTC"),
("workflow", "", "public"),
("workflow", "private", "private"),
("setup_content", "", True),
("setup_content", "f", False),
),
)
def test_parse_answers(answers_file, key: str, value: str, expected: str | bool):
answers = {key: value}
result = parse_answers(answers_file, answers)
assert result[key] == expected

0 comments on commit e81264b

Please sign in to comment.