PageRenderTime 74ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/db/library/dbscript/model.php

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