wagtailsettings_models.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. """
  2. Custom wagtail settings used by Wagtail CRX.
  3. Settings are user-configurable on a per-site basis (multisite).
  4. Global project or developer settings should be defined in wagtailcrx.settings.py .
  5. """
  6. from django.db import models
  7. from django.utils.translation import gettext_lazy as _
  8. from modelcluster.fields import ParentalKey
  9. from modelcluster.models import ClusterableModel
  10. from wagtail.admin.panels import FieldPanel, InlinePanel, HelpPanel, MultiFieldPanel
  11. from wagtail.models import Orderable
  12. from wagtail.contrib.settings.models import BaseSetting, register_setting
  13. from wagtail.images import get_image_model_string
  14. from wagtailcrx.fields import MonospaceField
  15. from wagtailcrx.settings import crx_settings
  16. from wagtailcrx.models.snippet_models import Navbar, Footer
  17. @register_setting(icon='cr-desktop')
  18. class LayoutSettings(ClusterableModel, BaseSetting):
  19. """
  20. Branding, navbar, and theme settings.
  21. """
  22. class Meta:
  23. verbose_name = _('Layout')
  24. if crx_settings.CRX_DB_TABLE_CODEREDCMS:
  25. db_table = "coderedcms_layoutsettings"
  26. logo = models.ForeignKey(
  27. get_image_model_string(),
  28. null=True,
  29. blank=True,
  30. on_delete=models.SET_NULL,
  31. related_name='+',
  32. verbose_name=_('Logo'),
  33. help_text=_('Brand logo used in the navbar and throughout the site')
  34. )
  35. favicon = models.ForeignKey(
  36. get_image_model_string(),
  37. null=True,
  38. blank=True,
  39. on_delete=models.SET_NULL,
  40. related_name='favicon',
  41. verbose_name=_('Favicon'),
  42. )
  43. navbar_color_scheme = models.CharField(
  44. blank=True,
  45. max_length=50,
  46. choices=None,
  47. default='',
  48. verbose_name=_('Navbar color scheme'),
  49. help_text=_('Optimizes text and other navbar elements for use with light or dark backgrounds.'), # noqa
  50. )
  51. navbar_class = models.CharField(
  52. blank=True,
  53. max_length=255,
  54. default='',
  55. verbose_name=_('Navbar CSS class'),
  56. help_text=_('Custom classes applied to navbar e.g. "bg-light", "bg-dark", "bg-primary".'),
  57. )
  58. navbar_fixed = models.BooleanField(
  59. default=False,
  60. verbose_name=_('Fixed navbar'),
  61. help_text=_('Fixed navbar will remain at the top of the page when scrolling.'),
  62. )
  63. navbar_wrapper_fluid = models.BooleanField(
  64. default=True,
  65. verbose_name=_('Full width navbar'),
  66. help_text=_('The navbar will fill edge to edge.'),
  67. )
  68. navbar_content_fluid = models.BooleanField(
  69. default=False,
  70. verbose_name=_('Full width navbar contents'),
  71. help_text=_('Content within the navbar will fill edge to edge.'),
  72. )
  73. navbar_collapse_mode = models.CharField(
  74. blank=True,
  75. max_length=50,
  76. choices=None,
  77. default='',
  78. verbose_name=_('Collapse navbar menu'),
  79. help_text=_('Control on what screen sizes to show and collapse the navbar menu links.'),
  80. )
  81. navbar_format = models.CharField(
  82. blank=True,
  83. max_length=50,
  84. choices=None,
  85. default='',
  86. verbose_name=_('Navbar format'),
  87. )
  88. navbar_search = models.BooleanField(
  89. default=True,
  90. verbose_name=_('Search box'),
  91. help_text=_('Show search box in navbar')
  92. )
  93. frontend_theme = models.CharField(
  94. blank=True,
  95. max_length=50,
  96. choices=None,
  97. default='',
  98. verbose_name=_('Theme variant'),
  99. )
  100. panels = [
  101. MultiFieldPanel(
  102. [
  103. FieldPanel('logo'),
  104. FieldPanel('favicon'),
  105. ],
  106. heading=_('Branding')
  107. ),
  108. InlinePanel(
  109. 'site_navbar',
  110. help_text=_('Choose one or more navbars for your site.'),
  111. heading=_('Site Navbars')
  112. ),
  113. MultiFieldPanel(
  114. [
  115. FieldPanel('navbar_color_scheme'),
  116. FieldPanel('navbar_class'),
  117. FieldPanel('navbar_fixed'),
  118. FieldPanel('navbar_wrapper_fluid'),
  119. FieldPanel('navbar_content_fluid'),
  120. FieldPanel('navbar_collapse_mode'),
  121. FieldPanel('navbar_format'),
  122. FieldPanel('navbar_search'),
  123. ],
  124. heading=_('Site Navbar Layout')
  125. ),
  126. InlinePanel(
  127. 'site_footer',
  128. help_text=_('Choose one or more footers for your site.'),
  129. heading=_('Site Footers')
  130. ),
  131. MultiFieldPanel(
  132. [
  133. FieldPanel('frontend_theme'),
  134. ],
  135. heading=_('Theming')
  136. ),
  137. ]
  138. def __init__(self, *args, **kwargs):
  139. """
  140. Inject custom choices and defaults into the form fields
  141. to enable customization of settings without causing migration issues.
  142. """
  143. super().__init__(*args, **kwargs)
  144. # Set choices dynamically.
  145. self._meta.get_field('frontend_theme').choices = (
  146. crx_settings.CRX_FRONTEND_THEME_CHOICES
  147. )
  148. self._meta.get_field('navbar_collapse_mode').choices = (
  149. crx_settings.CRX_FRONTEND_NAVBAR_COLLAPSE_MODE_CHOICES
  150. )
  151. self._meta.get_field('navbar_color_scheme').choices = (
  152. crx_settings.CRX_FRONTEND_NAVBAR_COLOR_SCHEME_CHOICES
  153. )
  154. self._meta.get_field('navbar_format').choices = (
  155. crx_settings.CRX_FRONTEND_NAVBAR_FORMAT_CHOICES
  156. )
  157. # Set default dynamically.
  158. if not self.id:
  159. self.frontend_theme = crx_settings.CRX_FRONTEND_THEME_DEFAULT
  160. self.navbar_class = crx_settings.CRX_FRONTEND_NAVBAR_CLASS_DEFAULT
  161. self.navbar_collapse_mode = crx_settings.CRX_FRONTEND_NAVBAR_COLLAPSE_MODE_DEFAULT
  162. self.navbar_color_scheme = crx_settings.CRX_FRONTEND_NAVBAR_COLOR_SCHEME_DEFAULT
  163. self.navbar_format = crx_settings.CRX_FRONTEND_NAVBAR_FORMAT_DEFAULT
  164. class NavbarOrderable(Orderable, models.Model):
  165. class Meta:
  166. if crx_settings.CRX_DB_TABLE_CODEREDCMS:
  167. db_table = "coderedcms_navbarorderable"
  168. navbar_chooser = ParentalKey(
  169. LayoutSettings,
  170. related_name="site_navbar",
  171. verbose_name=_('Site Navbars')
  172. )
  173. navbar = models.ForeignKey(
  174. Navbar,
  175. blank=True,
  176. null=True,
  177. on_delete=models.CASCADE,
  178. )
  179. panels = [
  180. FieldPanel("navbar")
  181. ]
  182. class FooterOrderable(Orderable, models.Model):
  183. class Meta:
  184. if crx_settings.CRX_DB_TABLE_CODEREDCMS:
  185. db_table = "coderedcms_footerorderable"
  186. footer_chooser = ParentalKey(
  187. LayoutSettings,
  188. related_name="site_footer",
  189. verbose_name=_('Site Footers')
  190. )
  191. footer = models.ForeignKey(
  192. Footer,
  193. blank=True,
  194. null=True,
  195. on_delete=models.CASCADE,
  196. )
  197. panels = [
  198. FieldPanel("footer")
  199. ]
  200. @register_setting(icon='cr-google')
  201. class AnalyticsSettings(BaseSetting):
  202. """
  203. Tracking and Google Analytics.
  204. """
  205. class Meta:
  206. verbose_name = _('Tracking')
  207. if crx_settings.CRX_DB_TABLE_CODEREDCMS:
  208. db_table = "coderedcms_analyticssettings"
  209. ga_tracking_id = models.CharField(
  210. blank=True,
  211. max_length=255,
  212. verbose_name=_('UA Tracking ID'),
  213. help_text=_('Your Google "Universal Analytics" tracking ID (begins with "UA-")'),
  214. )
  215. ga_g_tracking_id = models.CharField(
  216. blank=True,
  217. max_length=255,
  218. verbose_name=_('G Tracking ID'),
  219. help_text=_('Your Google Analytics 4 tracking ID (begins with "G-")'),
  220. )
  221. ga_track_button_clicks = models.BooleanField(
  222. default=False,
  223. verbose_name=_('Track button clicks'),
  224. help_text=_('Track all button clicks using Google Analytics event tracking. Event tracking details can be specified in each button’s advanced settings options.'), # noqa
  225. )
  226. gtm_id = models.CharField(
  227. blank=True,
  228. max_length=255,
  229. verbose_name=_('Google Tag Manager ID'),
  230. help_text=_('Begins with "GTM-"'),
  231. )
  232. head_scripts = MonospaceField(
  233. blank=True,
  234. null=True,
  235. verbose_name=_('<head> tracking scripts'),
  236. help_text=_('Add tracking scripts between the <head> tags.'),
  237. )
  238. body_scripts = MonospaceField(
  239. blank=True,
  240. null=True,
  241. verbose_name=_('<body> tracking scripts'),
  242. help_text=_('Add tracking scripts toward closing <body> tag.'),
  243. )
  244. panels = [
  245. HelpPanel(
  246. heading=_('Know your tracking'),
  247. content=_(
  248. '<h3><b>Which tracking IDs do I need?</b></h3>'
  249. '<p>Before adding tracking to your site, '
  250. '<a href="https://docs.coderedcorp.com/wagtail-crx/how_to/add_tracking_scripts.html" ' # noqa
  251. 'target="_blank">read about the difference between UA, G, GTM, '
  252. 'and other tracking IDs</a>.</p>'
  253. ),
  254. ),
  255. MultiFieldPanel(
  256. [
  257. FieldPanel('ga_tracking_id'),
  258. FieldPanel('ga_g_tracking_id'),
  259. FieldPanel('ga_track_button_clicks'),
  260. ],
  261. heading=_('Google Analytics'),
  262. ),
  263. MultiFieldPanel(
  264. [
  265. FieldPanel('gtm_id'),
  266. ],
  267. heading=_('Google Tag Manager'),
  268. ),
  269. MultiFieldPanel(
  270. [
  271. FieldPanel('head_scripts'),
  272. FieldPanel('body_scripts'),
  273. ],
  274. heading=_('Other Tracking Scripts')
  275. )
  276. ]
  277. @register_setting(icon='cr-universal-access')
  278. class ADASettings(BaseSetting):
  279. """
  280. Accessibility related options.
  281. """
  282. class Meta:
  283. verbose_name = 'Accessibility'
  284. if crx_settings.CRX_DB_TABLE_CODEREDCMS:
  285. db_table = "coderedcms_adasettings"
  286. skip_navigation = models.BooleanField(
  287. default=False,
  288. verbose_name=_('Show skip navigation link'),
  289. help_text=_('Shows a "Skip Navigation" link above the navbar that takes you directly to the main content.'), # noqa
  290. )
  291. panels = [
  292. MultiFieldPanel(
  293. [
  294. FieldPanel('skip_navigation'),
  295. ],
  296. heading=_('Accessibility')
  297. )
  298. ]
  299. @register_setting(icon='cog')
  300. class GeneralSettings(BaseSetting):
  301. """
  302. Various site-wide settings. A good place to put
  303. one-off settings that don't belong anywhere else.
  304. """
  305. class Meta:
  306. verbose_name = _('General')
  307. if crx_settings.CRX_DB_TABLE_CODEREDCMS:
  308. db_table = "coderedcms_generalsettings"
  309. from_email_address = models.CharField(
  310. blank=True,
  311. max_length=255,
  312. verbose_name=_('From email address'),
  313. help_text=_('The default email address this site appears to send from. For example: "sender@example.com" or "Sender Name <sender@example.com>" (without quotes)'), # noqa
  314. )
  315. search_num_results = models.PositiveIntegerField(
  316. default=10,
  317. verbose_name=_('Number of results per page'),
  318. )
  319. external_new_tab = models.BooleanField(
  320. default=False,
  321. verbose_name=_('Open all external links in new tab')
  322. )
  323. panels = [
  324. MultiFieldPanel(
  325. [
  326. FieldPanel('from_email_address'),
  327. ],
  328. _('Email')
  329. ),
  330. MultiFieldPanel(
  331. [
  332. FieldPanel('search_num_results'),
  333. ],
  334. _('Search Settings')
  335. ),
  336. MultiFieldPanel(
  337. [
  338. FieldPanel('external_new_tab'),
  339. ],
  340. _('Links')
  341. ),
  342. ]
  343. @register_setting(icon='cr-puzzle-piece')
  344. class GoogleApiSettings(BaseSetting):
  345. """
  346. Settings for Google API services.
  347. """
  348. class Meta:
  349. verbose_name = _('Google API')
  350. if crx_settings.CRX_DB_TABLE_CODEREDCMS:
  351. db_table = "coderedcms_googleapisettings"
  352. google_maps_api_key = models.CharField(
  353. blank=True,
  354. max_length=255,
  355. verbose_name=_('Google Maps API Key'),
  356. help_text=_('The API Key used for Google Maps.')
  357. )
  358. @register_setting(icon='cr-puzzle-piece')
  359. class MailchimpApiSettings(BaseSetting):
  360. """
  361. Settings for Mailchimp API services.
  362. """
  363. class Meta:
  364. verbose_name = _('Mailchimp API')
  365. if crx_settings.CRX_DB_TABLE_CODEREDCMS:
  366. db_table = "coderedcms_mailchimpapisettings"
  367. mailchimp_api_key = models.CharField(
  368. blank=True,
  369. max_length=255,
  370. verbose_name=_('Mailchimp API Key'),
  371. help_text=_('The API Key used for Mailchimp.')
  372. )