From 05e9d29710c00146d846f7906d42158c81f37eba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Tue, 6 Apr 2021 19:03:44 +0200
Subject: [PATCH] general: add explicit registration of cron jobs (#52793)

---
 tests/test_publisher.py      |  2 +-
 wcs/data_sources.py          |  2 +-
 wcs/formdef.py               |  2 +-
 wcs/publisher.py             | 15 ++++-----------
 wcs/qommon/ident/password.py |  2 +-
 wcs/qommon/publisher.py      |  5 +++++
 wcs/wf/aggregation_email.py  |  2 +-
 wcs/wf/jump.py               |  2 +-
 wcs/workflows.py             |  3 +++
 9 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/tests/test_publisher.py b/tests/test_publisher.py
index 11f642cbf..8dc3f45d9 100644
--- a/tests/test_publisher.py
+++ b/tests/test_publisher.py
@@ -161,10 +161,10 @@ def test_get_tenants():
 
 
 def test_register_cronjobs():
-    assert not pub.cronjobs
     pub.register_cronjobs()
     assert 'apply_global_action_timeouts' in [x.function.__name__ for x in pub.cronjobs]
     assert 'clean_sessions' in [x.function.__name__ for x in pub.cronjobs]
+    assert 'evaluate_jumps' in [x.name for x in pub.cronjobs]
 
 
 def test_get_default_position():
diff --git a/wcs/data_sources.py b/wcs/data_sources.py
index 444776f5a..bbe892938 100644
--- a/wcs/data_sources.py
+++ b/wcs/data_sources.py
@@ -890,7 +890,7 @@ class RefreshAgendas(AfterJob):
         build_agenda_datasources(get_publisher())
 
 
-if get_publisher_class():
+def register_cronjob():
     # every hour: check for agenda datasources
     get_publisher_class().register_cronjob(
         CronJob(build_agenda_datasources, name='build_agenda_datasources', minutes=[0])
diff --git a/wcs/formdef.py b/wcs/formdef.py
index 47d695966..a66c2dfdc 100644
--- a/wcs/formdef.py
+++ b/wcs/formdef.py
@@ -1781,7 +1781,7 @@ def get_formdefs_of_all_kinds():
     return formdefs
 
 
-if get_publisher_class():
+def register_cronjobs():
     # once a day, look for:
     # * expired drafts
     get_publisher_class().register_cronjob(CronJob(clean_drafts, name='clean_drafts', hours=[2], minutes=[0]))
diff --git a/wcs/publisher.py b/wcs/publisher.py
index 8633b484d..a43971b3e 100644
--- a/wcs/publisher.py
+++ b/wcs/publisher.py
@@ -27,7 +27,7 @@ from django.utils.encoding import force_text
 
 from wcs.qommon import force_str
 
-from . import custom_views, sessions
+from . import custom_views, data_sources, formdef, sessions
 from .admin import RootDirectory as AdminRootDirectory
 from .backoffice import RootDirectory as BackofficeRootDirectory
 from .Defaults import *  # noqa pylint: disable=wildcard-import
@@ -44,15 +44,6 @@ except ImportError:
     pass
 
 
-# this is terribly ugly but import RootDirectory will import a bunch of things,
-# and some of them need a publisher to be set
-class StubWcsPublisher(QommonPublisher):
-    pass
-
-
-set_publisher_class(StubWcsPublisher)
-
-
 class UnpicklerClass(pickle.Unpickler):
     def find_class(self, module, name):
         if module == 'qommon.form':
@@ -69,7 +60,7 @@ class UnpicklerClass(pickle.Unpickler):
         return klass
 
 
-class WcsPublisher(StubWcsPublisher):
+class WcsPublisher(QommonPublisher):
     APP_NAME = 'wcs'
     APP_DIR = APP_DIR
     DATA_DIR = DATA_DIR
@@ -125,6 +116,8 @@ class WcsPublisher(StubWcsPublisher):
         cls.register_cronjob(
             CronJob(cls.apply_global_action_timeouts, name='evaluate_global_action_timeouts', minutes=[0])
         )
+        data_sources.register_cronjob()
+        formdef.register_cronjobs()
 
     def is_using_postgresql(self):
         return bool(self.has_site_option('postgresql') and self.cfg.get('postgresql', {}))
diff --git a/wcs/qommon/ident/password.py b/wcs/qommon/ident/password.py
index 5b12c5870..f6450816b 100644
--- a/wcs/qommon/ident/password.py
+++ b/wcs/qommon/ident/password.py
@@ -1423,7 +1423,7 @@ def handle_expired_tokens(publisher):
             token.remove_self()
 
 
-if get_publisher_class():
+def register_cronjobs():
     # at 6:00 in the morning, every day
     get_publisher_class().register_cronjob(CronJob(handle_unused_accounts, minutes=[0], hours=[6]))
     get_publisher_class().register_cronjob(CronJob(handle_expired_tokens, minutes=[0], hours=[6]))
diff --git a/wcs/qommon/publisher.py b/wcs/qommon/publisher.py
index 5460af0b9..1b3e782a1 100644
--- a/wcs/qommon/publisher.py
+++ b/wcs/qommon/publisher.py
@@ -540,6 +540,8 @@ class QommonPublisher(Publisher):
         from .ident import password
 
         classes.append(password.PasswordAuthMethod)
+        password.register_cronjobs()
+
         self.ident_methods = {}
         for klass in classes:
             self.ident_methods[klass.key] = klass
@@ -551,6 +553,9 @@ class QommonPublisher(Publisher):
     def register_cronjob(cls, cronjob):
         if not cls.cronjobs:
             cls.cronjobs = []
+        if cronjob.name and any(x for x in cls.cronjobs if x.name == cronjob.name):
+            # already registered
+            return
         cls.cronjobs.append(cronjob)
 
     def clean_nonces(self, delta=60, now=None):
diff --git a/wcs/wf/aggregation_email.py b/wcs/wf/aggregation_email.py
index daf1af5c0..c129e4b31 100644
--- a/wcs/wf/aggregation_email.py
+++ b/wcs/wf/aggregation_email.py
@@ -163,7 +163,7 @@ def send_aggregation_emails(publisher):
         emails.email(mail_subject, body, email_rcpt=role.get_emails())
 
 
-if get_publisher_class():
+def register_cronjob():
     # at 6:00 in the morning, every day but the week end
     get_publisher_class().register_cronjob(
         CronJob(
diff --git a/wcs/wf/jump.py b/wcs/wf/jump.py
index 9370a66f9..6629cd7a3 100644
--- a/wcs/wf/jump.py
+++ b/wcs/wf/jump.py
@@ -328,7 +328,7 @@ def _apply_timeouts(publisher):
                         break
 
 
-if get_publisher_class():
+def register_cronjob():
     # every JUMP_TIMEOUT_INTERVAL minutes check for expired status jump
     # timeouts.
     get_publisher_class().register_cronjob(
diff --git a/wcs/workflows.py b/wcs/workflows.py
index d01254fd1..3136e82c0 100644
--- a/wcs/workflows.py
+++ b/wcs/workflows.py
@@ -3333,5 +3333,8 @@ def load_extra():
     from .wf import timeout_jump  # noqa pylint: disable=unused-import
     from .wf import wscall  # noqa pylint: disable=unused-import
 
+    aggregation_email.register_cronjob()
+    jump.register_cronjob()
+
 
 from .wf.export_to_model import ExportToModel  # noqa pylint: disable=unused-import,wrong-import-position
-- 
2.31.0

