database.php 94 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068
  1. <?php
  2. /*
  3. FusionPBX
  4. Version: MPL 1.1
  5. The contents of this file are subject to the Mozilla Public License Version
  6. 1.1 (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.mozilla.org/MPL/
  9. Software distributed under the License is distributed on an "AS IS" basis,
  10. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. for the specific language governing rights and limitations under the
  12. License.
  13. The Original Code is FusionPBX
  14. The Initial Developer of the Original Code is
  15. Mark J Crane <[email protected]>
  16. Copyright (C) 2010 - 2022
  17. All Rights Reserved.
  18. Contributor(s):
  19. Mark J Crane <[email protected]>
  20. Luis Daniel Lucio Quiroz <[email protected]>
  21. */
  22. //define the database class
  23. if (!class_exists('database')) {
  24. class database {
  25. const TABLE_PREFIX = "v_";
  26. /**
  27. * Database connection
  28. * @access private
  29. * @var PDO object
  30. */
  31. private $db;
  32. /**
  33. * Driver to use.
  34. * @access private
  35. * @var string Can be pgsql, mysql, sqlite, odbc
  36. */
  37. private $driver;
  38. /**
  39. * Alias of driver.
  40. * @access private
  41. * @var string Can be pgsql, mysql, sqlite, odbc
  42. * @see $driver
  43. */
  44. private $type;
  45. /**
  46. * Host for database connection
  47. * @access private
  48. * @var string host name or IP address.
  49. */
  50. private $host;
  51. /**
  52. * Port number
  53. * @access private
  54. * @var int 1025 - 65534
  55. */
  56. private $port;
  57. /**
  58. * Database name
  59. * @access private
  60. * @var string
  61. */
  62. private $db_name;
  63. /**
  64. * Database security
  65. * @access private
  66. * @var boolean
  67. */
  68. private $db_secure;
  69. /**
  70. * Specifies the file name of the client SSL certificate
  71. * @access private
  72. * @var string full path
  73. */
  74. private $db_cert_authority;
  75. /**
  76. * Username used to connect
  77. * @access private
  78. * @var string
  79. */
  80. private $username;
  81. /**
  82. * Password used to connect
  83. * @access private
  84. * @var string
  85. */
  86. private $password;
  87. /**
  88. * Full path to file name.
  89. * @access private
  90. * @var string full path to file name
  91. */
  92. private $path;
  93. /**
  94. * Table name.
  95. * @access private
  96. * @var string sanitized
  97. */
  98. private $table;
  99. /**
  100. * Where clause(s) of an SQL statement.
  101. * <p>Array of arrays must be passed with each having the
  102. * following keys:
  103. * <ol><li>'name' - Any valid column name.</li>
  104. * <li>'operator' - Must be <b>one</b> of the following values: =, &gt;, &lt;, &gt;=, &lt;=, &lt;&gt;, !=</li>
  105. * <li>'value' - Value being matched</li></ol></p>
  106. * <p>Example Usage:</p>
  107. * <p><code>$db->where['SearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MySearchTerm'</code></p>
  108. * <p><code>$db->where['NextSearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyOtherSearchTerm'</code></p>
  109. * <p>Below is equivalent to the above.</p>
  110. * <p><code>$db->where[0] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyValue'</code></p>
  111. * <p><code>$db->where[1] = ['name'=>'MyColumn','operator'=>'=&gt;','value'=>'MyValue'</code></p>
  112. * @access private
  113. * @var array Two dimensional array of key value pairs
  114. * @see $order_by
  115. */
  116. public $where; //array
  117. /**
  118. * Order By clause(s) of an SQL statement.
  119. * <p>Array of arrays must be passed with each having the
  120. * following keys:
  121. * <ol><li>'name' - Any valid column name.</li>
  122. * <li>'operator' - Must be <b>one</b> of the following values: =, &gt;, &lt;, &gt;=, &lt;=, &lt;&gt;, !=</li>
  123. * <li>'value' - Value being matched</li></ol></p>
  124. * <p>Example Usage:</p>
  125. * <p><code>$db->where['SearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MySearchTerm'</code></p>
  126. * <p><code>$db->where['NextSearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyOtherSearchTerm'</code></p>
  127. * <p>Below is equivalent to the above.</p>
  128. * <p><code>$db->where[0] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyValue'</code></p>
  129. * <p><code>$db->where[1] = ['name'=>'MyColumn','operator'=>'=&gt;','value'=>'MyValue'</code></p>
  130. * @access private
  131. * @var array Two dimensional array of key value pairs
  132. * @see $where
  133. */
  134. public $order_by; //array
  135. /**
  136. * Ascending or Descending order.
  137. * @var string
  138. * @access private
  139. */
  140. private $order_type;
  141. /**
  142. * Numerical value to limit returned results.
  143. * @var int Used for 'LIMIT' in SQL statement.
  144. * @access private
  145. */
  146. private $limit;
  147. /**
  148. * Numerical value to offset returned results.
  149. * @var int Used for 'OFFSET' in SQL statement.
  150. * @access private
  151. */
  152. private $offset;
  153. /**
  154. * <p>Array of fields.</p>
  155. * <p>Fields are specified in 'name'=>'value' format.
  156. * <p>Used by {@link database::add() } and {@link database::update() }</p>
  157. * @access private
  158. * @var array Array of columns
  159. * @see database::add()
  160. * @see database::update()
  161. */
  162. private $fields;
  163. /**
  164. * Unknown property
  165. * @var unknown
  166. * @access private
  167. */
  168. private $count;
  169. /**
  170. * Unknown property
  171. * @var unknown
  172. * @access private
  173. */
  174. private $sql;
  175. /**
  176. * <p>Stores the result from the most recent query. The type will be based on what was requested.</p>
  177. * <p><b>NOTE:</b> If an error occurred on the last query the result is set to an empty string.</p>
  178. * @var mixed
  179. */
  180. private $result;
  181. /**
  182. * Stores the application name making the request.
  183. * @var string App name making database request.
  184. * @see $app_uuid
  185. * @access public
  186. */
  187. public $app_name;
  188. /**
  189. * Stores the application UUID making the request.
  190. * @var string
  191. * @see $app_name
  192. * @access public
  193. */
  194. public $app_uuid;
  195. /**
  196. * <p>Stores the domain UUID making the request.</p>
  197. * <p>This is defaulted to the Session domain UUID.</p>
  198. * @access private
  199. * @uses $_SESSION['domain_uuid'] <br>Default value upon object creation
  200. * @var string Domain UUID making request.
  201. */
  202. private $domain_uuid;
  203. /**
  204. * <p>Message for the query results.</p>
  205. * @var array Contains the message array after a query
  206. * @access private
  207. */
  208. private $message;
  209. /**
  210. * Called when the object is created
  211. */
  212. public function __construct() {
  213. if (!isset($this->domain_uuid) && isset($_SESSION['domain_uuid'])) {
  214. $this->domain_uuid = $_SESSION['domain_uuid'];
  215. }
  216. }
  217. /**
  218. * <p>Magic function called whenever a property is attempted to be set.</p>
  219. * <p>This is used to protect the values stored in the object properties.</p>
  220. * @param mixed $name Name of object property
  221. * @param mixed $value Value of property
  222. */
  223. public function __set($name,$value) {
  224. switch($name) {
  225. case 'name':
  226. case 'app_name':
  227. $this->app_name = self::sanitize($value);
  228. break;
  229. case 'message':
  230. if (is_array($value)) {
  231. $this->message = $value;
  232. } else {
  233. trigger_error('Message property must be set to array type', E_USER_ERROR);
  234. }
  235. break;
  236. case 'table':
  237. $this->table = self::sanitize($value);
  238. break;
  239. case 'db_name':
  240. $this->db_name = self::sanitize($value);
  241. break;
  242. case 'db':
  243. if ($name instanceof PDO) {
  244. $this->db = $value;
  245. } else {
  246. trigger_error('db property must be a PDO object!', E_USER_ERROR);
  247. }
  248. break;
  249. case 'count':
  250. break;
  251. case 'path':
  252. $value = realpath($value);
  253. if (file_exists($value)) {
  254. $this->path = $value;
  255. } else {
  256. trigger_error('Unable to find database path file!', E_USER_ERROR);
  257. }
  258. break;
  259. case 'db_cert_authority':
  260. if (!file_exists($value)) {
  261. trigger_error('db cert authority not found!', E_USER_WARNING);
  262. }
  263. $this->db_cert_authority = $value;
  264. break;
  265. case 'port':
  266. $value = (int)$value; // force cast to int
  267. if ($value > 1023 && $value < 65536) { $this->port = $value; } //valid values are 1024...65535
  268. else { trigger_error('Port not a valid range', E_USER_ERROR); }
  269. break;
  270. case 'app_uuid':
  271. case 'domain_uuid':
  272. if (is_uuid($value)) { $this->domain_uuid = $value; }
  273. break;
  274. case 'type':
  275. case 'driver':
  276. switch($value) {
  277. case 'pgsql':
  278. case 'mysql':
  279. case 'sqlite':
  280. case 'odbc':
  281. $this->type = $value;
  282. $this->driver = $value;
  283. break;
  284. default:
  285. trigger_error("Type/Driver must be set to pgsql,mysql,sqlite,odbc", E_USER_ERROR);
  286. break;
  287. }
  288. case 'offset':
  289. case 'limit':
  290. if (is_int($value)) {
  291. $this->$name = $value;
  292. } else {
  293. trigger_error('Offset or Limit not set to valid integer. Resetting to zero!', E_USER_WARNING);
  294. }
  295. break;
  296. case '':
  297. trigger_error('Database property must not be empty', E_USER_ERROR);
  298. break;
  299. case 'null':
  300. case null:
  301. trigger_error('Database property must not be null', E_USER_ERROR);
  302. break;
  303. case 'debug':
  304. $this->debug = $value;
  305. }
  306. }
  307. /**
  308. * Magic function called whenever a property is requested.
  309. * <p>If any case statement is removed then access to the variable will be removed.</p>
  310. * @param mixed $name object property
  311. * @return mixed
  312. */
  313. public function __get($name) {
  314. //remove any case statement below to remove access to the variable
  315. switch($name) {
  316. case 'name':
  317. return $this->app_name;
  318. case 'app_name':
  319. case 'app_uuid':
  320. case 'db':
  321. case 'db_cert_authority':
  322. case 'db_name':
  323. case 'db_secure':
  324. case 'domain_uuid':
  325. case 'driver':
  326. case 'fields':
  327. case 'host':
  328. case 'limit':
  329. case 'message':
  330. case 'offset':
  331. case 'order_by':
  332. case 'order_type':
  333. case 'password':
  334. case 'path':
  335. case 'port':
  336. case 'result':
  337. case 'sql':
  338. case 'table':
  339. case 'type':
  340. case 'username':
  341. case 'where':
  342. case 'debug':
  343. return $this->$name;
  344. case 'count':
  345. return $this->count();
  346. default:
  347. trigger_error('Object property not available', E_USER_ERROR);
  348. }
  349. }
  350. /**
  351. * <p>Connect to the database.</p>
  352. * <p>Database driver must be set before calling connect.</p>
  353. * <p>For types other than sqlite. Execution will stop on failure.</p>
  354. * @depends database::driver Alias of database::type.
  355. *
  356. */
  357. public function connect() {
  358. //set the include path
  359. $conf = glob("{/usr/local/etc,/etc}/fusionpbx/config.conf", GLOB_BRACE);
  360. set_include_path(parse_ini_file($conf[0])['document.root']);
  361. //parset the config.conf file
  362. $conf = parse_ini_file($conf[0]);
  363. //get the database connection settings
  364. $db_type = $conf['database.0.type'];
  365. $db_host = $conf['database.0.host'];
  366. $db_port = $conf['database.0.port'];
  367. $db_name = $conf['database.0.name'];
  368. $db_username = $conf['database.0.username'];
  369. $db_password = $conf['database.0.password'];
  370. //debug info
  371. //echo "db type:".$db_type."\n";
  372. //echo "db host:".$db_host."\n";
  373. //echo "db port:".$db_port."\n";
  374. //echo "db name:".$db_name."\n";
  375. //echo "db username:".$db_username."\n";
  376. //echo "db password:".$db_password."\n";
  377. //echo "db path:".$db_path."\n";
  378. //echo "</pre>\n";
  379. //set defaults
  380. if (!isset($this->driver) && isset($db_type)) { $this->driver = $db_type; }
  381. if (!isset($this->type) && isset($db_type)) { $this->type = $db_type; }
  382. if (!isset($this->host) && isset($db_host)) { $this->host = $db_host; }
  383. if (!isset($this->port) && isset($db_port)) { $this->port = $db_port; }
  384. if (!isset($this->db_name) && isset($db_name)) { $this->db_name = $db_name; }
  385. if (!isset($this->db_secure) && isset($db_secure)) {
  386. $this->db_secure = $db_secure;
  387. }
  388. else {
  389. $this->db_secure = false;
  390. }
  391. if (!isset($this->username) && isset($db_username)) { $this->username = $db_username; }
  392. if (!isset($this->password) && isset($db_password)) { $this->password = $db_password; }
  393. if (!isset($this->path) && isset($db_path)) { $this->path = $db_path; }
  394. if ($this->driver == "sqlite") {
  395. if (empty($this->db_name)) {
  396. $server_name = $_SERVER["SERVER_NAME"];
  397. $server_name = str_replace ("www.", "", $server_name);
  398. $db_name_short = $server_name;
  399. $this->db_name = $server_name.'.db';
  400. }
  401. else {
  402. $db_name_short = $this->db_name;
  403. }
  404. $this->path = realpath($this->path);
  405. if (file_exists($this->path.'/'.$this->db_name)) {
  406. //connect to the database
  407. $this->db = new PDO('sqlite:'.$this->path.'/'.$this->db_name); //sqlite 3
  408. //PRAGMA commands
  409. $this->db->query('PRAGMA foreign_keys = ON;');
  410. $this->db->query('PRAGMA journal_mode = wal;');
  411. //add additional functions to SQLite so that they are accessible inside SQL
  412. //bool PDO::sqliteCreateFunction ( string function_name, callback callback [, int num_args] )
  413. $this->db->sqliteCreateFunction('md5', 'php_md5', 1);
  414. $this->db->sqliteCreateFunction('unix_timestamp', 'php_unix_timestamp', 1);
  415. $this->db->sqliteCreateFunction('now', 'php_now', 0);
  416. $this->db->sqliteCreateFunction('sqlitedatatype', 'php_sqlite_data_type', 2);
  417. $this->db->sqliteCreateFunction('strleft', 'php_left', 2);
  418. $this->db->sqliteCreateFunction('strright', 'php_right', 2);
  419. }
  420. else {
  421. echo "not found";
  422. }
  423. }
  424. if ($this->driver == "mysql") {
  425. try {
  426. //mysql pdo connection
  427. if (strlen($this->host) == 0 && empty($this->port)) {
  428. //if both host and port are empty use the unix socket
  429. $this->db = new PDO("mysql:host=$this->host;unix_socket=/var/run/mysqld/mysqld.sock;dbname=$this->db_name", $this->username, $this->password);
  430. }
  431. else {
  432. if (empty($this->port)) {
  433. //leave out port if it is empty
  434. $this->db = new PDO("mysql:host=$this->host;dbname=$this->db_name;", $this->username, $this->password, array(
  435. PDO::ATTR_ERRMODE,
  436. PDO::ERRMODE_EXCEPTION
  437. ));
  438. }
  439. else {
  440. $this->db = new PDO("mysql:host=$this->host;port=$this->port;dbname=$this->db_name;", $this->username, $this->password, array(
  441. PDO::ATTR_ERRMODE,
  442. PDO::ERRMODE_EXCEPTION
  443. ));
  444. }
  445. }
  446. }
  447. catch (PDOException $error) {
  448. print "error: " . $error->getMessage() . "<br/>";
  449. die();
  450. }
  451. }
  452. if ($this->driver == "pgsql") {
  453. //database connection
  454. try {
  455. if (!empty($this->host)) {
  456. if (empty($this->port)) { $this->port = "5432"; }
  457. if ($this->db_secure === true) {
  458. $this->db = new PDO("pgsql:host=$this->host port=$this->port dbname=$this->db_name user=$this->username password=$this->password sslmode=verify-ca sslrootcert=$this->db_cert_authority");
  459. }
  460. else {
  461. $this->db = new PDO("pgsql:host=$this->host port=$this->port dbname=$this->db_name user=$this->username password=$this->password");
  462. }
  463. }
  464. else {
  465. $this->db = new PDO("pgsql:dbname=$this->db_name user=$this->username password=$this->password");
  466. }
  467. }
  468. catch (PDOException $error) {
  469. print "error: " . $error->getMessage() . "<br/>";
  470. die();
  471. }
  472. }
  473. if ($this->driver == "odbc") {
  474. //database connection
  475. try {
  476. $this->db = new PDO("odbc:".$this->db_name, $this->username, $this->password);
  477. }
  478. catch (PDOException $e) {
  479. echo 'Connection failed: ' . $e->getMessage();
  480. }
  481. }
  482. }
  483. /**
  484. * Returns the table names from the database.
  485. * @return array tables
  486. * @depends connect()
  487. */
  488. public function tables() {
  489. $result = [];
  490. //connect to the database if needed
  491. if (!$this->db) {
  492. $this->connect();
  493. }
  494. if ($this->type == "sqlite") {
  495. $sql = "SELECT name FROM sqlite_master ";
  496. $sql .= "WHERE type='table' ";
  497. $sql .= "order by name;";
  498. }
  499. if ($this->type == "pgsql") {
  500. $sql = "select table_name as name ";
  501. $sql .= "from information_schema.tables ";
  502. $sql .= "where table_schema='public' ";
  503. $sql .= "and table_type='BASE TABLE' ";
  504. $sql .= "order by table_name ";
  505. }
  506. if ($this->type == "mysql") {
  507. $sql = "show tables";
  508. }
  509. if ($this->type == "mssql") {
  510. $sql = "SELECT * FROM sys.Tables order by name asc";
  511. }
  512. $prep_statement = $this->db->prepare(check_sql($sql));
  513. $prep_statement->execute();
  514. $tmp = $prep_statement->fetchAll(PDO::FETCH_NAMED);
  515. if ($this->type == "pgsql" || $this->type == "sqlite" || $this->type == "mssql") {
  516. if (is_array($tmp)) {
  517. foreach ($tmp as &$row) {
  518. $result[]['name'] = $row['name'];
  519. }
  520. }
  521. }
  522. if ($this->type == "mysql") {
  523. if (is_array($tmp)) {
  524. foreach ($tmp as &$row) {
  525. $table_array = array_values($row);
  526. $result[]['name'] = $table_array[0];
  527. }
  528. }
  529. }
  530. return $result;
  531. }
  532. /**
  533. * Returns table information from the database.
  534. * @return array table info
  535. * @depends connect()
  536. */
  537. public function table_info() {
  538. //public $db;
  539. //public $type;
  540. //public $table;
  541. //public $name;
  542. //connect to the database if needed
  543. if (!$this->db) {
  544. $this->connect();
  545. }
  546. //get the table info
  547. if (empty($this->table)) { return false; }
  548. if ($this->type == "sqlite") {
  549. $sql = "PRAGMA table_info(".$this->table.");";
  550. }
  551. if ($this->type == "pgsql") {
  552. $sql = "SELECT ordinal_position, ";
  553. $sql .= "column_name, ";
  554. $sql .= "data_type, ";
  555. $sql .= "column_default, ";
  556. $sql .= "is_nullable, ";
  557. $sql .= "character_maximum_length, ";
  558. $sql .= "numeric_precision ";
  559. $sql .= "FROM information_schema.columns ";
  560. $sql .= "WHERE table_name = '".$this->table."' ";
  561. $sql .= "and table_catalog = '".$this->db_name."' ";
  562. $sql .= "ORDER BY ordinal_position; ";
  563. }
  564. if ($this->type == "mysql") {
  565. $sql = "DESCRIBE ".$this->table.";";
  566. }
  567. if ($this->type == "mssql") {
  568. $sql = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '".$this->table."'";
  569. }
  570. $prep_statement = $this->db->prepare($sql);
  571. $prep_statement->execute();
  572. //set the result array
  573. return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  574. }
  575. /**
  576. * Checks if the table exists in the database.
  577. * <p><b>Note:</b><br>
  578. * Table name must be sanitized. Otherwise, a warning will be
  579. * emitted and false will be returned.</p>
  580. * @param type $table_name Sanitized name of the table to search for.
  581. * @return boolean Returns <i>true</i> if the table exists and <i>false</i> if it does not.
  582. * @depends connect()
  583. */
  584. public function table_exists ($table_name) {
  585. if (self::sanitize($table_name) != $table_name) {
  586. trigger_error('Table Name must be sanitized', E_USER_WARNING);
  587. return false;
  588. }
  589. //connect to the database if needed
  590. if (!$this->db) {
  591. $this->connect();
  592. }
  593. //query table store to see if the table exists
  594. $sql = "";
  595. if ($this->type == "sqlite") {
  596. $sql .= "SELECT * FROM sqlite_master WHERE type='table' and name='$table_name' ";
  597. }
  598. if ($this->type == "pgsql") {
  599. $sql .= "select * from pg_tables where schemaname='public' and tablename = '$table_name' ";
  600. }
  601. if ($this->type == "mysql") {
  602. $sql .= "SELECT TABLE_NAME FROM information_schema.tables WHERE table_schema = '$db_name' and TABLE_NAME = '$table_name' ";
  603. }
  604. $prep_statement = $this->db->prepare($sql);
  605. $prep_statement->execute();
  606. $result = $prep_statement->fetchAll(PDO::FETCH_NAMED);
  607. if (count($result) > 0) {
  608. return true; //table exists
  609. }
  610. else {
  611. return false; //table doesn't exist
  612. }
  613. }
  614. /**
  615. * Queries {@link database::table_info()} to return the fields.
  616. * @access public
  617. * @return array Two dimensional array
  618. * @depends table_info()
  619. */
  620. public function fields() {
  621. //public $db;
  622. //public $type;
  623. //public $table;
  624. //public $name;
  625. //initialize the array
  626. $result = [];
  627. //get the table info
  628. $table_info = $this->table_info();
  629. //set the list of fields
  630. if ($this->type == "sqlite") {
  631. if (is_array($table_info)) {
  632. foreach($table_info as $row) {
  633. $result[]['name'] = $row['name'];
  634. }
  635. }
  636. }
  637. if ($this->type == "pgsql") {
  638. if (is_array($table_info)) {
  639. foreach($table_info as $row) {
  640. $result[]['name'] = $row['column_name'];
  641. }
  642. }
  643. }
  644. if ($this->type == "mysql") {
  645. if (is_array($table_info)) {
  646. foreach($table_info as $row) {
  647. $result[]['name'] = $row['Field'];
  648. }
  649. }
  650. }
  651. if ($this->type == "mssql") {
  652. if (is_array($table_info)) {
  653. foreach($table_info as $row) {
  654. $result[]['name'] = $row['COLUMN_NAME'];
  655. }
  656. }
  657. }
  658. //return the result array
  659. return $result;
  660. }
  661. /**
  662. * Searches database using the following object properties:
  663. * <ol>
  664. * <li>table - sanitized name of the table {@see database::table}</li>
  665. * <li>where - where clause {@see database::where}</li>
  666. * <li>order_by - order_by clause {@see database::order_by}</li>
  667. * <li>limit - limit clause {@see database::limit}</li>
  668. * <li>offset - offset clause {@see database::offset}</li>
  669. * </ol>
  670. * @return boolean
  671. * @depends connect()
  672. */
  673. public function find() {
  674. //connect;
  675. //table;
  676. //where;
  677. //order_by;
  678. //limit;
  679. //offset;
  680. //connect to the database if needed
  681. if (!$this->db) {
  682. $this->connect();
  683. }
  684. //get data from the database
  685. $sql = "select * from ".$this->table." ";
  686. if ($this->where) {
  687. $i = 0;
  688. if (is_array($this->where)) {
  689. foreach($this->where as $row) {
  690. //sanitize the name
  691. $array['name'] = self::sanitize($array['name']);
  692. //validate the operator
  693. switch ($row['operator']) {
  694. case "<": break;
  695. case ">": break;
  696. case "<=": break;
  697. case ">=": break;
  698. case "=": break;
  699. case "<>": break;
  700. case "!=": break;
  701. default:
  702. //invalid operator
  703. return false;
  704. }
  705. //build the sql
  706. if ($i == 0) {
  707. //$sql .= 'where '.$row['name']." ".$row['operator']." '".$row['value']."' ";
  708. $sql .= 'where '.$row['name']." ".$row['operator']." :".$row['name']." ";
  709. }
  710. else {
  711. //$sql .= "and ".$row['name']." ".$row['operator']." '".$row['value']."' ";
  712. $sql .= "and ".$row['name']." ".$row['operator']." :".$row['name']." ";
  713. }
  714. //add the name and value to the params array
  715. $params[$row['name']] = $row['value'];
  716. //increment $i
  717. $i++;
  718. }
  719. }
  720. }
  721. if (is_array($this->order_by)) {
  722. $sql .= "order by ";
  723. $i = 1;
  724. if (is_array($this->order_by)) {
  725. foreach($this->order_by as $row) {
  726. //sanitize the name
  727. $row['name'] = self::sanitize($row['name']);
  728. //sanitize the order
  729. switch ($row['order']) {
  730. case "asc":
  731. break;
  732. case "desc":
  733. break;
  734. default:
  735. $row['order'] = '';
  736. }
  737. //build the sql
  738. if (count($this->order_by) == $i) {
  739. $sql .= $row['name']." ".$row['order']." ";
  740. }
  741. else {
  742. $sql .= $row['name']." ".$row['order'].", ";
  743. }
  744. //increment $i
  745. $i++;
  746. }
  747. }
  748. }
  749. //limit
  750. if (isset($this->limit) && is_numeric($this->limit)) {
  751. $sql .= "limit ".$this->limit." ";
  752. }
  753. //offset
  754. if (isset($this->offset) && is_numeric($this->offset)) {
  755. $sql .= "offset ".$this->offset." ";
  756. }
  757. $prep_statement = $this->db->prepare($sql);
  758. if ($prep_statement) {
  759. $prep_statement->execute($params);
  760. $array = $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  761. unset($prep_statement);
  762. return $array;
  763. }
  764. else {
  765. return false;
  766. }
  767. }
  768. // Use this function to execute complex queries
  769. public function execute($sql, $parameters = null, $return_type = 'all') {
  770. //connect to the database if needed
  771. if (!$this->db) {
  772. $this->connect();
  773. }
  774. //set the error mode
  775. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  776. //execute the query, and return the results
  777. try {
  778. $prep_statement = $this->db->prepare($sql);
  779. if (is_array($parameters)) {
  780. $prep_statement->execute($parameters);
  781. }
  782. else {
  783. $prep_statement->execute();
  784. }
  785. $message["message"] = "OK";
  786. $message["code"] = "200";
  787. $message["sql"] = $sql;
  788. if (is_array($parameters)) {
  789. $message["parameters"] = $parameters;
  790. }
  791. $this->message = $message;
  792. //return the results
  793. switch($return_type) {
  794. case 'all':
  795. return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  796. case 'row':
  797. return $prep_statement->fetch(PDO::FETCH_ASSOC);
  798. case 'column';
  799. return $prep_statement->fetchColumn();
  800. default:
  801. return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  802. }
  803. }
  804. catch(PDOException $e) {
  805. $message["message"] = "Bad Request";
  806. $message["code"] = "400";
  807. $message["error"]["message"] = $e->getMessage();
  808. if ($this->debug["sql"]) {
  809. $message["sql"] = $sql;
  810. }
  811. if (is_array($parameters)) {
  812. $message["parameters"] = $parameters;
  813. }
  814. $this->message = $message;
  815. return false;
  816. }
  817. }
  818. public function add() {
  819. //connect to the database if needed
  820. if (!$this->db) {
  821. $this->connect();
  822. }
  823. //sanitize the table name
  824. //$this->table = self::sanitize($this->table); // no longer needed
  825. //count the fields
  826. $field_count = count($this->fields);
  827. //add data to the database
  828. $sql = "insert into ".$this->table;
  829. $sql .= " (";
  830. $i = 1;
  831. if (is_array($this->fields)) {
  832. foreach($this->fields as $name => $value) {
  833. $name = self::sanitize($name);
  834. if (count($this->fields) == $i) {
  835. $sql .= $name." \n";
  836. }
  837. else {
  838. $sql .= $name.", \n";
  839. }
  840. $i++;
  841. }
  842. }
  843. $sql .= ") \n";
  844. $sql .= "values \n";
  845. $sql .= "(\n";
  846. $i = 1;
  847. if (is_array($this->fields)) {
  848. foreach($this->fields as $name => $value) {
  849. $name = self::sanitize($name);
  850. if ($field_count == $i) {
  851. if (!empty($value)) {
  852. //$sql .= "'".$value."' ";
  853. $sql .= ":".$name." \n";
  854. $params[$name] = trim($value);
  855. }
  856. else {
  857. $sql .= "null \n";
  858. }
  859. }
  860. else {
  861. if (!empty($value)) {
  862. //$sql .= "'".$value."', ";
  863. $sql .= ":".$name.", \n";
  864. $params[$name] = trim($value);
  865. }
  866. else {
  867. $sql .= "null, \n";
  868. }
  869. }
  870. $i++;
  871. }
  872. }
  873. $sql .= ")\n";
  874. //execute the query, show exceptions
  875. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  876. //reduce prepared statement latency
  877. if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  878. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  879. }
  880. //prepare the sql and parameters and then execute the query
  881. try {
  882. //$this->sql = $sql;
  883. //$this->db->exec($sql);
  884. $prep_statement = $this->db->prepare($sql);
  885. $prep_statement->execute($params);
  886. }
  887. catch(PDOException $e) {
  888. echo "<b>Error:</b><br />\n";
  889. echo "<table>\n";
  890. echo "<tr>\n";
  891. echo "<td>\n";
  892. echo $e->getMessage();
  893. echo "</td>\n";
  894. echo "</tr>\n";
  895. echo "</table>\n";
  896. }
  897. unset($sql, $prep_statement, $this->fields);
  898. }
  899. public function update() {
  900. //connect to the database if needed
  901. if (!$this->db) {
  902. $this->connect();
  903. }
  904. //sanitize the table name
  905. //$this->table = self::sanitize($this->table); // no longer needed
  906. //udate the database
  907. $sql = "update ".$this->table." set ";
  908. $i = 1;
  909. if (is_array($this->fields)) {
  910. foreach($this->fields as $name => $value) {
  911. $name = self::sanitize($name);
  912. if (count($this->fields) == $i) {
  913. if (!empty($name) && $value == null) {
  914. $sql .= $name." = null ";
  915. }
  916. else {
  917. //$sql .= $name." = '".$value."' ";
  918. $sql .= $name." = :".$name." ";
  919. $params[$name] = trim($value);
  920. }
  921. }
  922. else {
  923. if (!empty($name) && $value == null) {
  924. $sql .= $name." = null, ";
  925. }
  926. else {
  927. //$sql .= $name." = '".$value."', ";
  928. $sql .= $name." = :".$name.", ";
  929. $params[$name] = trim($value);
  930. }
  931. }
  932. $i++;
  933. }
  934. }
  935. $i = 0;
  936. if (is_array($this->where)) {
  937. foreach($this->where as $row) {
  938. //sanitize the name
  939. $row['name'] = self::sanitize($row['name']);
  940. //validate the operator
  941. switch ($row['operator']) {
  942. case "<": break;
  943. case ">": break;
  944. case "<=": break;
  945. case ">=": break;
  946. case "=": break;
  947. case "<>": break;
  948. case "!=": break;
  949. default:
  950. //invalid operator
  951. return false;
  952. }
  953. //build the sql
  954. if ($i == 0) {
  955. //$sql .= $row['name']." ".$row['operator']." '".$row['value']."' ";
  956. $sql .= "where ".$row['name']." ".$row['operator']." :".$row['name']." ";
  957. }
  958. else {
  959. //$sql .= $row['name']." ".$row['operator']." '".$row['value']."' ";
  960. $sql .= "and ".$row['name']." ".$row['operator']." :".$row['name']." ";
  961. }
  962. //add the name and value to the params array
  963. $params[$row['name']] = $row['value'];
  964. //increment $i
  965. $i++;
  966. }
  967. }
  968. //$this->db->exec(check_sql($sql));
  969. $prep_statement = $this->db->prepare($sql);
  970. $prep_statement->execute($params);
  971. unset($prep_statement);
  972. unset($this->fields);
  973. unset($this->where);
  974. unset($sql);
  975. }
  976. public function delete(array $array) {
  977. //set the default value
  978. $retval = true;
  979. //return the array
  980. if (!is_array($array)) { return false; }
  981. //connect to the database if needed
  982. if (!$this->db) {
  983. $this->connect();
  984. }
  985. //set the message id
  986. $m = 0;
  987. //debug sql
  988. //$this->debug["sql"] = true;
  989. //set the message id
  990. $m = 0;
  991. //loop through the array
  992. $checked = false;
  993. $x = 0;
  994. foreach ($array as $parent_name => $tables) {
  995. if (is_array($tables)) {
  996. foreach ($tables as $id => $row) {
  997. //prepare the variables
  998. $parent_name = self::sanitize($parent_name);
  999. $parent_key_name = self::singular($parent_name)."_uuid";
  1000. //build the delete array
  1001. if ($row['checked'] == 'true') {
  1002. //set checked to true
  1003. $checked = true;
  1004. //delete the child data
  1005. if (isset($row[$parent_key_name])) {
  1006. $new_array[$parent_name][$x][$parent_key_name] = $row[$parent_key_name];
  1007. }
  1008. //remove the row from the main array
  1009. unset($array[$parent_name][$x]);
  1010. }
  1011. //loop through the fields
  1012. foreach($row as $field_name => $field_value) {
  1013. //find the child tables
  1014. $y = 0;
  1015. if (is_array($field_value)) {
  1016. //prepare the variables
  1017. $child_name = self::sanitize($field_name);
  1018. $child_key_name = self::singular($child_name)."_uuid";
  1019. //loop through the child rows
  1020. foreach ($field_value as $sub_row) {
  1021. //build the delete array
  1022. if ($row['checked'] == 'true') {
  1023. //set checked to true
  1024. $checked = true;
  1025. //delete the child data
  1026. $new_array[$child_name][][$child_key_name] = $sub_row[$child_key_name];
  1027. //remove the row from the main array
  1028. unset($array[$parent_name][$x][$child_name][$y]);
  1029. }
  1030. //increment the value
  1031. $y++;
  1032. }
  1033. }
  1034. }
  1035. //increment the value
  1036. $x++;
  1037. }
  1038. }
  1039. }
  1040. //if not checked then copy the array to delete array
  1041. if (!$checked) {
  1042. $new_array = $array;
  1043. }
  1044. //get the current data
  1045. if (count($new_array) > 0) {
  1046. //build an array of tables, fields, and values
  1047. foreach($new_array as $table_name => $rows) {
  1048. foreach($rows as $row) {
  1049. foreach($row as $field_name => $field_value) {
  1050. $keys[$table_name][$field_name][] = $field_value;
  1051. }
  1052. }
  1053. }
  1054. //use the array to get a copy of the parent data before deleting it
  1055. foreach($new_array as $table_name => $rows) {
  1056. foreach($rows as $row) {
  1057. $table_name = self::sanitize($table_name);
  1058. $sql = "select * from ".self::TABLE_PREFIX.$table_name." ";
  1059. $i = 0;
  1060. foreach($row as $field_name => $field_value) {
  1061. if ($i == 0) { $sql .= "where "; } else { $sql .= "and "; }
  1062. $sql .= $field_name." in ( ";
  1063. $i = 0;
  1064. foreach($keys[$table_name][$field_name] as $field_value) {
  1065. $field_name = self::sanitize($field_name);
  1066. if ($i > 0) { $sql .= " ,"; }
  1067. $sql .= " :".$field_name."_".$i." ";
  1068. $i++;
  1069. }
  1070. $sql .= ") ";
  1071. $i = 0;
  1072. foreach($keys[$table_name][$field_name] as $field_value) {
  1073. $parameters[$field_name.'_'.$i] = $field_value;
  1074. $i++;
  1075. }
  1076. }
  1077. }
  1078. if (!empty($field_value)) {
  1079. $results = $this->execute($sql, $parameters, 'all');
  1080. unset($parameters);
  1081. if (is_array($results)) {
  1082. $old_array[$table_name] = $results;
  1083. }
  1084. }
  1085. }
  1086. //get relations array
  1087. $relations = self::get_relations($parent_name);
  1088. //add child data to the old array
  1089. foreach($old_array as $parent_name => $rows) {
  1090. //get relations array
  1091. $relations = self::get_relations($parent_name);
  1092. //loop through the rows
  1093. $x = 0;
  1094. foreach($rows as $row) {
  1095. if (is_array($relations)) {
  1096. foreach ($relations as $relation) {
  1097. if ($relation['key']['action']['delete'] == 'cascade') {
  1098. //set the child table
  1099. $child_table = $relation['table'];
  1100. //remove the v_ prefix
  1101. if (substr($child_table, 0, strlen(self::TABLE_PREFIX)) == self::TABLE_PREFIX) {
  1102. $child_table = substr($child_table, strlen(self::TABLE_PREFIX));
  1103. }
  1104. //get the child data
  1105. $sql = "select * from ".self::TABLE_PREFIX.$child_table." ";
  1106. $sql .= "where ".$relation['field']." = :".$relation['field'];
  1107. $parameters[$relation['field']] = $row[$relation['field']];
  1108. $results = $this->execute($sql, $parameters, 'all');
  1109. unset($parameters);
  1110. if (is_array($results) && $parent_name !== $child_table) {
  1111. $old_array[$parent_name][$x][$child_table] = $results;
  1112. }
  1113. //delete the child data
  1114. if (isset($row[$relation['field']]) && !empty($row[$relation['field']])) {
  1115. $sql = "delete from ".self::TABLE_PREFIX.$child_table." ";
  1116. $sql .= "where ".$relation['field']." = :".$relation['field'];
  1117. $parameters[$relation['field']] = $row[$relation['field']];
  1118. // $this->execute($sql, $parameters);
  1119. }
  1120. unset($parameters);
  1121. }
  1122. }
  1123. }
  1124. $x++;
  1125. }
  1126. }
  1127. }
  1128. //start the atomic transaction
  1129. $this->db->beginTransaction();
  1130. //delete the current data
  1131. foreach($new_array as $table_name => $rows) {
  1132. //echo "table: ".$table_name."\n";
  1133. foreach($rows as $row) {
  1134. if (permission_exists(self::singular($table_name).'_delete')) {
  1135. $sql = "delete from ".self::TABLE_PREFIX.$table_name." ";
  1136. $i = 0;
  1137. foreach($row as $field_name => $field_value) {
  1138. //echo "field: ".$field_name." = ".$field_value."\n";
  1139. if ($i == 0) { $sql .= "where "; } else { $sql .= "and "; }
  1140. $sql .= $field_name." = :".$field_name." ";
  1141. $parameters[$field_name] = $field_value;
  1142. $i++;
  1143. }
  1144. try {
  1145. $this->execute($sql, $parameters);
  1146. $message["message"] = "OK";
  1147. $message["code"] = "200";
  1148. $message["uuid"] = $id;
  1149. $message["details"][$m]["name"] = $this->name;
  1150. $message["details"][$m]["message"] = "OK";
  1151. $message["details"][$m]["code"] = "200";
  1152. //$message["details"][$m]["uuid"] = $parent_key_value;
  1153. if ($this->debug["sql"]) {
  1154. $message["details"][$m]["sql"] = $sql;
  1155. }
  1156. $this->message = $message;
  1157. $m++;
  1158. unset($sql);
  1159. unset($statement);
  1160. }
  1161. catch(PDOException $e) {
  1162. $retval = false;
  1163. $message["message"] = "Bad Request";
  1164. $message["code"] = "400";
  1165. $message["details"][$m]["name"] = $this->name;
  1166. $message["details"][$m]["message"] = $e->getMessage();
  1167. $message["details"][$m]["code"] = "400";
  1168. if ($this->debug["sql"]) {
  1169. $message["details"][$m]["sql"] = $sql;
  1170. }
  1171. $this->message = $message;
  1172. $m++;
  1173. }
  1174. unset($parameters);
  1175. } //if permission
  1176. } //foreach rows
  1177. } //foreach $array
  1178. //commit the atomic transaction
  1179. $this->db->commit();
  1180. //set the action if not set
  1181. $transaction_type = 'delete';
  1182. //get the UUIDs
  1183. $user_uuid = $_SESSION['user_uuid'];
  1184. //log the transaction results
  1185. if (file_exists($_SERVER["PROJECT_ROOT"]."/app/database_transactions/app_config.php")) {
  1186. $sql = "insert into ".self::TABLE_PREFIX."database_transactions ";
  1187. $sql .= "(";
  1188. $sql .= "database_transaction_uuid, ";
  1189. if (isset($this->domain_uuid) && is_uuid($this->domain_uuid)) {
  1190. $sql .= "domain_uuid, ";
  1191. }
  1192. if (isset($user_uuid) && is_uuid($user_uuid)) {
  1193. $sql .= "user_uuid, ";
  1194. }
  1195. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  1196. $sql .= "app_uuid, ";
  1197. }
  1198. if (isset($this->app_name) && !empty($this->app_name)) {
  1199. $sql .= "app_name, ";
  1200. }
  1201. $sql .= "transaction_code, ";
  1202. $sql .= "transaction_address, ";
  1203. $sql .= "transaction_type, ";
  1204. $sql .= "transaction_date, ";
  1205. $sql .= "transaction_old, ";
  1206. $sql .= "transaction_new, ";
  1207. $sql .= "transaction_result ";
  1208. $sql .= ")";
  1209. $sql .= "values ";
  1210. $sql .= "(";
  1211. $sql .= "'".uuid()."', ";
  1212. if (isset($this->domain_uuid) && is_uuid($this->domain_uuid)) {
  1213. $sql .= "'".$this->domain_uuid."', ";
  1214. }
  1215. if (isset($user_uuid) && is_uuid($user_uuid)) {
  1216. $sql .= ":user_uuid, ";
  1217. }
  1218. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  1219. $sql .= ":app_uuid, ";
  1220. }
  1221. if (isset($this->app_name) && !empty($this->app_name)) {
  1222. $sql .= ":app_name, ";
  1223. }
  1224. $sql .= "'".$message["code"]."', ";
  1225. $sql .= ":remote_address, ";
  1226. $sql .= "'".$transaction_type."', ";
  1227. $sql .= "now(), ";
  1228. if (is_array($old_array)) {
  1229. $sql .= ":transaction_old, ";
  1230. }
  1231. else {
  1232. $sql .= "null, ";
  1233. }
  1234. if (is_array($new_array)) {
  1235. $sql .= ":transaction_new, ";
  1236. }
  1237. else {
  1238. $sql .= "null, ";
  1239. }
  1240. $sql .= ":transaction_result ";
  1241. $sql .= ")";
  1242. $statement = $this->db->prepare($sql);
  1243. if (isset($user_uuid) && is_uuid($user_uuid)) {
  1244. $statement->bindParam(':user_uuid', $user_uuid);
  1245. }
  1246. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  1247. $statement->bindParam(':app_uuid', $this->app_uuid);
  1248. }
  1249. if (isset($this->app_name) && !empty($this->app_name)) {
  1250. $statement->bindParam(':app_name', $this->app_name);
  1251. }
  1252. $statement->bindParam(':remote_address', $_SERVER['REMOTE_ADDR']);
  1253. if (is_array($old_array)) {
  1254. $old_json = json_encode($old_array, JSON_PRETTY_PRINT);
  1255. $statement->bindParam(':transaction_old', $old_json);
  1256. }
  1257. if (is_array($new_array)) {
  1258. $new_json = json_encode($new_array, JSON_PRETTY_PRINT);
  1259. $statement->bindParam(':transaction_new', $new_json);
  1260. }
  1261. $result = json_encode($this->message, JSON_PRETTY_PRINT);
  1262. $statement->bindParam(':transaction_result', $result);
  1263. $statement->execute();
  1264. unset($sql);
  1265. }
  1266. return $retval;
  1267. } //delete
  1268. /**
  1269. * Counts the number of rows.
  1270. * @return int Represents the number of counted rows or -1 if failed.
  1271. */
  1272. public function count() {
  1273. //connect to the database if needed
  1274. if (!$this->db) {
  1275. $this->connect();
  1276. }
  1277. //sanitize the table name
  1278. //$this->table = self::sanitize($this->table); // no longer needed
  1279. //get the number of rows
  1280. $sql = "select count(*) as num_rows from ".$this->table." ";
  1281. if ($this->where) {
  1282. $i = 0;
  1283. if (is_array($this->where)) {
  1284. foreach($this->where as $row) {
  1285. //sanitize the name
  1286. $row['name'] = self::sanitize($row['name']);
  1287. //validate the operator
  1288. switch ($row['operator']) {
  1289. case "<": break;
  1290. case ">": break;
  1291. case "<=": break;
  1292. case ">=": break;
  1293. case "=": break;
  1294. case "<>": break;
  1295. case "!=": break;
  1296. default:
  1297. //invalid operator
  1298. return -1;
  1299. }
  1300. //build the sql
  1301. if ($i == 0) {
  1302. $sql .= "where ".$row['name']." ".$row['operator']." :".$row['name']." ";
  1303. }
  1304. else {
  1305. $sql .= "and ".$row['name']." ".$row['operator']." :".$row['name']." ";
  1306. }
  1307. //add the name and value to the params array
  1308. $params[$row['name']] = $row['value'];
  1309. //increment $i
  1310. $i++;
  1311. }
  1312. }
  1313. }
  1314. //unset($this->where); //should not be objects resposibility
  1315. $prep_statement = $this->db->prepare($sql);
  1316. if ($prep_statement) {
  1317. $prep_statement->execute($params);
  1318. $row = $prep_statement->fetch(PDO::FETCH_ASSOC);
  1319. if ($row['num_rows'] > 0) {
  1320. return $row['num_rows'];
  1321. }
  1322. else {
  1323. return 0;
  1324. }
  1325. }
  1326. unset($prep_statement);
  1327. } //count
  1328. /**
  1329. * Performs a select query on database using the <b>$sql</b> statement supplied.
  1330. * @param type $sql Valid SQL statement.
  1331. * @param type $parameters Value can be <i>array</i>, empty string, or <i>null</i>.
  1332. * @param type $return_type Values can be set to <i>all</i>, <i>row</i>, or <i>column</i>.
  1333. * @return mixed Returned values can be array, string, boolean, int, or false. This is dependent on <i>$return_type</i>.
  1334. */
  1335. public function select($sql, $parameters = '', $return_type = 'all') {
  1336. //connect to the database if needed
  1337. if (!$this->db) {
  1338. $this->connect();
  1339. }
  1340. //set the error mode
  1341. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  1342. //reduce prepared statement latency
  1343. if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  1344. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  1345. }
  1346. //execute the query and return the results
  1347. try {
  1348. $prep_statement = $this->db->prepare($sql);
  1349. if (is_array($parameters)) {
  1350. $prep_statement->execute($parameters);
  1351. }
  1352. else {
  1353. $prep_statement->execute();
  1354. }
  1355. $message["message"] = "OK";
  1356. $message["code"] = "200";
  1357. $message["sql"] = $sql;
  1358. if (is_array($parameters)) {
  1359. $message["parameters"] = $parameters;
  1360. }
  1361. $this->message = $message;
  1362. //return the results
  1363. switch($return_type) {
  1364. case 'all':
  1365. return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  1366. case 'row':
  1367. return $prep_statement->fetch(PDO::FETCH_ASSOC);
  1368. case 'column':
  1369. return $prep_statement->fetchColumn();
  1370. default:
  1371. return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  1372. }
  1373. }
  1374. catch(PDOException $e) {
  1375. $message["message"] = "Bad Request";
  1376. $message["code"] = "400";
  1377. $message["error"]["message"] = $e->getMessage();
  1378. if ($this->debug["sql"]) {
  1379. $message["sql"] = $sql;
  1380. }
  1381. if (is_array($parameters)) {
  1382. $message["parameters"] = $parameters;
  1383. }
  1384. $this->message = $message;
  1385. return false;
  1386. }
  1387. } //select
  1388. /**
  1389. * Sets the object <i>$result</i> to sql array
  1390. * @param array $array Array containing the table name, uuid, SQL and where clause.
  1391. * @return database Returns the database object or null.
  1392. */
  1393. public function find_new(array $array) {
  1394. //connect to the database if needed
  1395. if (!$this->db) {
  1396. $this->connect();
  1397. }
  1398. //set the name
  1399. if (isset($array['name'])) {
  1400. $this->name = $array['name'];
  1401. }
  1402. //set the uuid
  1403. if (isset($array['uuid'])) {
  1404. $this->uuid = $array['uuid'];
  1405. }
  1406. //build the query
  1407. $sql = "SELECT * FROM ".self::TABLE_PREFIX . $this->name . " ";
  1408. if (isset($this->uuid)) {
  1409. //get the specific uuid
  1410. $sql .= "WHERE " . self::singular($this->name) . "_uuid = '" . $this->uuid . "' ";
  1411. } else {
  1412. //where
  1413. $i = 0;
  1414. if (isset($array['where'])) {
  1415. foreach ($array['where'] as $row) {
  1416. if (isset($row['operator'])) {
  1417. //validate the operator
  1418. switch ($row['operator']) {
  1419. case "<": break;
  1420. case ">": break;
  1421. case "<=": break;
  1422. case ">=": break;
  1423. case "=": break;
  1424. case "<>": break;
  1425. case "!=": break;
  1426. default:
  1427. //invalid operator
  1428. return null;
  1429. }
  1430. //build the sql
  1431. if ($i == 0) {
  1432. $sql .= "WHERE " . $row['name'] . " " . $row['operator'] . " :" . $row['value'] . " ";
  1433. } else {
  1434. $sql .= "AND " . $row['name'] . " " . $row['operator'] . " :" . $row['value'] . " ";
  1435. }
  1436. }
  1437. //add the name and value to the params array
  1438. $params[$row['name']] = $row['value'];
  1439. //increment $i
  1440. $i++;
  1441. }
  1442. }
  1443. //order by
  1444. if (isset($array['order_by'])) {
  1445. $array['order_by'] = self::sanitize($array['order_by']);
  1446. $sql .= "ORDER BY " . $array['order_by'] . " ";
  1447. }
  1448. //limit
  1449. if (isset($array['limit']) && is_numeric($array['limit'])) {
  1450. $sql .= "LIMIT " . $array['limit'] . " ";
  1451. }
  1452. //offset
  1453. if (isset($array['offset']) && is_numeric($array['offset'])) {
  1454. $sql .= "OFFSET " . $array['offset'] . " ";
  1455. }
  1456. }
  1457. //execute the query, and return the results
  1458. try {
  1459. $prep_statement = $this->db->prepare($sql);
  1460. $prep_statement->execute($params);
  1461. $message["message"] = "OK";
  1462. $message["code"] = "200";
  1463. $message["details"][$m]["name"] = $this->name;
  1464. $message["details"][$m]["message"] = "OK";
  1465. $message["details"][$m]["code"] = "200";
  1466. if ($this->debug["sql"]) {
  1467. $message["details"][$m]["sql"] = $sql;
  1468. }
  1469. $this->message = $message;
  1470. $this->result = $prep_statement->fetchAll(PDO::FETCH_NAMED);
  1471. unset($prep_statement);
  1472. $m++;
  1473. } catch (PDOException $e) {
  1474. $message["message"] = "Bad Request";
  1475. $message["code"] = "400";
  1476. $message["details"][$m]["name"] = $this->name;
  1477. $message["details"][$m]["message"] = $e->getMessage();
  1478. $message["details"][$m]["code"] = "400";
  1479. if ($this->debug["sql"]) {
  1480. $message["details"][$m]["sql"] = $sql;
  1481. }
  1482. $this->message = $message;
  1483. $this->result = '';
  1484. $m++;
  1485. }
  1486. return $this;
  1487. }
  1488. /**
  1489. * Stores the passed UUID in the object
  1490. * @param string $uuid A valid UUID must be passed
  1491. * @return database Returns this object
  1492. */
  1493. public function uuid(string $uuid) {
  1494. $this->uuid = $uuid;
  1495. return $this;
  1496. }
  1497. /**
  1498. * Copies records and appends <i>suffix</i> to the column <i>description</i> data
  1499. * @param array $array Three dimensional Array. The first dimension is the table name without the prefix 'v_'. Second dimension in the row value as int. Third dimension is the column name.
  1500. * @return bool Returns <b>true</b> on success and <b>false</b> on failure.
  1501. */
  1502. public function copy(array $array, $suffix = '(Copy)') {
  1503. //set default return value
  1504. $retval = false;
  1505. //return the array
  1506. if (!is_array($array)) { return $retval; }
  1507. //initialize array
  1508. $copy_array = [];
  1509. //set the message id
  1510. $m = 0;
  1511. //loop through the array
  1512. $x = 0;
  1513. foreach ($array as $parent_name => $tables) {
  1514. if (is_array($tables)) {
  1515. foreach ($tables as $id => $row) {
  1516. //prepare the variables
  1517. $parent_name = self::sanitize($parent_name);
  1518. $parent_key_name = self::singular($parent_name)."_uuid";
  1519. //build the copy array
  1520. if ($row['checked'] == 'true') {
  1521. //set checked to true
  1522. $checked = true;
  1523. //copy the child data
  1524. if (is_uuid($row[$parent_key_name])) {
  1525. $copy_array[$parent_name][$x][$parent_key_name] = $row[$parent_key_name];
  1526. }
  1527. //remove the row from the main array
  1528. unset($array[$parent_name][$x]);
  1529. //loop through the fields
  1530. foreach($row as $field_name => $field_value) {
  1531. //find the child tables
  1532. if (is_array($field_value)) {
  1533. //prepare the variables
  1534. $child_name = self::sanitize($field_name);
  1535. $child_key_name = self::singular($child_name)."_uuid";
  1536. //loop through the child rows
  1537. $y = 0;
  1538. foreach ($field_value as $sub_row) {
  1539. //delete the child data
  1540. $copy_array[$child_name][][$child_key_name] = $sub_row[$child_key_name];
  1541. //remove the row from the main array
  1542. unset($array[$parent_name][$x][$child_name][$y]);
  1543. //increment the value
  1544. $y++;
  1545. }
  1546. }
  1547. }
  1548. }
  1549. //increment the value
  1550. $x++;
  1551. }
  1552. }
  1553. }
  1554. //get the current data
  1555. if (count($copy_array) > 0) {
  1556. //build an array of tables, fields, and values
  1557. foreach($copy_array as $table_name => $rows) {
  1558. foreach($rows as $row) {
  1559. foreach($row as $field_name => $field_value) {
  1560. $keys[$table_name][$field_name][] = $field_value;
  1561. }
  1562. }
  1563. }
  1564. //unset the array
  1565. unset($array);
  1566. //use the array to get a copy of the paent data before deleting it
  1567. foreach($copy_array as $table_name => $rows) {
  1568. foreach($rows as $row) {
  1569. $table_name = self::sanitize($table_name);
  1570. $sql = "select * from ".self::TABLE_PREFIX.$table_name." ";
  1571. $i = 0;
  1572. foreach($row as $field_name => $field_value) {
  1573. if ($i == 0) { $sql .= "where "; } else { $sql .= "and "; }
  1574. $sql .= $field_name." in ( ";
  1575. $i = 0;
  1576. foreach($keys[$table_name][$field_name] as $field_value) {
  1577. $field_name = self::sanitize($field_name);
  1578. if ($i > 0) { $sql .= " ,"; }
  1579. $sql .= " :".$field_name."_".$i." ";
  1580. $i++;
  1581. }
  1582. $sql .= ") ";
  1583. $i = 0;
  1584. foreach($keys[$table_name][$field_name] as $field_value) {
  1585. $parameters[$field_name.'_'.$i] = $field_value;
  1586. $i++;
  1587. }
  1588. }
  1589. }
  1590. $results = $this->execute($sql, $parameters, 'all');
  1591. unset($parameters);
  1592. if (is_array($results)) {
  1593. $array[$table_name] = $results;
  1594. }
  1595. }
  1596. //add child data to the old array
  1597. foreach($copy_array as $parent_name => $rows) {
  1598. //get relations array
  1599. $relations = self::get_relations($parent_name);
  1600. //loop through the rows
  1601. $x = 0;
  1602. foreach($rows as $row) {
  1603. if (is_array($relations)) {
  1604. foreach ($relations as $relation) {
  1605. //set the child table
  1606. $child_table = $relation['table'];
  1607. //remove the v_ prefix
  1608. if (substr($child_table, 0, strlen(self::TABLE_PREFIX)) == self::TABLE_PREFIX) {
  1609. $child_table = substr($child_table, strlen(self::TABLE_PREFIX));
  1610. }
  1611. //get the child data
  1612. $sql = "select * from ".self::TABLE_PREFIX.$child_table." ";
  1613. $sql .= "where ".$relation['field']." = :".$relation['field'];
  1614. $parameters[$relation['field']] = $row[$relation['field']];
  1615. $results = $this->execute($sql, $parameters, 'all');
  1616. unset($parameters);
  1617. if (is_array($results)) {
  1618. $array[$parent_name][$x][$child_table] = $results;
  1619. }
  1620. }
  1621. }
  1622. $x++;
  1623. }
  1624. }
  1625. }
  1626. //update the parent and child keys
  1627. $checked = false;
  1628. $x = 0;
  1629. foreach ($array as $parent_name => $tables) {
  1630. if (is_array($tables)) {
  1631. foreach ($tables as $id => $row) {
  1632. //prepare the variables
  1633. $parent_name = self::sanitize($parent_name);
  1634. $parent_key_name = self::singular($parent_name)."_uuid";
  1635. $parent_key_value = uuid();
  1636. //update the parent key id
  1637. $array[$parent_name][$x][$parent_key_name] = $parent_key_value;
  1638. //add copy to the description
  1639. if (isset($array[$parent_name][$x][self::singular($parent_name).'_description'])) {
  1640. $array[$parent_name][$x][self::singular($parent_name).'_description'] = $suffix.$array[$parent_name][$x][self::singular($parent_name).'_description'];
  1641. }
  1642. //loop through the fields
  1643. foreach($row as $field_name => $field_value) {
  1644. //find the child tables
  1645. $y = 0;
  1646. if (is_array($field_value)) {
  1647. //prepare the variables
  1648. $child_name = self::sanitize($field_name);
  1649. $child_key_name = self::singular($child_name)."_uuid";
  1650. //loop through the child rows
  1651. foreach ($field_value as $sub_row) {
  1652. //update the parent key id
  1653. $array[$parent_name][$x][$child_name][$y][$parent_key_name] = $parent_key_value;
  1654. //udpate the child key id
  1655. $array[$parent_name][$x][$child_name][$y][$child_key_name] = uuid();
  1656. //increment the value
  1657. $y++;
  1658. }
  1659. }
  1660. }
  1661. //increment the value
  1662. $x++;
  1663. }
  1664. }
  1665. }
  1666. //save the copy of the data
  1667. if (is_array($array) && count($array) > 0) {
  1668. $retval = $this->save($array);
  1669. unset($array);
  1670. }
  1671. return $retval;
  1672. } //end function copy
  1673. /**
  1674. * Toggles fields on a table using the <i>toggle_field</i> array values within the app object.
  1675. * @param array $array Three dimensional Array. The first dimension is the table name without the prefix 'v_'. Second dimension in the row value as int. Third dimension is the column name.
  1676. * @return bool Returns <b>true</b> on success and <b>false</b> on failure.
  1677. * @depends database::save()
  1678. * @depends database::get_apps()
  1679. */
  1680. public function toggle(array $array) {
  1681. //return the array
  1682. if (!is_array($array)) { return false; }
  1683. //set the message id
  1684. $m = 0;
  1685. //loop through the array
  1686. if (is_array($array)) {
  1687. $x = 0;
  1688. foreach ($array as $parent_name => $tables) {
  1689. if (is_array($tables)) {
  1690. foreach ($tables as $id => $row) {
  1691. //prepare the variables
  1692. $parent_name = self::sanitize($parent_name);
  1693. $parent_key_name = self::singular($parent_name)."_uuid";
  1694. //build the toggle array
  1695. if ($row['checked'] == 'true') {
  1696. //toggle the field value
  1697. //$toggle_array[$parent_name][$x][$parent_key_name] = $row[$parent_key_name];
  1698. $toggle_array[$parent_name][$x] = $row;
  1699. //remove the row from the main array
  1700. unset($array[$parent_name][$x]);
  1701. }
  1702. //loop through the fields
  1703. foreach($row as $field_name => $field_value) {
  1704. //find the child tables
  1705. $y = 0;
  1706. if (is_array($field_value)) {
  1707. //prepare the variables
  1708. $child_name = self::sanitize($field_name);
  1709. $child_key_name = self::singular($child_name)."_uuid";
  1710. //loop through the child rows
  1711. foreach ($field_value as $sub_row) {
  1712. //build the delete array
  1713. if ($action == 'delete' && $sub_row['checked'] == 'true') {
  1714. //delete the child data
  1715. $delete_array[$child_name][$y][$child_key_name] = $sub_row[$child_key_name];
  1716. //remove the row from the main array
  1717. unset($array[$parent_name][$x][$child_name][$y]);
  1718. }
  1719. //increment the value
  1720. $y++;
  1721. }
  1722. }
  1723. }
  1724. //increment the value
  1725. $x++;
  1726. }
  1727. }
  1728. }
  1729. }
  1730. //unset the original array
  1731. unset($array);
  1732. //get the $apps array from the installed apps from the core and mod directories
  1733. if (!is_array($_SESSION['apps'])) {
  1734. self::get_apps();
  1735. }
  1736. //search through all fields to see if toggle field exists
  1737. if (is_array($_SESSION['apps'])) {
  1738. foreach ($_SESSION['apps'] as $x => $app) {
  1739. if (is_array($app['db'])) {
  1740. foreach ($app['db'] as $y => $row) {
  1741. if (is_array($row['table']['name'])) {
  1742. $table_name = $row['table']['name']['text'];
  1743. }
  1744. else {
  1745. $table_name = $row['table']['name'];
  1746. }
  1747. if ($table_name === self::TABLE_PREFIX.$parent_name) {
  1748. if (is_array($row['fields'])) {
  1749. foreach ($row['fields'] as $field) {
  1750. if (isset($field['toggle'])) {
  1751. $toggle_field = $field['name'];
  1752. $toggle_values = $field['toggle'];
  1753. }
  1754. }
  1755. }
  1756. }
  1757. }
  1758. }
  1759. }
  1760. }
  1761. //get the current values from the database
  1762. foreach ($toggle_array as $table_name => $table) {
  1763. $x = 0;
  1764. foreach($table as $row) {
  1765. $child_name = self::sanitize($table_name);
  1766. $child_key_name = self::singular($child_name)."_uuid";
  1767. $array[$table_name][$x][$child_key_name] = $row[$child_key_name];
  1768. $array[$table_name][$x][$toggle_field] = ($row[$toggle_field] === $toggle_values[0]) ? $toggle_values[1] : $toggle_values[0];
  1769. $x++;
  1770. }
  1771. }
  1772. unset($toggle_array);
  1773. //save the array
  1774. return $this->save($array);
  1775. } //end function toggle
  1776. /**
  1777. * <p>Save an array to the database.</p>
  1778. * <p>Usage Example:<br><code>$database = new database();<br>$database->app_name = "MyApp";<br>$database->app_uuid = "12345678-1234-1234-1234-123456789abc";<br>$row = 0;<br>$array['mytable'][$row]['mycolumn'] = "myvalue";<br>if ($database->save($array)) { <br>&nbsp;&nbsp;echo "Saved Successfully.";<br> } else {<br>&nbsp;&nbsp;echo "Save Failed.";<br>}</code></p>
  1779. * @param array $array Three dimensional Array. The first dimension is the table name without the prefix 'v_'. Second dimension in the row value as int. Third dimension is the column name.
  1780. * @param bool $transaction_save
  1781. * @return boolean Returns <b>true</b> on success and <b>false</b> on failure of one or more failed write attempts.
  1782. */
  1783. public function save(array &$array, bool $transaction_save = true) {
  1784. //set default return value
  1785. $retval = true;
  1786. //return the array
  1787. if (!is_array($array)) { return false; }
  1788. //set the message id
  1789. $m = 0;
  1790. //build the json string from the array
  1791. $new_json = json_encode($array, JSON_PRETTY_PRINT);
  1792. //debug sql
  1793. //$this->debug["sql"] = true;
  1794. //connect to the database if needed
  1795. if (!$this->db) {
  1796. $this->connect();
  1797. }
  1798. //start the atomic transaction
  1799. $this->db->beginTransaction();
  1800. //loop through the array
  1801. if (is_array($array)) foreach ($array as $schema_name => $schema_array) {
  1802. $this->name = $schema_name;
  1803. if (is_array($schema_array)) foreach ($schema_array as $schema_id => $array) {
  1804. //set the variables
  1805. $table_name = self::TABLE_PREFIX.$this->name;
  1806. $parent_key_name = self::singular($this->name)."_uuid";
  1807. $parent_key_name = self::sanitize($parent_key_name);
  1808. //if the uuid is set then set parent key exists and value
  1809. //determine if the parent_key_exists
  1810. $parent_key_exists = false;
  1811. if (isset($array[$parent_key_name])) {
  1812. $parent_key_value = $array[$parent_key_name];
  1813. $parent_key_exists = true;
  1814. }
  1815. else {
  1816. if (isset($this->uuid)) {
  1817. $parent_key_exists = true;
  1818. $parent_key_value = $this->uuid;
  1819. }
  1820. else {
  1821. $parent_key_value = uuid();
  1822. }
  1823. }
  1824. //allow characters found in the uuid only.
  1825. $parent_key_value = self::sanitize($parent_key_value);
  1826. //get the parent field names
  1827. $parent_field_names = array();
  1828. if (is_array($array)) {
  1829. foreach ($array as $key => $value) {
  1830. if (!is_array($value)) {
  1831. $parent_field_names[] = self::sanitize($key);
  1832. }
  1833. }
  1834. }
  1835. //determine action update or delete and get the original data
  1836. if ($parent_key_exists) {
  1837. $sql = "SELECT ".implode(", ", $parent_field_names)." FROM ".$table_name." ";
  1838. $sql .= "WHERE ".$parent_key_name." = '".$parent_key_value."'; ";
  1839. $prep_statement = $this->db->prepare($sql);
  1840. if ($prep_statement) {
  1841. //get the data
  1842. try {
  1843. $prep_statement->execute();
  1844. $result = $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  1845. }
  1846. catch(PDOException $e) {
  1847. echo $sql."<br />\n";
  1848. echo 'Caught exception: '. $e->getMessage()."<br /><br />\n";
  1849. echo $sql. "<br /><br />\n";
  1850. exit;
  1851. }
  1852. //set the action
  1853. if (count($result) > 0) {
  1854. $action = "update";
  1855. $old_array[$schema_name] = $result;
  1856. }
  1857. else {
  1858. $action = "add";
  1859. }
  1860. }
  1861. unset($prep_statement, $result);
  1862. }
  1863. else {
  1864. $action = "add";
  1865. }
  1866. //add a record
  1867. if ($action == "add") {
  1868. if (permission_exists(self::singular($this->name).'_add')) {
  1869. $params = array();
  1870. $sql = "INSERT INTO ".self::TABLE_PREFIX.$this->name." ";
  1871. $sql .= "(";
  1872. if (!$parent_key_exists) {
  1873. $sql .= $parent_key_name.", ";
  1874. }
  1875. if (is_array($array)) {
  1876. foreach ($array as $array_key => $array_value) {
  1877. if (!is_array($array_value)) {
  1878. $array_key = self::sanitize($array_key);
  1879. if ($array_key != 'insert_user' &&
  1880. $array_key != 'insert_date' &&
  1881. $array_key != 'update_user' &&
  1882. $array_key != 'update_date') {
  1883. $sql .= $array_key.", ";
  1884. }
  1885. }
  1886. }
  1887. }
  1888. $sql .= "insert_date, ";
  1889. $sql .= "insert_user ";
  1890. $sql .= ") ";
  1891. $sql .= "VALUES ";
  1892. $sql .= "(";
  1893. if (!$parent_key_exists) {
  1894. $sql .= "'".$parent_key_value."', ";
  1895. }
  1896. if (is_array($array)) {
  1897. foreach ($array as $array_key => $array_value) {
  1898. if (!is_array($array_value)) {
  1899. if ($array_key != 'insert_user' &&
  1900. $array_key != 'insert_date' &&
  1901. $array_key != 'update_user' &&
  1902. $array_key != 'update_date') {
  1903. if (empty($array_value)) {
  1904. $sql .= "null, ";
  1905. }
  1906. elseif ($array_value === "now()") {
  1907. $sql .= "now(), ";
  1908. }
  1909. elseif ($array_value === "user_uuid()") {
  1910. $sql .= ':'.$array_key.", ";
  1911. $params[$array_key] = $_SESSION['user_uuid'];
  1912. }
  1913. elseif ($array_value === "remote_address()") {
  1914. $sql .= ':'.$array_key.", ";
  1915. $params[$array_key] = $_SERVER['REMOTE_ADDR'];
  1916. }
  1917. else {
  1918. $sql .= ':'.$array_key.", ";
  1919. $params[$array_key] = trim($array_value);
  1920. }
  1921. }
  1922. }
  1923. }
  1924. }
  1925. $sql .= "now(), ";
  1926. $sql .= ":insert_user ";
  1927. $sql .= ");";
  1928. //add insert user parameter
  1929. $params['insert_user'] = $_SESSION['user_uuid'];
  1930. //set the error mode
  1931. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  1932. //reduce prepared statement latency
  1933. if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  1934. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  1935. }
  1936. //execute the query and return the results
  1937. try {
  1938. //$this->db->query(check_sql($sql));
  1939. $prep_statement = $this->db->prepare($sql);
  1940. $prep_statement->execute($params);
  1941. unset($prep_statement);
  1942. $message["message"] = "OK";
  1943. $message["code"] = "200";
  1944. $message["uuid"] = $parent_key_value;
  1945. $message["details"][$m]["name"] = $this->name;
  1946. $message["details"][$m]["message"] = "OK";
  1947. $message["details"][$m]["code"] = "200";
  1948. $message["details"][$m]["uuid"] = $parent_key_value;
  1949. if ($this->debug["sql"]) {
  1950. $message["details"][$m]["sql"] = $sql;
  1951. if (is_array($params)) {
  1952. $message["details"][$m]["params"] = $params;
  1953. }
  1954. }
  1955. unset($params);
  1956. $this->message = $message;
  1957. $m++;
  1958. }
  1959. catch(PDOException $e) {
  1960. $retval = false;
  1961. $message["message"] = "Bad Request";
  1962. $message["code"] = "400";
  1963. $message["details"][$m]["name"] = $this->name;
  1964. $message["details"][$m]["message"] = $e->getMessage();
  1965. $message["details"][$m]["code"] = "400";
  1966. $message["details"][$m]["array"] = $array;
  1967. if ($this->debug["sql"]) {
  1968. $message["details"][$m]["sql"] = $sql;
  1969. if (is_array($params)) {
  1970. $message["details"][$m]["params"] = $params;
  1971. }
  1972. }
  1973. unset($params);
  1974. $this->message = $message;
  1975. $m++;
  1976. }
  1977. unset($sql);
  1978. }
  1979. else {
  1980. $retval = false;
  1981. $message["name"] = $this->name;
  1982. $message["message"] = "Forbidden, does not have '".self::singular($this->name)."_add'";
  1983. $message["code"] = "403";
  1984. $message["line"] = __line__;
  1985. $this->message[] = $message;
  1986. $m++;
  1987. }
  1988. }
  1989. //edit a specific uuid
  1990. if ($action == "update") {
  1991. if (permission_exists(self::singular($this->name).'_edit')) {
  1992. //parent data
  1993. $params = array();
  1994. $sql = "UPDATE ".self::TABLE_PREFIX.$this->name." SET ";
  1995. if (is_array($array)) {
  1996. foreach ($array as $array_key => $array_value) {
  1997. if (!is_array($array_value) && $array_key != $parent_key_name) {
  1998. $array_key = self::sanitize($array_key);
  1999. if (empty($array_value)) {
  2000. $sql .= $array_key." = null, ";
  2001. }
  2002. elseif ($array_value === "now()") {
  2003. $sql .= $array_key." = now(), ";
  2004. }
  2005. elseif ($array_value === "user_uuid()") {
  2006. $sql .= $array_key." = :".$array_key.", ";
  2007. $params[$array_key] = $_SESSION['user_uuid'];
  2008. }
  2009. elseif ($array_value === "remote_address()") {
  2010. $sql .= $array_key." = :".$array_key.", ";
  2011. $params[$array_key] = $_SERVER['REMOTE_ADDR'];
  2012. }
  2013. else {
  2014. $sql .= $array_key." = :".$array_key.", ";
  2015. $params[$array_key] = trim($array_value);
  2016. }
  2017. }
  2018. }
  2019. }
  2020. //add the modified date and user
  2021. $sql .= "update_date = now(), ";
  2022. $sql .= "update_user = :update_user ";
  2023. $params['update_user'] = $_SESSION['user_uuid'];
  2024. //add the where with the parent name and value
  2025. $sql .= "WHERE ".$parent_key_name." = '".$parent_key_value."'; ";
  2026. $sql = str_replace(", WHERE", " WHERE", $sql);
  2027. //add update user parameter
  2028. $params['update_user'] = $_SESSION['user_uuid'];
  2029. //set the error mode
  2030. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  2031. //reduce prepared statement latency
  2032. if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  2033. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  2034. }
  2035. //execute the query and return the results
  2036. try {
  2037. $prep_statement = $this->db->prepare($sql);
  2038. $prep_statement->execute($params);
  2039. //$this->db->query(check_sql($sql));
  2040. $message["message"] = "OK";
  2041. $message["code"] = "200";
  2042. $message["uuid"] = $parent_key_value;
  2043. $message["details"][$m]["name"] = $this->name;
  2044. $message["details"][$m]["message"] = "OK";
  2045. $message["details"][$m]["code"] = "200";
  2046. $message["details"][$m]["uuid"] = $parent_key_value;
  2047. if ($this->debug["sql"]) {
  2048. $message["details"][$m]["sql"] = $sql;
  2049. if (is_array($params)) {
  2050. $message["details"][$m]["params"] = $params;
  2051. }
  2052. }
  2053. unset($params);
  2054. $this->message = $message;
  2055. $m++;
  2056. unset($sql);
  2057. }
  2058. catch(PDOException $e) {
  2059. $retval = false;
  2060. $message["message"] = "Bad Request";
  2061. $message["code"] = "400";
  2062. $message["details"][$m]["name"] = $this->name;
  2063. $message["details"][$m]["message"] = $e->getMessage();
  2064. $message["details"][$m]["code"] = "400";
  2065. if ($this->debug["sql"]) {
  2066. $message["details"][$m]["sql"] = $sql;
  2067. if (is_array($params)) {
  2068. $message["details"][$m]["params"] = $params;
  2069. }
  2070. }
  2071. unset($params);
  2072. $this->message = $message;
  2073. $m++;
  2074. }
  2075. }
  2076. else {
  2077. $retval = false;
  2078. $message["message"] = "Forbidden, does not have '".self::singular($this->name)."_edit'";
  2079. $message["code"] = "403";
  2080. $message["line"] = __line__;
  2081. $this->message = $message;
  2082. $m++;
  2083. }
  2084. }
  2085. //unset the variables
  2086. unset($sql, $action);
  2087. //child data
  2088. if (is_array($array)) {
  2089. foreach ($array as $key => $value) {
  2090. if (is_array($value)) {
  2091. $child_table_name = self::TABLE_PREFIX.$key;
  2092. $child_table_name = self::sanitize($child_table_name);
  2093. foreach ($value as $id => $row) {
  2094. //prepare the variables
  2095. $child_name = self::singular($key);
  2096. $child_name = self::sanitize($child_name);
  2097. $child_key_name = $child_name."_uuid";
  2098. //determine if the parent key exists in the child array
  2099. $parent_key_exists = false;
  2100. if (!isset($array[$parent_key_name])) {
  2101. $parent_key_exists = true;
  2102. }
  2103. //determine if the uuid exists
  2104. $uuid_exists = false;
  2105. if (is_array($row)) foreach ($row as $k => $v) {
  2106. if ($child_key_name == $k) {
  2107. if (!empty($v)) {
  2108. $child_key_value = trim($v);
  2109. $uuid_exists = true;
  2110. break;
  2111. }
  2112. }
  2113. else {
  2114. $uuid_exists = false;
  2115. }
  2116. }
  2117. //allow characters found in the uuid only
  2118. if (isset($child_key_value)) {
  2119. $child_key_value = self::sanitize($child_key_value);
  2120. }
  2121. //get the child field names
  2122. $child_field_names = array();
  2123. if (is_array($row)) {
  2124. foreach ($row as $k => $v) {
  2125. if (!is_array($v) && $k !== 'checked') {
  2126. $child_field_names[] = self::sanitize($k);
  2127. }
  2128. }
  2129. }
  2130. //determine sql update or delete and get the original data
  2131. if ($uuid_exists) {
  2132. $sql = "SELECT ". implode(", ", $child_field_names)." FROM ".$child_table_name." ";
  2133. $sql .= "WHERE ".$child_key_name." = '".$child_key_value."'; ";
  2134. try {
  2135. $prep_statement = $this->db->prepare($sql);
  2136. if ($prep_statement) {
  2137. //get the data
  2138. $prep_statement->execute();
  2139. $child_array = $prep_statement->fetch(PDO::FETCH_ASSOC);
  2140. //set the action
  2141. if (is_array($child_array)) {
  2142. $action = "update";
  2143. }
  2144. else {
  2145. $action = "add";
  2146. }
  2147. //add to the parent array
  2148. if (is_array($child_array)) {
  2149. $old_array[$schema_name][$schema_id][$key][] = $child_array;
  2150. }
  2151. }
  2152. unset($prep_statement);
  2153. }
  2154. catch(PDOException $e) {
  2155. echo $sql."<br />\n";
  2156. echo 'Caught exception: '. $e->getMessage()."<br /><br />\n";
  2157. echo $sql. "<br /><br />\n";
  2158. exit;
  2159. }
  2160. }
  2161. else {
  2162. $action = "add";
  2163. }
  2164. //update the child data
  2165. if ($action == "update") {
  2166. if (permission_exists($child_name.'_edit')) {
  2167. $sql = "UPDATE ".$child_table_name." SET ";
  2168. if (is_array($row)) {
  2169. foreach ($row as $k => $v) {
  2170. if (!is_array($v) && ($k != $parent_key_name || $k != $child_key_name)) {
  2171. $k = self::sanitize($k);
  2172. if (empty($v)) {
  2173. $sql .= $k." = null, ";
  2174. }
  2175. elseif ($v === "now()") {
  2176. $sql .= $k." = now(), ";
  2177. }
  2178. elseif ($v === "user_uuid()") {
  2179. $sql .= $k." = :".$k.", ";
  2180. $params[$k] = $_SESSION['user_uuid'];
  2181. }
  2182. elseif ($v === "remote_address()") {
  2183. $sql .= $k." = :".$k.", ";
  2184. $params[$k] = $_SERVER['REMOTE_ADDR'];
  2185. }
  2186. else {
  2187. $sql .= $k." = :".$k.", ";
  2188. $params[$k] = trim($v);
  2189. }
  2190. }
  2191. }
  2192. }
  2193. //add the modified date and user
  2194. $sql .= "update_date = now(), ";
  2195. $sql .= "update_user = :update_user ";
  2196. $params['update_user'] = $_SESSION['user_uuid'];
  2197. //add the where with the parent name and value
  2198. $sql .= "WHERE ".$parent_key_name." = '".$parent_key_value."' ";
  2199. $sql .= "AND ".$child_key_name." = '".$child_key_value."'; ";
  2200. $sql = str_replace(", WHERE", " WHERE", $sql);
  2201. //set the error mode
  2202. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  2203. //reduce prepared statement latency
  2204. if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  2205. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  2206. }
  2207. //$prep_statement->bindParam(':domain_uuid', $this->domain_uuid );
  2208. try {
  2209. //$this->db->query(check_sql($sql));
  2210. $prep_statement = $this->db->prepare($sql);
  2211. $prep_statement->execute($params);
  2212. unset($prep_statement);
  2213. $message["details"][$m]["name"] = $key;
  2214. $message["details"][$m]["message"] = "OK";
  2215. $message["details"][$m]["code"] = "200";
  2216. $message["details"][$m]["uuid"] = $child_key_value;
  2217. if ($this->debug["sql"]) {
  2218. $message["details"][$m]["sql"] = $sql;
  2219. if (is_array($params)) {
  2220. $message["details"][$m]["params"] = $params;
  2221. }
  2222. }
  2223. unset($params);
  2224. $this->message = $message;
  2225. $m++;
  2226. }
  2227. catch(PDOException $e) {
  2228. $retval = false;
  2229. if ($message["code"] = "200") {
  2230. $message["message"] = "Bad Request";
  2231. $message["code"] = "400";
  2232. }
  2233. $message["details"][$m]["name"] = $key;
  2234. $message["details"][$m]["message"] = $e->getMessage();
  2235. $message["details"][$m]["code"] = "400";
  2236. if ($this->debug["sql"]) {
  2237. $message["details"][$m]["sql"] = $sql;
  2238. if (is_array($params)) {
  2239. $message["details"][$m]["params"] = $params;
  2240. }
  2241. }
  2242. unset($params);
  2243. $this->message = $message;
  2244. $m++;
  2245. }
  2246. }
  2247. else {
  2248. $retval = false;
  2249. $message["name"] = $child_name;
  2250. $message["message"] = "Forbidden, does not have '{$child_name}_edit'";
  2251. $message["code"] = "403";
  2252. $message["line"] = __line__;
  2253. $this->message = $message;
  2254. $m++;
  2255. }
  2256. } //action update
  2257. //add the child data
  2258. if ($action == "add") {
  2259. if (permission_exists($child_name.'_add')) {
  2260. //determine if child or parent key exists
  2261. $child_key_name = $child_name.'_uuid';
  2262. $parent_key_exists = false;
  2263. $child_key_exists = false;
  2264. if (is_array($row)) {
  2265. foreach ($row as $k => $v) {
  2266. if ($k == $parent_key_name) {
  2267. $parent_key_exists = true;
  2268. }
  2269. if ($k == $child_key_name) {
  2270. $child_key_exists = true;
  2271. $child_key_value = trim($v);
  2272. }
  2273. }
  2274. }
  2275. if (!$child_key_value) {
  2276. $child_key_value = uuid();
  2277. }
  2278. //build the insert
  2279. $sql = "INSERT INTO ".$child_table_name." ";
  2280. $sql .= "(";
  2281. if (!$parent_key_exists) {
  2282. $sql .= self::singular($parent_key_name).", ";
  2283. }
  2284. if (!$child_key_exists) {
  2285. $sql .= self::singular($child_key_name).", ";
  2286. }
  2287. if (is_array($row)) {
  2288. foreach ($row as $k => $v) {
  2289. if (!is_array($v)) {
  2290. $k = self::sanitize($k);
  2291. if ($k != 'insert_user' &&
  2292. $k != 'insert_date' &&
  2293. $k != 'update_user' &&
  2294. $k != 'update_date') {
  2295. $sql .= $k.", ";
  2296. }
  2297. }
  2298. }
  2299. }
  2300. $sql .= "insert_date, ";
  2301. $sql .= "insert_user ";
  2302. $sql .= ") ";
  2303. $sql .= "VALUES ";
  2304. $sql .= "(";
  2305. if (!$parent_key_exists) {
  2306. $sql .= "'".$parent_key_value."', ";
  2307. }
  2308. if (!$child_key_exists) {
  2309. $sql .= "'".$child_key_value."', ";
  2310. }
  2311. if (is_array($row)) {
  2312. foreach ($row as $k => $v) {
  2313. if (!is_array($v)) {
  2314. if ($k != 'insert_user' &&
  2315. $k != 'insert_date' &&
  2316. $k != 'update_user' &&
  2317. $k != 'update_date') {
  2318. if (empty($v)) {
  2319. $sql .= "null, ";
  2320. }
  2321. elseif ($v === "now()") {
  2322. $sql .= "now(), ";
  2323. }
  2324. elseif ($v === "user_uuid()") {
  2325. $sql .= ':'.$k.", ";
  2326. $params[$k] = $_SESSION['user_uuid'];
  2327. }
  2328. elseif ($v === "remote_address()") {
  2329. $sql .= ':'.$k.", ";
  2330. $params[$k] = $_SERVER['REMOTE_ADDR'];
  2331. }
  2332. else {
  2333. $k = self::sanitize($k);
  2334. if ($k != 'insert_user' &&
  2335. $k != 'insert_date' &&
  2336. $k != 'update_user' &&
  2337. $k != 'update_date') {
  2338. $sql .= ':'.$k.", ";
  2339. $params[$k] = trim($v);
  2340. }
  2341. }
  2342. }
  2343. }
  2344. }
  2345. }
  2346. $sql .= "now(), ";
  2347. $sql .= ":insert_user ";
  2348. $sql .= ");";
  2349. //add insert user parameter
  2350. $params['insert_user'] = $_SESSION['user_uuid'];
  2351. //set the error mode
  2352. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  2353. //reduce prepared statement latency
  2354. if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  2355. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  2356. }
  2357. //execute the query and return the results
  2358. try {
  2359. $prep_statement = $this->db->prepare($sql);
  2360. $prep_statement->execute($params);
  2361. unset($prep_statement);
  2362. $message["details"][$m]["name"] = $key;
  2363. $message["details"][$m]["message"] = "OK";
  2364. $message["details"][$m]["code"] = "200";
  2365. $message["details"][$m]["uuid"] = $child_key_value;
  2366. if ($this->debug["sql"]) {
  2367. $message["details"][$m]["sql"] = $sql;
  2368. if (is_array($params)) {
  2369. $message["details"][$m]["params"] = $params;
  2370. }
  2371. }
  2372. unset($params);
  2373. $this->message = $message;
  2374. $m++;
  2375. }
  2376. catch(PDOException $e) {
  2377. $retval = false;
  2378. if ($message["code"] = "200") {
  2379. $message["message"] = "Bad Request";
  2380. $message["code"] = "400";
  2381. }
  2382. $message["details"][$m]["name"] = $key;
  2383. $message["details"][$m]["message"] = $e->getMessage();
  2384. $message["details"][$m]["code"] = "400";
  2385. if ($this->debug["sql"]) {
  2386. $message["details"][$m]["sql"] = $sql;
  2387. if (is_array($params)) {
  2388. $message["details"][$m]["params"] = $params;
  2389. }
  2390. }
  2391. unset($params);
  2392. $this->message = $message;
  2393. $m++;
  2394. }
  2395. }
  2396. else {
  2397. $retval = false;
  2398. $message["name"] = $child_name;
  2399. $message["message"] = "Forbidden, does not have '{$child_name}_add'";
  2400. $message["code"] = "403";
  2401. $message["line"] = __line__;
  2402. $this->message = $message;
  2403. $m++;
  2404. }
  2405. } //action add
  2406. //unset the variables
  2407. unset($sql, $action, $child_key_name, $child_key_value);
  2408. } // foreach value
  2409. } //is array
  2410. } //foreach array
  2411. }
  2412. } // foreach schema_array
  2413. } // foreach main array
  2414. $this->message = $message;
  2415. //commit the atomic transaction
  2416. $this->db->commit();
  2417. //set the action if not set
  2418. if (strlen($action ?? '') === 0) {
  2419. if (is_array($old_array)) {
  2420. $transaction_type = 'update';
  2421. }
  2422. else {
  2423. $transaction_type = 'add';
  2424. }
  2425. }
  2426. else {
  2427. $transaction_type = $action;
  2428. }
  2429. //get the UUIDs
  2430. $user_uuid = $_SESSION['user_uuid'];
  2431. //log the transaction results
  2432. if ($transaction_save && file_exists($_SERVER["PROJECT_ROOT"]."/app/database_transactions/app_config.php")) {
  2433. try {
  2434. $sql = "insert into ".self::TABLE_PREFIX."database_transactions ";
  2435. $sql .= "(";
  2436. $sql .= "database_transaction_uuid, ";
  2437. $sql .= "domain_uuid, ";
  2438. if (isset($user_uuid) && is_uuid($user_uuid)) {
  2439. $sql .= "user_uuid, ";
  2440. }
  2441. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  2442. $sql .= "app_uuid, ";
  2443. }
  2444. if (isset($this->app_name) && !empty($this->app_name)) {
  2445. $sql .= "app_name, ";
  2446. }
  2447. $sql .= "transaction_code, ";
  2448. $sql .= "transaction_address, ";
  2449. $sql .= "transaction_type, ";
  2450. $sql .= "transaction_date, ";
  2451. $sql .= "transaction_old, ";
  2452. $sql .= "transaction_new, ";
  2453. $sql .= "transaction_result ";
  2454. $sql .= ")";
  2455. $sql .= "values ";
  2456. $sql .= "(";
  2457. $sql .= "'".uuid()."', ";
  2458. if (is_null($this->domain_uuid)) {
  2459. $sql .= "null, ";
  2460. }
  2461. else {
  2462. $sql .= "'".$this->domain_uuid."', ";
  2463. }
  2464. if (isset($user_uuid) && is_uuid($user_uuid)) {
  2465. $sql .= ":user_uuid, ";
  2466. }
  2467. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  2468. $sql .= ":app_uuid, ";
  2469. }
  2470. if (isset($this->app_name) && !empty($this->app_name)) {
  2471. $sql .= ":app_name, ";
  2472. }
  2473. $sql .= "'".$message["code"]."', ";
  2474. $sql .= ":remote_address, ";
  2475. $sql .= "'".$transaction_type."', ";
  2476. $sql .= "now(), ";
  2477. if (is_array($old_array)) {
  2478. $sql .= ":transaction_old, ";
  2479. }
  2480. else {
  2481. $sql .= "null, ";
  2482. }
  2483. if (is_array($array)) {
  2484. $sql .= ":transaction_new, ";
  2485. }
  2486. else {
  2487. $sql .= "null, ";
  2488. }
  2489. $sql .= ":transaction_result ";
  2490. $sql .= ")";
  2491. $statement = $this->db->prepare($sql);
  2492. if (isset($user_uuid) && is_uuid($user_uuid)) {
  2493. $statement->bindParam(':user_uuid', $user_uuid);
  2494. }
  2495. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  2496. $statement->bindParam(':app_uuid', $this->app_uuid);
  2497. }
  2498. if (isset($this->app_name) && !empty($this->app_name)) {
  2499. $statement->bindParam(':app_name', $this->app_name);
  2500. }
  2501. $statement->bindParam(':remote_address', $_SERVER['REMOTE_ADDR']);
  2502. if (is_array($old_array)) {
  2503. $old_json = json_encode($old_array, JSON_PRETTY_PRINT);
  2504. $statement->bindParam(':transaction_old', $old_json);
  2505. }
  2506. if (isset($new_json)) {
  2507. $statement->bindParam(':transaction_new', $new_json);
  2508. }
  2509. $message = json_encode($this->message, JSON_PRETTY_PRINT);
  2510. $statement->bindParam(':transaction_result', $message);
  2511. $statement->execute();
  2512. unset($sql);
  2513. }
  2514. catch(PDOException $e) {
  2515. echo $e->getMessage();
  2516. exit;
  2517. }
  2518. }
  2519. return $retval;
  2520. } //save method
  2521. /**
  2522. * Converts a plural English word to singular.
  2523. * @param string $word English word
  2524. * @return string Singular version of English word
  2525. * @internal Moved to class to conserve resources.
  2526. */
  2527. public static function singular(string $word) {
  2528. //"-es" is used for words that end in "-x", "-s", "-z", "-sh", "-ch" in which case you add
  2529. if (substr($word, -2) == "es") {
  2530. if (substr($word, -4) == "sses") { // eg. 'addresses' to 'address'
  2531. return substr($word,0,-2);
  2532. }
  2533. elseif (substr($word, -3) == "ses") { // eg. 'databases' to 'database' (necessary!)
  2534. return substr($word,0,-1);
  2535. }
  2536. elseif (substr($word, -3) == "ies") { // eg. 'countries' to 'country'
  2537. return substr($word,0,-3)."y";
  2538. }
  2539. elseif (substr($word, -3, 1) == "x") {
  2540. return substr($word,0,-2);
  2541. }
  2542. elseif (substr($word, -3, 1) == "s") {
  2543. return substr($word,0,-2);
  2544. }
  2545. elseif (substr($word, -3, 1) == "z") {
  2546. return substr($word,0,-2);
  2547. }
  2548. elseif (substr($word, -4, 2) == "sh") {
  2549. return substr($word,0,-2);
  2550. }
  2551. elseif (substr($word, -4, 2) == "ch") {
  2552. return substr($word,0,-2);
  2553. }
  2554. else {
  2555. return rtrim($word, "s");
  2556. }
  2557. }
  2558. else {
  2559. return rtrim($word, "s");
  2560. }
  2561. }
  2562. /**
  2563. * Gets the $apps array from the installed apps from the core and mod directories and writes it to $_SESSION['apps'] overwriting previous values.
  2564. * @uses $_SERVER['DOCUMENT_ROOT'] Global variable
  2565. * @uses PROJECT_PATH Global variable
  2566. * @return null Does not return any values
  2567. * @internal Moved to class to conserve resources.
  2568. */
  2569. public static function get_apps() {
  2570. //get the $apps array from the installed apps from the core and mod directories
  2571. $config_list = glob($_SERVER["DOCUMENT_ROOT"] . PROJECT_PATH . "/*/*/app_config.php");
  2572. $x = 0;
  2573. if (is_array($config_list)) {
  2574. foreach ($config_list as &$config_path) {
  2575. include($config_path);
  2576. $x++;
  2577. }
  2578. }
  2579. $_SESSION['apps'] = $apps;
  2580. }
  2581. /**
  2582. * Returns the depth of an array
  2583. * @param array $array Reference to array
  2584. * @return int Depth of array
  2585. * @internal Moved to class to conserve resources.
  2586. */
  2587. public static function array_depth(array &$array) {
  2588. $depth = 0;
  2589. if (is_array($array)) {
  2590. $depth++;
  2591. foreach ($array as $value) {
  2592. if (is_array($value)) {
  2593. $depth = self::array_depth($value) + 1;
  2594. }
  2595. }
  2596. }
  2597. return $depth;
  2598. }
  2599. /**
  2600. * Searches through all fields to see if domain_uuid exists
  2601. * @param string $name
  2602. * @uses $_SESSION['apps'] directly
  2603. * @return boolean <b>true</b> on success and <b>false</b> on failure
  2604. * @see database::get_apps()
  2605. */
  2606. public static function domain_uuid_exists($name) {
  2607. //get the $apps array from the installed apps from the core and mod directories
  2608. if (!is_array($_SESSION['apps'])) {
  2609. self::get_apps();
  2610. }
  2611. //search through all fields to see if domain_uuid exists
  2612. $apps = $_SESSION['apps'];
  2613. if (is_array($apps)) {
  2614. foreach ($apps as $x => &$app) {
  2615. if (is_array($app['db'])) {
  2616. foreach ($app['db'] as $y => &$row) {
  2617. if (is_array($row['table']['name'])) {
  2618. $table_name = $row['table']['name']['text'];
  2619. }
  2620. else {
  2621. $table_name = $row['table']['name'];
  2622. }
  2623. if ($table_name === self::TABLE_PREFIX.$name) {
  2624. if (is_array($row['fields'])) {
  2625. foreach ($row['fields'] as $field) {
  2626. if ($field['name'] == "domain_uuid") {
  2627. return true;
  2628. }
  2629. } //foreach
  2630. } //is array
  2631. }
  2632. } //foreach
  2633. } //is array
  2634. } //foreach
  2635. } //is array
  2636. //not found
  2637. return false;
  2638. }
  2639. /**
  2640. * Get Relations searches through all fields to find relations
  2641. * @param string $schema Table name
  2642. * @return array Returns array or false
  2643. * @internal Moved to class to conserve resources.
  2644. */
  2645. public static function get_relations($schema) {
  2646. //remove the v_ prefix
  2647. if (substr($schema, 0, strlen(self::TABLE_PREFIX)) == self::TABLE_PREFIX) {
  2648. $schema = substr($schema, strlen(self::TABLE_PREFIX));
  2649. }
  2650. //sanitize the values
  2651. $schema = self::sanitize($schema);
  2652. //get the apps array
  2653. $config_list = glob($_SERVER["DOCUMENT_ROOT"] . PROJECT_PATH . "/{core,app}/{".$schema.",".self::singular($schema)."}/app_config.php", GLOB_BRACE);
  2654. foreach ($config_list as &$config_path) {
  2655. include($config_path);
  2656. }
  2657. //search through all fields to find relations
  2658. if (is_array($apps)) {
  2659. foreach ($apps as $x => &$app) {
  2660. foreach ($app['db'] as $y => &$row) {
  2661. foreach ($row['fields'] as $z => $field) {
  2662. if ($field['deprecated'] != "true") {
  2663. if ($field['key']['type'] == "foreign") {
  2664. if ($row['table']['name'] == self::TABLE_PREFIX.$schema || $field['key']['reference']['table'] == self::TABLE_PREFIX.$schema) {
  2665. //get the field name
  2666. if (is_array($field['name'])) {
  2667. $field_name = trim($field['name']['text']);
  2668. }
  2669. else {
  2670. $field_name = trim($field['name']);
  2671. }
  2672. //build the array
  2673. $relations[$i]['table'] = $row['table']['name'];
  2674. $relations[$i]['field'] = $field_name;
  2675. $relations[$i]['key']['type'] = $field['key']['type'];
  2676. $relations[$i]['key']['table'] = $field['key']['reference']['table'];
  2677. $relations[$i]['key']['field'] = $field['key']['reference']['field'];
  2678. if (isset($field['key']['reference']['action'])) {
  2679. $relations[$i]['key']['action'] = $field['key']['reference']['action'];
  2680. }
  2681. //increment the value
  2682. $i++;
  2683. }
  2684. }
  2685. }
  2686. unset($field_name);
  2687. }
  2688. }
  2689. }
  2690. }
  2691. //return the array
  2692. if (is_array($relations)) {
  2693. return $relations;
  2694. } else {
  2695. return false;
  2696. }
  2697. }
  2698. /**
  2699. * Returns a sanitized string value safe for database or table name.
  2700. * @param string $value To be sanitized
  2701. * @return string Sanitized using preg_replace('#[^a-zA-Z0-9_\-]#', '')
  2702. * @see preg_replace()
  2703. */
  2704. public static function sanitize(string $value) {
  2705. return preg_replace('#[^a-zA-Z0-9_\-]#', '', $value);
  2706. }
  2707. /**
  2708. * Returns a new connected database object.<br>
  2709. * <p>This allows a shortcut for a common syntax. For more information
  2710. * on how the connection happens see {@link database::__construct()} and
  2711. * {@link database::connect()}</p>
  2712. * <p><b>Usage:</b><br>
  2713. * <code>&nbsp; $database_object = database::new();</code></p>
  2714. * @return database new instance of database object already connected
  2715. * @see database::__construct()
  2716. * @see database::connect()
  2717. */
  2718. public static function new() {
  2719. $db = new database();
  2720. $db->connect();
  2721. return $db;
  2722. }
  2723. } //class database
  2724. } //!class_exists
  2725. //addtitional functions for sqlite
  2726. if (!function_exists('php_md5')) {
  2727. function php_md5($string) {
  2728. return md5($string);
  2729. }
  2730. }
  2731. if (!function_exists('php_unix_time_stamp')) {
  2732. function php_unix_time_stamp($string) {
  2733. return strtotime($string);
  2734. }
  2735. }
  2736. if (!function_exists('php_now')) {
  2737. function php_now() {
  2738. return date("Y-m-d H:i:s");
  2739. }
  2740. }
  2741. if (!function_exists('php_left')) {
  2742. function php_left($string, $num) {
  2743. return substr($string, 0, $num);
  2744. }
  2745. }
  2746. if (!function_exists('php_right')) {
  2747. function php_right($string, $num) {
  2748. return substr($string, (strlen($string)-$num), strlen($string));
  2749. }
  2750. }
  2751. /*
  2752. //example usage
  2753. //find
  2754. require_once "resources/classes/database.php";
  2755. $database = new database;
  2756. $database->domain_uuid = $_SESSION["domain_uuid"];
  2757. $database->type = $db_type;
  2758. $database->table = "v_extensions";
  2759. $where[0]['name'] = 'domain_uuid';
  2760. $where[0]['value'] = $_SESSION["domain_uuid"];
  2761. $where[0]['operator'] = '=';
  2762. $database->where = $where;
  2763. $order_by[0]['name'] = 'extension';
  2764. $database->order_by = $order_by;
  2765. $database->order_type = 'desc';
  2766. $database->limit = '2';
  2767. $database->offset = '0';
  2768. $database->find();
  2769. print_r($database->result);
  2770. //insert
  2771. require_once "resources/classes/database.php";
  2772. $database = new database;
  2773. $database->domain_uuid = $_SESSION["domain_uuid"];
  2774. $database->table = "v_ivr_menus";
  2775. $fields[0]['name'] = 'domain_uuid';
  2776. $fields[0]['value'] = $_SESSION["domain_uuid"];
  2777. echo $database->count();
  2778. */
  2779. ?>