1010from requests import HTTPError
1111from time import sleep
1212
13+ from .api .system_properties import get_sys_property , set_sys_property
1314from .api .ui_themes import get_workarena_theme_variants
1415from .api .user import create_user
1516from .api .utils import table_api_call , table_column_info
3839 EXPECTED_USER_FORM_FIELDS_PATH ,
3940 # Patch flag for reports
4041 REPORT_PATCH_FLAG ,
41- REPORT_DATE_FILTER ,
42+ REPORT_FILTER_PROPERTY ,
4243 # Supported ServiceNow releases
4344 SNOW_SUPPORTED_RELEASES ,
4445 # For workflows setup
5152from .utils import url_login
5253
5354
54- def _set_sys_property ( property_name : str , value : str ) :
55+ def _is_dev_portal_instance () -> bool :
5556 """
56- Set a sys_property in the instance.
57+ Check if the instance is a ServiceNow Developer Portal instance.
5758
58- """
59- instance = SNowInstance ()
60-
61- property = table_api_call (
62- instance = instance ,
63- table = "sys_properties" ,
64- params = {"sysparm_query" : f"name={ property_name } " , "sysparm_fields" : "sys_id" },
65- )["result" ]
66-
67- if not property :
68- property_sysid = ""
69- method = "POST"
70- else :
71- property_sysid = "/" + property [0 ]["sys_id" ]
72- method = "PUT"
73-
74- property = table_api_call (
75- instance = instance ,
76- table = f"sys_properties{ property_sysid } " ,
77- method = method ,
78- json = {"name" : property_name , "value" : value },
79- )
80-
81- # Verify that the property was updated
82- assert property ["result" ]["value" ] == value , f"Error setting { property_name } ."
83-
84-
85- def _get_sys_property (property_name : str ) -> str :
86- """
87- Get a sys_property from the instance.
59+ Returns:
60+ --------
61+ bool: True if the instance is a developer portal instance, False otherwise.
8862
8963 """
9064 instance = SNowInstance ()
91-
92- property_value = table_api_call (
93- instance = instance ,
94- table = "sys_properties" ,
95- params = {"sysparm_query" : f"name={ property_name } " , "sysparm_fields" : "value" },
96- )["result" ][0 ]["value" ]
97-
98- return property_value
65+ # Check if the instance url has the for devXXXXXX.service-now.com format (where X is a digit)
66+ if re .match (r"^https?://dev\d{6}\.service-now\.com" , instance .snow_url ):
67+ logging .info ("Detected a developer portal instance..." )
68+ return True
69+ logging .info ("Detected an internal instance..." )
70+ return False
9971
10072
10173def _install_update_set (path : str , name : str ):
@@ -797,7 +769,9 @@ def enable_url_login():
797769 Configure the instance to allow login via URL.
798770
799771 """
800- _set_sys_property (property_name = "glide.security.restrict.get.login" , value = "false" )
772+ set_sys_property (
773+ instance = SNowInstance (), property_name = "glide.security.restrict.get.login" , value = "false"
774+ )
801775 logging .info ("URL login enabled." )
802776
803777
@@ -808,7 +782,21 @@ def disable_password_policies():
808782 Notes: this is required to allow the creation of users with weak passwords.
809783
810784 """
811- _set_sys_property (property_name = "glide.security.password.policy.enabled" , value = "false" )
785+ set_sys_property (
786+ instance = SNowInstance (),
787+ property_name = "glide.security.password.policy.enabled" ,
788+ value = "false" ,
789+ )
790+ set_sys_property (
791+ instance = SNowInstance (), property_name = "glide.apply.password_policy.on_login" , value = "false"
792+ )
793+ # The following is not supported on developer portal instances
794+ if not _is_dev_portal_instance ():
795+ set_sys_property (
796+ instance = SNowInstance (),
797+ property_name = "glide.authenticate.api.user.reset_password.mandatory" ,
798+ value = "false" ,
799+ )
812800 logging .info ("Password policies disabled." )
813801
814802
@@ -817,8 +805,14 @@ def disable_guided_tours():
817805 Hide guided tour popups
818806
819807 """
820- _set_sys_property (property_name = "com.snc.guided_tours.sp.enable" , value = "false" )
821- _set_sys_property (property_name = "com.snc.guided_tours.standard_ui.enable" , value = "false" )
808+ set_sys_property (
809+ instance = SNowInstance (), property_name = "com.snc.guided_tours.sp.enable" , value = "false"
810+ )
811+ set_sys_property (
812+ instance = SNowInstance (),
813+ property_name = "com.snc.guided_tours.standard_ui.enable" ,
814+ value = "false" ,
815+ )
822816 logging .info ("Guided tours disabled." )
823817
824818
@@ -836,7 +830,9 @@ def disable_analytics_popups():
836830 Disable analytics popups (needs to be done through UI since Vancouver release)
837831
838832 """
839- _set_sys_property (property_name = "glide.analytics.enabled" , value = "false" )
833+ set_sys_property (
834+ instance = SNowInstance (), property_name = "glide.analytics.enabled" , value = "false"
835+ )
840836 logging .info ("Analytics popups disabled." )
841837
842838
@@ -850,7 +846,8 @@ def setup_ui_themes():
850846 check_ui_themes_installed ()
851847
852848 logging .info ("Setting default UI theme" )
853- _set_sys_property (
849+ set_sys_property (
850+ instance = SNowInstance (),
854851 property_name = "glide.ui.polaris.theme.custom" ,
855852 value = get_workarena_theme_variants (SNowInstance ())[0 ]["theme.sys_id" ],
856853 )
@@ -894,7 +891,9 @@ def check_ui_themes_installed():
894891
895892def set_home_page ():
896893 logging .info ("Setting default home page" )
897- _set_sys_property (property_name = "glide.login.home" , value = "/now/nav/ui/home" )
894+ set_sys_property (
895+ instance = SNowInstance (), property_name = "glide.login.home" , value = "/now/nav/ui/home"
896+ )
898897
899898
900899def wipe_system_admin_preferences ():
@@ -918,9 +917,9 @@ def wipe_system_admin_preferences():
918917 )
919918
920919
921- def is_report_filter_using_time (filter ):
920+ def is_report_filter_using_relative_time (filter ):
922921 """
923- Heuristic to check if a report is filtering based on time
922+ Heuristic to check if a report is filtering based on relative time
924923
925924 This aims to detect the use of functions like "gs.endOfToday()". To avoid hardcoding all of them,
926925 we simply check for the use of keywords. Our filter is definitely too wide, but that's ok.
@@ -938,6 +937,32 @@ def patch_report_filters():
938937 logging .info ("Patching reports with date filter..." )
939938
940939 instance = SNowInstance ()
940+ filter_config = instance .report_filter_config
941+
942+ # If the report date filter is already set, we use the existing values (would be the case on reinstall)
943+ if not filter_config :
944+ # Set the report date filter to current date as YYYY-MM-DD and time filter to current time as HH:MM:SS
945+ now = datetime .now ()
946+ report_date_filter = now .strftime ("%Y-%m-%d" )
947+ report_time_filter = now .strftime ("%H:%M:%S" )
948+ # ... save the filter config
949+ logging .info (
950+ f"Setting report date filter to { report_date_filter } and time filter to { report_time_filter } via { REPORT_FILTER_PROPERTY } "
951+ )
952+ set_sys_property (
953+ instance = instance ,
954+ property_name = REPORT_FILTER_PROPERTY ,
955+ value = json .dumps (
956+ {"report_date_filter" : report_date_filter , "report_time_filter" : report_time_filter }
957+ ),
958+ )
959+ else :
960+ # Use the existing configuration
961+ logging .info (
962+ f"Using existing report date filter { filter_config ['report_date_filter' ]} and time filter { filter_config ['report_time_filter' ]} "
963+ )
964+ report_date_filter = filter_config ["report_date_filter" ]
965+ report_time_filter = filter_config ["report_time_filter" ]
941966
942967 # Get all reports that are not already patched
943968 reports = table_api_call (
@@ -959,27 +984,31 @@ def patch_report_filters():
959984 logging .info (f"Discarding report { report ['title' ]} { report ['sys_id' ]} ..." )
960985 raise NotImplementedError () # Mark for deletion
961986
962- if not is_report_filter_using_time (report ["filter" ]):
987+ if not is_report_filter_using_relative_time (report ["filter" ]):
963988 # That's a report we want to keep (use date cutoff filter)
964- filter_date = REPORT_DATE_FILTER
989+ filter_date = report_date_filter
990+ filter_time = report_time_filter
965991 logging .info (
966992 f"Keeping report { report ['title' ]} { report ['sys_id' ]} (columns: { sys_created_on_cols } )..."
967993 )
968994 else :
969- # XXX: We do not support reports with filters that rely on time (e.g., last 10 days) because
995+ # XXX: We do not support reports with filters that rely on relative time (e.g., last 10 days) because
970996 # there are not stable. In this case, we don't delete them but add a filter to make
971997 # them empty. They will be shown as "No data available".
972998 logging .info (
973999 f"Disabling report { report ['title' ]} { report ['sys_id' ]} because it uses time filters..."
9741000 )
9751001 filter_date = "1900-01-01"
1002+ filter_time = "00:00:00"
9761003
1004+ # Format the filter
9771005 filter = "" .join (
9781006 [
979- f"^{ col } <javascript:gs.dateGenerate('{ filter_date } ','00:00:00 ')"
1007+ f"^{ col } <javascript:gs.dateGenerate('{ filter_date } ','{ filter_time } ')"
9801008 for col in sys_created_on_cols
9811009 ]
9821010 ) + ("^" if len (report ["filter" ]) > 0 and not report ["filter" ].startswith ("^" ) else "" )
1011+ # Patch the report with the new filter
9831012 table_api_call (
9841013 instance = instance ,
9851014 table = f"sys_report/{ report ['sys_id' ]} " ,
@@ -1055,7 +1084,11 @@ def setup():
10551084
10561085 # Save installation date
10571086 logging .info ("Saving installation date" )
1058- _set_sys_property (property_name = "workarena.installation.date" , value = datetime .now ().isoformat ())
1087+ set_sys_property (
1088+ instance = SNowInstance (),
1089+ property_name = "workarena.installation.date" ,
1090+ value = datetime .now ().isoformat (),
1091+ )
10591092
10601093 logging .info ("WorkArena setup complete." )
10611094
@@ -1068,7 +1101,9 @@ def main():
10681101 logging .basicConfig (level = logging .INFO )
10691102
10701103 try :
1071- past_install_date = _get_sys_property ("workarena.installation.date" )
1104+ past_install_date = get_sys_property (
1105+ instance = SNowInstance (), property_name = "workarena.installation.date"
1106+ )
10721107 logging .info (f"Detected previous installation on { past_install_date } . Reinstalling..." )
10731108 except :
10741109 past_install_date = "never"
0 commit comments