Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added more PHPUnit test cases, small security fix to search form

  • Loading branch information...
commit 339c1c8856f984e9ef4ba63e091b949331b493e1 1 parent 90b4468
@snytkine snytkine authored
Showing with 2,296 additions and 753 deletions.
  1. +75 −29 lib/Lampcms/Answer.php
  2. +3 −4 lib/Lampcms/AnswerParser.php
  3. +1 −1  lib/Lampcms/CacheHeaders.php
  4. +2 −2 lib/Lampcms/Controllers/Answer.php
  5. +6 −7 lib/Lampcms/Controllers/Delete.php
  6. +1 −1  lib/Lampcms/Controllers/Editor.php
  7. +2 −1  lib/Lampcms/Controllers/Retag.php
  8. +5 −3 lib/Lampcms/Controllers/Search.php
  9. +14 −13 lib/Lampcms/Controllers/Shred.php
  10. +8 −4 lib/Lampcms/Controllers/Vote.php
  11. +1 −1  lib/Lampcms/Cookie.php
  12. +2 −4 lib/Lampcms/CookieAuth.php
  13. +24 −12 lib/Lampcms/DB.php
  14. +32 −15 lib/Lampcms/Interfaces/All.php
  15. +10 −2 lib/Lampcms/Modules/Observers/EmailNotifier.php
  16. +2 −9 lib/Lampcms/Modules/Search/IndexerMySQL.php
  17. +3 −4 lib/Lampcms/Modules/Search/MySQL.php
  18. +2 −3 lib/Lampcms/{ → Modules/Search}/TitleTagsTable.php
  19. +18 −4 lib/Lampcms/Mongo.php
  20. +3 −5 lib/Lampcms/MongoCache.php
  21. +12 −12 lib/Lampcms/MongoDoc.php
  22. +1 −2  lib/Lampcms/MongoIncrementor.php
  23. +18 −36 lib/Lampcms/Object.php
  24. +208 −70 lib/Lampcms/Question.php
  25. +1 −1  lib/Lampcms/RegBlock.php
  26. +0 −2  lib/Lampcms/Relatedtags.php
  27. +15 −15 lib/Lampcms/Request.php
  28. +2 −2 lib/Lampcms/Responder.php
  29. +0 −1  lib/Lampcms/SearchFactory.php
  30. +0 −218 lib/Lampcms/SimilarItems.php
  31. +1 −1  lib/Lampcms/String.php
  32. +0 −1  lib/Lampcms/Stub.php
  33. +2 −2 lib/Lampcms/Tokenizer.php
  34. +7 −35 lib/Lampcms/User.php
  35. +9 −0 lib/Lampcms/UserAuth.php
  36. +1 −1  lib/Lampcms/UserTags.php
  37. +1 −2  lib/Lampcms/UserTwitter.php
  38. +151 −40 lib/Lampcms/Utf8String.php
  39. +11 −97 lib/Lampcms/Validate.php
  40. +1 −3 lib/Lampcms/WebPage.php
  41. +16 −1 phpunit.xml
  42. +286 −0 tests/AnswerTest.php
  43. +0 −12 tests/ArrayDefaultsTest.php
  44. +75 −0 tests/Fixtures/MockAnswer.php
  45. +3 −3 tests/Fixtures/MockQuestion.php
  46. +2 −3 tests/Fixtures/MockUser.php
  47. +72 −0 tests/Fixtures/tplQtags.php
  48. +2 −2 tests/LampcmsArrayTest.php
  49. +85 −0 tests/LampcmsObjectTest.php
  50. +37 −21 tests/LampcmsUnitTestCase.php
  51. +102 −0 tests/MongoIncrementorTest.php
  52. +312 −11 tests/QuestionTest.php
  53. +0 −1  tests/RegistryTest.php
  54. +201 −0 tests/RequestTest.php
  55. +89 −0 tests/ResourceTest.php
  56. +1 −1  tests/UserTest.php
  57. +173 −0 tests/Utf8StringTest.php
  58. +72 −8 tests/bootstrap.php
  59. +8 −7 www/index.php
  60. +11 −8 www/style/1/www/tplQrecent.php
  61. +9 −9 www/style/1/www/tplSearchForm.php
  62. +5 −1 www/style/1/www/tplSimquestions.php
  63. +80 −0 www/update_comments.php
