Skip to content
Open
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
56 changes: 56 additions & 0 deletions tabcmd/commands/site/edit_site_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@
from tabcmd.execution.logger_config import log


def _str_to_bool(value):
"""Coerce a 'true'/'false' string (from argparse choices) to a real bool.

TSC SiteItem boolean setters are guarded by @property_is_boolean and reject
strings, so we must convert before assignment.
"""
if value is None:
return None
return str(value).lower() == "true"


class EditSiteCommand(Server):
"""
Command to change the name of a site or its web folder name. Users can also use this command to allow or deny
Expand All @@ -24,6 +35,7 @@ def define_args(edit_site_parser):
args_group.add_argument("--site-name", default=None, dest="new_site_name", help=_("editsite.options.site-name"))
set_common_site_args(args_group)
set_site_status_arg(args_group)
set_edit_site_only_args(args_group)

@classmethod
def run_command(cls, args):
Expand All @@ -41,6 +53,50 @@ def run_command(cls, args):
site_item.storage_quota = args.storage_quota
if args.status:
site_item.state = args.status

# New flags for W-tabcmd-437. Every attribute is only touched when the
# corresponding argparse dest is not None, so an unpassed flag leaves
# the server-side setting unchanged.
if getattr(args, "guest_access_enabled", None) is not None:
site_item.guest_access_enabled = _str_to_bool(args.guest_access_enabled)

if getattr(args, "cache_warmup_enabled", None) is not None:
site_item.cache_warmup_enabled = args.cache_warmup_enabled

if getattr(args, "subscription_email", None) is not None:
# Matches Classic: set the value AND toggle the *_enabled flag.
# An empty string clears the value and disables the feature.
# NOTE: TSC RequestFactory currently lowercases this value (bug to
# file upstream); user@Domain.com will be sent as user@domain.com.
site_item.custom_subscription_email = args.subscription_email
site_item.custom_subscription_email_enabled = bool(args.subscription_email)

if getattr(args, "subscription_footer", None) is not None:
# Same two-attribute wiring as subscription_email. TSC also
# lowercases this string; tracked as an upstream TSC bug.
site_item.custom_subscription_footer = args.subscription_footer
site_item.custom_subscription_footer_enabled = bool(args.subscription_footer)

if getattr(args, "web_extraction_enabled", None) is not None:
site_item.web_extraction_enabled = _str_to_bool(args.web_extraction_enabled)

if getattr(args, "allow_subscriptions", None) is not None:
# Inverted: REST API exposes this as disableSubscriptions.
site_item.disable_subscriptions = not args.allow_subscriptions

if getattr(args, "allow_web_authoring", None) is not None:
site_item.authoring_enabled = args.allow_web_authoring

if getattr(args, "allow_mobile_snapshots", None) is not None:
site_item.sheet_image_enabled = args.allow_mobile_snapshots

if getattr(args, "use_default_time_zone", None):
# Only set use_default_time_zone; do NOT set time_zone -- argparse
# already enforces mutual exclusion between the two flags.
site_item.use_default_time_zone = True
elif getattr(args, "time_zone", None) is not None:
site_item.time_zone = args.time_zone

try:
logger.info(_("editsite.status").format(site_item.name))
server.sites.update(site_item)
Expand Down
122 changes: 122 additions & 0 deletions tabcmd/execution/global_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,128 @@ def set_site_status_arg(parser):
return parser


# only in edit-site: flags that map onto TSC SiteItem attributes but are not
# currently wired in create_site_command. Kept out of set_common_site_args so
# createsite --help does not list options its run_command ignores. See #437.
def set_edit_site_only_args(parser):
# guest access: Classic passes true/false as a string value
parser.add_argument(
"-g",
"--guest-access-enabled",
choices=["true", "false"],
help=_("editsite.options.guest_access_enabled"),
)

# cache warmup: paired positive/negative boolean, default None so an
# unpassed flag is a no-op (leaves the server setting unchanged)
cache_warmup_group = parser.add_mutually_exclusive_group()
cache_warmup_group.add_argument(
"--cache-warmup",
dest="cache_warmup_enabled",
action="store_true",
default=None,
help=_("editsite.options.cache_warmup"),
)
cache_warmup_group.add_argument(
"--no-cache-warmup",
dest="cache_warmup_enabled",
action="store_false",
default=None,
help=_("editsite.options.no_cache_warmup"),
)

# subscription email: also flips custom_subscription_email_enabled
parser.add_argument(
"-e",
"--subscription-email",
default=None,
help=_("editsite.options.subscription_email"),
)

# subscription footer: also flips custom_subscription_footer_enabled.
# -f matches Classic tabcmd editsite parity; editsite does not use -f for --filename.
parser.add_argument(
"-f",
"--subscription-footer",
default=None,
help=_("editsite.options.subscription_footer"),
)

# web extraction: string value, coerced to bool at wire time
parser.add_argument(
"--web-extraction-enabled",
choices=["true", "false"],
help=_("editsite.options.web_extraction_enabled"),
)

# allow subscriptions: inverted onto disable_subscriptions in run_command
allow_subs_group = parser.add_mutually_exclusive_group()
allow_subs_group.add_argument(
"--allow-subscriptions",
dest="allow_subscriptions",
action="store_true",
default=None,
help=_("editsite.options.allow_subscriptions"),
)
allow_subs_group.add_argument(
"--no-allow-subscriptions",
dest="allow_subscriptions",
action="store_false",
default=None,
help=_("editsite.options.no_allow_subscriptions"),
)

# allow web authoring: direct mapping onto authoring_enabled
authoring_group = parser.add_mutually_exclusive_group()
authoring_group.add_argument(
"--allow-web-authoring",
dest="allow_web_authoring",
action="store_true",
default=None,
help=_("editsite.options.allow_web_authoring"),
)
authoring_group.add_argument(
"--no-allow-web-authoring",
dest="allow_web_authoring",
action="store_false",
default=None,
help=_("editsite.options.no_allow_web_authoring"),
)

# allow mobile snapshots: maps onto sheet_image_enabled (NOT
# mobile_biometrics_enabled, which is a separate biometric-auth feature)
mobile_group = parser.add_mutually_exclusive_group()
mobile_group.add_argument(
"--allow-mobile-snapshots",
dest="allow_mobile_snapshots",
action="store_true",
default=None,
help=_("editsite.options.allow_mobile_snapshots"),
)
mobile_group.add_argument(
"--no-allow-mobile-snapshots",
dest="allow_mobile_snapshots",
action="store_false",
default=None,
help=_("editsite.options.no_allow_mobile_snapshots"),
)

# time zone: string value, mutually exclusive with --use-default-time-zone
tz_group = parser.add_mutually_exclusive_group()
tz_group.add_argument(
"--time-zone",
default=None,
help=_("editsite.options.time_zone"),
)
tz_group.add_argument(
"--use-default-time-zone",
action="store_true",
default=None,
help=_("editsite.options.use_default_time_zone"),
)
return parser


# mismatched arguments: createsite says --url, editsite says --site-id
# just let both commands use either of them
def set_site_id_args(parser):
Expand Down
14 changes: 14 additions & 0 deletions tabcmd/locales/en/tabcmd_messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ editsite.options.site-name=Display name of the site
editsite.options.status=Change availability of site. Must be either ''{0}'' or ''{1}''
editsite.options.extract_encryption_mode=Extract encryption mode: disabled, enabled, or enforced. If not set, then it is not changed on the server.
editsite.options.run_now_enabled=Allow [or deny] Run Now option for this site. Default is set to allow Run Now
editsite.options.guest_access_enabled=Allow or deny guest access to the site. Value must be ''true'' or ''false''
editsite.options.cache_warmup=Enable cache warmup for the site
editsite.options.no_cache_warmup=Disable cache warmup for the site
editsite.options.subscription_email=Custom "from" email address used for subscription emails. Pass an empty string to clear
editsite.options.subscription_footer=Custom footer text appended to subscription emails. Pass an empty string to clear
editsite.options.web_extraction_enabled=Allow or deny web data extraction on the site. Value must be ''true'' or ''false''
editsite.options.allow_subscriptions=Allow subscriptions on the site
editsite.options.no_allow_subscriptions=Disable subscriptions on the site
editsite.options.allow_web_authoring=Allow web authoring on the site
editsite.options.no_allow_web_authoring=Disable web authoring on the site
editsite.options.allow_mobile_snapshots=Allow mobile app snapshots (sheet images) on the site
editsite.options.no_allow_mobile_snapshots=Disable mobile app snapshots (sheet images) on the site
editsite.options.time_zone=Time zone for the site (IANA identifier, e.g. America/Los_Angeles). Mutually exclusive with --use-default-time-zone
editsite.options.use_default_time_zone=Use the server default time zone for the site. Mutually exclusive with --time-zone
editsite.short_description=Edit a site
editsite.status=Edit site ''{0}'' on the server...
encryptextracts.short_description=Encrypt extracts on a site
Expand Down
139 changes: 139 additions & 0 deletions tests/parsers/test_parser_edit_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,142 @@ def test_edit_site_parser_optional_arguments_archive(self):
assert args.site_id == "1234", args
assert args.status == "ACTIVE", args
assert args.run_now_enabled == "true", args

# -------- New flags added for issue #437 --------

def test_edit_site_parser_guest_access_enabled_true(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--guest-access-enabled", "true"])
assert args.guest_access_enabled == "true", args

def test_edit_site_parser_guest_access_enabled_short_flag(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "-g", "false"])
assert args.guest_access_enabled == "false", args

def test_edit_site_parser_guest_access_enabled_rejects_invalid(self):
with self.assertRaises(SystemExit):
self.parser_under_test.parse_args([commandname, "site-to-edit", "--guest-access-enabled", "yes"])

def test_edit_site_parser_cache_warmup_enabled(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--cache-warmup"])
assert args.cache_warmup_enabled is True, args

def test_edit_site_parser_cache_warmup_disabled(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--no-cache-warmup"])
assert args.cache_warmup_enabled is False, args

def test_edit_site_parser_cache_warmup_default_is_none(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit"])
assert args.cache_warmup_enabled is None, args

def test_edit_site_parser_cache_warmup_mutually_exclusive(self):
with self.assertRaises(SystemExit):
self.parser_under_test.parse_args([commandname, "site-to-edit", "--cache-warmup", "--no-cache-warmup"])

def test_edit_site_parser_subscription_email(self):
args = self.parser_under_test.parse_args(
[commandname, "site-to-edit", "--subscription-email", "alerts@example.com"]
)
assert args.subscription_email == "alerts@example.com", args

def test_edit_site_parser_subscription_email_short_flag(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "-e", "hi@example.com"])
assert args.subscription_email == "hi@example.com", args

def test_edit_site_parser_subscription_footer(self):
args = self.parser_under_test.parse_args(
[commandname, "site-to-edit", "--subscription-footer", "Contact IT for help"]
)
assert args.subscription_footer == "Contact IT for help", args

def test_edit_site_parser_subscription_footer_short_flag(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "-f", "Contact IT"])
assert args.subscription_footer == "Contact IT", args

def test_edit_site_parser_web_extraction_enabled(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--web-extraction-enabled", "true"])
assert args.web_extraction_enabled == "true", args

def test_edit_site_parser_web_extraction_enabled_rejects_invalid(self):
with self.assertRaises(SystemExit):
self.parser_under_test.parse_args([commandname, "site-to-edit", "--web-extraction-enabled", "yes"])

def test_edit_site_parser_allow_subscriptions(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--allow-subscriptions"])
assert args.allow_subscriptions is True, args

def test_edit_site_parser_no_allow_subscriptions(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--no-allow-subscriptions"])
assert args.allow_subscriptions is False, args

def test_edit_site_parser_allow_subscriptions_default_is_none(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit"])
assert args.allow_subscriptions is None, args

def test_edit_site_parser_allow_subscriptions_mutually_exclusive(self):
with self.assertRaises(SystemExit):
self.parser_under_test.parse_args(
[
commandname,
"site-to-edit",
"--allow-subscriptions",
"--no-allow-subscriptions",
]
)

def test_edit_site_parser_allow_web_authoring(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--allow-web-authoring"])
assert args.allow_web_authoring is True, args

def test_edit_site_parser_no_allow_web_authoring(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--no-allow-web-authoring"])
assert args.allow_web_authoring is False, args

def test_edit_site_parser_allow_web_authoring_mutually_exclusive(self):
with self.assertRaises(SystemExit):
self.parser_under_test.parse_args(
[
commandname,
"site-to-edit",
"--allow-web-authoring",
"--no-allow-web-authoring",
]
)

def test_edit_site_parser_allow_mobile_snapshots(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--allow-mobile-snapshots"])
assert args.allow_mobile_snapshots is True, args

def test_edit_site_parser_no_allow_mobile_snapshots(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--no-allow-mobile-snapshots"])
assert args.allow_mobile_snapshots is False, args

def test_edit_site_parser_allow_mobile_snapshots_mutually_exclusive(self):
with self.assertRaises(SystemExit):
self.parser_under_test.parse_args(
[
commandname,
"site-to-edit",
"--allow-mobile-snapshots",
"--no-allow-mobile-snapshots",
]
)

def test_edit_site_parser_time_zone(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--time-zone", "America/Los_Angeles"])
assert args.time_zone == "America/Los_Angeles", args

def test_edit_site_parser_use_default_time_zone(self):
args = self.parser_under_test.parse_args([commandname, "site-to-edit", "--use-default-time-zone"])
assert args.use_default_time_zone is True, args

def test_edit_site_parser_time_zone_mutually_exclusive(self):
with self.assertRaises(SystemExit):
self.parser_under_test.parse_args(
[
commandname,
"site-to-edit",
"--time-zone",
"UTC",
"--use-default-time-zone",
]
)