PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/db/library/dbscript/model.php

https://github.com/tjgillies/dbscript
PHP | 1147 lines | 815 code | 191 blank | 141 comment | 205 complexity | 0738df38a73975735d18e1d635ce6754 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /**
  3. * dbscript -- restful openid framework
  4. * @version 0.6.0 -- 22-October-2008
  5. * @author Brian Hendrickson <brian@dbscript.net>
  6. * @link http://dbscript.net/
  7. * @copyright Copyright 2008 Brian Hendrickson
  8. * @package dbscript
  9. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  10. */
  11. /**
  12. * Data Model
  13. *
  14. * Describes a database table: fields, rules and relationships.
  15. *
  16. * Automatically composes simple JOIN queries for relationships
  17. * described in the models via has_many, has_one, etc.
  18. *
  19. * Usage:
  20. * <code>
  21. * // define a new table named photos
  22. * $model =& $db->model( 'Photo' );
  23. *
  24. * // define the fields in the table
  25. * $model->auto_field( 'id' );
  26. * $model->file_field( 'image' );
  27. * $model->char_field( 'title', array('len'=>255) );
  28. * $model->text_field( 'caption' );
  29. *
  30. * // create the table in the database
  31. * $model->save();
  32. *
  33. * // deny access to everybody, unless they are an administrator
  34. * $model->let_access( 'all:never all:admin' );
  35. *
  36. * // function to test whether current user is an administrator
  37. * function admin() {
  38. * return true;
  39. * }
  40. * </code>
  41. *
  42. * More info...
  43. * {@link http://dbscript.net/model}
  44. *
  45. * @package dbscript
  46. * @author Brian Hendrickson <brian@dbscript.net>
  47. * @access public
  48. * @version 0.6.0 -- 22-October-2008
  49. */
  50. class Model {
  51. /**
  52. * name of the database table
  53. * @var string
  54. */
  55. var $table;
  56. /**
  57. * true if table exists in database
  58. * @var boolean
  59. */
  60. var $exists;
  61. /**
  62. * list of field names
  63. * @var string[]
  64. */
  65. var $field_array;
  66. /**
  67. * list of field attributes
  68. * @var string[]
  69. */
  70. var $field_attrs;
  71. /**
  72. * list of security access rules
  73. * @var string[]
  74. */
  75. var $access_list;
  76. /**
  77. * table has entry_id field
  78. * @var boolean
  79. */
  80. var $has_metadata;
  81. /**
  82. * name of primary key field
  83. * @var string
  84. */
  85. var $primary_key;
  86. /**
  87. * name of collection key field
  88. * @var string
  89. */
  90. var $uri_key;
  91. /**
  92. * list of relationships to other tables
  93. * @var string[]
  94. */
  95. var $relations;
  96. /**
  97. * proper CamelCase name of data model object
  98. * @var string
  99. */
  100. var $custom_class;
  101. /**
  102. * default blob field for the model
  103. * @var string
  104. */
  105. var $blob;
  106. /**
  107. * list of public methods
  108. * @var string[]
  109. */
  110. var $allowed_methods;
  111. /**
  112. * when querying with find(), offset by x records
  113. * @var integer
  114. */
  115. var $offset;
  116. /**
  117. * limit query to x records
  118. * @var integer
  119. */
  120. var $limit;
  121. /**
  122. * order query by this column
  123. * @var string
  124. */
  125. var $orderby;
  126. /**
  127. * order query ASC or DESC
  128. * @var string
  129. */
  130. var $order;
  131. /**
  132. * list of Collection/Feed params for layout
  133. * @var string[]
  134. */
  135. var $params;
  136. /**
  137. * hide this table
  138. * @var boolean
  139. */
  140. var $hidden;
  141. var $find_by;
  142. function insert_from_post( &$req ) {
  143. trigger_before( 'insert_from_post', $this, $req );
  144. global $db;
  145. $fields = $this->fields_from_request($req);
  146. foreach ($fields as $table=>$fieldlist) {
  147. // for each table in the submission do
  148. $pkfield = $db->models[$table]->primary_key;
  149. // blank record
  150. $rec = $db->models[$table]->base();
  151. $content_type = 'text/html';
  152. // set attributes
  153. $mdl =& $db->get_table($table);
  154. if (!($mdl->can_create( $table )))
  155. trigger_error( "Sorry, you do not have permission to " . $req->action . " " . $table, E_USER_ERROR );
  156. foreach ( $fieldlist as $field=>$type ) {
  157. if ($this->has_metadata && is_blob($table.'.'.$field)) {
  158. if (isset($_FILES[strtolower(classify($table))]['name'][$field]))
  159. $content_type = type_of( $_FILES[strtolower(classify($table))]['name'][$field] );
  160. }
  161. $rec->set_value( $field, $req->params[strtolower(classify($table))][$field] );
  162. }
  163. // save
  164. if ($table != $this->table) {
  165. $relfield = strtolower(classify($this->table))."_id";
  166. if (isset($mdl->field_array[$relfield])) {
  167. if ($req->params['id'] > 0) {
  168. $rec->set_value($relfield,$req->params['id']);
  169. }
  170. }
  171. }
  172. $result = $rec->save_changes();
  173. if ( !$result )
  174. trigger_error( "The record could not be saved into the database.", E_USER_ERROR );
  175. if ( $this->has_metadata ) {
  176. $atomentry = $db->models['entries']->base();
  177. if ($atomentry) {
  178. $atomentry->set_value( 'etag', getEtag( $rec->$pkfield ) );
  179. $atomentry->set_value( 'resource', $table );
  180. $atomentry->set_value( 'record_id', $rec->$pkfield );
  181. $atomentry->set_value( 'content_type', $content_type );
  182. $atomentry->set_value( 'last_modified', timestamp() );
  183. $atomentry->set_value( 'person_id', get_person_id() );
  184. $aresult = $atomentry->save_changes();
  185. if ($aresult) {
  186. if ( array_key_exists( 'entry_id', $rec->attributes ))
  187. $rec->set_value( 'entry_id', $atomentry->id );
  188. if ( array_key_exists( 'person_id', $rec->attributes ))
  189. $rec->set_value( 'person_id', get_person_id() );
  190. $rec->save_changes();
  191. }
  192. }
  193. if (($rec->table == $this->table) && isset($rec->id)) {
  194. $req->set_param( 'id', $rec->id );
  195. $req->id = $rec->id;
  196. $Category =& $db->model('Category');
  197. $Entry =& $db->model('Entry');
  198. foreach($req->params as $cname=>$catval) {
  199. if (substr($cname,0,8) == 'category') {
  200. $added = array();
  201. if (!in_array($req->$cname, $added)) {
  202. $join =& $db->get_table($Entry->join_table_for('categories', 'entries'));
  203. $j = $join->base();
  204. $j->set_value('entry_id',$atomentry->id);
  205. $c = $Category->find_by('term',$req->$cname);
  206. if ($c) {
  207. $j->set_value('category_id',$c->id);
  208. $j->save_changes();
  209. $added[] = $req->$cname;
  210. } elseif (!empty($req->$cname)) {
  211. if (isset_admin_email()) {
  212. $c = $Category->base();
  213. $c->set_value( 'name', $req->$cname);
  214. $c->set_value( 'term', strtolower($req->$cname));
  215. $c->save();
  216. $j->set_value('category_id',$c->id);
  217. $j->save_changes();
  218. $added[] = $req->$cname;
  219. admin_alert( "created a new category: ".$req->$cname." at ".$req->base );
  220. }
  221. }
  222. }
  223. }
  224. }
  225. }
  226. }
  227. }
  228. trigger_after( 'insert_from_post', $this, $rec );
  229. }
  230. function update_from_post( &$req ) {
  231. trigger_before( 'update_from_post', $this, $req );
  232. global $db;
  233. $fields = $this->fields_from_request($req);
  234. if (isset($fields[$req->resource]))
  235. $fieldsarr = $fields[$req->resource];
  236. if (!(isset($fieldsarr)))
  237. trigger_error( "The fields were not found in the request.".print_r($fields), E_USER_ERROR );
  238. if ( $this->has_metadata ) {
  239. $Person =& $db->model('Person');
  240. $Group =& $db->model('Group');
  241. if (!(isset($req->params['entry']['etag'])))
  242. trigger_error( "Sorry, the etag was not submitted with the database entry", E_USER_ERROR );
  243. $atomentry = $db->models['entries']->find_by( 'etag', $req->params['entry']['etag'] );
  244. if (!$atomentry->exists) {
  245. $atomentry = $db->models['entries']->base();
  246. $atomentry->set_value( 'etag', getEtag( srand(date("s")) ) );
  247. $atomentry->set_value( 'resource', $req->resource );
  248. $atomentry->set_value( 'record_id', $rec->$pkfield );
  249. $atomentry->set_value( 'content_type', $content_type );
  250. $atomentry->set_value( 'last_modified', timestamp() );
  251. $atomentry->set_value( 'person_id', get_person_id() );
  252. $aresult = $atomentry->save_changes();
  253. }
  254. $p = $Person->find( get_person_id() );
  255. if (!($p->id == $atomentry->attributes['person_id']) && !$this->can_superuser($req->resource))
  256. trigger_error( "Sorry, your id does not match the owner of the database entry", E_USER_ERROR );
  257. $recid = $atomentry->attributes['record_id'];
  258. if (empty($recid))
  259. trigger_error('The input form eTag did not match a record_id in entries.', E_USER_ERROR);
  260. } else {
  261. $recid = $req->id;
  262. if (empty($recid))
  263. trigger_error('The record id was not found in the "id" form field.', E_USER_ERROR);
  264. }
  265. $rec = $this->find( $recid );
  266. foreach ( $fieldsarr as $field=>$type ) {
  267. if ($this->has_metadata && is_blob($rec->table.'.'.$field)) {
  268. if (isset($_FILES[strtolower(classify($rec->table))]['name'][$field])) {
  269. if ( $this->has_metadata ) {
  270. $content_type = type_of( $_FILES[strtolower(classify($rec->table))]['name'][$field] );
  271. $atomentry->set_value( 'content_type', $content_type );
  272. }
  273. }
  274. }
  275. $rec->set_value( $field, $req->params[strtolower(classify($rec->table))][$field] );
  276. }
  277. $result = $rec->save_changes();
  278. foreach ($fields as $table=>$fieldlist) {
  279. // for each table in the submission do
  280. $mdl =& $db->get_table($table);
  281. if (!($mdl->can_write_fields( $fieldlist )))
  282. trigger_error( "Sorry, you do not have permission to " . $req->action . " " . $table, E_USER_ERROR );
  283. if (!(in_array( $table, array('entries',$rec->table), true ))) {
  284. $rel = $rec->FirstChild( $table );
  285. foreach ($fieldlist as $field=>$type)
  286. $rel->set_value( $field, $req->params[strtolower(classify($table))][$field] );
  287. $rel->save_changes();
  288. }
  289. }
  290. if ($result) {
  291. $req->set_param( 'id', $rec->id );
  292. if ( $this->has_metadata ) {
  293. $atomentry->set_value( 'last_modified', timestamp() );
  294. $atomentry->save_changes();
  295. }
  296. } else {
  297. trigger_error( "The record could not be updated in the database.", E_USER_ERROR );
  298. }
  299. trigger_after( 'update_from_post', $this, $rec );
  300. }
  301. function delete_from_post( &$req ) {
  302. trigger_before( 'delete_from_post', $this, $req );
  303. global $db;
  304. if ($this->has_metadata && !(isset($req->params['entry']['etag'])))
  305. trigger_error( "Sorry, the etag was not submitted with the database entry", E_USER_ERROR );
  306. $fields = $this->fields_from_request($req);
  307. if ($this->has_metadata) {
  308. $atomentry = $db->models['entries']->find_by( 'etag', $req->params['entry']['etag'] );
  309. $recid = $atomentry->attributes['record_id'];
  310. } else {
  311. $recid = $req->id;
  312. }
  313. $rec = $this->find( $recid );
  314. if ($this->has_metadata) {
  315. $Person =& $db->model('Person');
  316. $Group =& $db->model('Group');
  317. $p = $Person->find( get_person_id() );
  318. if (!($p->id == $atomentry->attributes['person_id']) && !$this->can_superuser($req->resource))
  319. trigger_error( "Sorry, your id does not match the owner of the database entry", E_USER_ERROR );
  320. }
  321. $coll = environment('collection_cache');
  322. if ($this->has_metadata && isset($coll[$req->resource]) && $coll[$req->resource]['location'] == 'aws') {
  323. $ext = extension_for($atomentry->content_type);
  324. $pkname = $rec->primary_key;
  325. $aws_file = $rec->table . $rec->$pkname . "." . $ext;
  326. lib_include( 'S3' );
  327. $s3 = new S3( environment('awsAccessKey'), environment('awsSecretKey') );
  328. if (!$s3)
  329. trigger_error( 'Sorry, there was a problem connecting to Amazon Web Services', E_USER_ERROR );
  330. if (!($s3->deleteObject(environment('awsBucket'), urlencode($aws_file))))
  331. trigger_error( 'Sorry, there was a problem deleting the file from Amazon Web Services', E_USER_ERROR );
  332. }
  333. $result = $db->delete_record($rec);
  334. trigger_after( 'delete_from_post', $this, $req );
  335. }
  336. function fields_from_request( &$req ) {
  337. trigger_before('fields_from_request',$this,$this);
  338. global $db;
  339. $fields = array();
  340. $obj = strtolower(classify($this->table));
  341. foreach ($this->field_array as $fieldname=>$datatypename) {
  342. if (isset($req->params[$obj][$fieldname])) {
  343. $fields[$this->table][$fieldname] = $datatypename;
  344. }
  345. }
  346. foreach ($this->field_array as $fieldname=>$datatypename) {
  347. if (isset($_FILES[$obj]['name'][$fieldname])) {
  348. $fields[$this->table][$fieldname] = $datatypename;
  349. $req->params[$obj][$fieldname] = $_FILES[$obj]['tmp_name'][$fieldname];
  350. }
  351. }
  352. foreach ($this->relations as $table=>$vals) {
  353. if ( isset( $db->models[$table] )) {
  354. $obj = strtolower(classify($table));
  355. foreach ( $db->models[$table]->field_array as $fieldname=>$datatypename ) {
  356. if (!($table == 'entries') && isset($req->params[$obj][$fieldname]))
  357. $fields[$table][$fieldname] = $datatypename;
  358. }
  359. foreach ( $db->models[$table]->field_array as $fieldname=>$datatypename ) {
  360. if (!($table == 'entries') && isset($_FILES[$obj]['name'][$fieldname])){
  361. $fields[$table][$fieldname] = $datatypename;
  362. $req->params[$obj][$fieldname] = $_FILES[$obj]['tmp_name'][$fieldname];
  363. }
  364. }
  365. }
  366. }
  367. return $fields;
  368. }
  369. function db_field( $field, $alias ) {
  370. trigger_before('db_field',$this,$this);
  371. if (!(isset($this->$alias) && isset($this->data_array[$field])))
  372. $this->$alias =& $this->data_array[$field];
  373. }
  374. function exists() {
  375. return ( count( $this->field_array ) > 0 );
  376. }
  377. function base() {
  378. trigger_before('base',$this,$this);
  379. global $db;
  380. return new Record($this->table,$db);
  381. }
  382. function register( $table ) {
  383. trigger_before('register',$this,$this);
  384. global $db;
  385. if (!(isset($this->table)))
  386. $this->table = $table;
  387. if (!(isset($this->access_list)))
  388. $this->access_list = array();
  389. if (!(isset($this->relations)))
  390. $this->relations = array();
  391. if (!(isset($this->allowed_methods)))
  392. $this->allowed_methods = array( 'get', 'post', 'put', 'delete' );
  393. if (!(isset($this->field_array)))
  394. $this->field_array = $db->get_fields( $this->table );
  395. if ( array_key_exists( 'entry_id', $this->field_array ))
  396. $this->has_metadata = true;
  397. else
  398. $this->has_metadata = false;
  399. $this->hidden = false;
  400. if ( count( $this->field_array ) > 0 )
  401. $this->exists = true;
  402. else
  403. $this->exists = false;
  404. if (isset($this->field_array[$this->table."_primary_key"])) {
  405. $this->set_primary_key( $this->field_array[$this->table."_primary_key"] );
  406. $this->field_array = drop_array_element($this->field_array,$this->table."_primary_key");
  407. }
  408. if (!(in_array($this->table,array('db_sessions'))))
  409. $this->save();
  410. foreach ($this->relations as $table=>$vals) {
  411. //$this->field_array[$field]
  412. $custom_class = classify($table);
  413. if (class_exists($custom_class)) {
  414. if ($table != 'entries') {
  415. if (isset($db->tables[$table]))
  416. $obj =& $db->tables[$table];
  417. else
  418. $obj = new $custom_class();
  419. if ( array_key_exists( 'target_id', $obj->field_array )) {
  420. if ( array_key_exists( 'entry_id', $this->field_array )) {
  421. $k = 'entry_id';
  422. $fk = $table.'.target_id';
  423. $this->relations[$table]['col'] = $k;
  424. $this->relations[$table]['fkey'] = $fk;
  425. }
  426. }
  427. }
  428. }
  429. }
  430. }
  431. function class_init() {
  432. trigger_before('class_init',$this,$this);
  433. if (method_exists( $this, 'init' ))
  434. $this->init();
  435. }
  436. function set_field( $field, $data_type, $arr=NULL ) {
  437. $this->field_array[$field] = $data_type;
  438. if (!($arr == NULL))
  439. $this->field_attributes( $field, $arr );
  440. }
  441. function set_uri_key( $field ) {
  442. $this->uri_key = $field;
  443. }
  444. function set_primary_key( $field ) {
  445. trigger_before('set_primary_key',$this,$this);
  446. $this->primary_key = $field;
  447. if (!$this->uri_key)
  448. $this->uri_key = $field;
  449. }
  450. function field_attributes( $field, $arr ) {
  451. $this->set_attribute( $arr, $field );
  452. }
  453. function let_access( $fields ) {
  454. trigger_before('let_access',$this,$this);
  455. $this->let_read( $fields );
  456. $this->let_write( $fields );
  457. $this->let_create( $fields );
  458. $this->let_delete( $fields );
  459. $this->let_superuser( $fields );
  460. }
  461. function let_read( $fields ) {
  462. trigger_before('let_read',$this,$this);
  463. $args = explode( " ", $fields );
  464. if (!(count($args)>0)) trigger_error( "invalid data model access rule", E_USER_ERROR );
  465. foreach ( $args as $str) {
  466. $pair = split( ":", $str );
  467. if (!(count($pair)==2)) trigger_error( "invalid data model access rule", E_USER_ERROR );
  468. if ($pair[0] == 'all') {
  469. foreach ( $this->field_array as $field => $data_type ) {
  470. if (!(isset($this->access_list['read'][$field][$pair[1]])))
  471. $this->access_list['read'][$field][] = $pair[1];
  472. }
  473. } else {
  474. if (!(isset($this->access_list['read'][$pair[0]][$pair[1]])))
  475. $this->access_list['read'][$pair[0]][] = $pair[1];
  476. }
  477. }
  478. }
  479. function let_write( $fields ) {
  480. trigger_before('let_write',$this,$this);
  481. $args = explode( " ", $fields );
  482. if (!(count($args)>0)) trigger_error( "invalid data model access rule", E_USER_ERROR );
  483. foreach ( $args as $str) {
  484. $pair = split( ":", $str );
  485. if (!(count($pair)==2)) trigger_error( "invalid data model access rule", E_USER_ERROR );
  486. if ($pair[0] == 'all') {
  487. foreach ( $this->field_array as $field => $data_type ) {
  488. if (!(isset($this->access_list['write'][$field][$pair[1]])))
  489. $this->access_list['write'][$field][] = $pair[1];
  490. }
  491. } else {
  492. if (!(isset($this->access_list['write'][$pair[0]][$pair[1]])))
  493. $this->access_list['write'][$pair[0]][] = $pair[1];
  494. }
  495. }
  496. }
  497. function let_create( $fields ) {
  498. trigger_before('let_create',$this,$this);
  499. $args = explode( " ", $fields );
  500. if (!(count($args)>0)) trigger_error( "invalid data model access rule", E_USER_ERROR );
  501. $this->let_write( $fields );
  502. foreach ( $args as $str) {
  503. $pair = split( ":", $str );
  504. if (!(count($pair)==2)) trigger_error( "invalid data model access rule", E_USER_ERROR );
  505. if (!(isset($this->access_list['create'][$this->table][$pair[1]])))
  506. $this->access_list['create'][$this->table][] = $pair[1];
  507. }
  508. }
  509. function let_post( $fields ) {
  510. trigger_before('let_post',$this,$this);
  511. $this->let_create( $fields );
  512. }
  513. function let_modify( $fields ) {
  514. trigger_before('let_modify',$this,$this);
  515. $this->let_write( $fields );
  516. }
  517. function let_put( $fields ) {
  518. trigger_before('let_put',$this,$this);
  519. $this->let_write( $fields );
  520. }
  521. function let_delete( $fields ) {
  522. trigger_before('let_delete',$this,$this);
  523. $args = explode( " ", $fields );
  524. if (!(count($args)>0)) trigger_error( "invalid data model access rule", E_USER_ERROR );
  525. foreach ( $args as $str) {
  526. $pair = split( ":", $str );
  527. if (!(count($pair)==2)) trigger_error( "invalid data model access rule", E_USER_ERROR );
  528. if (!(isset($this->access_list['delete'][$this->table][$pair[1]])))
  529. $this->access_list['delete'][$this->table][] = $pair[1];
  530. }
  531. }
  532. function let_superuser( $fields ) {
  533. trigger_before('let_superuser',$this,$this);
  534. $args = explode( " ", $fields );
  535. if (!(count($args)>0)) trigger_error( "invalid data model access rule", E_USER_ERROR );
  536. foreach ( $args as $str) {
  537. $pair = split( ":", $str );
  538. if (!(count($pair)==2)) trigger_error( "invalid data model access rule", E_USER_ERROR );
  539. if (!(isset($this->access_list['superuser'][$this->table][$pair[1]])))
  540. $this->access_list['superuser'][$this->table][] = $pair[1];
  541. }
  542. }
  543. function set_action( $method ) {
  544. trigger_before('set_action',$this,$this);
  545. $this->allowed_methods[] = $method;
  546. }
  547. function set_param( $param, $value ) {
  548. $this->$param = $value;
  549. }
  550. function set_blob( $value ) {
  551. $this->blob = $value;
  552. }
  553. function is_allowed( $method ) {
  554. trigger_before('is_allowed',$this,$this);
  555. return in_array( $method, $this->allowed_methods, true );
  556. return false;
  557. }
  558. function has_and_belongs_to_many( $relstring ) {
  559. $this->set_relation( $relstring, 'child-many' );
  560. }
  561. function belongs_to( $relstring ) {
  562. $this->set_relation( $relstring, 'child-one' );
  563. }
  564. function has_many( $relstring ) {
  565. $this->set_relation( $relstring, 'parent-many' );
  566. }
  567. function has_one( $relstring ) {
  568. $this->set_relation( $relstring, 'parent-one' );
  569. }
  570. function validates_presence_of( $field ) {
  571. $this->set_attribute( 'required', $field );
  572. }
  573. function validates_uniqueness_of( $field ) {
  574. $this->set_attribute( 'unique', $field );
  575. }
  576. function is_unique_value( $value, $field ) {
  577. global $db;
  578. $value = $db->escape_string($value);
  579. $result = $db->get_result("select $field from ".$this->table." where ".$field." = '$value'");
  580. return (!($result && $db->num_rows($result) > 0));
  581. }
  582. function set_attribute( $attr, $field ) {
  583. if (is_array($attr))
  584. $this->field_attrs[$field]['values'] = $attr;
  585. else
  586. $this->field_attrs[$field][$attr] = true;
  587. }
  588. function set_hidden() {
  589. $this->hidden = true;
  590. }
  591. function foreign_key_for( $table ) {
  592. trigger_before('foreign_key_for',$this,$this);
  593. global $db;
  594. if (!(isset($db->models[$table]))) {
  595. $fields = $db->get_fields( $table );
  596. if (isset($fields[$table."_primary_key"]))
  597. $pk = $fields[$table."_primary_key"];
  598. else
  599. $pk = 'id';
  600. } else {
  601. $fields =& $db->models[$table]->field_array;
  602. $pk = $db->models[$table]->primary_key;
  603. }
  604. if (array_key_exists(
  605. strtolower(classify($table))."_".$pk, $this->field_array )) {
  606. return $pk;
  607. } elseif ( array_key_exists(
  608. strtolower(classify($this->table))."_".$this->primary_key, $fields )) {
  609. return strtolower(classify($this->table))."_".$this->primary_key;
  610. } else {
  611. return $pk;
  612. }
  613. }
  614. function join_table_for( $t1, $t2 ) {
  615. trigger_before('join_table_for',$this,$this);
  616. if ($t1 < $t2)
  617. return $t1 . "_" . $t2;
  618. else
  619. return $t2 . "_" . $t1;
  620. }
  621. function set_relation( $relstring, $type ) {
  622. //echo $this->table . " " .$relstring."<BR>";
  623. if (!(isset($this->table)))
  624. $this->table = tableize( get_class( $this ));
  625. global $db;
  626. $f = split( ":", $relstring );
  627. if (count($f)==2) {
  628. $k = $f[0];
  629. $fk = $f[1];
  630. } else {
  631. if ( array_key_exists( $relstring.'_id', $this->field_array ))
  632. $k = $relstring.'_id';
  633. else
  634. $k = $this->primary_key;
  635. $fk = $relstring;
  636. }
  637. $fo = split("\.",$fk);
  638. if (count($fo)==2) {
  639. $table = tableize($fo[0]);
  640. $fkk = $fo[1];
  641. } else {
  642. $table = tableize($fk);
  643. $fk = $table.".".$this->foreign_key_for( $table );
  644. }
  645. if ($type == 'child-many') {
  646. if (!$db->table_exists($this->join_table_for($table, $this->table))) {
  647. $join =& $db->get_table($this->join_table_for($table, $this->table));
  648. if (!($join->exists)) {
  649. $join->int_field( strtolower(classify($this->table))."_".$k );
  650. $join->int_field( strtolower(classify($table))."_".$this->foreign_key_for( $table) );
  651. $join->save();
  652. }
  653. }
  654. }
  655. if (!(isset($this->relations[$table]))) {
  656. $this->relations[$table]['type'] = $type;
  657. $this->relations[$table]['col'] = $k;
  658. $this->relations[$table]['fkey'] = $fk;
  659. $this->relations[$table]['tab'] = $table;
  660. }
  661. }
  662. function can_write_fields( $fields ) {
  663. $return = false;
  664. foreach( $fields as $key=>$val ) {
  665. if ( $this->can_write( $key ) ) {
  666. $return = true;
  667. } else {
  668. return false;
  669. }
  670. }
  671. return $return;
  672. }
  673. function can_read_fields( $fields ) {
  674. $return = false;
  675. // array of field=>datatype
  676. foreach( $fields as $key=>$val ) {
  677. if ( $this->can_read( $key ) ) {
  678. $return = true;
  679. } else {
  680. return false;
  681. }
  682. }
  683. return $return;
  684. }
  685. function can_read( $resource ) {
  686. if (!(isset($this->access_list['read'][$resource]))) return false;
  687. foreach ( $this->access_list['read'][$resource] as $callback ) {
  688. if ( function_exists( $callback ) ) {
  689. if ($callback())
  690. return true;
  691. } else {
  692. if ( member_of( $callback ))
  693. return true;
  694. }
  695. }
  696. return false;
  697. }
  698. function can_write( $resource ) {
  699. if (!(isset($this->access_list['write'][$resource]))) return false;
  700. foreach ( $this->access_list['write'][$resource] as $callback ) {
  701. if ( function_exists( $callback ) ) {
  702. if ($callback())
  703. return true;
  704. } else {
  705. if ( member_of( $callback ))
  706. return true;
  707. }
  708. }
  709. return false;
  710. }
  711. function can_create( $resource ) {
  712. if (!(isset($this->access_list['create'][$resource]))) return false;
  713. foreach ( $this->access_list['create'][$resource] as $callback ) {
  714. if ( function_exists( $callback ) ) {
  715. if ($callback())
  716. return true;
  717. } else {
  718. if ( member_of( $callback ))
  719. return true;
  720. }
  721. }
  722. return false;
  723. }
  724. function can_delete( $resource ) {
  725. if (!(isset($this->access_list['delete'][$resource]))) return false;
  726. foreach ( $this->access_list['delete'][$resource] as $callback ) {
  727. if ( function_exists( $callback ) ) {
  728. if ($callback())
  729. return true;
  730. } else {
  731. if ( member_of( $callback ))
  732. return true;
  733. }
  734. }
  735. return false;
  736. }
  737. function can_superuser( $resource ) {
  738. if (!(isset($this->access_list['superuser'][$resource]))) return false;
  739. foreach ( $this->access_list['superuser'][$resource] as $callback ) {
  740. if ( function_exists( $callback ) ) {
  741. if ($callback())
  742. return true;
  743. } else {
  744. if ( member_of( $callback ))
  745. return true;
  746. }
  747. }
  748. return false;
  749. }
  750. function rewind() {
  751. $this->MoveFirst();
  752. }
  753. function set_routes($table) {
  754. trigger_before('set_routes',$this,$this);
  755. global $request;
  756. if (empty($table))
  757. trigger_error( 'no table name when creating a route in set_routes', E_USER_ERROR );
  758. $request->connect(
  759. $table,
  760. array(
  761. 'requirements' => array ( '[a-z]+' ),
  762. 'resource' => $table
  763. )
  764. );
  765. if ($request->resource == $table && $request->id > 1)
  766. $request->connect(
  767. classify($table),
  768. array(
  769. 'requirements' => array ( '[a-z]+', '[0-9]+' ),
  770. 'resource' => $table,
  771. 'id' => $request->id
  772. )
  773. );
  774. }
  775. function save() {
  776. trigger_before('save',$this,$this);
  777. global $db;
  778. trigger_before( 'save', $this, $db );
  779. if (!(isset($this->table)))
  780. $this->table = tableize( get_class( $this ));
  781. if ($this->table == 'models')
  782. return;
  783. if (!(isset($db->tables)))
  784. $db->tables = $db->get_tables();
  785. if ( !( in_array( $this->table, $db->tables ) ) ) {
  786. if (count($this->field_array)>0) {
  787. if (!(isset($this->primary_key)))
  788. $this->auto_field( 'id' );
  789. $db->add_table( $this->table, $this->field_array );
  790. $this->class_init();
  791. } else {
  792. return NULL;
  793. }
  794. }
  795. if ( !( isset( $this->primary_key )) && $this->table != 'db_sessions')
  796. trigger_error("The ".$this->table." table must have a primary key. Example: ".$this->table."->set_primary_key('field')".@mysql_error($this->conn), E_USER_ERROR );
  797. $this->exists = true;
  798. $this->set_routes( $this->table );
  799. trigger_after( 'save', $this, $db );
  800. }
  801. function migrate() {
  802. // schema sync
  803. global $db;
  804. if (empty($this->table))
  805. return;
  806. if (!(isset($db->tables)))
  807. $db->tables = $db->get_tables();
  808. if ( ( in_array( $this->table, $db->tables ) ) ) {
  809. $fields = $db->get_fields( $this->table );
  810. foreach ( $this->field_array as $field => $data_type ) {
  811. if ( !( array_key_exists( $field, $fields ) ) ) {
  812. $db->add_field( $this->table, $field, $data_type );
  813. }
  814. }
  815. }
  816. #if ( !( isset( $this->primary_key ))) {
  817. # if (isset($fields[$this->table."_primary_key"]))
  818. # $this->set_primary_key( $fields[$this->table."_primary_key"] );
  819. #}
  820. #foreach ( $fields as $field => $type ) {
  821. # if ( !( array_key_exists( $field, $this->field_array ) ) ) {
  822. # if ( !( $this->table."_primary_key" == $field ) ) {
  823. # $this->set_field( $field, $type );
  824. # }
  825. # }
  826. #}
  827. }
  828. function is_blob($field) {
  829. trigger_before('is_blob',$this,$this);
  830. global $db;
  831. return ( $db->get_mapped_datatype( $this->field_array[$field] ) === 'blob' );
  832. }
  833. function find( $id=NULL, $find_by=NULL ) {
  834. trigger_before('find',$this,$this);
  835. global $db;
  836. global $request;
  837. trigger_before( 'find', $this, $db );
  838. if (isset($this->find_by) && $find_by == NULL)
  839. $find_by = $this->find_by;
  840. if (isset($this->id) && $id == NULL)
  841. $find_by = $this->find_by;
  842. if ($id != NULL)
  843. $id = $db->escape_string($id);
  844. if ($find_by != NULL)
  845. foreach ($find_by as $k=>$v)
  846. $v = $db->escape_string($v);
  847. // special index-find subselect behavior for (metadata) tables (tables with a target_id field)
  848. if (( strstr( $request->action, "index" )) && array_key_exists( 'target_id', $this->field_array ))
  849. $find_by = 'target_id';
  850. $db->recordsets[$this->table] = $db->get_recordset($this->get_query($id, $find_by));
  851. $rs =& $db->recordsets[$this->table];
  852. unset($this->find_by);
  853. unset($this->id);
  854. if (!$rs) return false;
  855. if ( $id != NULL && $rs->rowcount > 0 )
  856. if ( $find_by != NULL )
  857. return $rs->Load( $this->table, 0 );
  858. else
  859. return $rs->Load( $this->table, $rs->rowmap[$this->table][$id] );
  860. trigger_after( 'find', $this, $db );
  861. return false;
  862. }
  863. function find_by( $col, $val = 1 ) {
  864. trigger_before('find_by',$this,$this);
  865. return $this->find( $val, $col );
  866. }
  867. function MoveFirst() {
  868. trigger_before('MoveFirst',$this,$this);
  869. global $db;
  870. if (!(isset($db->recordsets[$this->table])))
  871. $this->find();
  872. $rs =& $db->recordsets[$this->table];
  873. if (!$rs) return false;
  874. return $rs->MoveFirst( $this->table );
  875. }
  876. function MoveNext() {
  877. trigger_before('MoveNext',$this,$this);
  878. global $db;
  879. if (!(isset($db->recordsets[$this->table])))
  880. $this->find();
  881. $rs =& $db->recordsets[$this->table];
  882. if (!$rs) return false;
  883. return $rs->MoveNext( $this->table );
  884. }
  885. function session_exists() {
  886. if (isset($_SESSION[$this->table."_submission"]))
  887. return true;
  888. return false;
  889. }
  890. function set_limit( $limit ) {
  891. if ( $limit > 0 ) $this->limit = $limit;
  892. }
  893. function set_offset( $offset ) {
  894. if ( $offset > 0 ) $this->offset = $offset;
  895. }
  896. function set_groupby( $col ) {
  897. if ( strlen( $col ) > 0 ) $this->groupby = $this->table . "." . $col;
  898. }
  899. function set_orderby( $col ) {
  900. if ( strlen( $col ) > 0 ) $this->orderby = $this->table . "." . $col;
  901. }
  902. function set_order( $order ) {
  903. if ( strlen( $order ) > 0 ) $this->order = $order;
  904. }
  905. function rowcount() {
  906. global $db;
  907. $rs =& $db->recordsets[$this->table];
  908. if (!$rs) return 0;
  909. return $rs->num_rows( $this->table );
  910. }
  911. function auto_field( $field, $options = null ) {
  912. global $db;
  913. $db->auto_field( $field, $this, $options );
  914. }
  915. function enum_field( $field, $options = null ) {
  916. global $db;
  917. $db->enum_field( $field, $this, $options );
  918. }
  919. function float_field( $field, $options = null ) {
  920. global $db;
  921. $db->float_field( $field, $this, $options );
  922. }
  923. function bool_field( $field, $options = null ) {
  924. global $db;
  925. $db->bool_field( $field, $this, $options );
  926. }
  927. function char_field( $field, $options = null ) {
  928. global $db;
  929. $db->char_field( $field, $this, $options );
  930. }
  931. function date_field( $field, $options = null ) {
  932. global $db;
  933. $db->date_field( $field, $this, $options );
  934. }
  935. function file_field( $field, $options = null ) {
  936. global $db;
  937. $db->file_field( $field, $this, $options );
  938. }
  939. function int_field( $field, $options = null ) {
  940. global $db;
  941. $db->int_field( $field, $this, $options );
  942. }
  943. function text_field( $field, $options = null ) {
  944. global $db;
  945. $db->text_field( $field, $this, $options );
  946. }
  947. function time_field( $field, $options = null ) {
  948. global $db;
  949. $db->time_field( $field, $this, $options );
  950. }
  951. function get_query( $id=NULL, $find_by=NULL ) {
  952. global $db;
  953. return $db->get_query( $id, $find_by, $this );
  954. }
  955. }
  956. ?>