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