PageRenderTime 56ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/extension/ezjscore/classes/ezjscajaxcontent.php

http://github.com/ezsystems/ezpublish
PHP | 577 lines | 399 code | 58 blank | 120 comment | 78 complexity | 565b76ce3514c650d7efcbb35f86189a MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. //
  3. // Definition of ezjscAjaxContent class
  4. //
  5. // Created on: <5-Aug-2007 00:00:00 ar>
  6. //
  7. // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
  8. // SOFTWARE NAME: eZ JSCore extension for eZ Publish
  9. // SOFTWARE RELEASE: 1.x
  10. // COPYRIGHT NOTICE: Copyright (C) 1999-2014 eZ Systems AS
  11. // SOFTWARE LICENSE: GNU General Public License v2.0
  12. // NOTICE: >
  13. // This program is free software; you can redistribute it and/or
  14. // modify it under the terms of version 2.0 of the GNU General
  15. // Public License as published by the Free Software Foundation.
  16. //
  17. // This program is distributed in the hope that it will be useful,
  18. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. // GNU General Public License for more details.
  21. //
  22. // You should have received a copy of version 2.0 of the GNU General
  23. // Public License along with this program; if not, write to the Free
  24. // Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  25. // MA 02110-1301, USA.
  26. //
  27. //
  28. // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
  29. //
  30. // Simplifying and encoding content objects / nodes to json
  31. // using the php json extension included in php 5.2
  32. class ezjscAjaxContent
  33. {
  34. /**
  35. * Constructor
  36. */
  37. protected function __construct()
  38. {
  39. }
  40. /**
  41. * Clone
  42. */
  43. protected function __clone()
  44. {
  45. }
  46. /**
  47. * Gets the first most prefered response type as defined by http_accept
  48. * uses post and get parameter if present, if not falls back to the one
  49. * defined in http header. First parameter lets you define fallback value
  50. * if none of the alternatives in second parameter is found. Second parameter
  51. * lets you limit the allowes types with a alias hash. xhtml, json, xml and
  52. * text are default allowed types.
  53. *
  54. * @param string $default
  55. * @param array $aliasList
  56. * @return string
  57. */
  58. public static function getHttpAccept( $default = 'xhtml', $aliasList = array( 'html' => 'xhtml',
  59. 'json' => 'json',
  60. 'javascript' => 'json',
  61. 'xml' => 'xml',
  62. 'text' => 'text' ) )
  63. {
  64. $acceptList = array();
  65. if ( isset($_POST['http_accept']) )
  66. $acceptList = explode( ',', $_POST['http_accept'] );
  67. else if ( isset($_POST['HTTP_ACCEPT']) )
  68. $acceptList = explode( ',', $_POST['HTTP_ACCEPT'] );
  69. else if ( isset($_GET['http_accept']) )
  70. $acceptList = explode( ',', $_GET['http_accept'] );
  71. else if ( isset($_GET['HTTP_ACCEPT']) )
  72. $acceptList = explode( ',', $_GET['HTTP_ACCEPT'] );
  73. else if ( isset($_SERVER['HTTP_ACCEPT']) )
  74. $acceptList = explode( ',', $_SERVER['HTTP_ACCEPT'] );
  75. foreach( $acceptList as $accept )
  76. {
  77. foreach( $aliasList as $alias => $returnType )
  78. {
  79. if ( strpos( $accept, $alias ) !== false )
  80. {
  81. $default = $returnType;
  82. break 2;
  83. }
  84. }
  85. }
  86. return $default;
  87. }
  88. /**
  89. * Encodes the content based on http accept values, more on
  90. * this on the getHttpAccept function.
  91. * Will simply implode the return value if array and not xml or
  92. * json is prefered return type.
  93. *
  94. * @param mixed $ret
  95. * @param string $type
  96. * @return string
  97. */
  98. public static function autoEncode( $ret, $type = null )
  99. {
  100. if ( $type === null )
  101. $type = self::getHttpAccept( );
  102. if ( $type === 'xml' )
  103. return self::xmlEncode( $ret );
  104. else if ( $type === 'json' )
  105. return json_encode( $ret );
  106. else
  107. return self::textEncode( $ret );
  108. }
  109. /**
  110. * Encodes mixed value to string or comma seperated list of strings
  111. *
  112. * @param mixed $mix
  113. * @return string
  114. */
  115. public static function textEncode( $mix )
  116. {
  117. if ( is_array( $mix ) )
  118. return implode(',', array_map( array('ezjscAjaxContent', 'textEncode'), array_filter( $mix ) ) );
  119. return htmlspecialchars( $mix );
  120. }
  121. /**
  122. * Function for encoding content object(s) or node(s) to simplified
  123. * json objects, xml or array hash
  124. *
  125. * @param mixed $obj
  126. * @param array $params
  127. * @param string $type
  128. * @return mixed
  129. */
  130. public static function nodeEncode( $obj, $params = array(), $type = 'json' )
  131. {
  132. if ( is_array( $obj ) )
  133. {
  134. $ret = array();
  135. foreach ( $obj as $ob )
  136. {
  137. $ret[] = self::simplify( $ob, $params );
  138. }
  139. }
  140. else
  141. {
  142. $ret = self::simplify( $obj, $params );
  143. }
  144. if ( $type === 'xml' )
  145. return self::xmlEncode( $ret );
  146. else if ( $type === 'json' )
  147. return json_encode( $ret );
  148. else
  149. return $ret;
  150. }
  151. /**
  152. * Function for simplifying a content object or node
  153. *
  154. * @param mixed $obj
  155. * @param array $params
  156. * @return array
  157. */
  158. public static function simplify( $obj, $params = array() )
  159. {
  160. if ( !$obj )
  161. {
  162. return array();
  163. }
  164. else if ( $obj instanceof eZContentObject)
  165. {
  166. $node = $obj->attribute( 'main_node' );
  167. $contentObject = $obj;
  168. }
  169. else if ( $obj instanceof eZContentObjectTreeNode || $obj instanceof eZFindResultNode )
  170. {
  171. $node = $obj;
  172. $contentObject = $obj->attribute( 'object' );
  173. }
  174. else if( isset( $params['fetchNodeFunction'] ) && method_exists( $obj, $params['fetchNodeFunction'] ) )
  175. {
  176. // You can supply fetchNodeFunction parameter to be able to support other node related classes
  177. $node = call_user_func( array( $obj, $params['fetchNodeFunction'] ) );
  178. if ( !$node instanceof eZContentObjectTreeNode )
  179. {
  180. return '';
  181. }
  182. $contentObject = $node->attribute( 'object' );
  183. }
  184. else if ( is_array( $obj ) )
  185. {
  186. return $obj; // Array is returned as is
  187. }
  188. else
  189. {
  190. return ''; // Other passed objects are not supported
  191. }
  192. $ini = eZINI::instance( 'site.ini' );
  193. $params = array_merge( array(
  194. 'dataMap' => array(), // collection of identifiers you want to load, load all with array('all')
  195. 'fetchPath' => false, // fetch node path
  196. 'fetchSection' => false, // fetch section
  197. 'fetchChildrenCount' => false,
  198. 'dataMapType' => array(), //if you want to filter datamap by type
  199. 'loadImages' => false,
  200. 'imagePreGenerateSizes' => array('small') //Pre generated images, loading all can be quite time consuming
  201. ), $params );
  202. if ( !isset( $params['imageSizes'] ) )// list of available image sizes
  203. {
  204. $imageIni = eZINI::instance( 'image.ini' );
  205. $params['imageSizes'] = $imageIni->variable( 'AliasSettings', 'AliasList' );
  206. }
  207. if ( $params['imageSizes'] === null || !isset( $params['imageSizes'][0] ) )
  208. $params['imageSizes'] = array();
  209. if ( !isset( $params['imageDataTypes'] ) )
  210. $params['imageDataTypes'] = $ini->variable( 'ImageDataTypeSettings', 'AvailableImageDataTypes' );
  211. $ret = array();
  212. $attributeArray = array();
  213. $ret['name'] = htmlentities( $contentObject->attribute( 'name' ), ENT_QUOTES, "UTF-8" );
  214. $ret['contentobject_id'] = $ret['id'] = (int) $contentObject->attribute( 'id' );
  215. $ret['contentobject_remote_id'] = $contentObject->attribute( 'remote_id' );
  216. $ret['contentobject_state'] = implode( ", ", $contentObject->attribute( 'state_identifier_array' ) );
  217. $ret['main_node_id'] = (int)$contentObject->attribute( 'main_node_id' );
  218. $ret['version'] = (int)$contentObject->attribute( 'current_version' );
  219. $ret['modified'] = $contentObject->attribute( 'modified' );
  220. $ret['published'] = $contentObject->attribute( 'published' );
  221. $ret['section_id'] = (int) $contentObject->attribute( 'section_id' );
  222. $ret['current_language'] = $contentObject->attribute( 'current_language' );
  223. $ret['owner_id'] = (int) $contentObject->attribute( 'owner_id' );
  224. $ret['class_id'] = (int) $contentObject->attribute( 'contentclass_id' );
  225. $ret['class_name'] = $contentObject->attribute( 'class_name' );
  226. $ret['path_identification_string'] = $node ? $node->attribute( 'path_identification_string' ) : '';
  227. $ret['translations'] = eZContentLanguage::decodeLanguageMask($contentObject->attribute( 'language_mask' ), true);
  228. $ret['can_edit'] = $contentObject->attribute( 'can_edit' );
  229. if ( isset( $params['formatDate'] ) )
  230. {
  231. $ret['modified_date'] = self::formatLocaleDate( $contentObject->attribute( 'modified' ), $params['formatDate'] );
  232. $ret['published_date'] = self::formatLocaleDate( $contentObject->attribute( 'published' ), $params['formatDate'] );
  233. }
  234. if ( isset( $params['fetchCreator'] ) )
  235. {
  236. $creator = $contentObject->attribute( 'current' )->attribute('creator');
  237. if ( $creator instanceof eZContentObject )
  238. {
  239. $ret['creator'] = array( 'id' => $creator->attribute( 'id' ),
  240. 'name' => $creator->attribute('name') );
  241. }
  242. else
  243. {
  244. $ret['creator'] = array( 'id' => $contentObject->attribute( 'current' )->attribute('creator_id'),
  245. 'name' => null );// user has been deleted
  246. }
  247. }
  248. if ( isset( $params['fetchClassIcon'] ) )
  249. {
  250. $operator = new eZWordToImageOperator();
  251. $tpl = eZTemplate::instance();
  252. $operatorValue = $contentObject->attribute( 'class_identifier' );
  253. $operatorParameters = array( array( array( 1, 'small' ) ) );
  254. $namedParameters = array();
  255. $operatorName = 'class_icon';
  256. $operator->modify(
  257. $tpl, $operatorName, $operatorParameters, '', '',
  258. $operatorValue, $namedParameters, array()
  259. );
  260. $ret['class_icon'] = $operatorValue;
  261. }
  262. if ( isset( $params['fetchThumbPreview'] ) )
  263. {
  264. $thumbUrl = '';
  265. $thumbWidth = 0;
  266. $thumbHeight = 0;
  267. $thumbDataType = isset( $params['thumbDataType'] ) ? $params['thumbDataType'] : 'ezimage';
  268. $thumbImageSize = isset( $params['thumbImageSize'] ) ? $params['thumbImageSize'] : 'small';
  269. foreach( $contentObject->attribute( 'data_map' ) as $key => $atr )
  270. {
  271. if ( $atr->attribute( 'data_type_string' ) == $thumbDataType
  272. && $atr->attribute( 'has_content' ) )
  273. {
  274. $imageContent = $atr->attribute( 'content' );
  275. if ( $imageContent->hasAttribute( $thumbImageSize ) )
  276. $imageAlias = $imageContent->attribute( $thumbImageSize );
  277. else
  278. eZDebug::writeError( "Image alias does not exist: '{$thumbImageSize}', missing from image.ini?",
  279. __METHOD__ );
  280. $thumbUrl = isset( $imageAlias['full_path'] ) ? $imageAlias['full_path'] : '';
  281. $thumbWidth = isset( $imageAlias['width'] ) ? (int) $imageAlias['width'] : 0;
  282. $thumbHeight = isset( $imageAlias['height'] ) ? (int) $imageAlias['height'] : 0;
  283. if ( $thumbUrl !== '' )
  284. {
  285. eZURI::transformURI( $thumbUrl, true, null, false );
  286. }
  287. break;
  288. }
  289. }
  290. $ret['thumbnail_url'] = $thumbUrl;
  291. $ret['thumbnail_width'] = $thumbWidth;
  292. $ret['thumbnail_height'] = $thumbHeight;
  293. }
  294. if ( $params['fetchSection'] )
  295. {
  296. $section = eZSection::fetch( $ret['section_id'] );
  297. if ( $section instanceof eZSection )
  298. {
  299. $ret['section'] = array(
  300. 'id' => $section->attribute('id'),
  301. 'name' => $section->attribute('name'),
  302. 'navigation_part_identifier' => $section->attribute('navigation_part_identifier'),
  303. 'locale' => $section->attribute('locale'),
  304. );
  305. }
  306. else
  307. {
  308. $ret['section'] = null;
  309. }
  310. }
  311. if ( $node )
  312. {
  313. // optimization for eZ Publish 4.1 (avoid fetching class)
  314. if ( $node->hasAttribute( 'is_container' ) )
  315. {
  316. $ret['class_identifier'] = $node->attribute( 'class_identifier' );
  317. $ret['is_container'] = (int) $node->attribute( 'is_container' );
  318. }
  319. else
  320. {
  321. $class = $contentObject->attribute( 'content_class' );
  322. $ret['class_identifier'] = $class->attribute( 'identifier' );
  323. $ret['is_container'] = (int) $class->attribute( 'is_container' );
  324. }
  325. $ret['node_id'] = (int) $node->attribute( 'node_id' );
  326. $ret['parent_node_id'] = (int) $node->attribute( 'parent_node_id' );
  327. $ret['node_remote_id'] = $node->attribute( 'remote_id' );
  328. $ret['url_alias'] = $node->attribute( 'url_alias' );
  329. $ret['url'] = $node->url();
  330. // force system url on empty urls (root node)
  331. if ( $ret['url'] === '' )
  332. $ret['url'] = 'content/view/full/' . $node->attribute( 'node_id' );
  333. eZURI::transformURI( $ret['url'] );
  334. $ret['depth'] = (int) $node->attribute( 'depth' );
  335. $ret['priority'] = (int) $node->attribute( 'priority' );
  336. $ret['hidden_status_string'] = $node->attribute( 'hidden_status_string' );
  337. if ( $params['fetchPath'] )
  338. {
  339. $ret['path'] = array();
  340. foreach ( $node->attribute( 'path' ) as $n )
  341. {
  342. $ret['path'][] = self::simplify( $n );
  343. }
  344. }
  345. else
  346. {
  347. $ret['path'] = false;
  348. }
  349. if ( $params['fetchChildrenCount'] )
  350. {
  351. $ret['children_count'] = $ret['is_container'] ? (int) $node->attribute( 'children_count' ) : 0;
  352. }
  353. else
  354. {
  355. $ret['children_count'] = false;
  356. }
  357. }
  358. else
  359. {
  360. $class = $contentObject->attribute( 'content_class' );
  361. $ret['class_identifier'] = $class->attribute( 'identifier' );
  362. $ret['is_container'] = (int) $class->attribute( 'is_container' );
  363. }
  364. $ret['image_attributes'] = array();
  365. if ( is_array( $params['dataMap'] ) && is_array( $params['dataMapType'] ) )
  366. {
  367. $dataMap = $contentObject->attribute( 'data_map' );
  368. $datatypeBlacklist = array_fill_keys(
  369. $ini->variable( 'ContentSettings', 'DatatypeBlackListForExternal' ),
  370. true
  371. );
  372. foreach( $dataMap as $key => $atr )
  373. {
  374. $dataTypeString = $atr->attribute( 'data_type_string' );
  375. //if ( in_array( $dataTypeString, $params['imageDataTypes'], true) !== false )
  376. if ( !in_array( 'all' ,$params['dataMap'], true )
  377. && !in_array( $key ,$params['dataMap'], true )
  378. && !in_array( $dataTypeString, $params['dataMapType'], true )
  379. && !( $params['loadImages'] && in_array( $dataTypeString, $params['imageDataTypes'], true ) ) )
  380. {
  381. continue;
  382. }
  383. $attributeArray[ $key ]['id'] = $atr->attribute( 'id' );
  384. $attributeArray[ $key ]['type'] = $dataTypeString;
  385. $attributeArray[ $key ]['identifier'] = $key;
  386. if ( isset ( $datatypeBlacklist[$dataTypeString] ) )
  387. $attributeArray[ $key ]['content'] = null;
  388. else
  389. $attributeArray[ $key ]['content'] = $atr->toString();
  390. // images
  391. if ( in_array( $dataTypeString, $params['imageDataTypes'], true) && $atr->hasContent() )
  392. {
  393. $content = $atr->attribute( 'content' );
  394. $imageArray = array();
  395. if ( $content != null )
  396. {
  397. foreach( $params['imageSizes'] as $size )
  398. {
  399. $imageArray[ $size ] = false;
  400. if ( in_array( $size, $params['imagePreGenerateSizes'], true ) )
  401. {
  402. if ( $content->hasAttribute( $size ) )
  403. $imageArray[ $size ] = $content->attribute( $size );
  404. else
  405. eZDebug::writeError( "Image alias does not exist: '$size', missing from image.ini?",
  406. __METHOD__ );
  407. }
  408. }
  409. $ret['image_attributes'][] = $key;
  410. }
  411. if ( !isset( $imageArray['original'] ) )
  412. $imageArray['original'] = $content->attribute( 'original' );
  413. array_walk_recursive(
  414. $imageArray,
  415. function ( &$element, $key )
  416. {
  417. // These fields can contain non utf-8 content
  418. // badly handled by mb_check_encoding
  419. // so they are just encoded in base64
  420. // see https://jira.ez.no/browse/EZP-21358
  421. if ( $key == "MakerNote" || $key == "UserComment")
  422. {
  423. $element = base64_encode( (string)$element );
  424. }
  425. // json_encode/xmlEncode need UTF8 encoded strings
  426. // (exif) metadata might not be for instance
  427. // see https://jira.ez.no/browse/EZP-19929
  428. else if ( !mb_check_encoding( $element, 'UTF-8' ) )
  429. {
  430. $element = mb_convert_encoding(
  431. (string)$element, 'UTF-8'
  432. );
  433. }
  434. if( $key === 'url' )
  435. {
  436. eZURI::transformURI( $element, true );
  437. }
  438. }
  439. );
  440. $attributeArray[ $key ]['content'] = $imageArray;
  441. }
  442. }
  443. }
  444. $ret['data_map'] = $attributeArray;
  445. return $ret;
  446. }
  447. /**
  448. * Encodes simple multilevel array and hash values to valid xml string
  449. *
  450. * @param mixed $hash
  451. * @param string $childName
  452. * @return string
  453. */
  454. public static function xmlEncode( $hash, $childName = 'child' )
  455. {
  456. $xml = new XmlWriter();
  457. $xml->openMemory();
  458. $xml->startDocument('1.0', 'UTF-8');
  459. $xml->startElement('root');
  460. self::xmlWrite( $xml, $hash, $childName );
  461. $xml->endElement();
  462. return $xml->outputMemory( true );
  463. }
  464. /**
  465. * Recursive xmlWriter function called by xmlEncode
  466. *
  467. * @param XMLWriter $xml
  468. * @param mixed $hash
  469. * @param string $childName
  470. */
  471. protected static function xmlWrite( XMLWriter $xml, $hash, $childName = 'child' )
  472. {
  473. foreach( $hash as $key => $value )
  474. {
  475. if( is_array( $value ) )
  476. {
  477. if ( is_numeric( $key ) )
  478. $xml->startElement( $childName );
  479. else
  480. $xml->startElement( $key );
  481. self::xmlWrite( $xml, $value );
  482. $xml->endElement();
  483. continue;
  484. }
  485. if ( is_numeric( $key ) )
  486. {
  487. $xml->writeElement( $childName, $value );
  488. }
  489. else
  490. {
  491. $xml->writeElement( $key, $value );
  492. }
  493. }
  494. }
  495. /**
  496. * Format date timestamp according to currently used locale code
  497. * Allowed are following types:
  498. * - time
  499. * - shorttime
  500. * - date
  501. * - shortdate
  502. * - datetime
  503. * - shortdatetime
  504. *
  505. * @param integer $timestamp
  506. * @param string $type
  507. * @return string
  508. */
  509. public static function formatLocaleDate( $timestamp, $type )
  510. {
  511. $formattedDate = null;
  512. $locale = eZLocale::instance();
  513. $method = $locale->getFormattingFunction( $type );
  514. if ( $method )
  515. $formattedDate = $locale->$method( $timestamp );
  516. return $formattedDate;
  517. }
  518. }
  519. ?>