1: <?php
2: /**
3: * The Horde_LoginTasks:: class provides a set of methods for dealing with
4: * login tasks to run upon login to Horde applications.
5: *
6: * Copyright 2001-2012 Horde LLC (http://www.horde.org/)
7: *
8: * See the enclosed file COPYING for license information (LGPL). If you
9: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
10: *
11: * @author Michael Slusarz <slusarz@horde.org>
12: * @category Horde
13: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
14: * @package LoginTasks
15: */
16: class Horde_LoginTasks
17: {
18: /* Interval settings. */
19: // Do task yearly (First login after/on January 1).
20: const YEARLY = 1;
21: // Do task monthly (First login after/on first of month).
22: const MONTHLY = 2;
23: // Do task weekly (First login after/on a Sunday).
24: const WEEKLY = 3;
25: // Do task daily (First login of the day).
26: const DAILY = 4;
27: // Do task every login.
28: const EVERY = 5;
29: // Do task on first login only.
30: const FIRST_LOGIN = 6;
31: // Do task once only.
32: const ONCE = 7;
33:
34: /* Display styles. */
35: const DISPLAY_CONFIRM_NO = 1;
36: const DISPLAY_CONFIRM_YES = 2;
37: const DISPLAY_AGREE = 3;
38: const DISPLAY_NOTICE = 4;
39: const DISPLAY_NONE = 5;
40:
41: /* Priority settings */
42: const PRIORITY_HIGH = 1;
43: const PRIORITY_NORMAL = 2;
44:
45: /**
46: * The Horde_LoginTasks_Backend object provides all utilities we need for
47: * handling the login tasks.
48: *
49: * @var Horde_LoginTasks_Backend
50: */
51: private $_backend;
52:
53: /**
54: * The Horde_LoginTasks_Tasklist object for this login.
55: *
56: * @var Horde_LoginTasks_Tasklist
57: */
58: protected $_tasklist;
59:
60: /**
61: * Constructor.
62: *
63: * @param Horde_LoginTasks_Backend $backend The backend to use.
64: */
65: public function __construct(Horde_LoginTasks_Backend $backend)
66: {
67: $this->_backend = $backend;
68:
69: /* Retrieves a cached tasklist or make sure one is created. */
70: $this->_tasklist = $this->_backend->getTasklistFromCache();
71:
72: if (empty($this->_tasklist)) {
73: $this->_createTaskList();
74: }
75:
76: register_shutdown_function(array($this, 'shutdown'));
77: }
78:
79: /**
80: * Tasks to run on session shutdown.
81: */
82: public function shutdown()
83: {
84: if (isset($this->_tasklist)) {
85: $this->_backend->storeTasklistInCache($this->_tasklist);
86: }
87: }
88:
89: /**
90: * Creates the list of login tasks that are available for this session
91: * (stored in a Horde_LoginTasks_Tasklist object).
92: */
93: protected function _createTaskList()
94: {
95: /* Create a new Horde_LoginTasks_Tasklist object. */
96: $this->_tasklist = new Horde_LoginTasks_Tasklist();
97:
98: /* Get last task run date(s). Array keys are app names, values are
99: * last run timestamps. Special key '_once' contains list of
100: * ONCE tasks previously run. */
101: $lasttask = $this->_backend->getLastRun();
102:
103: /* Create time objects for today's date and last task run date. */
104: $cur_date = getdate();
105:
106: foreach ($this->_backend->getTasks() as $classname => $app) {
107: $ob = new $classname();
108:
109: /* If marked inactive, skip the task. */
110: if (!$ob->active) {
111: continue;
112: }
113:
114: $addtask = false;
115:
116: if ($ob->interval == self::FIRST_LOGIN) {
117: $addtask = empty($lasttask[$app]);
118: } else {
119: $lastrun = getdate(empty($lasttask[$app])
120: ? time()
121: : $lasttask[$app]);
122:
123: switch ($ob->interval) {
124: case self::YEARLY:
125: $addtask = ($cur_date['year'] > $lastrun['year']);
126: break;
127:
128: case self::MONTHLY:
129: $addtask = (($cur_date['year'] > $lastrun['year']) ||
130: ($cur_date['mon'] > $lastrun['mon']));
131: break;
132:
133: case self::WEEKLY:
134: $days = date('L', $lastrun[0]) ? 366 : 365;
135: $addtask = (($cur_date['wday'] < $lastrun['wday']) ||
136: (($cur_date['year'] == $lastrun['year']) &&
137: ($cur_date['yday'] >= $lastrun['yday'] + 7)) ||
138: (($cur_date['year'] > $lastrun['year']) &&
139: ($cur_date['yday'] >= $lastrun['yday'] + 7 - $days)));
140: break;
141:
142: case self::DAILY:
143: $addtask = (($cur_date['year'] > $lastrun['year']) ||
144: ($cur_date['yday'] > $lastrun['yday']));
145: break;
146:
147: case self::EVERY:
148: $addtask = true;
149: break;
150:
151: case self::ONCE:
152: if (empty($lasttask['_once']) ||
153: !in_array($classname, $lasttask['_once'])) {
154: $addtask = true;
155: $lasttask['_once'][] = $classname;
156: $this->_backend->setLastRun($lasttask);
157: }
158: break;
159: }
160: }
161:
162: if ($addtask) {
163: $this->_tasklist->addTask($ob);
164: }
165: }
166:
167: /* If tasklist is empty, we can simply set it to true now. */
168: if ($this->_tasklist->isDone()) {
169: $this->_tasklist = true;
170: }
171: }
172:
173: /**
174: * Do operations needed for this login.
175: *
176: * This function will generate the list of tasks to perform during this
177: * login and will redirect to the login tasks page if necessary. This is
178: * the function that should be called from the application upon login.
179: *
180: * @param array $opts Options:
181: * - confirmed: (array) The list of confirmed tasks.
182: * - url: (string) The URL to redirect to when finished.
183: * - user_confirmed: (boolean) If true, indicates that any pending
184: * actions have been confirmed by the user.
185: *
186: * @return mixed Null in case no redirection took place, the return value
187: * from the backend redirect() call otherwise.
188: */
189: public function runTasks(array $opts = array())
190: {
191: if (!isset($this->_tasklist) ||
192: ($this->_tasklist === true)) {
193: return;
194: }
195:
196: $opts = array_merge(array(
197: 'confirmed' => array(),
198: 'url' => null,
199: 'user_confirmed' => false
200: ), $opts);
201:
202: if (empty($this->_tasklist->target)) {
203: $this->_tasklist->target = $opts['url'];
204: }
205:
206: /* Perform ready tasks now. */
207: foreach ($this->_tasklist->ready(!$this->_tasklist->processed || $opts['user_confirmed']) as $key => $val) {
208: if (($val instanceof Horde_LoginTasks_SystemTask) ||
209: in_array($val->display, array(self::DISPLAY_AGREE, self::DISPLAY_NOTICE, self::DISPLAY_NONE)) ||
210: in_array($key, $opts['confirmed'])) {
211: $val->execute();
212: }
213: }
214:
215: $processed = $this->_tasklist->processed;
216: $this->_tasklist->processed = true;
217:
218: /* If we've successfully completed every task in the list (or skipped
219: * it), record now as the last time login tasks was run. */
220: if ($this->_tasklist->isDone()) {
221: $this->_backend->markLastRun();
222:
223: $url = $this->_tasklist->target;
224:
225: /* This will prevent us from having to store the entire tasklist
226: * object in the session, while still indicating we have
227: * completed the login tasks for this application. */
228: $this->_tasklist = true;
229:
230: if ($opts['user_confirmed']) {
231: return $this->_backend->redirect($url);
232: }
233: } elseif ((!$processed || $opts['user_confirmed']) &&
234: $this->_tasklist->needDisplay()) {
235: return $this->_backend->redirect($this->getLoginTasksUrl());
236: }
237: }
238:
239: /**
240: * Generate the list of tasks that need to be displayed.
241: * This is the function called from the login tasks page every time it
242: * is loaded.
243: *
244: * @return array The list of tasks that need to be displayed.
245: */
246: public function displayTasks()
247: {
248: if (!isset($this->_tasklist) ||
249: ($this->_tasklist === true)) {
250: return;
251: }
252:
253: return $this->_tasklist->needDisplay(true);
254: }
255:
256: /**
257: * Generate the login tasks URL.
258: *
259: * @return string The login tasks URL.
260: */
261: public function getLoginTasksUrl()
262: {
263: return $this->_backend->getLoginTasksUrl();
264: }
265:
266: /**
267: * Labels for the class constants.
268: *
269: * @return array A mapping of constant to gettext string.
270: */
271: static public function getLabels()
272: {
273: return array(
274: self::YEARLY => Horde_LoginTasks_Translation::t("Yearly"),
275: self::MONTHLY => Horde_LoginTasks_Translation::t("Monthly"),
276: self::WEEKLY => Horde_LoginTasks_Translation::t("Weekly"),
277: self::DAILY => Horde_LoginTasks_Translation::t("Daily"),
278: self::EVERY => Horde_LoginTasks_Translation::t("Every Login")
279: );
280: }
281: }
282: