1: <?php
2: /**
3: * Wicked_Driver defines an API for implementing storage backends for Wicked.
4: *
5: * Copyright 2003-2012 Horde LLC (http://www.horde.org/)
6: *
7: * See the enclosed file COPYING for license information (GPL). If you
8: * did not receive this file, see http://www.horde.org/licenses/gpl.
9: *
10: * @author Tyler Colbert <tyler@colberts.us>
11: * @package Wicked
12: */
13: abstract class Wicked_Driver
14: {
15: /**
16: * Hash containing connection parameters.
17: *
18: * @var array
19: */
20: protected $_params = array();
21:
22: /**
23: * VFS object for storing attachments.
24: *
25: * @var VFS
26: */
27: protected $_vfs;
28:
29: /**
30: * Constructor.
31: *
32: * @param array $params A hash containing connection parameters.
33: */
34: public function __construct($params = array())
35: {
36: $this->_params = $params;
37: }
38:
39: /**
40: * Accessor to manage a VFS instance.
41: *
42: * @throws Wicked_Exception
43: */
44: public function getVFS()
45: {
46: if (!$this->_vfs) {
47: try {
48: $this->_vfs = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Vfs')->create();
49: } catch (Horde_Vfs_Exception $e) {
50: throw new Wicked_Exception($e);
51: }
52: }
53:
54: return $this->_vfs;
55: }
56:
57: /**
58: * Retrieves the page of a particular name from the database.
59: *
60: * @param string $pagename The name of the page to retrieve
61: *
62: */
63: abstract function retrieveByName($pagename);
64:
65: /**
66: * Retrieves a historic version of a page.
67: *
68: * @abstract
69: * @param string $pagename The name of the page to retrieve.
70: * @param string $version The version to retrieve.
71: *
72: */
73: abstract function retrieveHistory($pagename, $version);
74:
75: /**
76: * Logs a hit to $pagename.
77: *
78: * @param string $pagename The page that was viewed.
79: */
80: abstract function logPageView($pagename);
81:
82: /**
83: * Creates a new page.
84: *
85: * @abstract
86: *
87: * @param string $pagename The new page's name.
88: * @param string $text The new page's text.
89: */
90: abstract function newPage($pagename, $text);
91:
92: abstract function updateText($pagename, $text, $changelog);
93:
94: abstract function renamePage($pagename, $newname);
95:
96: public function getPageId($pagename)
97: {
98: $pages = $this->getPages();
99: $ids = array_flip($pages);
100: return isset($ids[$pagename]) ? $ids[$pagename] : false;
101: }
102:
103: public function getPage($pagename)
104: {
105: return array();
106: }
107:
108: public function getPageById($id)
109: {
110: return array();
111: }
112:
113: public function getSpecialPages()
114: {
115: static $pages;
116: if (isset($pages)) {
117: return $pages;
118: }
119:
120: $dh = opendir(WICKED_BASE . '/lib/Page');
121: $pages = array();
122: while (($dent = readdir($dh)) !== false) {
123: if (!preg_match('/(.*)\.php$/', $dent, $matches)) {
124: continue;
125: }
126: $pageName = $matches[1];
127: if ($pageName == 'StandardPage') {
128: continue;
129: }
130: $pages[$pageName] = $pageName;
131: }
132: closedir($dh);
133: return $pages;
134: }
135:
136: public function getPages($special = true)
137: {
138: return array();
139: }
140:
141: public function pageExists($pagename)
142: {
143: return in_array($pagename, $this->getPages());
144: }
145:
146: abstract function getAllPages();
147:
148: abstract function getHistory($pagename);
149:
150: /**
151: * Returns the most recently changed pages.
152: *
153: * @param integer $days The number of days to look back.
154: *
155: * @return array Pages.
156: */
157: abstract function getRecentChanges($days = 3);
158:
159: /**
160: * Returns the most popular pages.
161: *
162: * @abstract
163: *
164: * @param integer $limit The number of most popular pages to return.
165: *
166: * @return array Pages.
167: */
168: abstract function mostPopular($limit = 10);
169:
170: /**
171: * Returns the least popular pages.
172: *
173: * @abstract
174: *
175: * @param integer $limit The number of least popular pages to return.
176: *
177: * @return array Pages.
178: */
179: abstract function leastPopular($limit = 10);
180:
181: /**
182: * Finds pages with matches in text or title.
183: *
184: * @abstract
185: *
186: * @param string $searchtext The search expression (Google-like).
187: *
188: * @return array A list of pages
189: */
190: abstract function searchText($searchtext);
191:
192: abstract function getBackLinks($pagename);
193:
194: abstract function getLikePages($pagename);
195:
196: /**
197: * Retrieves data on files attached to a page.
198: *
199: * @abstract
200: *
201: * @param string $pageId This is the Id of the page for which we'd
202: * like to find attached files.
203: * @param boolean $allversions Whether to include all versions. If false
204: * or omitted, only the most recent version
205: * of each attachment is returned.
206: * @return array An array of key/value arrays describing the attached
207: * files.
208: */
209: abstract function getAttachedFiles($pageId, $allversions = false);
210:
211: /**
212: * Attaches a file to a page or update an attachment.
213: *
214: * @param array $file This is a key/value array describing the
215: * attachment:<pre>
216: * 'page_id' => This is the id of the page to which we would
217: * like to attach the file.
218: * 'attachment_name' => This is the filename of the attachment.
219: * 'change_log' => A change log entry for this attach or update
220: * operation. (Optional)
221: * 'change_author' => The user uploading this file. If not present,
222: * the currently logged-in user is assumed.</pre>
223: * @param string $data This is the contents of the file to be attached.
224: *
225: * @throws Wicked_Exception
226: */
227: public function attachFile($file, $data)
228: {
229: $vfs = $this->getVFS();
230: if (!isset($file['change_author'])) {
231: $file['change_author'] = $GLOBALS['registry']->getAuth();
232: }
233: $result = $this->_attachFile($file);
234:
235: /* We encode the path quoted printable so we won't get any nasty
236: * characters the filesystem might reject. */
237: $path = Wicked::VFS_ATTACH_PATH . '/' . $file['page_id'];
238: try {
239: $vfs->writeData($path, $file['attachment_name'] . ';' . $result, $data, true);
240: } catch (Horde_Vfs_Exception $e) {
241: throw new Wicked_Exception($e);
242: }
243: }
244:
245: /**
246: * Remove a single version or all versions of an attachment to
247: * $pageId from the VFS backend.
248: *
249: * @param integer $pageId The Id of the page the file is attached to.
250: * @param string $attachment The name of the file.
251: * @param string $version If specified, the version to delete. If null,
252: * then all versions of $attachment will be removed.
253: *
254: * @throws Wicked_Exception
255: */
256: public function removeAttachment($pageId, $attachment, $version = null)
257: {
258: $vfs = $this->getVFS();
259: $path = Wicked::VFS_ATTACH_PATH . '/' . $pageId;
260:
261: $fileList = $this->getAttachedFiles($pageId, true);
262: foreach ($fileList as $file) {
263: $fileversion = $file['attachment_version'];
264: if ($file['attachment_name'] == $attachment &&
265: (is_null($version) || $fileversion == $version)) {
266: /* Skip any attachments that don't exist so they can
267: * be cleared out of the backend. */
268: if (!$vfs->exists($path, $attachment . ';' . $fileversion)) {
269: continue;
270: }
271: try {
272: $vfs->deleteFile($path, $attachment . ';' . $fileversion);
273: } catch (Horde_Vfs_Exception $e) {
274: throw new Wicked_Exception($e);
275: }
276: }
277: }
278: }
279:
280: /**
281: * Removes all attachments to $pageId from the VFS backend.
282: *
283: * @param integer $pageId The Id of the page to remove attachments from.
284: *
285: * @throws Wicked_Exception
286: */
287: public function removeAllAttachments($pageId)
288: {
289: if (empty($pageId)) {
290: throw new Wicked_Exception('Cannot delete all attachments of all pages at once');
291: }
292:
293: $vfs = $this->getVFS();
294: if (!$vfs->isFolder(Wicked::VFS_ATTACH_PATH, $pageId)) {
295: return;
296: }
297:
298: try {
299: $vfs->deleteFolder(Wicked::VFS_ATTACH_PATH, $pageId, true);
300: } catch (Horde_Vfs_Exception $e) {
301: throw new Wicked_Exception($e);
302: }
303: }
304:
305: /**
306: * Handles the driver-specific portion of attaching a file.
307: *
308: * Wicked_Driver::attachFile() calls down to this method for the driver-
309: * specific portion, and then uses VFS to store the attachment.
310: *
311: * @abstract
312: *
313: * @access protected
314: *
315: * @param array $file See Wicked_Driver::attachFile().
316: *
317: * @return boolean The new version of the file attached
318: * @throws Wicked_Exception
319: */
320: abstract protected function _attachFile($file);
321:
322: /**
323: * Retrieves the contents of an attachment.
324: *
325: * @param string $pageId This is the name of the page to which the file
326: * is attached.
327: * @param string $filename This is the name of the attachment.
328: * @param string $version This is the version of the attachment.
329: *
330: * @return string The file's contents.
331: * @throws Wicked_Exception
332: */
333: public function getAttachmentContents($pageId, $filename, $version)
334: {
335: $vfs = $this->getVFS();
336: $path = Wicked::VFS_ATTACH_PATH . '/' . $pageId;
337:
338: try {
339: return $vfs->read($path, $filename . ';' . $version);
340: } catch (Horde_Vfs_Exception $e) {
341: throw new Wicked_Exception($e);
342: }
343: }
344:
345: abstract function removeVersion($pagename, $version);
346:
347: public function removeAllVersions($pagename)
348: {
349: /* When deleting a page, also delete all its attachments. */
350: $this->removeAllAttachments($this->getPageId($pagename));
351: }
352:
353: abstract function searchTitles($searchtext);
354:
355: /**
356: * Returns the charset used by the backend.
357: *
358: * @return string The backend's charset
359: */
360: public function getCharset()
361: {
362: return 'UTF-8';
363: }
364: }
365: