TripalEntityCollection.inc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. <?php
  2. class TripalEntityCollection {
  3. /**
  4. * The name of the bundles (i.e. content type) to which the entities belong.
  5. */
  6. protected $bundles = array();
  7. /**
  8. * The collection ID
  9. */
  10. protected $collection_id = NULL;
  11. /**
  12. * The name of this collection.
  13. */
  14. protected $collection_name = '';
  15. /**
  16. * An array of numeric entities IDs.
  17. */
  18. protected $ids = array();
  19. /**
  20. * An array of field IDs.
  21. */
  22. protected $fields = array();
  23. /**
  24. * The user object of the user that owns the collection.
  25. */
  26. protected $user = array();
  27. /**
  28. * The date that the collection was created.
  29. */
  30. protected $create_date = '';
  31. /**
  32. * The list of downloaders available for this bundle.
  33. */
  34. protected $downloaders = array();
  35. /**
  36. * The description for this collection.
  37. */
  38. protected $description = '';
  39. /**
  40. * Constructs a new instance of the TripalEntityCollection class.
  41. */
  42. public function __construct() {
  43. }
  44. /**
  45. * Deletes the current collection
  46. */
  47. public function delete() {
  48. if (!$this->collection_id) {
  49. throw new Exception('This data collection object has not yet been loaded. Cannot delete.');
  50. }
  51. try {
  52. // Delete from the tripal collection table.
  53. db_delete('tripal_collection')
  54. ->condition('collection_id', $this->collection_id)
  55. ->execute();
  56. // Delete the field groups from the tripal_bundle_collection table.
  57. db_delete('tripal_bundle_collection')
  58. ->condition('collection_id', $this->collection_id)
  59. ->execute();
  60. // Remove any files that may have been created
  61. foreach ($this->downloaders as $class_name => $label) {
  62. tripal_load_include_downloader_class($class_name);
  63. $outfile = $this->getOutfile($class_name);
  64. $downloader = new $class_name($this->bundles, $this->ids, $this->fields,
  65. $outfile, $this->getUserID());
  66. $downloader->delete();
  67. }
  68. // Reset the class to defaults.
  69. $this->collection_id = NULL;
  70. $this->collection_name = '';
  71. $this->create_date = '';
  72. $this->description = '';
  73. }
  74. catch (Exception $e) {
  75. throw new Exception('Cannot delete collection: ' . $e->getMessage());
  76. }
  77. }
  78. /**
  79. * Loads an existing collection using a collection ID.
  80. *
  81. * @param $collection_id
  82. * The ID of the collection to load.
  83. *
  84. * @throws Exception
  85. */
  86. public function load($collection_id) {
  87. // Make sure we have a numeric job_id.
  88. if (!$collection_id or !is_numeric($collection_id)) {
  89. throw new Exception("You must provide the collection_id to load the collection.");
  90. }
  91. $collection = db_select('tripal_collection', 'tc')
  92. ->fields('tc')
  93. ->condition('collection_id', $collection_id)
  94. ->execute()
  95. ->fetchObject();
  96. if (!$collection) {
  97. throw new Exception("Cannot find a collection with the ID provided.");
  98. }
  99. // Fix the date/time fields.
  100. $this->collection_name = $collection->collection_name;
  101. $this->create_date = $collection->create_date;
  102. $this->user = user_load($collection->uid);
  103. $this->description = $collection->description;
  104. $this->collection_id = $collection->collection_id;
  105. /* Add the IDs, Fields, Bundles for this collection from the
  106. * collection_bundle table.
  107. */
  108. $this->bundles = $this->getBundles();
  109. // If more than one bundle plop into associative array.
  110. $bundle_name = "";
  111. if (count($this->bundles) > 1) {
  112. foreach ($this->bundles as $bundle) {
  113. $bundle_name = $bundle->bundle_name;
  114. $ids[$bundle_name] = $this->getEntityIDs($bundle_name);
  115. $fields[$bundle_name] = $this->getFields($bundle_name);
  116. }
  117. $this->ids = $ids;
  118. $this->fields = $fields;
  119. }
  120. else {
  121. if (!empty($this->bundles)) {
  122. $bundle_name = $this->bundles[0]->bundle_name;
  123. $this->ids = $this->getEntityIDs($bundle_name);
  124. $this->fields = $this->getFields($bundle_name);
  125. }
  126. }
  127. // Iterate through the fields and find out what download formats are
  128. // supported for this basket.
  129. foreach ($this->fields as $field_group) {
  130. foreach ($field_group as $field_id) {
  131. // Check is $field_groups is an array because if it is that means we
  132. // nested arrays we need to deal with.
  133. if (is_array($field_id)) {
  134. foreach ($field_id as $field) {
  135. $field_info = field_info_field_by_id($field);
  136. if (!$field_info) {
  137. continue;
  138. }
  139. $field_name = $field_info['field_name'];
  140. $field_type = $field_info['type'];
  141. $field_module = $field_info['module'];
  142. $instance = field_info_instance('TripalEntity', $field_name, $bundle_name);
  143. $downloaders = array();
  144. // All fields should support the Tab and CSV downloaders.
  145. tripal_load_include_downloader_class('TripalTabDownloader');
  146. $this->downloaders['TripalTabDownloader'] = TripalTabDownloader::$label;
  147. tripal_load_include_downloader_class('TripalCSVDownloader');
  148. $this->downloaders['TripalCSVDownloader'] = TripalCSVDownloader::$label;
  149. if (tripal_load_include_field_class($field_type)) {
  150. $settings = $field_type::$default_instance_settings;
  151. if (array_key_exists('download_formatters', $settings)) {
  152. foreach ($settings['download_formatters'] as $class_name) {
  153. if (!array_key_exists($class_name, $this->downloaders)) {
  154. tripal_load_include_downloader_class($class_name);
  155. $this->downloaders[$class_name] = $class_name::$label;
  156. }
  157. }
  158. }
  159. }
  160. }
  161. }
  162. else {
  163. $field = field_info_field_by_id($field_id);
  164. if (!$field) {
  165. continue;
  166. }
  167. $field_name = $field['field_name'];
  168. $field_type = $field['type'];
  169. $field_module = $field['module'];
  170. $instance = field_info_instance('TripalEntity', $field_name, $bundle_name);
  171. $downloaders = array();
  172. // All fields should support the Tab and CSV downloaders.
  173. tripal_load_include_downloader_class('TripalTabDownloader');
  174. $this->downloaders['TripalTabDownloader'] = TripalTabDownloader::$label;
  175. tripal_load_include_downloader_class('TripalCSVDownloader');
  176. $this->downloaders['TripalCSVDownloader'] = TripalCSVDownloader::$label;
  177. if (tripal_load_include_field_class($field_type)) {
  178. $settings = $field_type::$default_instance_settings;
  179. if (array_key_exists('download_formatters', $settings)) {
  180. foreach ($settings['download_formatters'] as $class_name) {
  181. if (!array_key_exists($class_name, $this->downloaders)) {
  182. tripal_load_include_downloader_class($class_name);
  183. $this->downloaders[$class_name] = $class_name::$label;
  184. }
  185. }
  186. }
  187. }
  188. }
  189. }
  190. }
  191. }
  192. /**
  193. * Creates a new unique collection ID used as a look up against the
  194. * tripal_collection_bundle to find fields, ids, and bundles.
  195. *
  196. * @param $details
  197. * An association array containing the details for a collection. The
  198. * details must include the following key/value pairs:
  199. * - uid: The ID of the user that owns the collection
  200. * - collection_name: The name of the collection
  201. * - description: A user supplied description for the collection.
  202. *
  203. * @throws Exception
  204. */
  205. public function create($details) {
  206. if (!$details['uid']) {
  207. throw new Exception("Must provide a 'uid' key to TripalEntityCollection::create().");
  208. }
  209. if (!$details['collection_name']) {
  210. throw new Exception("Must provide a 'collection_name' key to TripalEntityCollection::create().");
  211. }
  212. // Before inserting the new collection make sure we don't violote the unique
  213. // constraint that a user can only have one collection of the give name.
  214. $has_match = db_select('tripal_collection', 'tc')
  215. ->fields('tc', array('collection_id'))
  216. ->condition('uid', $details['uid'])
  217. ->condition('collection_name', $details['collection_name'])
  218. ->execute()
  219. ->fetchField();
  220. if ($has_match) {
  221. throw new Exception('Cannot create the collection. One with this name already exists');
  222. }
  223. try {
  224. $collection_id = db_insert('tripal_collection')
  225. ->fields(array(
  226. 'collection_name' => $details['collection_name'],
  227. 'create_date' => time(),
  228. 'uid' => $details['uid'],
  229. 'description' => array_key_exists('description', $details) ? $details['description'] : '',
  230. ))
  231. ->execute();
  232. // Now add the second table with bundle info.
  233. $this->addFields($details, $collection_id);
  234. // Now load the job into this object.
  235. $this->load($collection_id);
  236. }
  237. catch (Exception $e) {
  238. throw new Exception('Cannot create collection: ' . $e->getMessage());
  239. }
  240. }
  241. /**
  242. * Creates a new tripal_collection_bundle entry.
  243. *
  244. * @param $details
  245. * An association array containing the details for a collection. The
  246. * details must include the following key/value pairs:
  247. * - bundle_name: The name of the TripalEntity content type.
  248. * - ids: An array of the entity IDs that form the collection.
  249. * - fields: An array of the field IDs that the collection is limited to.
  250. *
  251. * @throws Exception
  252. */
  253. public function addFields($details, $collection_id) {
  254. if (!$details['bundle_name']) {
  255. throw new Exception("Must provide a 'bundle_name' key to TripalEntityCollection::add().");
  256. }
  257. if (!$details['ids']) {
  258. throw new Exception("Must provide a 'ids' key to TripalEntityCollection::add().");
  259. }
  260. if (!$details['fields']) {
  261. throw new Exception("Must provide a 'fields' key to TripalEntityCollection::add().");
  262. }
  263. try {
  264. $collection_bundle_id = db_insert('tripal_collection_bundle')
  265. ->fields(array(
  266. 'bundle_name' => $details['bundle_name'],
  267. 'ids' => serialize($details['ids']),
  268. 'fields' => serialize($details['fields']),
  269. 'collection_id' => $collection_id,
  270. ))
  271. ->execute();
  272. // Now load the job into this object.
  273. //$this->load($collection_bundle_id);
  274. }
  275. catch (Exception $e) {
  276. throw new Exception('Cannot create collection: ' . $e->getMessage());
  277. }
  278. }
  279. /**
  280. * Retrieves the list of bundles associated with the collection.
  281. *
  282. * @return
  283. * An array of bundles.
  284. */
  285. public function getBundles() {
  286. $collection_id = $this->collection_id;
  287. // Return the bundles from the collection_bundle table.
  288. $result = db_select('tripal_collection_bundle')
  289. ->fields('tripal_collection_bundle', array('bundle_name'))
  290. ->condition('collection_id', $collection_id, '=')
  291. ->execute()
  292. ->fetchAll();
  293. return $result;
  294. }
  295. /**
  296. * Retrieves the list of appropriate download formatters for the basket.
  297. *
  298. * @return
  299. * An associative array where the key is the TripalFieldDownloader class
  300. * name and the value is the human-readable lable for the formatter.
  301. */
  302. public function getDownloadFormatters() {
  303. return $this->downloaders;
  304. }
  305. /**
  306. * Retrieves the list of entity IDs.
  307. *
  308. * @return
  309. * An array of numeric enity IDs.
  310. */
  311. public function getEntityIDs($bundle_name) {
  312. $collection_id = $this->collection_id;
  313. // Return the bundles from the collection_bundle table.
  314. $result = db_select('tripal_collection_bundle')
  315. ->fields('tripal_collection_bundle', array('ids'))
  316. ->condition('collection_id', $collection_id, '=')
  317. ->condition('bundle_name', $bundle_name, '=')
  318. ->execute()
  319. ->fetchAll();
  320. // Unserialize the array of standard class objects.
  321. $unserialized_result = [];
  322. foreach ($result as $id_list) {
  323. $unserialized_id_list = unserialize($id_list->ids);
  324. foreach ($id_list as $item) {
  325. $unserialized_result[] = $unserialized_id_list;
  326. }
  327. }
  328. return $unserialized_result;
  329. }
  330. /**
  331. * Retrieves the list of fields in the basket.
  332. *
  333. * @return
  334. * An array of numeric field IDs.
  335. */
  336. public function getFields($bundle_name) {
  337. $collection_id = $this->collection_id;
  338. // Return the bundles from the collection_bundle table.
  339. $result = db_select('tripal_collection_bundle')
  340. ->fields('tripal_collection_bundle', array('fields'))
  341. ->condition('collection_id', $collection_id, '=')
  342. ->condition('bundle_name', $bundle_name, '=')
  343. ->execute()
  344. ->fetchAll();
  345. // Unserialize the array of standard class objects.
  346. $unserialized_result = [];
  347. foreach ($result as $field_list) {
  348. $unserialized_field_list = unserialize($field_list->fields);
  349. foreach ($field_list as $item) {
  350. $unserialized_result[] = $unserialized_field_list;
  351. }
  352. }
  353. return $unserialized_result;
  354. }
  355. /**
  356. * Retrieves the date that the basket was created.
  357. *
  358. * @param $formatted
  359. * If TRUE then the date time will be formatted for human readability.
  360. * @return
  361. * A UNIX time stamp string containing the date or a human-readable
  362. * string if $formatted = TRUE.
  363. */
  364. public function getCreateDate($formatted = TRUE) {
  365. if ($formatted) {
  366. return format_date($this->create_date);
  367. }
  368. return $this->create_date;
  369. }
  370. /**
  371. * Retreives the name of the collection.
  372. *
  373. * @return
  374. * A string containing the name of the collection.
  375. */
  376. public function getName() {
  377. return $this->collection_name;
  378. }
  379. /**
  380. * Retrieves the collection ID.
  381. *
  382. * @return
  383. * A numeric ID for this collection.
  384. */
  385. public function getCollectionID(){
  386. return $this->collection_id;
  387. }
  388. /**
  389. * Retreives the collection description
  390. *
  391. * @return
  392. * A string containing the description of the collection.
  393. */
  394. public function getDescription() {
  395. return $this->description;
  396. }
  397. /**
  398. * Retrieves the user object of the user that owns the collection
  399. *
  400. * @return
  401. * A Drupal user object.
  402. */
  403. public function getUser() {
  404. return $this->user;
  405. }
  406. /**
  407. * Retrieves the ID of the user that owns the collection
  408. *
  409. * @return
  410. * The numeric User ID.
  411. */
  412. public function getUserID() {
  413. if ($this->user) {
  414. return $this->user->uid;
  415. }
  416. return NULL;
  417. }
  418. /**
  419. * Retrieves the output filename for the desired formatter.
  420. *
  421. * @param $formatter
  422. * The class name of the formatter to use. The formatter must
  423. * be compatible with the data collection.
  424. *
  425. * @throws Exception
  426. */
  427. public function getOutfile($formatter) {
  428. if(!$this->isFormatterCompatible($formatter)) {
  429. throw new Exception(t('The formatter, "%formatter", is not compatible with this data collection.', array('%formatter' => $formatter)));
  430. }
  431. if (!tripal_load_include_downloader_class($formatter)) {
  432. throw new Exception(t('Cannot find the formatter named "@formatter".', array('@formatter', $formatter)));
  433. }
  434. $extension = $formatter::$default_extension;
  435. $create_date = $this->getCreateDate(FALSE);
  436. $outfile = preg_replace('/[^\w]/', '_', ucwords($this->collection_name)) . '_collection' . '_' . $create_date . '.' . $extension;
  437. return $outfile;
  438. }
  439. /**
  440. * Indicates if the given formatter is compatible with the data collection.
  441. *
  442. * @param $formatter
  443. * The class name of the formatter to check.
  444. * @return boolean
  445. * TRUE if the formatter is compatible, FALSE otherwise.
  446. */
  447. public function isFormatterCompatible($formatter) {
  448. foreach ($this->downloaders as $class_name => $label) {
  449. if ($class_name == $formatter) {
  450. return TRUE;
  451. }
  452. }
  453. return FALSE;
  454. }
  455. /**
  456. * Writes the collection to all file downloadable formats.
  457. *
  458. * @throws Exception
  459. */
  460. public function writeAll() {
  461. foreach ($this->downloaders as $class_name => $label) {
  462. $this->write($class_name);
  463. }
  464. }
  465. /**
  466. * Retrieves the URL for the downloadable file.
  467. *
  468. * @param $formatter
  469. * The name of the class
  470. */
  471. public function getOutfileURL($formatter) {
  472. $outfile = $this->getOutfilePath($formatter);
  473. }
  474. /**
  475. * Retrieves the path for the downloadable file.
  476. *
  477. * The path is in the Drupal URI format.
  478. *
  479. * @param $formatter
  480. * The name of the class
  481. */
  482. public function getOutfilePath($formatter) {
  483. if(!$this->isFormatterCompatible($formatter)) {
  484. throw new Exception(t('The formatter, "@formatter", is not compatible with this data collection.', array('@formatter' => $formatter)));
  485. }
  486. if (!tripal_load_include_downloader_class($formatter)) {
  487. throw new Exception(t('Cannot find the formatter named "@formatter".', array('@formatter', $formatter)));
  488. }
  489. $outfile = $this->getOutfile($formatter);
  490. // Make sure the user directory exists
  491. $user_dir = 'public://tripal/users/' . $this->user->uid;
  492. $outfilePath = $user_dir. '/' . $outfile;
  493. return $outfilePath;
  494. }
  495. /**
  496. * Writes the collection to a file.
  497. *
  498. * @param formatter
  499. * The name of the formatter class to use (e.g. TripalTabDownloader). The
  500. * formatter must be compatible with the data collection.
  501. *
  502. * @throws Exception
  503. */
  504. public function write($formatter) {
  505. if (!$this->isFormatterCompatible($formatter)) {
  506. throw new Exception(t('The formatter, "@formatter", is not compatible with this data collection.', array('@formatter' => $formatter)));
  507. }
  508. if (!tripal_load_include_downloader_class($formatter)) {
  509. throw new Exception(t('Cannot find the formatter named "@formatter".', array('@formatter', $formatter)));
  510. }
  511. $outfile = $this->getOutfile($formatter);
  512. // Filter out fields that aren't supported by the formatter.
  513. $supported_fields = array();
  514. foreach ($this->fields as $field_group) {
  515. foreach ($field_group as $field_id) {
  516. // Check is $field_id is an array because if it is that means we
  517. // nested arrays we need to deal with.
  518. if (is_array($field_id)) {
  519. foreach ($field_id as $field) {
  520. // If the formatter is TripalTabDownloader or TripalCSVDownloader then
  521. // we always want to support the field.
  522. if ($formatter == 'TripalTabDownloader' or $formatter == 'TripalCSVDownloader') {
  523. if (!in_array($field, $supported_fields)) {
  524. $supported_fields[] = $field;
  525. }
  526. continue;
  527. }
  528. // Otherwise, find out if the formatter specified is supporte by the
  529. // field and if so then add it to our list of supported fields.
  530. $field_info = field_info_field_by_id($field);
  531. $field_name = $field_info['field_name'];
  532. $field_type = $field_info['type'];
  533. if (tripal_load_include_field_class($field_type)) {
  534. $settings = $field_type::$default_instance_settings;
  535. if (array_key_exists('download_formatters', $settings)) {
  536. if (in_array($formatter, $settings['download_formatters'])) {
  537. $supported_fields[] = $field;
  538. }
  539. }
  540. }
  541. }
  542. }
  543. else {
  544. // If the formatter is TripalTabDownloader or TripalCSVDownloader then
  545. // we always want to support the field.
  546. if ($formatter == 'TripalTabDownloader' or $formatter == 'TripalCSVDownloader') {
  547. if (!in_array($field_id, $supported_fields)) {
  548. $supported_fields[] = $field_id;
  549. }
  550. continue;
  551. }
  552. // Otherwise, find out if the formatter specified is supporte by the
  553. // field and if so then add it to our list of supported fields.
  554. $field = field_info_field_by_id($field_id);
  555. $field_name = $field['field_name'];
  556. $field_type = $field['type'];
  557. if (tripal_load_include_field_class($field_type)) {
  558. $settings = $field_type::$default_instance_settings;
  559. if (array_key_exists('download_formatters', $settings)) {
  560. if (in_array($formatter, $settings['download_formatters'])) {
  561. $supported_fields[] = $field_id;
  562. }
  563. }
  564. }
  565. }
  566. }
  567. }
  568. $downloader = new $formatter($this->bundles, $this->ids, $supported_fields, $outfile, $this->user->uid);
  569. $downloader->write();
  570. }
  571. }