added job to send users group summary
This commit is contained in:
parent
840289e360
commit
2698995cc2
|
@ -4,6 +4,8 @@ September 2020
|
|||
- Show password prompt when a user with expired password logs into LAM admin interface (requires PHP 7.2)
|
||||
- Better error messages on login when account is expired/deactivated/...
|
||||
- Windows users: group display format can be configured (cn/dn)
|
||||
- LAM Pro:
|
||||
-> Windows: new cron job to send users a summary of their managed groups
|
||||
|
||||
01.05.2020 7.2
|
||||
- Unix: allow to create group with same name during user creation
|
||||
|
|
|
@ -1141,6 +1141,11 @@ mysql> GRANT ALL PRIVILEGES ON lam_cron.* TO 'lam_cron'@'localhost';
|
|||
move expired accounts</link></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><link linkend="job_windows_notify_groups">Windows: Notify
|
||||
users about their managed groups</link></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><link linkend="job_freeradius_move_expired">FreeRadius:
|
||||
Delete or move expired accounts</link></para>
|
||||
|
@ -1829,6 +1834,95 @@ mysql> GRANT ALL PRIVILEGES ON lam_cron.* TO 'lam_cron'@'localhost';
|
|||
</table>
|
||||
</section>
|
||||
|
||||
<section id="job_windows_notify_groups">
|
||||
<title>Windows: Notify users about their managed groups</title>
|
||||
|
||||
<para>This will send your users an email with the groups they
|
||||
manage. This also includes a list of users in these groups. The
|
||||
users and groups are searched using the user+group account types
|
||||
that are specified in server profile.</para>
|
||||
|
||||
<para>You need to activate the Windows module for users to be able
|
||||
to add this job. The job can be added multiple times.</para>
|
||||
|
||||
<screenshot>
|
||||
<graphic fileref="images/jobs_windowsNotifyGroups.png"/>
|
||||
</screenshot>
|
||||
|
||||
<para><table>
|
||||
<title>Options</title>
|
||||
|
||||
<tgroup cols="2">
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><emphasis role="bold">Option</emphasis></entry>
|
||||
|
||||
<entry><emphasis
|
||||
role="bold">Description</emphasis></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>From address</entry>
|
||||
|
||||
<entry>The email address to set as FROM.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Reply-to address</entry>
|
||||
|
||||
<entry>Optional Reply-to address for email.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>CC address</entry>
|
||||
|
||||
<entry>Optional CC mail address.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>BCC address</entry>
|
||||
|
||||
<entry>Optional BCC mail address.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Subject</entry>
|
||||
|
||||
<entry>The email subject line. Supports wildcards, see
|
||||
below.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>HTML format</entry>
|
||||
|
||||
<entry>Send email as HTML instead of plain text.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Text</entry>
|
||||
|
||||
<entry>The email body text. Supports wildcards, see
|
||||
below.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Period</entry>
|
||||
|
||||
<entry>Defines how often the mail is sent (e.g.
|
||||
quarterly).</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>Wildcards:</para>
|
||||
|
||||
<para>You can enter LDAP attributes as wildcards in the form
|
||||
@@ATTRIBUTE_NAME@@. E.g. to add the user's common name use "@@cn@@".
|
||||
For the common name it would be "@@cn@@".</para>
|
||||
|
||||
<para>Use the wildcard "@@LAM_MANAGED_GROUPS@@" to insert the group
|
||||
listing. This wildcard is mandatory.</para>
|
||||
</section>
|
||||
|
||||
<section id="job_freeradius_move_expired">
|
||||
<title>FreeRadius: Delete or move expired accounts</title>
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
|
@ -430,6 +430,16 @@ $helpArray = array (
|
|||
"Headline" => _('Target DN'),
|
||||
"Text" => _('The expired accounts will be moved to this DN.')
|
||||
),
|
||||
'810' => array(
|
||||
"Headline" => _('Text'),
|
||||
"Text" => _('The mail text of all mails.') .
|
||||
_('You can use wildcards for LDAP attributes in the form @@attribute@@ (e.g. @@uid@@ for the user name).')
|
||||
. ' ' . _('The managed groups need to be added with @@LAM_MANAGED_GROUPS@@.')
|
||||
),
|
||||
'811' => array(
|
||||
"Headline" => _('Period'),
|
||||
"Text" => _('This defines how often the email is sent (e.g. each month).')
|
||||
),
|
||||
);
|
||||
|
||||
/* This is a sample help entry. Just copy this line an modify the values between the [] brackets.
|
||||
|
|
|
@ -3915,7 +3915,8 @@ class windowsUser extends baseModule implements passwordService {
|
|||
return array(
|
||||
new WindowsPasswordNotifyJob(),
|
||||
new WindowsAccountExpirationCleanupJob(),
|
||||
new WindowsAccountExpirationNotifyJob()
|
||||
new WindowsAccountExpirationNotifyJob(),
|
||||
new WindowsManagedGroupsNotifyJob()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4147,6 +4148,299 @@ if (interface_exists('\LAM\JOB\Job', false)) {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Job to notify users about their managed groups.
|
||||
*
|
||||
* @package jobs
|
||||
*/
|
||||
class WindowsManagedGroupsNotifyJob extends \LAM\JOB\PasswordExpirationJob {
|
||||
|
||||
const MANAGED_GROUPS = 'LAM_MANAGED_GROUPS';
|
||||
const PERIOD_MONTHLY = 'MONTHLY';
|
||||
const PERIOD_QUARTERLY = 'QUARTERLY';
|
||||
const PERIOD_HALF_YEARLY = 'HALF_YEARLY';
|
||||
const PERIOD_YEARLY = 'YEARLY';
|
||||
|
||||
/**
|
||||
* Returns the alias name of the job.
|
||||
*
|
||||
* @return String name
|
||||
*/
|
||||
public function getAlias() {
|
||||
return _('Windows') . ': ' . _('Notify users about their managed groups');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getDescription() {
|
||||
return _('This will send each user a summary of the managed groups and their members.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getConfigOptions($jobID) {
|
||||
$prefix = $this->getConfigPrefix();
|
||||
$container = new htmlResponsiveRow();
|
||||
$container->add(new htmlResponsiveInputField(_('From address'), $prefix . '_mailFrom' . $jobID, null, '800', true), 12);
|
||||
$container->add(new htmlResponsiveInputField(_('Reply-to address'), $prefix . '_mailReplyTo' . $jobID, null, '801'), 12);
|
||||
$container->add(new htmlResponsiveInputField(_('CC address'), $prefix . '_mailCC' . $jobID, null, '805'), 12);
|
||||
$container->add(new htmlResponsiveInputField(_('BCC address'), $prefix . '_mailBCC' . $jobID, null, '806'), 12);
|
||||
$container->add(new htmlResponsiveInputField(_('Subject'), $prefix . '_mailSubject' . $jobID, null, '802'), 12);
|
||||
$container->add(new htmlResponsiveInputCheckbox($prefix . '_mailIsHTML' . $jobID, false, _('HTML format'), '553'), 12);
|
||||
$container->add(new htmlResponsiveInputTextarea($prefix . '_mailtext' . $jobID, '', 50, 4, _('Text'), '810'), 12);
|
||||
$periodOptions = array(
|
||||
_('Monthly') => self::PERIOD_MONTHLY,
|
||||
_('Quarterly') => self::PERIOD_QUARTERLY,
|
||||
_('Half-yearly') => self::PERIOD_HALF_YEARLY,
|
||||
_('Yearly') => self::PERIOD_YEARLY,
|
||||
);
|
||||
$periodSelect = new htmlResponsiveSelect($prefix . '_period' . $jobID, $periodOptions, array(), _('Period'), '811');
|
||||
$periodSelect->setHasDescriptiveElements(true);
|
||||
$periodSelect->setSortElements(false);
|
||||
$container->add($periodSelect, 12);
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function checkConfigOptions($jobID, $options) {
|
||||
$prefix = $this->getConfigPrefix();
|
||||
$errors = array();
|
||||
// from address
|
||||
if (empty($options[$prefix . '_mailFrom' . $jobID][0])
|
||||
|| !(get_preg($options[$prefix . '_mailFrom' . $jobID][0], 'email')
|
||||
|| get_preg($options[$prefix . '_mailFrom' . $jobID][0], 'emailWithName'))) {
|
||||
$errors[] = array('ERROR', _('Please enter a valid email address!'), _('From address'));
|
||||
}
|
||||
// reply-to
|
||||
if (!empty($options[$prefix . '_mailReplyTo' . $jobID][0])
|
||||
&& !get_preg($options[$prefix . '_mailReplyTo' . $jobID][0], 'email')
|
||||
&& !get_preg($options[$prefix . '_mailReplyTo' . $jobID][0], 'emailWithName')) {
|
||||
$errors[] = array('ERROR', _('Please enter a valid email address!'), _('Reply-to address'));
|
||||
}
|
||||
// CC address
|
||||
if (!empty($options[$prefix . '_mailCC' . $jobID][0])
|
||||
&& !get_preg($options[$prefix . '_mailCC' . $jobID][0], 'email')
|
||||
&& !get_preg($options[$prefix . '_mailCC' . $jobID][0], 'emailWithName')) {
|
||||
$errors[] = array('ERROR', _('Please enter a valid email address!'), _('CC address'));
|
||||
}
|
||||
// BCC address
|
||||
if (!empty($options[$prefix . '_mailBCC' . $jobID][0])
|
||||
&& !get_preg($options[$prefix . '_mailBCC' . $jobID][0], 'email')
|
||||
&& !get_preg($options[$prefix . '_mailBCC' . $jobID][0], 'emailWithName')) {
|
||||
$errors[] = array('ERROR', _('Please enter a valid email address!'), _('BCC address'));
|
||||
}
|
||||
// text
|
||||
$mailText = implode('', $options[$prefix . '_mailtext' . $jobID]);
|
||||
if (empty($mailText)) {
|
||||
$errors[] = array('ERROR', _('Please set a email text.'));
|
||||
}
|
||||
if (strpos($mailText, '@@' . self::MANAGED_GROUPS . '@@') === false) {
|
||||
$errors[] = array('ERROR', _('Please add the wildcard for the list of managed groups.'), '@@' . self::MANAGED_GROUPS . '@@');
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function getPolicyOptions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for users in LDAP.
|
||||
*
|
||||
* @param String $jobID unique job identifier
|
||||
* @param array $options config options (name => value)
|
||||
* @return array list of user attributes
|
||||
*/
|
||||
protected function findUsers($jobID, $options) {
|
||||
// read users
|
||||
$sysAttrs = array('managedObjects', 'mail');
|
||||
$attrs = $this->getAttrWildcards($jobID, $options);
|
||||
$attrs = array_values(array_unique(array_merge($attrs, $sysAttrs)));
|
||||
$users = searchLDAPByFilter('(&(mail=*)(managedObjects=*))', $attrs, array('user'));
|
||||
$groups = searchLDAPByFilter('(managedBy=*)', array('cn', 'member'), array('group'));
|
||||
$groupByDn = array();
|
||||
foreach ($groups as $group) {
|
||||
$groupByDn[$group['dn']] = $group;
|
||||
}
|
||||
$groups = null;
|
||||
foreach ($users as $index => $user) {
|
||||
$managedObjectDns = $user['managedobjects'];
|
||||
$managedGroups = array();
|
||||
foreach ($managedObjectDns as $managedObjectDn) {
|
||||
if (array_key_exists($managedObjectDn, $groupByDn)) {
|
||||
$managedGroups[] = $groupByDn[$managedObjectDn];
|
||||
}
|
||||
}
|
||||
$users[$index][strtolower(self::MANAGED_GROUPS)] = $managedGroups;
|
||||
}
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function execute($jobID, $options, &$pdo, $isDryRun, &$resultLog) {
|
||||
$this->jobResultLog = &$resultLog;
|
||||
$this->jobResultLog->logDebug("Configuration options:");
|
||||
foreach ($options as $key => $value) {
|
||||
if (strpos($key, $jobID) === false) {
|
||||
continue;
|
||||
}
|
||||
$this->jobResultLog->logDebug($key . ': ' . implode(', ', $value));
|
||||
}
|
||||
$now = new DateTime(null, getTimeZone());
|
||||
$baseDate = $this->getBaseDate($now);
|
||||
$monthInterval = $this->getMonthInterval($options, $jobID);
|
||||
if (!$this->shouldRun($pdo, $options, $jobID, $baseDate, $monthInterval)) {
|
||||
$this->jobResultLog->logDebug('No run needed yet');
|
||||
return;
|
||||
}
|
||||
$userResults = $this->findUsers($jobID, $options);
|
||||
$this->jobResultLog->logDebug("Found " . sizeof($userResults) . " users to send an email.");
|
||||
$isHTML = (!empty($options[$this->getConfigPrefix() . '_mailIsHTML' . $jobID][0]) && ($options[$this->getConfigPrefix() . '_mailIsHTML' . $jobID][0] == 'true'));
|
||||
foreach ($userResults as $user) {
|
||||
if (empty($user[strtolower(self::MANAGED_GROUPS)])) {
|
||||
continue;
|
||||
}
|
||||
$user[strtolower(self::MANAGED_GROUPS)][0] = $this->formatGroups($user[strtolower(self::MANAGED_GROUPS)], $isHTML);
|
||||
if ($isDryRun) {
|
||||
// no action for dry run
|
||||
$this->jobResultLog->logInfo("Managed groups text for " . $user['dn'] . ":\n" . $user[strtolower(self::MANAGED_GROUPS)][0]);
|
||||
$this->jobResultLog->logInfo('Not sending email to ' . $user['dn'] . ' because of dry run.');
|
||||
continue;
|
||||
}
|
||||
// send email
|
||||
$this->sendMail($options, $jobID, $user, null);
|
||||
}
|
||||
if (!$isDryRun) {
|
||||
$this->setDBLastPwdChangeTime($jobID, $pdo, $jobID, self::getLastEffectiveExecutionDate($baseDate, $monthInterval, $this->jobResultLog)->format('Y-m-d'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the job should run.
|
||||
*
|
||||
* @param $pdo PDO
|
||||
* @param $options job options
|
||||
* @param $jobId job id
|
||||
* @param DateTime $baseDate base date
|
||||
* @param int $monthInterval month interval
|
||||
* @return bool should run
|
||||
*/
|
||||
private function shouldRun(&$pdo, $options, $jobId, $baseDate, $monthInterval) {
|
||||
$dbLastChange = $this->getDBLastPwdChangeTime($jobId, $pdo, $jobId);
|
||||
if (empty($dbLastChange)) {
|
||||
return true;
|
||||
}
|
||||
$this->jobResultLog->logDebug('Base date: ' . $baseDate->format('Y-m-d'));
|
||||
$effectiveDate = self::getLastEffectiveExecutionDate($baseDate, $monthInterval, $this->jobResultLog);
|
||||
$dbLastChangeDate = DateTime::createFromFormat('Y-m-d', $dbLastChange, getTimeZone());
|
||||
$this->jobResultLog->logDebug('Last run date: ' . $dbLastChangeDate->format('Y-m-d'));
|
||||
return $effectiveDate > $dbLastChangeDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the month interval.
|
||||
*
|
||||
* @param arry $options config options
|
||||
* @param $jobId job id
|
||||
* @return int interval
|
||||
*/
|
||||
private function getMonthInterval($options, $jobId) {
|
||||
$monthInterval = 12;
|
||||
switch ($options[$this->getConfigPrefix() . '_period' . $jobId][0]) {
|
||||
case self::PERIOD_HALF_YEARLY:
|
||||
$monthInterval = 6;
|
||||
break;
|
||||
case self::PERIOD_QUARTERLY:
|
||||
$monthInterval = 3;
|
||||
break;
|
||||
case self::PERIOD_MONTHLY:
|
||||
$monthInterval = 1;
|
||||
break;
|
||||
}
|
||||
return $monthInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base date (first of month) for the current date.
|
||||
*
|
||||
* @param DateTime $currentDate current date
|
||||
* @return DateTime base date
|
||||
*/
|
||||
private function getBaseDate($currentDate) {
|
||||
$baseDateText = $currentDate->format('Y-m-') . '1';
|
||||
return DateTime::createFromFormat('Y-m-d', $baseDateText, getTimeZone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last effective execution date.
|
||||
*
|
||||
* @param DateTime $baseDate base date
|
||||
* @param int $monthInterval number of months in interval
|
||||
* @param \LAM\JOB\JobResultLog $resultLog result log
|
||||
*/
|
||||
public static function getLastEffectiveExecutionDate($baseDate, $monthInterval, $resultLog) {
|
||||
$month = $baseDate->format('m');
|
||||
$monthIndex = $month - 1;
|
||||
while (($monthIndex % $monthInterval) !== 0) {
|
||||
$monthIndex--;
|
||||
}
|
||||
$month = $monthIndex + 1;
|
||||
$effectiveDateString = $baseDate->format('Y-') . $month . '-1';
|
||||
$effectiveDate = DateTime::createFromFormat('Y-m-d', $effectiveDateString, getTimeZone());
|
||||
$resultLog->logDebug("Effective date: " . $effectiveDate->format('Y-m-d'));
|
||||
return $effectiveDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function checkSingleUser($jobID, $options, &$pdo, $now, $policyOptions, $user, $isDryRun) {
|
||||
// not used
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the managed groups.
|
||||
*
|
||||
* @param $managedGroups managed groups
|
||||
* @param bool $isHTML HTML email
|
||||
* @return string formatted text
|
||||
*/
|
||||
private function formatGroups($managedGroups, bool $isHTML) {
|
||||
$text = '';
|
||||
foreach ($managedGroups as $managedGroup) {
|
||||
if ($isHTML) {
|
||||
$text .= '<br><b>' . $managedGroup['cn'][0] . '</b><br>';
|
||||
}
|
||||
else {
|
||||
$text .= "\r\n" . $managedGroup['cn'][0] . "\r\n\r\n";
|
||||
}
|
||||
if (empty($managedGroup['member'])) {
|
||||
continue;
|
||||
}
|
||||
foreach ($managedGroup['member'] as $member) {
|
||||
$member = getAbstractDN($member);
|
||||
if ($isHTML) {
|
||||
$text .= ' ' . $member . '<br>';
|
||||
}
|
||||
else {
|
||||
$text .= " " . $member . "\r\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Job to notify users about account expiration.
|
||||
*
|
||||
|
|
|
@ -21,9 +21,9 @@ use PHPUnit\Framework\TestCase;
|
|||
|
||||
*/
|
||||
|
||||
include_once 'lam/lib/baseModule.inc';
|
||||
include_once 'lam/lib/modules.inc';
|
||||
include_once 'lam/lib/modules/windowsUser.inc';
|
||||
include_once __DIR__ . '/../../../lib/baseModule.inc';
|
||||
include_once __DIR__ . '/../../../lib/modules.inc';
|
||||
include_once __DIR__ . '/../../../lib/modules/windowsUser.inc';
|
||||
|
||||
/**
|
||||
* Checks the windowsUser class.
|
||||
|
@ -78,6 +78,33 @@ use PHPUnit\Framework\TestCase;
|
|||
return $seconds . '0000000';
|
||||
}
|
||||
|
||||
public function testWindowsManagedGroupsNotifyJob_getLastEffectiveExecutionDate() {
|
||||
if (!interface_exists('\LAM\JOB\Job', false)) {
|
||||
return;
|
||||
}
|
||||
$resultLog = new \LAM\JOB\JobResultLog();
|
||||
$baseDate = DateTime::createFromFormat('Y-m-d', '2020-08-21', getTimeZone());
|
||||
$this->assertEquals('2020-01-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 12, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-07-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 6, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-07-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 3, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-08-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 1, $resultLog)->format('Y-m-d'));
|
||||
$baseDate = DateTime::createFromFormat('Y-m-d', '2020-12-31', getTimeZone());
|
||||
$this->assertEquals('2020-01-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 12, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-07-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 6, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-10-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 3, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-12-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 1, $resultLog)->format('Y-m-d'));
|
||||
$baseDate = DateTime::createFromFormat('Y-m-d', '2020-01-01', getTimeZone());
|
||||
$this->assertEquals('2020-01-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 12, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-01-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 6, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-01-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 3, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-01-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 1, $resultLog)->format('Y-m-d'));
|
||||
$baseDate = DateTime::createFromFormat('Y-m-d', '2020-06-05', getTimeZone());
|
||||
$this->assertEquals('2020-01-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 12, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-01-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 6, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-04-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 3, $resultLog)->format('Y-m-d'));
|
||||
$this->assertEquals('2020-06-01', WindowsManagedGroupsNotifyJob::getLastEffectiveExecutionDate($baseDate, 1, $resultLog)->format('Y-m-d'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
Loading…
Reference in New Issue