1: <?php
2: /**
3: * This file contains the cContentTypeAbstract class.
4: *
5: * @package Core
6: * @subpackage ContentType
7: * @version SVN Revision $Rev:$
8: *
9: * @author Simon Sprankel
10: * @copyright four for business AG <www.4fb.de>
11: * @license http://www.contenido.org/license/LIZENZ.txt
12: * @link http://www.4fb.de
13: * @link http://www.contenido.org
14: */
15: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
16:
17: /**
18: * Abstract content type from which every content type should inherit.
19: *
20: * @package Core
21: * @subpackage ContentType
22: */
23: abstract class cContentTypeAbstract {
24:
25: /**
26: * Constant defining that the settings should be interpreted as plaintext.
27: *
28: * @var string
29: */
30: const SETTINGS_TYPE_PLAINTEXT = 'plaintext';
31:
32: /**
33: * Constant defining that the settings should be interpreted as XML.
34: * @var string
35: */
36: const SETTINGS_TYPE_XML = 'xml';
37:
38: /**
39: * Name of the content type, e.g.
40: * 'CMS_TEASER'.
41: *
42: * @var string
43: */
44: protected $_type = '';
45:
46: /**
47: * Prefix of the content type, e.g.
48: * 'teaser'.
49: *
50: * @var string
51: */
52: protected $_prefix = 'abstract';
53:
54: /**
55: * Whether the settings should be interpreted as plaintext or XML.
56: *
57: * @var string
58: */
59: protected $_settingsType = self::SETTINGS_TYPE_PLAINTEXT;
60:
61: /**
62: * ID of the content type, e.g.
63: * 3 if CMS_TEASER[3] is used.
64: *
65: * @var integer
66: */
67: protected $_id;
68:
69: /**
70: * Array containing the values of all content types.
71: *
72: * @var array
73: */
74: protected $_contentTypes;
75:
76: /**
77: * CONTENIDO configuration array
78: *
79: * @var array
80: */
81: protected $_cfg;
82:
83: /**
84: * idartlang of corresponding article
85: *
86: * @var integer
87: */
88: protected $_idArtLang;
89:
90: /**
91: * idart of corresponding article
92: *
93: * @var integer
94: */
95: protected $_idArt;
96:
97: /**
98: * idcat of corresponding article
99: *
100: * @var integer
101: */
102: protected $_idCat;
103:
104: /**
105: * CONTENIDO client id
106: *
107: * @var integer
108: */
109: protected $_client;
110:
111: /**
112: * CONTENIDO language id
113: *
114: * @var integer
115: */
116: protected $_lang;
117:
118: /**
119: * CONTENIDO session object
120: *
121: * @var cSession
122: */
123: protected $_session;
124:
125: /**
126: * CONTENIDO configuration array for currently active client
127: *
128: * @var array
129: */
130: protected $_cfgClient;
131:
132: /**
133: * Whether to generate XHTML
134: *
135: * @var boolean
136: */
137: protected $_useXHTML;
138:
139: /**
140: * The path to the upload directory.
141: *
142: * @var string
143: */
144: protected $_uploadPath;
145:
146: /**
147: * The raw settings from the DB.
148: *
149: * @var string
150: */
151: protected $_rawSettings = array();
152:
153: /**
154: * The parsed settings.
155: *
156: * @var array string
157: */
158: protected $_settings = array();
159:
160: /**
161: * List of form field names which are used by this content type!
162: *
163: * @var array
164: */
165: protected $_formFields = array();
166:
167: /**
168: * Initialises class attributes with values from cRegistry.
169: *
170: * @param string $rawSettings the raw settings in an XML structure or as
171: * plaintext
172: * @param int $id ID of the content type, e.g. 3 if CMS_TEASER[3] is
173: * used
174: * @param array $contentTypes array containing the values of all content
175: * types
176: */
177: public function __construct($rawSettings, $id, array $contentTypes) {
178: $this->_rawSettings = $rawSettings;
179: $this->_id = $id;
180: $this->_contentTypes = $contentTypes;
181:
182: $this->_idArtLang = cRegistry::getArticleLanguageId();
183: $this->_idArt = cRegistry::getArticleId();
184: $this->_idCat = cRegistry::getCategoryId();
185: $this->_cfg = cRegistry::getConfig();
186: $this->_client = cRegistry::getClientId();
187: $this->_lang = cRegistry::getLanguageId();
188: $this->_cfgClient = cRegistry::getClientConfig();
189: $this->_session = cRegistry::getSession();
190: $this->_useXHTML = cSecurity::toBoolean(getEffectiveSetting('generator', 'xhtml', 'false'));
191: $this->_uploadPath = $this->_cfgClient[$this->_client]['upl']['path'];
192:
193: $this->_readSettings();
194: }
195:
196: /**
197: * Reads all settings from the $_rawSettings attribute (XML or plaintext)
198: * and stores them in the $_settings attribute (associative array or
199: * plaintext).
200: */
201: protected function _readSettings() {
202: // if no settings have been given, do nothing
203: if (empty($this->_rawSettings)) {
204: return;
205: }
206: if ($this->_settingsType === self::SETTINGS_TYPE_XML) {
207: // if the settings should be interpreted as XML, process them accordingly
208: $this->_settings = cXmlBase::xmlStringToArray($this->_rawSettings);
209: // add the prefix to the settings array keys
210: foreach ($this->_settings as $key => $value) {
211: $this->_settings[$this->_prefix . '_' . $key] = $value;
212: unset($this->_settings[$key]);
213: }
214: } else {
215: // otherwise do not process the raw setting
216: $this->_settings = $this->_rawSettings;
217: }
218: }
219:
220: /**
221: * Function returns current content type configuration as array
222: *
223: * @return array
224: */
225: public function getConfiguration() {
226: return $this->_settings;
227: }
228:
229: /**
230: * Stores all values from the $_POST array in the $_settings attribute
231: * (associative array) and saves them in the database (XML).
232: */
233: protected function _storeSettings() {
234: $settingsToStore = '';
235: if ($this->_settingsType === self::SETTINGS_TYPE_XML) {
236: // if the settings should be stored as XML, process them accordingly
237: $settings = array();
238: // update the values in the settings array with the values from the
239: // $_POST array
240: foreach ($this->_formFields as $key) {
241: $keyWithoutPrefix = str_replace($this->_prefix . '_', '', $key);
242: if (isset($_POST[$key])) {
243: $this->_settings[$key] = $_POST[$key];
244: } else if (isset($_POST[$this->_prefix . '_array_' . $keyWithoutPrefix])) {
245: // key is of type prefix_array_field, so interpret value as an array
246: $this->_settings[$key] = explode(',', $_POST[$this->_prefix . '_array_' . $keyWithoutPrefix]);
247: }
248: $settings[$keyWithoutPrefix] = $this->_settings[$key];
249: }
250: $xml = cXmlBase::arrayToXml($settings, NULL, $this->_prefix);
251: $settingsToStore = $xml->asXML();
252: } else {
253: $settingsToStore = $this->_settings;
254: }
255: // store new settings in the database
256: conSaveContentEntry($this->_idArtLang, $this->_type, $this->_id, $settingsToStore);
257: }
258:
259: /**
260: * Since the content type code is evaled by php, the code has to be encoded.
261: *
262: * @param string $code code to encode
263: * @return string encoded code
264: */
265: protected function _encodeForOutput($code) {
266: $code = addslashes($code);
267: $code = str_replace("\\'", "'", $code);
268: $code = str_replace('\$', '\\$', $code);
269:
270: return $code;
271: }
272:
273: /**
274: * Builds an array with directory information from the given upload path.
275: *
276: * @param string $uploadPath path to upload directory (optional, default:
277: * root upload path
278: * of client)
279: * @return array with directory information (keys: name, path, sub)
280: */
281: public function buildDirectoryList($uploadPath = '') {
282: // make sure the upload path is set and ends with a slash
283: if ($uploadPath === '') {
284: $uploadPath = $this->_uploadPath;
285: }
286: if (substr($uploadPath, -1) !== '/') {
287: $uploadPath .= '/';
288: }
289:
290: $directories = array();
291:
292: if (is_dir($uploadPath)) {
293: if (false !== ($handle = cDirHandler::read($uploadPath, false, true))) {
294: foreach ($handle as $entry) {
295: if (cFileHandler::fileNameBeginsWithDot($entry) === false && is_dir($uploadPath . $entry)) {
296: $directory = array();
297: $directory['name'] = $entry;
298: $directory['path'] = str_replace($this->_uploadPath, '', $uploadPath);
299: $directory['sub'] = $this->buildDirectoryList($uploadPath . $entry);
300: $directories[] = $directory;
301: }
302: }
303: }
304: }
305:
306: usort($directories, function($a, $b) {
307: $a = mb_strtolower($a["name"]);
308: $b = mb_strtolower($b["name"]);
309: if($a < $b) {
310: return -1;
311: } else if($a > $b) {
312: return 1;
313: } else {
314: return 0;
315: }
316: });
317:
318: return $directories;
319: }
320:
321: /**
322: * Generates a directory list from the given directory information (which is
323: * typically built by {@link cContentTypeAbstract::buildDirectoryList}).
324: *
325: * @param array $dirs directory information
326: * @return string HTML code showing a directory list
327: */
328: public function generateDirectoryList(array $dirs) {
329: $template = new cTemplate();
330: $i = 1;
331:
332: foreach ($dirs as $dirData) {
333: // set the active class if this is the chosen directory
334: $divClass = ($this->_isActiveDirectory($dirData)) ? 'active' : '';
335: $template->set('d', 'DIVCLASS', $divClass);
336:
337: $template->set('d', 'TITLE', $dirData['path'] . $dirData['name']);
338: $template->set('d', 'DIRNAME', $dirData['name']);
339:
340: $liClasses = array();
341: // check if the directory should be shown expanded or collapsed
342: if ($this->_shouldDirectoryBeExpanded($dirData)) {
343: $template->set('d', 'SUBDIRLIST', $this->generateDirectoryList($dirData['sub']));
344: } else if (isset($dirData['sub']) && count($dirData['sub']) > 0) {
345: $liClasses[] = 'collapsed';
346: $template->set('d', 'SUBDIRLIST', '');
347: } else {
348: $template->set('d', 'SUBDIRLIST', '');
349: }
350: if ($i === count($dirs)) {
351: $liClasses[] = 'last';
352: }
353: $template->set('d', 'LICLASS', implode(' ', $liClasses));
354:
355: $i++;
356: $template->next();
357: }
358:
359: return $template->generate($this->_cfg['path']['contenido'] . 'templates/standard/template.cms_filelist_dirlistitem.html', true);
360: }
361:
362: /**
363: * Checks whether the directory defined by the given directory
364: * information is the currently active directory.
365: * Overwrite in subclasses if you use generateDirectoryList!
366: *
367: * @param array $dirData directory information
368: * @return boolean whether the directory is the currently active directory
369: */
370: protected function _isActiveDirectory(array $dirData) {
371: return false;
372: }
373:
374: /**
375: * Checks whether the directory defined by the given directory information
376: * should be shown expanded.
377: * Overwrite in subclasses if you use getDirectoryList!
378: *
379: * @param array $dirData directory information
380: * @return boolean whether the directory should be shown expanded
381: */
382: protected function _shouldDirectoryBeExpanded(array $dirData) {
383: return false;
384: }
385:
386: /**
387: * Checks whether the given $subDir is a subdirectory of the given $dir.
388: *
389: * @param string $subDir the potential subdirectory
390: * @param string $dir the parent directory
391: * @return boolean whether the given $subDir is a subdirectory of $dir
392: */
393: protected function _isSubdirectory($subDir, $dir) {
394: $dirArray = explode('/', $dir);
395: $expand = false;
396: $checkDir = '';
397:
398: // construct the whole directory in single steps and check if the given
399: // directory can be found
400: foreach ($dirArray as $dirPart) {
401: $checkDir .= '/' . $dirPart;
402: if ($checkDir === '/' . $subDir) {
403: $expand = true;
404: }
405: }
406:
407: return $expand;
408: }
409:
410: /**
411: * Generates the code which should be shown if this content type is shown in
412: * the frontend.
413: *
414: * @return string escaped HTML code which sould be shown if content type is
415: * shown in frontend
416: */
417: public abstract function generateViewCode();
418:
419: /**
420: * Generates the code which should be shown if this content type is edited.
421: *
422: * @return string escaped HTML code which should be shown if content type is
423: * edited
424: */
425: public abstract function generateEditCode();
426:
427: /**
428: * Checks if this content type can be edited by a WYSIWYG editor
429: * @return boolean
430: */
431: public function isWysiwygCompatible() {
432: return false;
433: }
434: }