[Cs-cms-commits] SF.net SVN: cs-cms:[4] trunk/0.1
Status: Alpha
Brought to you by:
crazedsanity
From: <cra...@us...> - 2009-02-20 05:40:10
|
Revision: 4 http://cs-cms.svn.sourceforge.net/cs-cms/?rev=4&view=rev Author: crazedsanity Date: 2009-02-20 05:40:04 +0000 (Fri, 20 Feb 2009) Log Message: ----------- Import of cs-blogger v0.2, from which this project will be derived (cs-blogger is also my project; once some things are figured out, one will re-use code from the other to avoid code duplication). Added Paths: ----------- trunk/0.1/CREDITS trunk/0.1/LICENSE trunk/0.1/VERSION trunk/0.1/abstract/ trunk/0.1/abstract/csb_blog.abstract.class.php trunk/0.1/abstract/csb_dataLayer.abstract.class.php trunk/0.1/csb_blog.class.php trunk/0.1/csb_blogComment.class.php trunk/0.1/csb_blogEntry.class.php trunk/0.1/csb_blogLocation.class.php trunk/0.1/csb_blogUser.class.php trunk/0.1/csb_location.class.php trunk/0.1/csb_permission.class.php trunk/0.1/schema/ trunk/0.1/schema/pgsql.schema.sql Property Changed: ---------------- trunk/0.1/ Property changes on: trunk/0.1 ___________________________________________________________________ Added: svn:ignore + .project Added: trunk/0.1/CREDITS =================================================================== --- trunk/0.1/CREDITS (rev 0) +++ trunk/0.1/CREDITS 2009-02-20 05:40:04 UTC (rev 4) @@ -0,0 +1,3 @@ + +Lead Developer: Dan Falconer (cra...@us...) + Added: trunk/0.1/LICENSE =================================================================== --- trunk/0.1/LICENSE (rev 0) +++ trunk/0.1/LICENSE 2009-02-20 05:40:04 UTC (rev 4) @@ -0,0 +1,291 @@ +NOTE: a full HTML version of this license can be found at: +http://www.opensource.org/licenses/gpl-license.php + +It has been reproduced below without any HTML. +========================================================================== + +The GNU General Public License (GPL) + +Version 2, June 1991 + + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS Added: trunk/0.1/VERSION =================================================================== --- trunk/0.1/VERSION (rev 0) +++ trunk/0.1/VERSION 2009-02-20 05:40:04 UTC (rev 4) @@ -0,0 +1,5 @@ +## Stores the current version of cs-cms, and it's source. Please do NOT modify this file. + +VERSION: 0.1 +PROJECT: cs-blogger +$HeadURL$ \ No newline at end of file Property changes on: trunk/0.1/VERSION ___________________________________________________________________ Added: svn:keywords + HeadURL Added: trunk/0.1/abstract/csb_blog.abstract.class.php =================================================================== --- trunk/0.1/abstract/csb_blog.abstract.class.php (rev 0) +++ trunk/0.1/abstract/csb_blog.abstract.class.php 2009-02-20 05:40:04 UTC (rev 4) @@ -0,0 +1,456 @@ +<?php + +require_once(dirname(__FILE__) .'/csb_dataLayer.abstract.class.php'); + +class csb_blogAbstract extends csb_dataLayerAbstract { + + /** Internal name of blog (looks like a permalink) */ + protected $blogName; + + /** Displayable name of blog */ + protected $blogDisplayName; + + /** Numeric ID of blog */ + protected $blogId=false; + + /** Location of blog */ + protected $blogLocation; + + //------------------------------------------------------------------------- + /** + * The constructor. + * + * @param $blogName (str) name of blog (NOT the display name) + * @param $dbType (str) Type of database (pgsql/mysql/sqlite) + * @param $dbParams (array) connection options for database + * + * @return exception throws an exception on error. + */ + public function __construct(array $dbParams=null) { + + //TODO: put these in the constructor args, or require CONSTANTS. + parent::__construct($dbParams); + + }//end __construct() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Initializes information about the selected blog. + * + * @param $blogName (str) name of blog (NOT the display name) + */ + public function initialize_locals($blogName) { + + if(!is_numeric($this->blogId)) { + $data = $this->get_blog_data_by_name($blogName); + + $var2index = array( + 'blogDisplayName' => 'blog_display_name', + 'blogName' => 'blog_name', + 'blogId' => 'blog_id', + 'blogLocation' => 'location' + ); + + foreach($var2index as $var=>$index) { + if(isset($data[$index]) && strlen($data[$index])) { + $this->$var = $data[$index]; + } + else { + throw new exception(__METHOD__ .": var ". $var ." not set from index ". $index .", no data (". $data[$index] .")"); + } + } + } + else { + throw new exception(__METHOD__ .": already initialized"); + } + }//end initialize_locals() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Retrieves protected (or private?) internal var values + * + * @param $var (str) name of internal var to retrieve + * + * @return exception throws an exception if named var doesn't exist. + */ + public function get_internal_var($var) { + if(isset($this->$var)) { + $retval = $this->$var; + } + else { + throw new exception(__METHOD__ .": invalid var name (". $var .")"); + } + + return($retval); + }//end get_internal_var() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + public function is_initialized() { + $retval = false; + if(is_numeric($this->blogId)) { + $retval = true; + } + return($retval); + }//end is_initialized() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Creates a "permalink" just from title (does NOT include blog location): + * lowercases, strips special characters, uses "_" in place of spaces and + * special characters (NEVER creates more than one "_" in a row) + * + * @param $title (str) string to create permalink from. + * + * @return exception throws exception on error + * @return (string) permalink + */ + public function create_permalink_from_title($title) { + if(is_string($title) && strlen($title) >= CSBLOG_TITLE_MINLEN) { + + $permalink = strtolower($title); + $permalink = preg_replace('/!/', '', $permalink); + $permalink = preg_replace('/&\+/', '-', $permalink); + $permalink = preg_replace('/\'/', '', $permalink); + $permalink = preg_replace("/[^a-zA-Z0-9_]/", "_", $permalink); + + if(!strlen($permalink)) { + throw new exception(__METHOD__ .": invalid filename (". $permalink .") from title=(". $title .")"); + } + + //consolidate multiple underscores... (" . . ." becomes "______", after this becomes just "_") + $permalink = preg_replace('/__*/', '_', $permalink); + } + else { + throw new exception(__METHOD__ .": invalid title (". $title .")"); + } + + return($permalink); + }//end create_permalink_from_title() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Encodes content using base64 + * + * @param $content (str) content to encode + * + * @return (string) encoded content. + */ + public function encode_content($content) { + //make it base64 data, so it is easy to insert. + $retval = base64_encode($content); + return($retval); + }//end encode_content() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Decoded content (reverse of encode_content()) + * + * @param $content (str) Encoded content to decode + * + * @return (string) Decoded content. + */ + public function decode_content($content) { + $retval = base64_decode($content); + return($retval); + }//end decode_content() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Retrieves all data about a blog (the main entry only) by its name. + * + * @param $blogName (str) name of blog (displayable or proper) + * + * @return exception throws exceptions on error + * @return (array) array of data about the blog. + */ + public function get_blog_data_by_name($blogName) { + if(strlen($blogName) > 3) { + $data = $this->get_blogs(array('b.blog_name'=>$blogName), 'blog_id'); + + if(count($data) == 1) { + $keys = array_keys($data); + $retval = $data[$keys[0]]; + } + else { + throw new exception(__METHOD__ .": too many records returned (". count($data) .")"); + } + } + else { + throw new exception(__METHOD__ .": invalid blog name (". $blogName .")"); + } + + return($retval); + }//end get_blog_data_by_name() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Same as get_blog_data_by_name(), but use blog_id to find it. + * + * @param $blogId (int) blog_id to retrieve info for. + * + * @return exception throws exception on error + * @return (array) array of data about the blog. + */ + public function get_blog_data_by_id($blogId) { + + if(is_numeric($blogId) && $blogId > 0) { + $data = $this->get_blogs(array('blog_id'=>$blogId), 'blog_id'); + if(count($data) == 1) { + $keys = array_keys($data); + $retval = $data[$keys[0]]; + } + else { + throw new exception(__METHOD__ .": invalid number of records returned (". count($data) .")"); + } + } + else { + throw new exception(__METHOD__ .": invalid blog id (". $blogId .")"); + } + + return($retval); + }//end get_blog_data_by_id() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Determines if there is one or more matching permalinks for the given + * blog; duplicates are given a suffix of "-N" (where "N" is the number of + * matching entries; first dup is given "-1", and so on). + * + * @param $blogId (int) blog_id for the permalink + * @param $permaLink (str) permalink to check + * + * @return exception thrown on error. + * @return + */ + public function check_permalink($blogId, $permalink) { + if(is_string($permalink) && strlen($permalink) >= CSBLOG_TITLE_MINLEN && is_numeric($blogId) && $blogId > 0) { + #if($permalink == $this->create_permalink_from_title($permalink)) { + $permalink = $this->create_permalink_from_title($permalink); + $sql = "SELECT * FROM csblog_entry_table WHERE blog_id=". $blogId + ." AND permalink='". $permalink ."' OR permalink LIKE '". $permalink ."-%'"; + + $numrows = $this->run_sql($sql, false); + + if($numrows >= 0) { + if($numrows >= 1) { + //got a record, give 'em the data back. + $retval = $permalink ."-". $numrows; + } + elseif($numrows == 0) { + $retval = $permalink; + } + else { + throw new exception(__METHOD__ .": unknown error, numrows=(". $numrows ."), dberror"); + } + } + else { + throw new exception(__METHOD__ .": invalid numrows (". $numrows .") or dberror"); + } + } + else { + throw new exception(__METHOD__ .": invalid permalink (". $permalink .") or blog_id (". $blogId .")"); + } + + return($retval); + }//end check_permalink() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + protected function get_age_hype($timestamp, $addBrackets=FALSE) { + if(strlen($timestamp >= 9)) { + if(!is_numeric($timestamp)) { + $timestamp = strtotime($timestamp); + } + + $age = time() - $timestamp; + switch($age) { + case ($age <= 1800): { + $extraText = '<font color="red"><b>Ink\'s still WET!</b></font>'; + } break; + + case ($age <= 3600): { + //modified less than an hour ago! + $extraText = '<font color="red"><b>Hot off the press!</b></font>'; + } break; + + case ($age <= 86400): { + //modified less than 24 hours ago. + $extraText = '<font color="red"><b>New!</b></font>'; + } break; + + case ($age <= 604800): { + //modified this week. + $extraText = '<font color="red">Less than a week old</font>'; + } break; + + case ($age <= 2592000): { + //modified this month. + $extraText = '<b>Less than a month old</b>'; + } break; + + case ($age <= 5184000): { + //modified in the last 2 months + $extraText = '<b>Updated last month</b>'; + } break; + + case ($age <= 7776000): { + $extraText = '<i>Updated 3 months ago</i>'; + } break; + + case ($age <= 10368000): { + $extraText = '<i>Updated 4 months ago</i>'; + } break; + + case ($age <= 12960000): { + $extraText = '<i>Updated 5 months ago</i>'; + } break; + + case ($age <= 15552000): { + $extraText = '<i>Updated in the last 6 months</i>'; + } break; + + default: { + $extraText = '<i>pretty old</i>'; + } + } + + if(strlen($extraText) && $addBrackets) { + $extraText = '['. $extraText .']'; + } + } + else { + throw new exception(__METHOD__ .": invalid timestamp (". $timestamp .")"); + } + + return($extraText); + }//end get_age_hype() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + public function get_most_recent_blog() { + $retval = $this->get_recent_blogs(1); + return($retval); + }//end get_most_recent_blog() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + public function get_recent_blogs($limit=5, $offset=0, $includeDrafts=false) { + if(is_numeric($this->blogId)) { + if(is_numeric($limit) && $limit > 0) { + if(is_numeric($offset) && $offset >= 0) { + $criteria = array( + 'blog_id' => $this->blogId, + 'is_draft' => 'f' + ); + if($includeDrafts === true) { + unset($criteria['is_draft']); + } + $retval = $this->get_blog_entries($criteria, 'post_timestamp DESC', $limit, $offset); + } + else { + throw new exception(__METHOD__ .": invalid offset (". $offset .")"); + } + } + else { + throw new exception(__METHOD__ .": invalid limit (". $limit .")"); + } + } + else { + throw new exception(__METHOD__ .": blogId not set"); + } + + return($retval); + }//end get_recent_blogs() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + protected function get_full_permalink($permalink) { + if(!strlen($this->blogLocation)) { + throw new exception(__METHOD__ .": missing internal blogLocation (". $this->blogLocation .")"); + } + elseif(!strlen($this->blogName)) { + throw new exception(__METHOD__ .": missing internal blogName (". $this->blogName .")"); + } + elseif(!strlen($permalink)) { + throw new exception(__METHOD__ .": invalid/missing permalink (". $permalink .")"); + } + else { + $retval = $this->blogLocation .'/'. $this->blogName .'/'. $permalink; + } + + return($retval); + }//end get_full_permalink() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + public function parse_full_permalink($fullPermalink) { + + if(strlen($fullPermalink) && preg_match("/\//", $fullPermalink)) { + $fullPermalink = preg_replace("/^\//", "", $fullPermalink); + $parts = explode("/", $fullPermalink); + + if(count($parts) >= 3) { + $permalink = array_pop($parts); + $blogName = array_pop($parts); + $location = "/". $this->gfObj->string_from_array($parts, NULL, "/"); + + $retval = array( + 'location' => $location, + 'blogName' => $blogName, + 'permalink' => $permalink + ); + } + else { + throw new exception(__METHOD__ .": not enough parts (i.e. location, blogName, & permalink) in " . + "full permalink (". $fullPermalink .")"); + } + } + else { + throw new exception(__METHOD__ .": invalid or non-full permalink given (". $fullPermalink .")"); + } + + return($retval); + }//end parse_full_permalink() + //------------------------------------------------------------------------- + + + +}// end blog{} +?> Added: trunk/0.1/abstract/csb_dataLayer.abstract.class.php =================================================================== --- trunk/0.1/abstract/csb_dataLayer.abstract.class.php (rev 0) +++ trunk/0.1/abstract/csb_dataLayer.abstract.class.php 2009-02-20 05:40:04 UTC (rev 4) @@ -0,0 +1,668 @@ +<?php + +require_once(dirname(__FILE__) .'/../../cs-content/cs_phpDB.class.php'); +require_once(dirname(__FILE__) .'/../../cs-content/cs_globalFunctions.class.php'); +require_once(dirname(__FILE__) .'/../../cs-versionparse/cs_version.abstract.class.php'); +require_once(dirname(__FILE__) .'/../csb_location.class.php'); +require_once(dirname(__FILE__) .'/../csb_permission.class.php'); + + +abstract class csb_dataLayerAbstract extends cs_versionAbstract { + + /** */ + protected $gfObj; + + /** */ + private $isConnected=false; + + /** */ + protected $dbParams; + + const DBTYPE='pgsql'; + + //------------------------------------------------------------------------- + /** + * Constructor (must be called from extending class) + * + * @param $dbType (str) type of database to connect to (pgsql/mysql/sqlite) + * @param $dbParams (array) list of connection parameters for selected db. + */ + function __construct(array $dbParams=NULL) { + $this->set_version_file_location(dirname(__FILE__) . '/../VERSION'); + $this->gfObj = new cs_globalFunctions(); + + //check that some required constants exist. + if(!defined('CSBLOG_TITLE_MINLEN')) { + define('CSBLOG_TITLE_MINLEN', 4); + } + + $this->dbParams = $dbParams; + $this->connect_db(); + }//end __construct() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + protected function connect_db() { + if($this->isConnected === false) { + if(is_array($this->dbParams)) { + $this->db = new cs_phpDB('pgsql'); + try { + $this->db->connect($this->dbParams); + } + catch(exception $e) { + throw new exception(__METHOD__ .": failed to connect to database, DETAILS: ". $e->getMessage()); + } + + //NOTE: if the call to "connect()" fails, it should throw an exception. + $this->isConnected=true; + } + else { + //no parameters passed. Try using constants. + + $constantList = array( + 'CSBLOG_DB_HOST' => 'host', + 'CSBLOG_DB_PORT' => 'port', + 'CSBLOG_DB_DBNAME' => 'dbname', + 'CSBLOG_DB_USER' => 'user', + 'CSBLOG_DB_PASSWORD' => 'password' + ); + + $dbParams = array(); + foreach($constantList as $constant=>$index) { + $value = ''; + if(defined($constant)) { + $value = constant($constant); + } + else { + if($index != 'password') { + throw new exception(__METHOD__ .": missing one or more constants for database connectivity (". $constant .")"); + } + } + $dbParams[$index] = $value; + } + $this->db = new cs_phpDB('pgsql'); + try { + $this->db->connect($dbParams); + } + catch(exception $e) { + throw new exception(__METHOD__ .": failed to connect to database using constants, DETAILS: ". $e->getMessage()); + } + + //NOTE: if the call to "connect()" fails, it should throw an exception. + $this->isConnected = true; + } + } + else { + $this->gfObj->debug_print(__METHOD__ .": already connected"); + } + }//end connect_db() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + public function run_sql($sql, $exceptionOnNoRows=true) { + //TODO: make EVERYTHING use this method, so it can automatically connect to the database. + if($this->isConnected !== true) { + $this->connect_db(); + } + + if($this->isConnected === true) { + if(is_string($sql) && strlen($sql)) { + + //run the statement & check the output. + $numrows = $this->db->exec($sql); + $dberror = $this->db->errorMsg(); + + if($exceptionOnNoRows === true && $numrows <= 0 && !strlen($dberror)) { + throw new exception(__METHOD__ .": no rows returned (". $numrows .")"); + } + elseif(is_numeric($numrows) && !strlen($dberror)) { + $retval = $numrows; + } + else { + throw new exception(__METHOD__ .": invalid numrows (". $numrows ."), failed to run SQL... " . + "DBERROR: ". $dberror ."<BR>\nSQL::: ". $sql); + } + + } + else { + throw new exception(__METHOD__ .": no sql to run"); + } + } + else { + throw new exception(__METHOD__ .": database isn't connected"); + } + + return($retval); + }//end run_sql() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Loads data into blank (or existing) database. + * + * @param void (void) none + * + * @return exception throws exception on error + * @return (int) Number of records existing in auth table. + */ + public function run_setup() { + $retval = false; + $this->connect_db(); + $this->db->beginTrans(); + $fs = new cs_fileSystem(dirname(__FILE__) .'/../schema'); + $mySchema = $fs->read(self::DBTYPE .'.schema.sql'); + + $retval = $this->db->exec($mySchema); + + #$internalValues = $this->get_version(true); + #foreach($internalValues as $name=>$value) { + # $sql = "INSERT INTO csblog_internal_data_table (internal_name, internal_value) " . + # "VALUES ('". $name ."', '". $value ."')"; + # $this->run_sql($sql); + #} + + $retval = 1; + $this->db->commitTrans(); + return($retval); + }//end run_setup() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Create a user in the database. This assumes that the blogger is in + * complete control of authentication, so this doesn't work if an existing + * mechanism is already in place. + * + * @param $username (str) username to create + * @param $password (str) unencrypted password for the user + * + * @return exception throws exceptions on error + * @return (int) UID of new user + */ + public function create_user($username, $password) { + + $username = $this->gfObj->cleanString($username, 'email'); + if($username != func_get_arg(0)) { + throw new exception(__METHOD__ .": username contained invalid characters (". $username ." != ". func_get_arg(0) .")"); + } + + $existingUser = $this->get_uid($username); + + if($existingUser === false) { + $encryptedPass = md5($username .'-'. $password); + $sql = 'INSERT INTO cs_authentication_table (username, passwd) VALUES ' . + "('". $username ."', '". $encryptedPass ."')"; + + $numrows = $this->run_sql($sql); + + if($numrows == 1) { + $sql = "SELECT currval('cs_authentication_table_uid_seq')"; + + $numrows = $this->run_sql($sql); + if($numrows == 1) { + $data = $this->db->farray(); + $retval = $data[0]; + } + else { + throw new exception(__METHOD__ .": invalid numrows (". $numrows .") while retrieving last inserted uid"); + } + } + else { + throw new exception(__METHOD__ .": failed insert (". $numrows ."): "); + } + } + else { + throw new exception(__METHOD__ .": user exists (". $username .")"); + } + + return($retval); + }//end create_user() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Retrieve UID for the given username (i.e. for checking dups) + * + * @param $username (str) username to check + * + * @return exception throws exception on error + * @return false boolean FALSE returned if no user + * @return (int) UID for existing user + */ + public function get_user($username) { + + if(strlen($username) && is_string($username) && !is_numeric($username)) { + $username = $this->gfObj->cleanString($username, 'email'); + if($username != func_get_arg(0)) { + throw new exception(__METHOD__ .": username contained invalid characters (". $username ." != ". func_get_arg(0) .")"); + } + $sql = "SELECT * FROM cs_authentication_table WHERE username='". $username ."'"; + $numrows = $this->run_sql($sql, false); + + if($numrows == 1) { + $retval = $this->db->farray_fieldnames(); + } + elseif($numrows == 0) { + $retval = false; + } + else { + throw new exception(__METHOD__ .": invalid numrows (". $numrows .")"); + } + } + else { + throw new exception(__METHOD__ .": invalid data for username (". $username .")"); + } + + return($retval); + }//end get_user() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + public function get_uid($username) { + $data = $this->get_user($username); + if(is_bool($data) && $data === false) { + $retval = $data; + } + else { + if(is_array($data) && isset($data['uid']) && is_numeric($data['uid'])) { + $retval = $data['uid']; + } + else { + throw new exception(__METHOD__ .": failed to locate uid column in return DATA::: ". $this->gfObj->debug_print($data,0)); + } + } + + return($retval); + }//end get_uid() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Creates new blog entry. + * + * @param $blogName (str) Display name of blog. + * @param $owner (str/int) UID of owner (username converted to uid) + * @param $location (str) location of blog + * + * @return exception throws exception on error + * @return (int) Newly created blog_id + */ + function create_blog($blogName, $owner, $location) { + if(!strlen($blogName)) { + throw new exception(__METHOD__ .": invalid blogName (". $blogName .")"); + } + elseif(!strlen($owner)) { + throw new exception(__METHOD__ .": invalid owner (". $owner .")"); + } + + if(!is_numeric($owner)) { + $username = $owner;//for later + $owner = $this->get_uid($owner); + if(!is_numeric($owner) || $owner < 1) { + throw new exception(__METHOD__ .": unable to find UID for user (". $username .")"); + } + } + + //attempt to get/create the location... + $loc = new csb_location($this->dbParams); + $locationId = $loc->get_location_id($location); + if(!is_numeric($locationId) || $locationId < 1) { + //TODO: should we really be creating this automatically? + $locationId = $loc->add_location($location); + } + + $formattedBlogName = $this->create_permalink_from_title($blogName); + $sql = "INSERT INTO csblog_blog_table ". $this->gfObj->string_from_array( + array( + 'blog_display_name' => $blogName, + 'blog_name' => $formattedBlogName, + 'uid' => $owner, + 'location_id' => $locationId + ), + 'insert', + NULL, + 'sql_insert' + ); + + $numrows = $this->run_sql($sql); + + if($numrows == 1) { + //pull the blogId. + $retval = $this->db->get_currval('csblog_blog_table_blog_id_seq'); + + if(is_numeric($retval) && $retval > 0) { + //Initialize locals now, if it hasn't been done yet. + if(defined('CSBLOG_SETUP_PENDING') && !$this->is_initialized()) { + $this->initialize_locals($formattedBlogName); + } + } + else { + throw new exception(__METHOD__ .": new blog_id (". $retval .") is invalid"); + } + } + else { + throw new exception(__METHOD__ .": invalid numrows (". $numrows .") returned, ERROR: ". $this->db->errorMsg()); + } + + return($retval); + }//end create_blog() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Create new entry for existing blog. + * + * @param $blogId (int) blog_id to associate entry with. + * @param $authorUid (int) UID of author + * @param $title (str) Title of blog. + * @param $content (str) Contents of blog. + * @param $optionalData (array) optional items to specify (i.e. + * post_timestamp) + * + * @return exception throws an exception on error + * @return (array) Array of data, indexes explain values + */ + public function create_entry($blogId, $authorUid, $title, $content, array $optionalData=NULL) { + + //check to make sure we've got all the proper fields and they're formatted appropriately. + $sqlArr = array(); + $cleanStringArr = array( + 'blog_id' => "integer", + 'author_uid' => "integer", + 'title' => "sql", + 'content' => "sql", + 'permalink' => "email" + ); + if(is_numeric($blogId) && $blogId > 0) { + $sqlArr['blog_id'] = $blogId; + } + else { + throw new exception(__METHOD__ .": invalid data for blogId (". $blogId .")"); + } + if(is_numeric($authorUid) && $authorUid > 0) { + $sqlArr['author_uid'] = $authorUid; + } + else { + throw new exception(__METHOD__ .": invalid data for authorUid (". $authorUid .")"); + } + if(is_string($title) && strlen($title) > CSBLOG_TITLE_MINLEN) { + $sqlArr['title'] = $title; + } + else { + throw new exception(__METHOD__ .": invalid data for title (". $title .")"); + } + + //only allow a few other optional fields (make sure they're the appropriate data type). + if(is_array($optionalData) && count($optionalData)) { + + //Valid optional fields are defined here. + $validOptionalFields = array( + 'post_timestamp' => 'datetime', + 'is_draft' => 'boolean' + ); + + $intersectedArray = array_intersect_key($optionalData, $validOptionalFields); + + if(is_array($intersectedArray) && count($intersectedArray)) { + foreach($intersectedArray as $fieldName => $value) { + if(!isset($sqlArr[$fieldName])) { + $sqlArr[$fieldName] = $value; + $cleanStringArr[$fieldName] = $validOptionalFields[$fieldName]; + } + else { + throw new exception(__METHOD__ .": index (". $fieldName .") already exists"); + } + } + } + } + + + //lets check to see that there is NOT already a blog like this... + $permalink = $this->create_permalink_from_title($title); + $checkLink = $this->check_permalink($blogId, $title); + + if($checkLink != $permalink) { + $permalink = $checkLink; + } + //set some fields that can't be specified... + $sqlArr['permalink'] = $permalink; + $sqlArr['content'] = $this->encode_content($content); + + //build the SQL statement. + $sql = "INSERT INTO csblog_entry_table ". $this->gfObj->string_from_array($sqlArr, 'insert', NULL, $cleanStringArr); + + //run the statement & check the output. + $numrows = $this->run_sql($sql); + + if(is_numeric($numrows) && $numrows == 1) { + $blogData = $this->get_blog_data_by_id($blogId); + $retval = array( + 'entry_id' => $this->db->get_currval('csblog_entry_table_entry_id_seq'), + 'full_permalink' => $this->get_full_permalink($sqlArr['permalink']) + ); + + //one final thing: update the main blog table with the newest post_timestamp. + $this->update_blog_last_post_timestamps(); + } + else { + throw new exception(__METHOD__ .": invalid numrows (". $numrows ."), failed to insert data"); + } + + return($retval); + }//end create_entry) + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + /** + * Retrieve a blog entry based on the FULL permalink (location included) + * + * @param $fullPermalink (str) Permalink (blog location + permalink) for + * entry. + * + * @return exception throws exception on error + * @return (array) Returns array of data, includes decoded content + */ + public function get_blog_entry($fullPermalink) { + //TODO: have this use get_blog_entries() + //the total permalink length should be at least double the minimum title length to include a path. + if(strlen($fullPermalink) > (CSBLOG_TITLE_MINLEN *2)) { + //now get the permalink separate from the title. + $parts = $this->parse_full_permalink($fullPermalink); + $permalink = $parts['permalink']; + $blogName = $parts['blogName']; + $location = $parts['location']; + + $criteria = array('bl.location'=>$location, 'b.blog_name'=>$blogName,'be.permalink'=>$permalink); + $data = $this->get_blog_entries($criteria,'be.entry_id'); + + if(count($data) == 1) { + $keys = array_keys($data); + $retval = $data[$keys[0]]; + } + elseif(count($data) > 1) { + throw new exception(__METHOD__ .": multiple records returned for same location (". count($data) .")"); + } + else { + throw new exception(__METHOD__ .": invalid number of records (". count($data) .") or dberror"); + } + } + else { + throw new exception(__METHOD__ .": failed to meet length requirement of ". (CSBLOG_TITLE_MINLEN *2)); + } + + return($retval); + }//end get_blog_entry() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + public function get_blog_entries(array $criteria, $orderBy, $limit=NULL, $offset=NULL) { + if(!is_array($criteria) || !count($criteria)) { + throw new exception(__METHOD__ .": invalid criteria"); + } + + //TODO: should be specifically limited to blogs that are accessible to current user. + $sql = "SELECT be.*, bl.location, b.blog_display_name, be.post_timestamp::date as date_short, " . + "b.blog_name, a.username FROM csblog_entry_table AS be INNER JOIN " . + "csblog_blog_table AS b ON (be.blog_id=b.blog_id) INNER JOIN " . + "csblog_location_table AS bl ON (b.location_id=bl.location_id) INNER JOIN " . + "cs_authentication_table AS a ON (a.uid=be.author_uid) WHERE "; + + //add stuff to the SQL... + foreach($criteria as $field=>$value) { + if(!preg_match('/^[a-z]{1,}\./', $field)) { + unset($criteria[$field]); + $field = "be.". $field; + $criteria[$field] = $value; + } + } + $sql .= $this->gfObj->string_from_array($criteria, 'select', NULL, 'sql'); + + if(strlen($orderBy)) { + $sql .= " ORDER BY ". $orderBy; + } + + if(is_numeric($limit) && $limit > 0) { + $sql .= " LIMIT ". $limit; + } + if(is_numeric($offset) && $limit > 0) { + $sql .= " OFFSET ". $offset; + } + + $numrows = $this->run_sql($sql); + + $retval = $this->db->farray_fieldnames('entry_id', true, false); + foreach($retval as $entryId=>$data) { + $retval[$entryId]['age_hype'] = $this->get_age_hype($data['post_timestamp']); + $retval[$entryId]['content'] = $this->decode_content($data['content']); + $retval[$entryId]['full_permalink'] = $this->get_full_permalink($data['permalink']); + + //make a formatted post_timestamp index. + $retval[$entryId]['formatted_post_timestamp'] = + strftime('%A, %B %d, %Y %I:%M %p', strtotime($data['post_timestamp'])); + + //format the username... + $retval[$entryId]['formatted_author_name'] = ucwords($data['username']); + + //make "is_draft" a real boolean. + $retval[$entryId]['is_draft'] = $this->gfObj->interpret_bool($data['is_draft']); + } + + return($retval); + }//end get_blog_entries() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + public function get_blogs(array $criteria, $orderBy=NULL, $limit=NULL, $offset=NULL) { + if(!is_array($criteria) || !count($criteria)) { + throw new exception(__METHOD__ .": invalid criteria"); + } + + //TODO: should be specifically limited to blogs that are accessible to current user. + $sql = "SELECT b.*, bl.location FROM csblog_blog_table AS b INNER JOIN " . + "csblog_location_table AS bl ON (b.location_id=bl.location_id) WHERE "; + + //add stuff to the SQL... + foreach($criteria as $field=>$value) { + if(!preg_match('/^[a-z]{1,}\./', $field)) { + unset($criteria[$field]); + $field = "b.". $field; + $criteria[$field] = $value; + } + } + $sql .= $this->gfObj->string_from_array($criteria, 'select', NULL, 'sql'); + + if(strlen($orderBy)) { + $sql .= " ORDER BY ". $orderBy; + } + else { + $sql .= " ORDER BY b.blog_id"; + } + + if(is_numeric($limit) && $limit > 0) { + $sql .= " LIMIT ". $limit; + } + if(is_numeric($offset) && $limit > 0) { + $sql .= " OFFSET ". $offset; + } + + $numrows = $this->run_sql($sql); + + $retval = $this->db->farray_fieldnames('blog_id', true, false); + + return($retval); + }//end get_blogs() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + protected function update_blog_data(array $updates) { + $sql = "UPDATE csblog_blog_table SET ". + $this->gfObj->string_from_array($updates, 'update', null, 'sql') . + " WHERE blog_id=". $this->blogId; + + try { + $this->db->beginTrans(); + $numrows = $this->run_sql($sql); + + if($numrows == 1) { + $retval = $numrows; + $this->db->commitTrans(); + } + else { + throw new exception(__METHOD__ .": updated invalid number of rows (". $numrows .") - transaction aborted"); + } + } + catch(exception $e) { + $this->db->rollbackTrans(); + throw new exception(__METHOD__ .": failed to update blog (". $numrows .")"); + } + + return($retval); + }//end update_blog_data() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + protected function update_blog_last_post_timestamps() { + $sql = "update csblog_blog_table AS b SET last_post_timestamp=" . + "(SELECT post_timestamp FROM csblog_entry_table WHERE " . + "blog_id=b.blog_id ORDER BY post_timestamp DESC limit 1)"; + + try { + $retval = $this->run_sql($sql); + } + catch(exception $e) { + throw new exception(__METHOD__ .": failed to update last_post_timestamp for blogs, DETAILS::: ". $e->getMessage()); + } + + return($retval); + }//end update_blog_last_post_timestamps() + //------------------------------------------------------------------------- + + +}//end dataLayerAbstract{} +?> Added: trunk/0.1/csb_blog.class.php =================================================================== --- trunk/0.1/csb_blog.class.php (rev 0) +++ trunk/0.1/csb_blog.class.php 2009-02-20 05:40:04 UTC (rev 4) @@ -0,0 +1,51 @@ +<?php + +require_once(dirname(__FILE__) .'/abstract/csb_blog.abstract.class.php'); + +class csb_blog extends csb_blogAbstract { + + /** Internal name of blog (looks like a permalink) */ + protected $blogName; + + /** Displayable name of blog */ + protected $blogDisplayName; + + /** Numeric ID of blog */ + protected $blogId=false; + + /** Location of blog */ + protected $blogLocation; + + //------------------------------------------------------------------------- + /** + * The constructor. + * + * @param $blogName (str) name of blog (NOT the display name) + * @param $dbType (str) Type of database (pgsql/mysql/sqlite) + * @param $dbParams (array) connection options for database + * + * @return exception throws an exception on error. + */ + public function __construct($blogName, array $dbParams=null) { + + //TODO: put these in the constructor args, or require CONSTANTS. + parent::__construct($dbParams); + + + if(!isset($blogName) || !strlen($blogName)) { + throw new exception(__METHOD__ .": invalid blog name (". $blogName .")"); + } + + $this->blogName = $blogName; + if(!defined('CSBLOG_SETUP_PENDING')) { + //proceed normally... + $this->initialize_locals($blogName); + } + + }//end __construct() + //------------------------------------------------------------------------- + + + +}// end blog{} +?> Added: trunk/0.1/csb_blogComment.class.php =================================================================== --- trunk/0.1/csb_blogComment.class.php (rev 0) +++ trunk/0.1/csb_blogComment.class.php 2009-02-20 05:40:04 UTC (rev 4) @@ -0,0 +1,185 @@ +<?php + +require_once(dirname(__FILE__) .'/abstract/csb_blog.abstract.class.php'); +require_once(dirname(__FILE__) .'/csb_blogEntry.class.php'); + +//TODO: consider moving add_comment() and get_comments() to csb_dataLayerAbstract{}, since they're data-type things. + +class csb_blogComment extends csb_blogAbstract { + + protected $blogEntryId; + + //------------------------------------------------------------------------- + public function __construct($fullPermalink, array $dbParams=NULL) { + + parent::__construct($dbParams); + + $blogData = $this->parse_full_permalink($fullPermalink); + + $this->blogLocation = $blogData['location']; + $this->blogName = $blogData['name']; + + }//end __construct() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + public function get_num_comments() { + $sql = "SELECT count(*) FROM csblog_comment_table WHERE entry_id=". $this->blogEntryId; + + try { + $numrows = $this->run_sql($sql); + + if($numrows = 1) { + $data = $this->db->farray(); + $retval = $data[0]; + } + else { + throw new exception(__METHOD__ .": failed to get number of comments, invalid numrows (". $numrows .")"); + } + } + catch(exception $e) { + throw new exception(__METHOD__ .": failed to retrieve number of comments, DETAILS::: ". $e->getMessage()); + } + + return($retval); + }//end get_num_comments() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + public function add_comment($authorUid, $title, $comment, array $ancestry=null, $isAnonymous=false) { + $sqlArr = array( + 'entry_id' => $this>blogEntryId, + 'title' => $title, + 'comment' => $this->encode_content($comment), + 'author_uid' => $authorUid, + 'is_anonymous' => $isAnonymous + ); + + $cleanArr = array( + 'entry_id' => 'numeric', + 'title' => 'sql', + 'comment' => 'sql', + 'author_uid' => 'numeric', + 'is_anonymous' => 'bool', + 'ancestry' => 'sql' + ); + + //handle ancestry, if given. + $ancestryStr = ""; + if(is_array($ancestry) && count($ancestry) > 0) { + foreach($ancestry as $id) { + if(is_numeric($id) && $id > 0) { + $ancestryStr = $this->gfObj->create_list($ancestryStr, $id, ':'); + } + else { + throw new exception(__METHOD__ .": invalid ancestor (". $id .")"); + } + } + $sqlArr['ancestry'] = $ancestryStr; + } + + $sql = "INSERT INTO csblog_comment_table ". $this->gfObj->string_from_array($sqlArr, 'insert', null, $cleanArr); + + try { + $this->db->beginTrans(); + $numrows = $this->run_sql($sql); + + if($numrows == 1) { + $retval = $this->db->get_currval('csblog_comment_table_comment_id_seq'); + $this->db->commitTrans(); + } + else { + $this->db->rollbackTrans(); + throw new exception(__METHOD__ .": failed to create record, invalid numrows (". $numrows .")"); + } + } + catch(exception $e) { + throw new exception(__METHOD__ .": failed to create comment, DETAILS::: ". $e->getMessage()); + } + + return($retval); + }//end add_comment() + //------------------------------------------------------------------------- + + + + //------------------------------------------------------------------------- + protected function get_comments(array $criteria, $orderBy=null, $limit=NULL, $offset=NULL) { + if(!is_array($criteria) || !count($criteria)) { + throw new exception(__METHOD__ .": invalid criteria"); + } + + //TODO: should be specifically limited to blogs that are accessible to current user. + $sql = "SELECT bc.*, be.permalink, bl.location, b.blog_display_name, be.post_timestamp::date as date_short, " . + "b.blog_name, b.blog_id FROM csblog_comment_table AS bc " . + "INNER JOIN csblog_entry_table AS be ON (be.entry_id=bc.entry_id) " . + "INNER JOIN csblog_blog_table AS b ON (be.blog_id=b.blog_id) " . + "INNER JOIN csblog_location_table AS bl ON (b.location_id=bl.location_id) " . + "WHERE "; + + //add stuff to the SQL... + foreach($criteria as $field=>$value) { + if(!preg_match('/^[a-z]{1,}\./', $field)) { + unset($criteria[$field]); + $field = "bc.". $field; + $cri... [truncated message content] |