View
104 lib/Lampcms/Answer.php
@@ -86,8 +86,7 @@ public function getResourceTypeId(){
* @return object $this
*/
public function setAccepted(){
- $this->offsetSet('accepted', true);
- $this->touch();
+ parent::offsetSet('accepted', true);
return $this;
}
@@ -99,8 +98,7 @@ public function setAccepted(){
* @return object $this
*/
public function unsetAccepted(){
- $this->offsetSet('accepted', false);
- $this->touch();
+ parent::offsetSet('accepted', false);
return $this;
}
@@ -150,8 +148,8 @@ public function getUsername(){
*/
public function setDeleted(User $user, $reason = null){
if(0 === $this->getDeletedTime()){
- $this->offsetSet('i_del_ts', time());
- $this->offsetSet('a_deleted',
+ parent::offsetSet('i_del_ts', time());
+ parent::offsetSet('a_deleted',
array(
'username' => $user->getDisplayName(),
'i_uid' => $user->getUid(),
@@ -160,8 +158,6 @@ public function setDeleted(User $user, $reason = null){
'hts' => date('F j, Y g:i a T')
)
);
-
- $this->touch();
}
return $this;
@@ -191,9 +187,7 @@ public function setEdited(User $user, $reason = ''){
'reason' => $reason,
'hts' => date('F j, Y g:i a T'));
- $this->offsetSet('a_edited', $aEdited);
-
- $this->touch();
+ parent::offsetSet('a_edited', $aEdited);
return $this;
}
@@ -246,14 +240,14 @@ public function addUpVote($inc = 1){
$score = (int)$this->offsetGet('i_votes');
$total = ($score + $inc);
- $this->offsetSet('i_up', max(0, ($tmp + $inc)) );
- $this->offsetSet('i_votes', $total );
+ parent::offsetSet('i_up', max(0, ($tmp + $inc)) );
+ parent::offsetSet('i_votes', $total );
/**
* Plural extension handling
*/
$v_s = (1 === abs($total) ) ? '' : 's';
- $this->offsetSet('v_s', $v_s);
+ parent::offsetSet('v_s', $v_s);
return $this;
}
@@ -273,14 +267,14 @@ public function addDownVote($inc = 1){
$score = (int)$this->offsetGet('i_votes');
$total = ($score - $inc);
- $this->offsetSet('i_down', max(0, ($tmp + $inc)) );
- $this->offsetSet('i_votes', $total);
+ parent::offsetSet('i_down', max(0, ($tmp + $inc)) );
+ parent::offsetSet('i_votes', $total);
/**
* Plural extension handling
*/
$v_s = (1 === abs($total) ) ? '' : 's';
- $this->offsetSet('v_s', $v_s);
+ parent::offsetSet('v_s', $v_s);
return $this;
}
@@ -387,11 +381,11 @@ public function addComment(CommentParser $oComment){
* because we don't need them here
*/
$aComment = $oComment->getArrayCopy();
- $aComment = array_intersect_key($aComment, array_flip($aKeys));
+ $aComment = \array_intersect_key($aComment, array_flip($aKeys));
$aComments[] = $aComment;
- $this->offsetSet('comments', $aComments);
+ $this->offsetSet('a_comments', $aComments);
$this->increaseCommentsCount();
return $this;
@@ -412,16 +406,29 @@ public function getCommentsCount(){
/**
*
- * Enter description here ...
+ * Increase value of i_comments by 1
+ * The i_comments is a counter
+ *
+ * @return object $this
*/
- public function increaseCommentsCount(){
+ public function increaseCommentsCount($count = 1){
+ if(!is_int($count)){
+ throw new \InvalidArgumentException('$count must be integer. was: '.gettype($count));
+ }
+
/**
* Now increase comments count
*/
$commentsCount = $this->getCommentsCount();
d('$commentsCount '.$commentsCount);
- $this->offsetSet('i_comments', ($commentsCount + 1) );
+ /**
+ * Must use parent::offsetSet because
+ * $this->offsetSet will point back to this
+ * method and enter infinite loop untill
+ * we run out of memory
+ */
+ parent::offsetSet('i_comments', ($commentsCount + $count) );
return $this;
}
@@ -439,30 +446,30 @@ public function increaseCommentsCount(){
*/
public function deleteComment($id){
- if(!$this->checkOffset('comments')){
+ if(0 === $this->getCommentsCount()){
e('This question does not have any comments');
return $this;
}
- $aComments = $this->offsetGet('comments');
+ $aComments = $this->offsetGet('a_comments');
for($i = 0; $i<count($aComments); $i+=1){
if($id == $aComments[$i]['_id']){
d('unsetting comment: '.$i);
- array_splice($aComments, $i, 1);
+ \array_splice($aComments, $i, 1);
break;
}
}
$newCount = count($aComments);
if( 0 === $newCount){
- $this->offsetUnset('comments');
+ $this->offsetUnset('a_comments');
} else {
- $this->offsetSet('comments', $aComments);
+ $this->offsetSet('a_comments', $aComments);
}
- $this->offsetSet('i_comments', $newCount );
+ $this->increaseCommentsCount(-1);
return $this;
}
@@ -475,7 +482,8 @@ public function deleteComment($id){
*
*/
public function getComments(){
- return $this->getFallback('comments', array());
+
+ return $this->offsetGet('a_comments');
}
@@ -502,4 +510,42 @@ public function getQuestionOwnerId(){
return (int)$this->offsetGet('i_quid');
}
+
+ /**
+ * This method prevents setting some
+ * values directly
+ *
+ * (non-PHPdoc)
+ * @see ArrayObject::offsetSet()
+ */
+ public function offsetSet($index, $newval){
+ switch($index){
+ case 'accepted':
+ throw new DevException('value of accepted cannot be set directly. Use setAccepted() or unsetAccepted() methods');
+ break;
+
+ case 'i_comments':
+ throw new DevException('value of i_comments cannot be set directly. Use increaseCommentsCount() method');
+ break;
+
+ case 'i_down':
+ case 'i_up':
+ case 'i_votes':
+ throw new DevException('value of '.$index.' keys cannot be set directly. Use addDownVote or addUpVote to add votes');
+ break;
+
+ case 'a_deleted':
+ case 'i_del_ts':
+ throw new DevException('value of '.$index.' cannot be set directly. Must use setDeleted() method for that');
+ break;
+
+ case 'a_edited':
+ throw new DevException('value of a_edited cannot be set directly. Must use setEdited() method for that');
+ break;
+
+ default:
+ parent::offsetSet($index, $newval);
+ }
+ }
+
}
View
7 lib/Lampcms/AnswerParser.php
@@ -193,7 +193,7 @@ protected function makeAnswer(){
$uid = $this->oSubmittedAnswer->getUserObject()->getUid();
$qid = $this->oSubmittedAnswer->getQid();
- $hash = hash('md5', strtolower($htmlBody.$qid));
+ $hash = hash('md5', \mb_strtolower($htmlBody.$qid));
/**
*
@@ -364,7 +364,8 @@ protected function updateQuestion(){
$this->oQuestion->updateAnswerCount()
->addContributor($oUser)
- ->setLastAnswerer($oUser);
+ ->setLatestAnswer($oUser, $this->oAnswer)
+ ->touch();
return $this;
}
@@ -377,8 +378,6 @@ protected function updateQuestion(){
* @return object $this
*/
protected function followQuestion(){
- d('cp');
-
$oFollowManager = new FollowManager($this->oRegistry);
$oFollowManager->followQuestion($this->oRegistry->Viewer, $this->oQuestion);
View
2  lib/Lampcms/CacheHeaders.php
@@ -122,7 +122,7 @@ public static function processCacheHeaders($etag = null, $lastModified = null, $
* may be notified
*
*/
- if(headers_sent($file, $line)){
+ if(\headers_sent($file, $line)){
e('LampcmsError Headers have already been sent in file '.$file. ' on line '.$line);
return true;
View
4 lib/Lampcms/Controllers/Answer.php
@@ -112,8 +112,8 @@ protected function process(){
$oAdapter = new AnswerParser($this->oRegistry);
try{
$oAnswer = $oAdapter->parse(new SubmittedAnswerWWW($this->oRegistry, $formVals));
- d('cp created new question');
- d('ans id: '.$oAnswer->_id);
+ d('cp created new question: '.print_r($oAnswer->getArrayCopy(), 1));
+ d('ans id: '.$oAnswer->getResourceId());
/**
* In case of ajax we need to send out a
View
13 lib/Lampcms/Controllers/Delete.php
@@ -215,17 +215,15 @@ protected function updateQuestion(){
$oQuestion = new \Lampcms\Question($this->oRegistry);
$oQuestion->by_id($this->oResource['i_qid']);
- $oQuestion->updateAnswerCount(-1);
+ $oQuestion->removeAnswer($this->oResource);
if((true === $this->oResource['accepted'])){
d('this was an accepted answer');
- $this->oResource['accepted'] = false;
- $oQuestion->offsetUnset('i_sel_ans');
+ $this->oResource->unsetAccepted();
}
- $oQuestion->removeContributor($this->oResource['i_uid'])
- ->touch()->save();
+ $oQuestion->touch()->save();
}
return $this;
@@ -313,7 +311,8 @@ protected function setDeleted(){
*/
$this->updateTags();
$this->removeFromIndex();
- $this->oResource->setDeleted($this->oRegistry->Viewer, $this->oRequest['note']);
+ $this->oResource->setDeleted($this->oRegistry->Viewer, $this->oRequest['note'])
+ ->touch();
d('new resource data: '.print_r($this->oResource->getArrayCopy(), 1));
@@ -360,7 +359,7 @@ protected function updateTags(){
}
} else {
$oQuestion = new \Lampcms\Question($this->oRegistry);
- $oQuestion->by_id($this->oResource['i_qid']);
+ $oQuestion->by_id($this->oResource->getQuestionId());
d('tags: ' . print_r($oQuestion['a_tags'], 1));
}
View
2  lib/Lampcms/Controllers/Editor.php
@@ -148,7 +148,7 @@ protected function process(){
}
$this->oResource->setEdited($this->oRegistry->Viewer, \strip_tags($formVals['reason']));
- $this->oResource->save();
+ $this->oResource->touch()->save();
$this->oRegistry->Dispatcher->post($this->oResource, 'onEdit');
View
3  lib/Lampcms/Controllers/Retag.php
@@ -292,7 +292,8 @@ protected function addNewTags(){
*/
protected function updateQuestion(){
- $this->oQuestion->retag($this->oRegistry->Viewer, $this->aSubmitted)->save();
+ $this->oQuestion->retag($this->oRegistry->Viewer, $this->aSubmitted)
+ ->save();
return $this;
}
View
8 lib/Lampcms/Controllers/Search.php
@@ -75,6 +75,8 @@ class Search extends WebPage
* @var bool
*/
protected $notAjaxPaginatable = true;
+
+ //protected $bRequirePost = true;
/**
* (non-PHPdoc)
@@ -87,7 +89,7 @@ protected function main(){
* $_GET as underlying array, and php
* already decodes $_GET or $_POST vars
*/
- $this->term = $this->oRequest['q'];
+ $this->term = $this->oRegistry->Request->getUTF8('q')->stripTags();
$this->aPageVars['qheader'] = '<h1>Search results for: '.$this->term.'</h1>';
$this->aPageVars['title'] = 'Questions matching &#39;'.$this->term.'&#39;';
@@ -95,7 +97,7 @@ protected function main(){
$this->oSearch = SearchFactory::factory($this->oRegistry);
- $this->oSearch->search();
+ $this->oSearch->search($this->term);
$this->makeTopTabs()
->makeInfo()
@@ -103,7 +105,7 @@ protected function main(){
}
protected function makeTopTabs(){
- d('cp');
+
$tabs = Urhere::factory($this->oRegistry)->get('tplToptabs', 'questions');
$this->aPageVars['topTabs'] = $tabs;
View
27 lib/Lampcms/Controllers/Shred.php
@@ -54,7 +54,8 @@
use \Lampcms\WebPage;
use \Lampcms\Request;
-use \Lampcms\Responder;
+use \Lampcms\Responder;
+use \Lampcms\Answer;
class Shred extends WebPage
{
@@ -115,7 +116,7 @@ protected function excludeAdmin(){
return $this;
}
-
+
/**
* This is important as it will cause
* the removal cached value of 'recent questions'
@@ -197,23 +198,23 @@ protected function deleteAnswers(){
if($cur && ($cur->count() > 0)){
foreach($cur as $a){
-
+
$oQuestion = new \Lampcms\Question($this->oRegistry);
try{
- $oQuestion->by_id((int)$a['i_qid']);
- $oQuestion->updateAnswerCount(-1);
-
- if((true === $a['accepted'])){
- d('this was an accepted answer');
- $oQuestion->offsetUnset('i_sel_ans');
- }
-
+ $oQuestion->by_id((int)$oAnswer->getQuestionId());
+ $oAnswer = new Answer($this->oRegistry, $a);
+ $oQuestion->removeAnswer($oAnswer);
$oQuestion->save();
+ /**
+ * setSaved() because we don't need auto-save feature
+ * to save each answer
+ * since all answers will be removed at end of this method
+ */
+ $oAnswer->setSaved();
} catch(\MongoException $e){
d('Question not found by _id: '.$a['i_qid']);
}
-
-
+
if(!empty($a['cc'])){
$this->aCountries[] = $a['cc'];
}
View
12 lib/Lampcms/Controllers/Vote.php
@@ -268,8 +268,12 @@ protected function setOwnerReputation(){
* Now need to calculate points
*
*/
- \Lampcms\User::factory($this->oRegistry)->by_id($uid)->setReputation($this->calculatePoints());
-
+ try{
+ \Lampcms\User::factory($this->oRegistry)->by_id($uid)->setReputation($this->calculatePoints());
+ } catch(\Exception $e){
+ e($e->getMessage().' in file: '.$e->getFile().' on line: '.$e->getLine());
+ }
+
return $this;
}
@@ -307,9 +311,9 @@ protected function calculatePoints(){
*/
protected function increaseVoteCount(){
if('up' === $this->voteType){
- $this->oResource->addUpVote($this->inc);
+ $this->oResource->addUpVote($this->inc)->touch(true);
} else {
- $this->oResource->addDownVote($this->inc);
+ $this->oResource->addDownVote($this->inc)->touch(true);
}
return $this;
View
2  lib/Lampcms/Cookie.php
@@ -85,7 +85,7 @@ public static function sendLoginCookie($intUserId, $strSID, $cookieName = 'uid')
* Google Friend Connect or Facebook connect
*/
- $salt = (defined('MOCK_COOKIE_SALT')) ? constant('MOCK_COOKIE_SALT') : COOKIE_SALT;
+ $salt = COOKIE_SALT;
$cookieSid = \hash('sha256', $intUserId.$salt);
$cookie = \http_build_query(array('uid' => $intUserId, 's' => $cookieSid));
View
6 lib/Lampcms/CookieAuth.php
@@ -96,11 +96,9 @@ public function authByCookie(){
$this->logLoginError($this->uid, $this->sid, true, null, 'cookie');
throw new CookieAuthException('wrong sid '.print_r($oUser, 1));
-
}
return $oUser;
-
}
@@ -160,7 +158,7 @@ protected function checkRequiredCookies(){
* must be equal to 's' value
* if any of these steps fails, throw Exception
*
- * @throws LampcmsCookieAuthException if cookie string
+ * @throws \Lampcms\CookieAuthException if cookie string
* does not parse or does not validate
*
* @return object $this
@@ -191,7 +189,7 @@ protected function validateCookieSalt(){
*/
$this->uid = (int)$this->uid;
- $salt = (defined('MOCK_COOKIE_SALT')) ? constant('MOCK_COOKIE_SALT') : COOKIE_SALT;
+ $salt = COOKIE_SALT;
d('cookie salt: '.$salt);
if($a['s'] !== \hash('sha256', $this->uid.$salt)){
View
36 lib/Lampcms/DB.php
@@ -208,11 +208,6 @@ public function __clone(){
protected function makeDsn(){
$this->aDB = $this->oIni->getSection('DB');
- if (null === $this->aDB) {
-
- throw new IniException('section "DB" does not exist in aIni');
- }
-
if (!isset($this->aDB['Database_username']) || !isset($this->aDB['Database_password'])) {
throw new IniException('Database_username OR Database_password not set');
@@ -235,25 +230,42 @@ protected function getDSN(){
if ( empty($this->aDB['Database_name']) || empty($this->aDB['Database_host']) ||
empty ($this->aDB['Database_type'])) {
- throw new IniException('Cannot create dsn because some required dns params are missing: '.
- print_r($this->aDB, true));
+ throw new IniException('Cannot create dsn because some required dns params are missing: '.print_r($this->aDB, true));
}
- $dbhost = strtolower($this->aDB['Database_host']);
+ /**
+ * LAMPCMS_TEST is the name we use in Unit Tests
+ * If the actual name is also LAMPCMS_TEST then
+ * Unit tests will destroy actual database during
+ * tests. This should not be allowed!
+ */
+ if('LAMPCMS_TEST' === \trim($this->aDB['Database_name']) ){
+ throw new DevException('Reserved name! You cannot name your database '.$this->aDB['Database_name'].' Please set different value of Database_name is !config.ini');
+ }
+
+ $dbhost = \strtolower($this->aDB['Database_host']);
+
+ /**
+ * Always try to use defined LAMPCMS_MYSQL_DB
+ * This is useful in Unit testing so we can
+ * define value for test database and not
+ * use live database!
+ *
+ * @var string
+ */
+ $dbname = (defined('LAMPCMS_MYSQL_DB')) ? LAMPCMS_MYSQL_DB : $this->aDB['Database_name'];
- $ret = strtolower($this->aDB['Database_type']).':host='.$dbhost;
+ $ret = \strtolower($this->aDB['Database_type']).':host='.$dbhost;
if ('localhost' !== $dbhost) {
-
if ( empty ($this->aDB['TCP_Port_number'])) {
throw new IniException('If Database_host is not "localhost" then "TCP_Port_number" MUST be defined');
}
$ret .= ';port='.$this->aDB['TCP_Port_number'];
-
}
- $ret .= ';dbname='.$this->aDB['Database_name'];
+ $ret .= ';dbname='.$dbname;
return $ret;
View
47 lib/Lampcms/Interfaces/All.php
@@ -131,7 +131,7 @@ public function delete($name);
* somewhere for debugging purposes.
*/
public function set($name, $val, $ttl = 63072000, $sDomain = null);
-
+
/**
* Function for setting or deleting login cookie
* the value of the s cookie is an md5 hash of user password
@@ -258,8 +258,8 @@ public function getResourceId();
* @return string
*/
public function getRoleId();
-
-
+
+
public function setRoleId($role);
}
@@ -613,10 +613,10 @@ public function getTumblrBlogUrl();
*
* @return string value to be used as 'group' param
* in WRITE API call
- *
+ *
*/
public function getTumblrBlogId();
-
+
public function setTumblrBlogs(array $blogs);
}
@@ -667,12 +667,12 @@ public function getBloggerBlogs();
/**
* Get title of default blog
- *
+ *
*/
public function getBloggerBlogTitle();
/**
- *
+ *
* Get url of default blog
*/
public function getBloggerBlogUrl();
@@ -681,14 +681,14 @@ public function getBloggerBlogUrl();
/**
* @return string value to be used as '<blogid>' param
* in WRITE API call
- *
+ *
*/
public function getBloggerBlogId();
-
+
/**
* Set value of 'blogs' under the 'blogger' element
- *
- *
+ *
+ *
* @param array $blogs array of all blogs
* user has on Blogger. Each element is an array
* with 3 keys: id, url, title
@@ -733,10 +733,7 @@ public function getBody();
}
/**
- * @todo
- * add setLastAnswerer()
- *
- * @author admin
+ * @author Dmitri Snytkine
*
*/
interface Question extends Post
@@ -807,7 +804,27 @@ public function setBestAnswer(\Lampcms\Answer $oAnswer);
public function updateAnswerCount($int = 1);
+ /**
+ *
+ * Adds the small array with link to last poster
+ * and time of last post and id of last answer
+ * to the a_latest element of the Question
+ *
+ * @param \Lampcms\User $oUser
+ * @param \Lampcms\Answer $oAnswer
+ */
+ public function setLatestAnswer(\Lampcms\User $oUser, \Lampcms\Answer $oAnswer);
+ /**
+ * Method to run when an answer is delete
+ * Deleting an Answer affects several values
+ * in Question like count of answers, status of question etc.
+ * even more so if that answer
+ * was also a "accepted" answer
+ *
+ * @param \Lampcms\Answer $oAnswer
+ */
+ public function removeAnswer(\Lampcms\Answer $oAnswer);
}
/**
View
12 lib/Lampcms/Modules/Observers/EmailNotifier.php
@@ -621,11 +621,20 @@ protected function notifyQuestionFollowers($qid = null, $excludeUid = 0){
if($qid){
$oQuestion = new \Lampcms\Question($this->oRegistry);
- $oQuestion->by_id((int)$qid);
+ try{
+ $oQuestion->by_id((int)$qid);
+ } catch(\Exception $e){
+ e($e->getMessage().' in file: '.$e->getFile().' on line: '.$e->getLine());
+ $oQuestion = null;
+ }
} else {
$oQuestion = $this->oQuestion;
}
+ if(null === $oQuestion){
+ return $this;
+ }
+
$updateType = ('onNewAnswer' === $this->eventName) ? 'answer' : 'comment';
$subj = sprintf(static::$QUESTION_FOLLOW_SUBJ, $updateType);
d('cp');
@@ -670,7 +679,6 @@ protected function notifyQuestionFollowers($qid = null, $excludeUid = 0){
*/
if(false !== $key = array_search($viewerID, $aFollowers)){
array_splice($aFollowers, $key, 1);
-
}
if(!empty($excludeUid)){
View
11 lib/Lampcms/Modules/Search/IndexerMySQL.php
@@ -116,10 +116,9 @@ public function indexQuestion(\Lampcms\Question $oQuestion){
$username = $oQuestion['username'];
$ulink = $oQuestion['ulink'];
$avatar = $oQuestion['avtr'];
- $tags_c = $oQuestion['tags_c'];
$tags_html = $oQuestion['tags_html'];
- d($qid.' title: '. $title. ' url: '. $url.' intro: '.$intro.' tags_c: '.$tags_c);
+ d($qid.' title: '. $title. ' url: '. $url.' intro: '.$intro);
$sql = 'INSERT INTO question_title
(
@@ -132,7 +131,6 @@ public function indexQuestion(\Lampcms\Question $oQuestion){
username,
userlink,
avtr,
- tags_c,
tags_html)
VALUES (
:qid,
@@ -144,7 +142,6 @@ public function indexQuestion(\Lampcms\Question $oQuestion){
:username,
:userlink,
:avatar,
- :tags_c,
:tags_html)';
@@ -155,7 +152,6 @@ public function indexQuestion(\Lampcms\Question $oQuestion){
$sth->bindParam(':qbody', $body, \PDO::PARAM_STR);
$sth->bindParam(':qurl', $url, \PDO::PARAM_STR);
$sth->bindParam(':qintro', $intro, \PDO::PARAM_STR);
- $sth->bindParam(':tags_c', $tags_c, \PDO::PARAM_STR);
$sth->bindParam(':tags_html', $tags_html, \PDO::PARAM_STR);
$sth->bindParam(':uid', $uid, \PDO::PARAM_INT);
$sth->bindParam(':username', $username, \PDO::PARAM_STR);
@@ -238,11 +234,10 @@ public function updateQuestion(\Lampcms\Question $oQuestion){
$username = $oQuestion['username'];
$ulink = $oQuestion['ulink'];
$avatar = $oQuestion['avtr'];
- $tags_c = $oQuestion['tags_c'];
$tags_html = $oQuestion['tags_html'];
$body = $oQuestion['body'];
- d($qid.' title: '. $title. ' url: '. $url.' intro: '.$intro.' tags_c: '.$tags_c);
+ d($qid.' title: '. $title. ' url: '. $url.' intro: '.$intro);
$sql = 'UPDATE question_title
SET
@@ -253,7 +248,6 @@ public function updateQuestion(\Lampcms\Question $oQuestion){
username = :username,
userlink = :userlink,
avtr = :avatar,
- tags_c = :tags_c,
tags_html = :tags_html
WHERE qid = :qid';
@@ -265,7 +259,6 @@ public function updateQuestion(\Lampcms\Question $oQuestion){
$sth->bindParam(':qbody', $body, \PDO::PARAM_STR);
$sth->bindParam(':qurl', $url, \PDO::PARAM_STR);
$sth->bindParam(':qintro', $intro, \PDO::PARAM_STR);
- $sth->bindParam(':tags_c', $tags_c, \PDO::PARAM_STR);
$sth->bindParam(':tags_html', $tags_html, \PDO::PARAM_STR);
$sth->bindParam(':username', $username, \PDO::PARAM_STR);
$sth->bindParam(':userlink', $ulink, \PDO::PARAM_STR);
View
7 lib/Lampcms/Modules/Search/MySQL.php
@@ -112,7 +112,7 @@ public function __construct(Registry $oRegistry){
public function search($term = null){
- $this->term = (!empty($term)) ? $term : $this->oRegistry->Request['q'];
+ $this->term = (!empty($term)) ? $term : $this->oRegistry->Request->getUTF8('q')->stripTags();
$this->getCondition()
->getCount()
@@ -154,7 +154,7 @@ protected function getCount(){
d('mysql error: '.$err);
if('42S02' === $e->getCode()){
- if(true === \Lampcms\TitleTagsTable::create($this->oRegistry)){
+ if(true === TitleTagsTable::create($this->oRegistry)){
return $this;
} else {
@@ -211,7 +211,6 @@ protected function getResults(){
DATE_FORMAT(ts, '%%M %%e, %%Y %%l:%%i %%p') as hts,
username,
avtr,
- tags_c,
tags_html
FROM question_title
WHERE %s
@@ -360,7 +359,7 @@ public function getSimilarQuestions(\Lampcms\Question $oQuestion){
d('mysql error: '.$err);
if('42S02' === $e->getCode()){
- if(true === \Lampcms\TitleTagsTable::create($this->oRegistry)){
+ if(true === TitleTagsTable::create($this->oRegistry)){
return $this;
} else {
View
5 lib/Lampcms/TitleTagsTable.php → lib/Lampcms/Modules/Search/TitleTagsTable.php
@@ -49,7 +49,7 @@
*
*/
-namespace Lampcms;
+namespace Lampcms\Modules\Search;
/**
*
@@ -74,7 +74,6 @@ class TitleTagsTable
`username` varchar(50) NOT NULL,
`userlink` varchar(60) NOT NULL COMMENT \'path to user profile, usually looks like this: /users/123/someuser\',
`avtr` text NOT NULL COMMENT \'path to user avatar at time of posting\',
- `tags_c` varchar(100) NOT NULL,
`tags_html` text NOT NULL,
UNIQUE KEY `qid` (`qid`),
KEY `uid` (`uid`),
@@ -83,7 +82,7 @@ class TitleTagsTable
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT=\'Table used for full text indexing of question title\';
';
- public static function create(Registry $oRegistry){
+ public static function create(\Lampcms\Registry $oRegistry){
d('Table "question_title" not found going to create it now');
$res = $oRegistry->Db->exec(self::SQL);
d('res: '.$res);
View
22 lib/Lampcms/Mongo.php
@@ -56,7 +56,7 @@
/**
* Wrapped class for working with
* php's MongoDB classes
- *
+ *
* @author Dmitri Snytkine
*
*/
@@ -101,7 +101,7 @@ public function __construct(Ini $oIni){
if(!\extension_loaded('mongo')){
exit('PHP mongo extension not loaded. Exiting');
}
-
+
$aOptions = array('connect' => true);
$aConfig = $oIni->getSection('MONGO');
d('$aConfig: '.print_r($aConfig, 1));
@@ -111,7 +111,7 @@ public function __construct(Ini $oIni){
* For Unit testing we define
* MONGO_DBNAME to be LAMPCMS_TEST
* so that actual database not used during testing
- *
+ *
*/
$this->dbname = (defined('MONGO_DBNAME')) ? constant('MONGO_DBNAME') : $aConfig['db'];
@@ -150,7 +150,7 @@ public function setDbName($name){
if(!is_string($name)){
throw new \InvalidArgumentException('$name must be a string. Was: '.gettype($name));
}
-
+
$this->dbname = $name;
return $this;
@@ -295,9 +295,23 @@ public function getDb(){
* @return object of type MongoCollection
*/
public function getCollection($collName){
+ if(!is_string($collName)){
+ throw new \InvalidArgumentException('Param $collName must be a string. was: '.gettype($collName));
+ }
+
return $this->conn->selectCollection($this->dbname, $collName);
}
+ /**
+ * Alias of getCollection()
+ * This is the same name as in php's MongoDB class
+ *
+ * @param string $collName
+ */
+ public function selectCollection($collName){
+ return $this->getCollection($collName);
+ }
+
/**
* Magic getter to simplify selecting collection
View
8 lib/Lampcms/MongoCache.php
@@ -90,7 +90,7 @@ class MongoCache implements Interfaces\Cache
/**
* Flag indicates to compress data
* this will save storage space if
- * values are fairly longs strings
+ * values are fairly long strings
* and if value is close to 4MB (limit of mongo document size)
* then you should use this option
*
@@ -227,8 +227,7 @@ public function getIds() {
* this equals to flushing cache completely
* all keys/values are gone after this call
*/
- public function flush()
- {
+ public function flush(){
$dropped = $this->_collection->drop();
d('dropped: '.print_r($dropped, 1));
@@ -247,8 +246,7 @@ public function flush()
* @return mixed null if not found
* or array
*/
- public function getRawData($key)
- {
+ public function getRawData($key){
$ret = $this->_collection->findOne(array('_id' => $key));
return (empty($ret)) ? null : $ret;
View
24 lib/Lampcms/MongoDoc.php
@@ -60,7 +60,7 @@
* @author Dmitri Snytkine implements \Serializable
*
*/
-class MongoDoc extends ArrayDefaults implements \Serializable
+class MongoDoc extends LampcmsArray implements \Serializable
{
/**
* Object of type Registry
@@ -171,8 +171,8 @@ class MongoDoc extends ArrayDefaults implements \Serializable
*
* @return object of this class OR class extending this class
*/
- public static function factory(Registry $oRegistry, $collectionName = null, array $a = array(), $default = ''){
- $o = new static($oRegistry, $collectionName, $a, $default);
+ public static function factory(Registry $oRegistry, $collectionName = null, array $a = array(), $default = null){
+ $o = new static($oRegistry, $collectionName, $a);
return $o;
}
@@ -189,8 +189,8 @@ public static function factory(Registry $oRegistry, $collectionName = null, arra
* set it to null or false, whatever you want to use for a default (fallback)
* value of any array key
*/
- public function __construct(Registry $oRegistry, $collectionName = null, array $a = array(), $default = ''){
- parent::__construct($a, $default);
+ public function __construct(Registry $oRegistry, $collectionName = null, array $a = array()){
+ parent::__construct($a);
$this->oRegistry = $oRegistry;
$this->collectionName = $collectionName;
$this->md5 = \md5(\serialize($a));
@@ -230,9 +230,11 @@ public function getMinAutoIncrement(){
* @see ArrayDefaults::offsetGet()
*/
public function offsetGet($name){
- $ret = parent::offsetGet($name);
-
- $prefix = substr($name, 0, 2);
+ //$ret = parent::offsetGet($name); // old way, when this was ArrayDefaults object - not anymore!
+ $ret = !$this->offsetExists($name) ? null : parent::offsetGet($name);
+
+ d(' looking for '.$name.' getting: '.var_export($ret, true));
+ $prefix = \substr($name, 0, 2);
switch($prefix){
case 'i_':
$ret = (int)$ret;
@@ -572,7 +574,7 @@ public function addArray(array $a){
*/
public function insert(){
- if(!$this->checkOffset($this->keyColumn) && $this->minAutoIncrement){
+ if(!$this->offsetExists($this->keyColumn) && $this->minAutoIncrement){
$_id = $this->getRegistry()->Incrementor->nextValue($this->collectionName, $this->minAutoIncrement);
d('setting value of _id to '.$_id);
$this->offsetSet('_id', $_id);
@@ -766,8 +768,7 @@ public function serialize(){
'collectionName' => $this->collectionName,
'md5' => $this->md5,
'bSaved' => $this->bSaved,
- 'keyColumn' => $this->keyColumn,
- 'defaultValue' => $this->defaultValue);
+ 'keyColumn' => $this->keyColumn);
unset($this->oRegistry);
@@ -783,7 +784,6 @@ public function unserialize($serialized){
$a = unserialize($serialized);
$this->exchangeArray($a['array']);
$this->collectionName = $a['collectionName'];
- $this->defaultValue = $a['defaultValue'];
$this->bSaved = $a['bSaved'];
$this->keyColumn = $a['keyColumn'];
$this->md5 = $a['md5'];
View
3  lib/Lampcms/MongoIncrementor.php
@@ -107,8 +107,7 @@ public function __construct(Mongo $oMongo){
*
* @return int value of next id for the collection
*/
- public function nextValue($collName, $initialId = 0, $try = 1)
- {
+ public function nextValue($collName, $initialId = 0, $try = 1){
if( $try > 100 ){
throw new \RuntimeException('Unable to get nextID for collection '.$collName.' after 100 tries');
View
54 lib/Lampcms/Object.php
@@ -329,6 +329,15 @@ public function getClass(){
public function __toString(){
return 'object of type: '.$this->getClass().' hashCode: '.$this->hashCode();
}
+
+ /**
+ * Getter for $this->oRegistry
+ *
+ * @return object of type Registry
+ */
+ public function getRegistry(){
+ return $this->oRegistry;
+ }
}
@@ -543,11 +552,19 @@ public function __isset($name){
* since we can no longer rely on the
* offsetExists() in this object,
* we are asking a parent object
+ *
+ * It ONLY works properly
+ * with this object and does not work
+ * in sub-class because in sub-class parent
+ * becomes THIS class and calling offsetExists
+ * on THIS class always returns true!
+ *
+ * Ideally this function should be eliminated
*
* @param string $name
* @return bool
*/
- public function checkOffset($name){
+ public final function checkOffset($name){
return parent::offsetExists($name);
}
@@ -612,41 +629,6 @@ public function resetDefaultValue(){
/**
- * If the key $key does not actually exists in
- * the array, then return the value passed as
- * second argument , if second param is not given ,then returns
- * value of $key
- *
- * otherwise return the value of $key
- *
- * @param string $key
- * @param mixed $default
- * @return mixed
- */
- public function getFallback($key, $default = null){
- if (parent::offsetExists($key)) {
-
- return parent::offsetGet($key);
- }
-
- return ( null !== $default) ? $default : $key;
- }
-
-
- /**
- *
- * Get value of $key previously converting
- * $key to lower case
- *
- * @param string $key
- * @param string $default
- */
- public function getFallbackLc($key, $default = null){
- return $this->getFallback(\mb_strtolower($key), $default);
- }
-
-
- /**
* This method lets you get undefined array keys as
* object properties
* for example if 'gagaga' key does not exist,
View
278 lib/Lampcms/Question.php
@@ -175,7 +175,7 @@ public function getUrl($short = false){
return ($short) ? $url : $url.$this->offsetGet('url');
}
-
+
/**
* (non-PHPdoc)
* @see Lampcms\Interfaces.Post::getBody()
@@ -184,7 +184,7 @@ public function getBody(){
return $this->offsetGet('b');
}
-
+
/**
* (non-PHPdoc)
* @see Lampcms\Interfaces.Post::getTitle()
@@ -193,7 +193,7 @@ public function getTitle(){
return $this->offsetGet('title');
}
-
+
/**
* (non-PHPdoc)
* @see Lampcms\Interfaces.Post::getSeoUrl()
@@ -201,17 +201,16 @@ public function getTitle(){
public function getSeoUrl(){
return $this->offsetGet('url');
}
-
-
+
+
/**
- * Should return false if NOT closed
- * otherwise either true or timestamp
- * of when it was closed
+ * Test to see if question is closed. If it is closed
+ * then returns array of data that contains
+ * Username, reason and time of when question was
+ * closed
*
- * @todo change this to just return offsetGet('i_closed)
- * it will return 0 if i_closed is not present
- * because if the new way we going to handle non-existent
- * offsets that start with 'i_' or end with 'id')
+ * @return mixed false if not closed | array of a_closed
+ * if is closed
*/
public function isClosed(){
$a = $this->offsetGet('a_closed');
@@ -240,8 +239,8 @@ public function getAnswerCount(){
*/
public function setClosed(User $closer, $reason = null){
- if(!$this->checkOffset('a_closed')){
- $this->offsetSet('a_closed', array(
+ if(!$this->offsetExists('a_closed')){
+ parent::offsetSet('a_closed', array(
'username' => $closer->getDisplayName(),
'i_uid' => $closer->getUid(),
'av' => $closer->getAvatarSrc(),
@@ -270,8 +269,8 @@ public function setClosed(User $closer, $reason = null){
*/
public function setDeleted(User $user, $reason = null){
if(0 === $this->getDeletedTime()){
- $this->offsetSet('i_del_ts', time());
- $this->offsetSet('a_deleted',
+ parent::offsetSet('i_del_ts', time());
+ parent::offsetSet('a_deleted',
array(
'username' => $user->getDisplayName(),
'i_uid' => $user->getUid(),
@@ -282,8 +281,6 @@ public function setDeleted(User $user, $reason = null){
);
}
- $this->touch();
-
return $this;
}
@@ -311,9 +308,7 @@ public function setEdited(User $user, $reason = ''){
'reason' => $reason,
'hts' => date('F j, Y g:i a T'));
- $this->offsetSet('a_edited', $aEdited);
-
- $this->touch();
+ parent::offsetSet('a_edited', $aEdited);
return $this;
}
@@ -332,8 +327,8 @@ public function setEdited(User $user, $reason = ''){
*/
public function retag(User $user, array $tags){
- $this->offsetSet('a_tags', $tags);
- $this->offsetSet('tags_html', \tplQtags::loop($tags, false));
+ parent::offsetSet('a_tags', $tags);
+ parent::offsetSet('tags_html', \tplQtags::loop($tags, false));
$b = $this->offsetGet('b');
d('b: '.$b);
@@ -343,7 +338,7 @@ public function retag(User $user, array $tags){
$this->offsetSet('b', $body);
- $this->setEdited($user, 'Retagged');
+ $this->setEdited($user, 'Retagged')->touch();
return $this;
}
@@ -365,13 +360,13 @@ public function retag(User $user, array $tags){
*/
public function setBestAnswer(Answer $oAnswer){
d('about to set status to accptd');
- $this->offsetSet('i_sel_ans', $oAnswer->getResourceId());
- $this->offsetSet('i_sel_uid', $oAnswer->getOwnerId());
+ parent::offsetSet('i_sel_ans', $oAnswer->getResourceId());
+ parent::offsetSet('i_sel_uid', $oAnswer->getOwnerId());
/**
* Now set the Answer object's accepted status to true
*/
- $oAnswer->setAccepted();
+ $oAnswer->setAccepted()->touch();
/**
* If Question is still not 'answered', means
@@ -405,10 +400,12 @@ public function setBestAnswer(Answer $oAnswer){
* @param int $inc
*/
public function updateAnswerCount($inc = 1){
+ if(!\is_int($inc)){
+ throw new \InvalidArgumentException('Param $inc must be an integer. was: '.gettype($inc));
+ }
+
$iAns = $this->offsetGet('i_ans');
d('$iAns '.$iAns );
- $newCount = max(0, ($iAns + $inc));
- d('$newCount: '.$newCount);
/**
* Set new value of i_ans but make sure
@@ -418,7 +415,10 @@ public function updateAnswerCount($inc = 1){
* is possible when we need to decrease answer count,
* that's why we need this guard here.
*/
- $this->offsetSet('i_ans', $newCount);
+ $newCount = max(0, ($iAns + $inc));
+ d('$newCount: '.$newCount);
+
+ parent::offsetSet('i_ans', $newCount);
/**
* Change the status to answrd
@@ -427,10 +427,10 @@ public function updateAnswerCount($inc = 1){
* of div to not be red, but it still does not
* make the question 'answered'
*/
- if($newCount > 0){
- $this->offsetSet('status', 'answrd');
- } else {
- $this->offsetSet('status', 'unans');
+ if($newCount < 1){
+ parent::offsetSet('status', 'unans');
+ } elseif('unans' === $this->offsetGet('status')){
+ parent::offsetSet('status', 'answrd');
}
/**
@@ -438,13 +438,11 @@ public function updateAnswerCount($inc = 1){
* a_s (plural suffix) to 's'
*/
if(1 !== ($newCount)){
- $this->offsetSet('ans_s', 's');
+ parent::offsetSet('ans_s', 's');
} else {
- $this->offsetSet('ans_s', '');
+ parent::offsetSet('ans_s', '');
}
- $this->touch();
-
return $this;
}
@@ -485,6 +483,10 @@ public function touch($etagOnly = false){
* @return object $this
*/
public function increaseViews(\Lampcms\User $Viewer, $inc = 1){
+ if(!\is_int($inc)){
+ throw new \InvalidArgumentException('Param $inc must be an integer. was: '.gettype($inc));
+ }
+
/**
* @todo Don't count question owner view
* For this we must be able to get Viewer from Registry
@@ -494,9 +496,9 @@ public function increaseViews(\Lampcms\User $Viewer, $inc = 1){
$viewerId = $Viewer->getUid();
/**
- * If guest, then don't check for dups
- * @todo this will be a problem if we at least don't check
- * for same session_id or ip address
+ * If guest, then there
+ * will be a problem if we at least don't check
+ * for same session_id
*/
$viewerId = (0 === $viewerId) ? session_id() : $viewerId;
@@ -515,7 +517,7 @@ public function increaseViews(\Lampcms\User $Viewer, $inc = 1){
/**
* If this is the first view, we will cheat a little
* and set the views to 2
- * There will not be just 1 view, and this way we don't
+ * There will never be just 1 view, and this way we don't
* have to worry about the plural suffix
*/
if(0 === $iViews && (1 === $inc)) {
@@ -527,7 +529,7 @@ public function increaseViews(\Lampcms\User $Viewer, $inc = 1){
$qid = (int)$this->offsetGet('_id');
try{
$collViews->insert(array('qid' => $qid, 'uid' => $viewerId, 'i_ts' => time()), array('safe' => true));
- $this->offsetSet('i_views', ($iViews + (int)$inc) );
+ parent::offsetSet('i_views', ($iViews + (int)$inc) );
/**
* If new value is NOT 1 then set
@@ -562,14 +564,14 @@ public function addUpVote($inc = 1){
$score = (int)$this->offsetGet('i_votes');
$total = ($score + $inc);
- $this->offsetSet('i_up', max(0, ($tmp + $inc)) );
- $this->offsetSet('i_votes', $total );
+ parent::offsetSet('i_up', max(0, ($tmp + $inc)) );
+ parent::offsetSet('i_votes', $total );
/**
* Plural extension handling
*/
$v_s = (1 === abs($total) ) ? '' : 's';
- $this->offsetSet('v_s', $v_s);
+ parent::offsetSet('v_s', $v_s);
return $this;
}
@@ -589,17 +591,17 @@ public function addDownVote($inc = 1){
$score = (int)$this->offsetGet('i_votes');
$total = ($score - $inc);
- $this->offsetSet('i_down', max(0, ($tmp + $inc)) );
+ parent::offsetSet('i_down', max(0, ($tmp + $inc)) );
/**
* Question can have negative score, so we allow it!
*/
- $this->offsetSet('i_votes', $total );
+ parent::offsetSet('i_votes', $total );
/**
* Plural extension handling
*/
$v_s = (1 === abs($total) ) ? '' : 's';
- $this->offsetSet('v_s', $v_s);
+ parent::offsetSet('v_s', $v_s);
return $this;
}
@@ -665,7 +667,7 @@ public function addComment(CommentParser $oComment){
$aComments[] = $aComment;
- $this->offsetSet('comments', $aComments);
+ $this->offsetSet('a_comments', $aComments);
$this->increaseCommentsCount();
/**
@@ -696,14 +698,17 @@ public function getCommentsCount(){
* Increase value of i_commets by 1
* @return object $this
*/
- public function increaseCommentsCount(){
+ public function increaseCommentsCount($count = 1){
+ if(!is_int($count)){
+ throw new \InvalidArgumentException('$count must be integer. was: '.gettype($count));
+ }
/**
* Now increase comments count
*/
$commentsCount = $this->getCommentsCount();
d('$commentsCount '.$commentsCount);
- $this->offsetSet('i_comments', ($commentsCount + 1) );
+ parent::offsetSet('i_comments', ($commentsCount + $count) );
return $this;
}
@@ -721,13 +726,13 @@ public function increaseCommentsCount(){
*/
public function deleteComment($id){
- if(!$this->checkOffset('comments')){
- e('This question does not have any comments');
+ if(0 === $this->getCommentsCount()){
+ d('This question does not have any comments');
return $this;
}
- $aComments = $this->offsetGet('comments');
+ $aComments = $this->offsetGet('a_comments');
for($i = 0; $i<count($aComments); $i+=1){
if($aComments[$i]['_id'] == $id){
@@ -739,12 +744,12 @@ public function deleteComment($id){
$newCount = count($aComments);
if( 0 === $newCount){
- $this->offsetUnset('comments');
+ $this->offsetUnset('a_comments');
} else {
- $this->offsetSet('comments', $aComments);
+ $this->offsetSet('a_comments', $aComments);
}
- $this->offsetSet('i_comments', $newCount );
+ parent::offsetSet('i_comments', $newCount );
return $this;
}
@@ -757,18 +762,25 @@ public function deleteComment($id){
* has made an answer or a comment
* to a question
*
+ * Contributors array is not unique,
+ * it can have more than one entry for
+ * the same user if user contributed multiple
+ * times. This way we can remove just one record
+ * and user is still considered a contributor
+ * as long as the same user has contributed other items
+ *
* @param mixed int | object $oUser object of type User
*/
public function addContributor($User){
- if(!is_int($User) && (!is_object($User) || !($User instanceof User))){
+ if(!\is_int($User) && (!\is_object($User) || !($User instanceof User))){
throw new \InvalidArgumentException('Value of $User can be only int or instance of User class. it was: '.var_export($User, true));
}
- $uid = (is_int($User)) ? $User : $User->getUid();
- $a = $this->getFallback('a_uids', array());
+ $uid = (\is_int($User)) ? $User : $User->getUid();
+ $a = $this->offsetGet('a_uids');
$a[] = $uid;
- $this->offsetSet('a_uids', $a);
+ parent::offsetSet('a_uids', $a);
return $this;
}
@@ -793,12 +805,12 @@ public function removeContributor($User){
}
$changed = false;
- $uid = (is_int($User)) ? $User : $User->getUid();
- $a = $this->getFallback('a_uids', array());
+ $uid = (\is_int($User)) ? $User : $User->getUid();
+ $a = $this->offsetGet('a_uids');
for($i = 0; $i<count($a); $i+=1){
if($uid == $a[$i]){
d('unsetting contributor: '.$uid. ' at array key: ' .$i);
- array_splice($a, $i, 1);
+ \array_splice($a, $i, 1);
$changed = true;
break;
}
@@ -872,7 +884,7 @@ public function removeFollower($User){
/**
* Sets value of lp_u : a link to Last Poster profile
* and lp_t a time of last post
- *
+ *
* @todo should make the last answerer an array
* and then just push the value there
* This way if answer is deleted we can just delete
@@ -883,10 +895,81 @@ public function removeFollower($User){
*
* @return object $this
*/
- public function setLastAnswerer(User $oUser){
+ public function setLatestAnswer(User $oUser, Answer $oAnswer){
+ $aLatest = $this->offsetGet('a_latest');
+ $a = array(
+ 'u' => '<a href="'.$oUser->getProfileUrl().'">'.$oUser->getDisplayName().'</a>',
+ 't' => date('F j, Y g:i a T', $oAnswer->getLastModified()),
+ 'id' => $oAnswer->getResourceId()
+ );
+
+ /**
+ * Latest answer data goes
+ * to top of array
+ */
+ \array_unshift($aLatest, $a);
- $this->offsetSet('lp_u', '<a href="'.$oUser->getProfileUrl().'">'.$oUser->getDisplayName().'</a>');
- $this->offsetSet('lp_t', date('F j, Y g:i a T'));
+ $this->offsetSet('a_latest', $aLatest);
+
+ return $this;
+ }
+
+
+ /**
+ * Removes one element from a_latest array
+ * that represents answer passed in param.
+ *
+ * If that array had only one element
+ * then also unset the whole 'a_latest' key
+ * from this object
+ *
+ * @param object $oAnswer object of type Answer
+ *
+ * @return object $this
+ */
+ public function removeAnswer(Answer $oAnswer){
+ $id = $oAnswer->getResourceId();
+ $aLatest = $this->offsetGet('a_latest');
+
+ for($i = 0; $i < count($aLatest); $i += 1){
+ if(!empty($aLatest[$i]) && ($id === $aLatest[$i]['id'])){
+ \array_splice($aLatest, $i, 1);
+ break;
+ }
+ }
+
+ if( 0 === count($aLatest)){
+ $this->offsetUnset('a_latest');
+ } else {
+ parent::offsetSet('a_latest', $aLatest);
+ }
+
+ /**
+ * If removed Answer was also a "accepted" answer
+ * then change status to just "answrd" here
+ *
+ * The updateAnswerCount(-1) method
+ * may then change the status to "unans"
+ * if it's determined that this was
+ * the only answer
+ *
+ * Also need to add this question to
+ * UNANSWERED_TAGS again because now
+ * this question is technically unanswered again
+ */
+ if((true === $oAnswer['accepted']) &&
+ ($id === $this->offsetGet('i_sel_ans'))
+ ){
+ parent::offsetSet('status', 'answrd');
+ $this->offsetUnset('i_sel_ans');
+ $this->offsetUnset('i_sel_uid');
+ UnansweredTags::factory($this->oRegistry)->set($this);
+ }
+
+ $this->updateAnswerCount(-1)
+ ->removeContributor($oAnswer->getOwnerId());
+
+ $this->touch(false);
return $this;
}
@@ -899,7 +982,7 @@ public function setLastAnswerer(User $oUser){
*
*/
public function getComments(){
- return $this->getFallback('comments', array());
+ return $this->offsetGet('a_comments');
}
@@ -921,4 +1004,59 @@ public function getQuestionOwnerId(){
public function getUsername(){
return $this->offsetGet('username');
}
+
+ /**
+ * This method prevents setting some
+ * values directly
+ *
+ * (non-PHPdoc)
+ * @see ArrayObject::offsetSet()
+ */
+ public function offsetSet($index, $newval){
+ switch($index){
+
+ case 'i_comments':
+ throw new DevException('value of i_comments cannot be set directly. Use increaseCommentsCount() method');
+ break;
+
+ case 'i_down':
+ case 'i_up':
+ case 'i_votes':
+ throw new DevException('value of '.$index.' keys cannot be set directly. Use addDownVote or addUpVote to add votes');
+ break;
+