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