1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 14:52:41 +01:00

Adding Ponder-related files.

Summary:
Ponder is similar in spirit to the Wiki tool, but uses a Q&A
format and up/downvotes to signal user sentiment. Popular
questions are moved to the top of the feed on a 5-minute
cycle based on age (younger is better) and vote count (higher
is better).

Pre-apologies for noob diff.

Test Plan:
- `./bin/phd list` Should include `PonderHeatDaemon`; phd launch it
  if necessary.

- Navigate to /ponder/ ; observe sanity when adding questions,
  voting on them, and adding answers.

- Confirm that questions and answers are linkable using Q5 / Q5#A5 formatted object links.

- Confirm that searching for Ponder Questions works using built-in
  search.

Feedback on code / schema / whatever organization very welcome.

Reviewers: nh, vrana, epriestley

Reviewed By: epriestley

CC: gmarcotte, aran, Korvin, starruler

Differential Revision: https://secure.phabricator.com/D3136
This commit is contained in:
Pieter Hooimeijer 2012-08-10 10:44:04 -07:00
parent 9030fe8b09
commit 64472dd7b8
50 changed files with 3204 additions and 51 deletions

View file

@ -0,0 +1,50 @@
CREATE TABLE `{$NAMESPACE}_ponder`.`ponder_question` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`phid` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`voteCount` int(10) NOT NULL,
`authorPHID` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`content` longtext CHARACTER SET utf8 NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
`contentSource` varchar(255) DEFAULT NULL,
`heat` float NOT NULL,
`answerCount` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `phid` (`phid`),
KEY `authorPHID` (`authorPHID`),
KEY `heat` (`heat`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `{$NAMESPACE}_ponder`.`ponder_answer` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`questionID` int(10) unsigned NOT NULL,
`phid` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`voteCount` int(10) NOT NULL,
`authorPHID` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`content` longtext CHARACTER SET utf8 NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
`contentSource` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `phid` (`phid`),
KEY `questionID` (`questionID`),
KEY `authorPHID` (`authorPHID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `{$NAMESPACE}_ponder`.`edge` (
`src` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`type` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`dst` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`seq` int(10) unsigned NOT NULL,
`dataID` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`src`,`type`,`dst`),
KEY `src` (`src`,`type`,`dateCreated`,`seq`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `{$NAMESPACE}_ponder`.`edgedata` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`data` longtext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

View file

@ -63,6 +63,13 @@ celerity_register_resource_map(array(
'disk' => '/rsrc/image/app/app_phriction.png',
'type' => 'png',
),
'/rsrc/image/app/app_ponder.png' =>
array(
'hash' => '9893ca3674af96884d3564ecbe101280',
'uri' => '/res/9893ca36/rsrc/image/app/app_ponder.png',
'disk' => '/rsrc/image/app/app_ponder.png',
'type' => 'png',
),
'/rsrc/image/app/app_settings.png' =>
array(
'hash' => '095fa0e61ec11d3f7e543ba48f5a5adb',
@ -70,10 +77,24 @@ celerity_register_resource_map(array(
'disk' => '/rsrc/image/app/app_settings.png',
'type' => 'png',
),
'/rsrc/image/application/ponder/downvote.png' =>
array(
'hash' => '46c5644a0fccb9e237a3363e07f50487',
'uri' => '/res/46c5644a/rsrc/image/application/ponder/downvote.png',
'disk' => '/rsrc/image/application/ponder/downvote.png',
'type' => 'png',
),
'/rsrc/image/application/ponder/upvote.png' =>
array(
'hash' => 'edd58ed3a09f3017c78601b1a5d0e7a7',
'uri' => '/res/edd58ed3/rsrc/image/application/ponder/upvote.png',
'disk' => '/rsrc/image/application/ponder/upvote.png',
'type' => 'png',
),
'/rsrc/image/apps.png' =>
array(
'hash' => 'f7cb4abeb73245fea4098a02fd784653',
'uri' => '/res/f7cb4abe/rsrc/image/apps.png',
'hash' => '134f6ac3d63e24070bf7a11b294ca72c',
'uri' => '/res/134f6ac3/rsrc/image/apps.png',
'disk' => '/rsrc/image/apps.png',
'type' => 'png',
),
@ -1608,6 +1629,32 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/application/phriction/phriction-document-preview.js',
),
'javelin-behavior-ponder-feedback-preview' =>
array(
'uri' => '/res/2e802dd9/rsrc/js/application/ponder/behavior-comment-preview.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-util',
3 => 'phabricator-shaped-request',
),
'disk' => '/rsrc/js/application/ponder/behavior-comment-preview.js',
),
'javelin-behavior-ponder-votebox' =>
array(
'uri' => '/res/6517d3f5/rsrc/js/application/ponder/behavior-votebox.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-util',
3 => 'phabricator-shaped-request',
),
'disk' => '/rsrc/js/application/ponder/behavior-votebox.js',
),
'javelin-behavior-project-create' =>
array(
'uri' => '/res/e91f3f8f/rsrc/js/application/projects/behavior-project-create.js',
@ -1635,7 +1682,7 @@ celerity_register_resource_map(array(
),
'javelin-behavior-repository-crossreference' =>
array(
'uri' => '/res/e0d58d3b/rsrc/js/application/repository/repository-crossreference.js',
'uri' => '/res/12700384/rsrc/js/application/repository/repository-crossreference.js',
'type' => 'js',
'requires' =>
array(
@ -2200,7 +2247,7 @@ celerity_register_resource_map(array(
),
'phabricator-app-buttons-css' =>
array(
'uri' => '/res/1e153463/rsrc/css/application/directory/phabricator-app-buttons.css',
'uri' => '/res/2d5d414c/rsrc/css/application/directory/phabricator-app-buttons.css',
'type' => 'css',
'requires' =>
array(
@ -2730,6 +2777,42 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/phriction/phriction-document-css.css',
),
'ponder-core-view-css' =>
array(
'uri' => '/res/4a6e2fc7/rsrc/css/application/ponder/core.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/ponder/core.css',
),
'ponder-feed-view-css' =>
array(
'uri' => '/res/df22bd20/rsrc/css/application/ponder/feed.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/ponder/feed.css',
),
'ponder-post-css' =>
array(
'uri' => '/res/32c960df/rsrc/css/application/ponder/post.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/ponder/post.css',
),
'ponder-vote-css' =>
array(
'uri' => '/res/923bcf97/rsrc/css/application/ponder/vote.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/ponder/vote.css',
),
'raphael-core' =>
array(
'uri' => '/res/3f48575a/rsrc/js/raphael/raphael.js',
@ -2777,7 +2860,7 @@ celerity_register_resource_map(array(
),
'syntax-highlighting-css' =>
array(
'uri' => '/res/5669beb6/rsrc/css/core/syntax.css',
'uri' => '/res/cb3b9dc0/rsrc/css/core/syntax.css',
'type' => 'css',
'requires' =>
array(
@ -2787,7 +2870,7 @@ celerity_register_resource_map(array(
), array(
'packages' =>
array(
'8b65e80d' =>
'04e1ab3e' =>
array(
'name' => 'core.pkg.css',
'symbols' =>
@ -2816,7 +2899,7 @@ celerity_register_resource_map(array(
21 => 'phabricator-flag-css',
22 => 'aphront-error-view-css',
),
'uri' => '/res/pkg/8b65e80d/core.pkg.css',
'uri' => '/res/pkg/04e1ab3e/core.pkg.css',
'type' => 'css',
),
'971b021e' =>
@ -2867,7 +2950,7 @@ celerity_register_resource_map(array(
'uri' => '/res/pkg/96bc37d6/differential.pkg.css',
'type' => 'css',
),
'ace0431a' =>
93479628 =>
array(
'name' => 'differential.pkg.js',
'symbols' =>
@ -2891,7 +2974,7 @@ celerity_register_resource_map(array(
16 => 'javelin-behavior-differential-dropdown-menus',
17 => 'javelin-behavior-buoyant',
),
'uri' => '/res/pkg/ace0431a/differential.pkg.js',
'uri' => '/res/pkg/93479628/differential.pkg.js',
'type' => 'js',
),
'c8ce2d88' =>
@ -2983,23 +3066,23 @@ celerity_register_resource_map(array(
'reverse' =>
array(
'aphront-attached-file-view-css' => '7839ae2d',
'aphront-crumbs-view-css' => '8b65e80d',
'aphront-dialog-view-css' => '8b65e80d',
'aphront-error-view-css' => '8b65e80d',
'aphront-form-view-css' => '8b65e80d',
'aphront-crumbs-view-css' => '04e1ab3e',
'aphront-dialog-view-css' => '04e1ab3e',
'aphront-error-view-css' => '04e1ab3e',
'aphront-form-view-css' => '04e1ab3e',
'aphront-headsup-action-list-view-css' => '96bc37d6',
'aphront-headsup-view-css' => '8b65e80d',
'aphront-list-filter-view-css' => '8b65e80d',
'aphront-pager-view-css' => '8b65e80d',
'aphront-panel-view-css' => '8b65e80d',
'aphront-side-nav-view-css' => '8b65e80d',
'aphront-table-view-css' => '8b65e80d',
'aphront-tokenizer-control-css' => '8b65e80d',
'aphront-tooltip-css' => '8b65e80d',
'aphront-typeahead-control-css' => '8b65e80d',
'aphront-headsup-view-css' => '04e1ab3e',
'aphront-list-filter-view-css' => '04e1ab3e',
'aphront-pager-view-css' => '04e1ab3e',
'aphront-panel-view-css' => '04e1ab3e',
'aphront-side-nav-view-css' => '04e1ab3e',
'aphront-table-view-css' => '04e1ab3e',
'aphront-tokenizer-control-css' => '04e1ab3e',
'aphront-tooltip-css' => '04e1ab3e',
'aphront-typeahead-control-css' => '04e1ab3e',
'differential-changeset-view-css' => '96bc37d6',
'differential-core-view-css' => '96bc37d6',
'differential-inline-comment-editor' => 'ace0431a',
'differential-inline-comment-editor' => '93479628',
'differential-local-commits-view-css' => '96bc37d6',
'differential-results-table-css' => '96bc37d6',
'differential-revision-add-comment-css' => '96bc37d6',
@ -3012,21 +3095,21 @@ celerity_register_resource_map(array(
'inline-comment-summary-css' => '96bc37d6',
'javelin-behavior' => '6fb20113',
'javelin-behavior-aphront-basic-tokenizer' => '97f65640',
'javelin-behavior-aphront-drag-and-drop' => 'ace0431a',
'javelin-behavior-aphront-drag-and-drop-textarea' => 'ace0431a',
'javelin-behavior-aphront-drag-and-drop' => '93479628',
'javelin-behavior-aphront-drag-and-drop-textarea' => '93479628',
'javelin-behavior-aphront-form-disable-on-submit' => '971b021e',
'javelin-behavior-audit-preview' => '5e68be89',
'javelin-behavior-buoyant' => 'ace0431a',
'javelin-behavior-differential-accept-with-errors' => 'ace0431a',
'javelin-behavior-differential-add-reviewers-and-ccs' => 'ace0431a',
'javelin-behavior-differential-comment-jump' => 'ace0431a',
'javelin-behavior-differential-diff-radios' => 'ace0431a',
'javelin-behavior-differential-dropdown-menus' => 'ace0431a',
'javelin-behavior-differential-edit-inline-comments' => 'ace0431a',
'javelin-behavior-differential-feedback-preview' => 'ace0431a',
'javelin-behavior-differential-keyboard-navigation' => 'ace0431a',
'javelin-behavior-differential-populate' => 'ace0431a',
'javelin-behavior-differential-show-more' => 'ace0431a',
'javelin-behavior-buoyant' => '93479628',
'javelin-behavior-differential-accept-with-errors' => '93479628',
'javelin-behavior-differential-add-reviewers-and-ccs' => '93479628',
'javelin-behavior-differential-comment-jump' => '93479628',
'javelin-behavior-differential-diff-radios' => '93479628',
'javelin-behavior-differential-dropdown-menus' => '93479628',
'javelin-behavior-differential-edit-inline-comments' => '93479628',
'javelin-behavior-differential-feedback-preview' => '93479628',
'javelin-behavior-differential-keyboard-navigation' => '93479628',
'javelin-behavior-differential-populate' => '93479628',
'javelin-behavior-differential-show-more' => '93479628',
'javelin-behavior-diffusion-commit-graph' => '5e68be89',
'javelin-behavior-diffusion-pull-lastmodified' => '5e68be89',
'javelin-behavior-maniphest-batch-selector' => '7707de41',
@ -3036,12 +3119,12 @@ celerity_register_resource_map(array(
'javelin-behavior-maniphest-transaction-preview' => '7707de41',
'javelin-behavior-phabricator-autofocus' => '971b021e',
'javelin-behavior-phabricator-keyboard-shortcuts' => '971b021e',
'javelin-behavior-phabricator-object-selector' => 'ace0431a',
'javelin-behavior-phabricator-object-selector' => '93479628',
'javelin-behavior-phabricator-oncopy' => '971b021e',
'javelin-behavior-phabricator-tooltips' => '971b021e',
'javelin-behavior-phabricator-watch-anchor' => '971b021e',
'javelin-behavior-refresh-csrf' => '971b021e',
'javelin-behavior-repository-crossreference' => 'ace0431a',
'javelin-behavior-repository-crossreference' => '93479628',
'javelin-behavior-workflow' => '971b021e',
'javelin-dom' => '6fb20113',
'javelin-event' => '6fb20113',
@ -3062,15 +3145,15 @@ celerity_register_resource_map(array(
'javelin-workflow' => '971b021e',
'maniphest-task-summary-css' => '7839ae2d',
'maniphest-transaction-detail-css' => '7839ae2d',
'phabricator-app-buttons-css' => '8b65e80d',
'phabricator-app-buttons-css' => '04e1ab3e',
'phabricator-content-source-view-css' => '96bc37d6',
'phabricator-core-buttons-css' => '8b65e80d',
'phabricator-core-css' => '8b65e80d',
'phabricator-directory-css' => '8b65e80d',
'phabricator-drag-and-drop-file-upload' => 'ace0431a',
'phabricator-core-buttons-css' => '04e1ab3e',
'phabricator-core-css' => '04e1ab3e',
'phabricator-directory-css' => '04e1ab3e',
'phabricator-drag-and-drop-file-upload' => '93479628',
'phabricator-dropdown-menu' => '971b021e',
'phabricator-flag-css' => '8b65e80d',
'phabricator-jump-nav' => '8b65e80d',
'phabricator-flag-css' => '04e1ab3e',
'phabricator-jump-nav' => '04e1ab3e',
'phabricator-keyboard-shortcut' => '971b021e',
'phabricator-keyboard-shortcut-manager' => '971b021e',
'phabricator-menu-item' => '971b021e',
@ -3078,11 +3161,11 @@ celerity_register_resource_map(array(
'phabricator-paste-file-upload' => '971b021e',
'phabricator-prefab' => '971b021e',
'phabricator-project-tag-css' => '7839ae2d',
'phabricator-remarkup-css' => '8b65e80d',
'phabricator-shaped-request' => 'ace0431a',
'phabricator-standard-page-view' => '8b65e80d',
'phabricator-remarkup-css' => '04e1ab3e',
'phabricator-shaped-request' => '93479628',
'phabricator-standard-page-view' => '04e1ab3e',
'phabricator-tooltip' => '971b021e',
'phabricator-transaction-view-css' => '8b65e80d',
'syntax-highlighting-css' => '8b65e80d',
'phabricator-transaction-view-css' => '04e1ab3e',
'syntax-highlighting-css' => '04e1ab3e',
),
));

View file

@ -554,6 +554,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationManiphest' => 'applications/maniphest/application/PhabricatorApplicationManiphest.php',
'PhabricatorApplicationPeople' => 'applications/people/application/PhabricatorApplicationPeople.php',
'PhabricatorApplicationPhriction' => 'applications/phriction/application/PhabricatorApplicationPhriction.php',
'PhabricatorApplicationPonder' => 'applications/ponder/application/PhabricatorApplicationPonder.php',
'PhabricatorApplicationProject' => 'applications/project/application/PhabricatorApplicationProject.php',
'PhabricatorApplicationSettings' => 'applications/people/application/PhabricatorApplicationSettings.php',
'PhabricatorApplicationStatusView' => 'applications/meta/view/PhabricatorApplicationStatusView.php',
@ -990,6 +991,7 @@ phutil_register_library_map(array(
'PhabricatorSearchIndexController' => 'applications/search/controller/PhabricatorSearchIndexController.php',
'PhabricatorSearchManiphestIndexer' => 'applications/search/index/indexer/PhabricatorSearchManiphestIndexer.php',
'PhabricatorSearchPhrictionIndexer' => 'applications/search/index/indexer/PhabricatorSearchPhrictionIndexer.php',
'PhabricatorSearchPonderIndexer' => 'applications/ponder/search/PhabricatorSearchPonderIndexer.php',
'PhabricatorSearchQuery' => 'applications/search/storage/PhabricatorSearchQuery.php',
'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php',
'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php',
@ -1137,6 +1139,34 @@ phutil_register_library_map(array(
'PhrictionEditController' => 'applications/phriction/controller/PhrictionEditController.php',
'PhrictionHistoryController' => 'applications/phriction/controller/PhrictionHistoryController.php',
'PhrictionListController' => 'applications/phriction/controller/PhrictionListController.php',
'PonderAddAnswerView' => 'applications/ponder/view/PonderAddAnswerView.php',
'PonderAnswer' => 'applications/ponder/storage/PonderAnswer.php',
'PonderAnswerEditor' => 'applications/ponder/editor/PonderAnswerEditor.php',
'PonderAnswerListView' => 'applications/ponder/view/PonderAnswerListView.php',
'PonderAnswerPreviewController' => 'applications/ponder/controller/PonderAnswerPreviewController.php',
'PonderAnswerQuery' => 'applications/ponder/query/PonderAnswerQuery.php',
'PonderAnswerSaveController' => 'applications/ponder/controller/PonderAnswerSaveController.php',
'PonderAnswerSummaryView' => 'applications/ponder/view/PonderAnswerSummaryView.php',
'PonderAnswerViewController' => 'applications/ponder/controller/PonderAnswerViewController.php',
'PonderCommentBodyView' => 'applications/ponder/view/PonderCommentBodyView.php',
'PonderConstants' => 'applications/ponder/PonderConstants.php',
'PonderController' => 'applications/ponder/controller/PonderController.php',
'PonderDAO' => 'applications/ponder/storage/PonderDAO.php',
'PonderFeedController' => 'applications/ponder/controller/PonderFeedController.php',
'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php',
'PonderQuestionAskController' => 'applications/ponder/controller/PonderQuestionAskController.php',
'PonderQuestionDetailView' => 'applications/ponder/view/PonderQuestionDetailView.php',
'PonderQuestionFeedView' => 'applications/ponder/view/PonderQuestionFeedView.php',
'PonderQuestionPreviewController' => 'applications/ponder/controller/PonderQuestionPreviewController.php',
'PonderQuestionQuery' => 'applications/ponder/query/PonderQuestionQuery.php',
'PonderQuestionSummaryView' => 'applications/ponder/view/PonderQuestionSummaryView.php',
'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php',
'PonderRuleQuestion' => 'infrastructure/markup/rule/PonderRuleQuestion.php',
'PonderUserProfileView' => 'applications/ponder/view/PonderUserProfileView.php',
'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php',
'PonderVotableView' => 'applications/ponder/view/PonderVotableView.php',
'PonderVoteEditor' => 'applications/ponder/editor/PonderVoteEditor.php',
'PonderVoteSaveController' => 'applications/ponder/controller/PonderVoteSaveController.php',
'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php',
),
'function' =>
@ -1641,6 +1671,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationManiphest' => 'PhabricatorApplication',
'PhabricatorApplicationPeople' => 'PhabricatorApplication',
'PhabricatorApplicationPhriction' => 'PhabricatorApplication',
'PhabricatorApplicationPonder' => 'PhabricatorApplication',
'PhabricatorApplicationProject' => 'PhabricatorApplication',
'PhabricatorApplicationSettings' => 'PhabricatorApplication',
'PhabricatorApplicationStatusView' => 'AphrontView',
@ -2029,6 +2060,7 @@ phutil_register_library_map(array(
'PhabricatorSearchIndexController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchManiphestIndexer' => 'PhabricatorSearchDocumentIndexer',
'PhabricatorSearchPhrictionIndexer' => 'PhabricatorSearchDocumentIndexer',
'PhabricatorSearchPonderIndexer' => 'PhabricatorSearchDocumentIndexer',
'PhabricatorSearchQuery' => 'PhabricatorSearchDAO',
'PhabricatorSearchResultView' => 'AphrontView',
'PhabricatorSearchSelectController' => 'PhabricatorSearchBaseController',
@ -2169,6 +2201,40 @@ phutil_register_library_map(array(
'PhrictionEditController' => 'PhrictionController',
'PhrictionHistoryController' => 'PhrictionController',
'PhrictionListController' => 'PhrictionController',
'PonderAddAnswerView' => 'AphrontView',
'PonderAnswer' =>
array(
0 => 'PonderDAO',
1 => 'PhabricatorMarkupInterface',
2 => 'PonderVotableInterface',
),
'PonderAnswerListView' => 'AphrontView',
'PonderAnswerPreviewController' => 'PonderController',
'PonderAnswerQuery' => 'PhabricatorOffsetPagedQuery',
'PonderAnswerSaveController' => 'PonderController',
'PonderAnswerSummaryView' => 'AphrontView',
'PonderAnswerViewController' => 'PonderController',
'PonderCommentBodyView' => 'AphrontView',
'PonderController' => 'PhabricatorController',
'PonderDAO' => 'PhabricatorLiskDAO',
'PonderFeedController' => 'PonderController',
'PonderQuestion' =>
array(
0 => 'PonderDAO',
1 => 'PhabricatorMarkupInterface',
2 => 'PonderVotableInterface',
),
'PonderQuestionAskController' => 'PonderController',
'PonderQuestionDetailView' => 'AphrontView',
'PonderQuestionFeedView' => 'AphrontView',
'PonderQuestionPreviewController' => 'PonderController',
'PonderQuestionQuery' => 'PhabricatorOffsetPagedQuery',
'PonderQuestionSummaryView' => 'AphrontView',
'PonderQuestionViewController' => 'PonderController',
'PonderRuleQuestion' => 'PhabricatorRemarkupRuleObjectName',
'PonderUserProfileView' => 'AphrontView',
'PonderVotableView' => 'AphrontView',
'PonderVoteSaveController' => 'PonderController',
'QueryFormattingTestCase' => 'PhabricatorTestCase',
),
));

View file

@ -320,7 +320,6 @@ class AphrontDefaultApplicationConfiguration
'/emailverify/(?P<code>[^/]+)/' =>
'PhabricatorEmailVerificationController',
);
}

View file

@ -42,4 +42,6 @@ final class PhabricatorPHIDConstants {
const PHID_TYPE_POST = 'POST';
const PHID_TYPE_TOBJ = 'TOBJ';
const PHID_TYPE_BLOG = 'BLOG';
const PHID_TYPE_QUES = 'QUES';
const PHID_TYPE_ANSW = 'ANSW';
}

View file

@ -439,6 +439,27 @@ final class PhabricatorObjectHandleData {
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_QUES:
$questions = id(new PonderQuestionQuery())
->withPHIDs($phids)
->execute();
$questions = mpull($questions, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($questions[$phid])) {
$handle->setName('Unknown Ponder Question');
} else {
$question = $questions[$phid];
$handle->setName(phutil_utf8_shorten($question->getTitle(), 60));
$handle->setURI(new PhutilURI('Q' . $question->getID()));
$handle->setComplete(true);
}
$handles[$phid] = $handle;
}
break;
default:
$loader = null;
if (isset($external_loaders[$type])) {

View file

@ -0,0 +1,26 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderConstants {
const UP_VOTE = 1;
const NONE_VOTE = 0;
const DOWN_VOTE = -1;
const ANSWERED_LITERAL = "answered";
const ASKED_LITERAL = "asked";
}

View file

@ -0,0 +1,61 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorApplicationPonder extends PhabricatorApplication {
public function getBaseURI() {
return '/ponder/';
}
public function getShortDescription() {
return 'Find Answers';
}
public function getIconURI() {
return celerity_get_resource_uri('/rsrc/image/app/app_ponder.png');
}
public function getFactObjectsForAnalysis() {
return array(
new PonderQuestion(),
);
}
public function loadStatus(PhabricatorUser $user) {
// replace with "x new unanswered questions" or some such
$status = array();
return $status;
}
public function getroutes() {
return array(
'/Q(?P<id>\d+)' => 'PonderQuestionViewController',
'/ponder/' => array(
'(?P<page>feed/)?' => 'PonderFeedController',
'(?P<page>profile)/' => 'PonderFeedController',
'answer/add/' => 'PonderAnswerSaveController',
'answer/preview/' => 'PonderAnswerPreviewController',
'question/ask/' => 'PonderQuestionAskController',
'question/preview/' => 'PonderQuestionPreviewController',
'(?P<kind>question)/vote/' => 'PonderVoteSaveController',
'(?P<kind>answer)/vote/' => 'PonderVoteSaveController'
));
}
}

View file

@ -0,0 +1,55 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderAnswerPreviewController
extends PonderController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$question_id = $request->getInt('question_id');
$question = PonderQuestionQuery::loadSingle($user, $question_id);
if (!$question) {
return new Aphront404Response();
}
$author_phid = $user->getPHID();
$object_phids = array($author_phid);
$handles = id(new PhabricatorObjectHandleData($object_phids))
->loadHandles();
$answer = new PonderAnswer();
$answer->setContent($request->getStr('content'));
$answer->setAuthorPHID($author_phid);
$view = new PonderCommentBodyView();
$view
->setQuestion($question)
->setTarget($answer)
->setPreview(true)
->setUser($user)
->setHandles($handles)
->setAction(PonderConstants::ANSWERED_LITERAL);
return id(new AphrontAjaxResponse())
->setContent($view->render());
}
}

View file

@ -0,0 +1,63 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderAnswerSaveController extends PonderController {
public function processRequest() {
$request = $this->getRequest();
if (!$request->isFormPost()) {
return new Aphront400Response();
}
$user = $request->getUser();
$question_id = $request->getInt('question_id');
$question = PonderQuestionQuery::loadSingle($user, $question_id);
if (!$question) {
return new Aphront404Response();
}
$answer = $request->getStr('answer');
$content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_WEB,
array(
'ip' => $request->getRemoteAddr(),
));
$res = new PonderAnswer();
$res
->setContent($answer)
->setAuthorPHID($user->getPHID())
->setVoteCount(0)
->setQuestionID($question_id)
->setContentSource($content_source);
id(new PonderAnswerEditor())
->setQuestion($question)
->setAnswer($res)
->saveAnswer();
PhabricatorSearchPonderIndexer::indexQuestion($question);
return id(new AphrontRedirectResponse())
->setURI(id(new PhutilURI('/Q'. $question->getID()))
->setFragment('A'.$res->getID()));
}
}

View file

@ -0,0 +1,41 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderAnswerViewController extends PonderController {
private $answerID;
public function willProcessRequest(array $data) {
$this->answerID = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$answer = id(new PonderAnswer())->load($this->answerID);
if (!$answer) {
return new Aphront404Response();
}
$question_id = $answer->getQuestionID();
return id(new AphrontRedirectResponse())
->setURI('/Q'.$question_id . '#A' . $answer->getID());
}
}

View file

@ -0,0 +1,35 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
abstract class PonderController extends PhabricatorController {
public function buildStandardPageResponse($view, array $data) {
$page = $this->buildStandardPageView();
$page->setApplicationName('Ponder!');
$page->setBaseURI('/ponder/');
$page->setTitle(idx($data, 'title'));
$page->setGlyph("\xE2\x97\xB3");
$page->appendChild($view);
$page->setSearchDefaultScope(PhabricatorSearchScope::SCOPE_QUESTIONS);
$response = new AphrontWebpageResponse();
return $response->setContent($page->render());
}
}

View file

@ -0,0 +1,143 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderFeedController extends PonderController {
private $page;
private $feedOffset;
private $questionOffset;
private $answerOffset;
const PAGE_FEED = "feed";
const PAGE_PROFILE = "profile";
const FEED_PAGE_SIZE = 20;
const PROFILE_QUESTION_PAGE_SIZE = 10;
const PROFILE_ANSWER_PAGE_SIZE = 10;
static $pages = array(
self::PAGE_FEED => "Popular Questions",
self::PAGE_PROFILE => "User Profile"
);
public function willProcessRequest(array $data) {
if (isset($data['page'])) {
$this->page = $data['page'];
}
if (!isset(self::$pages[$this->page])) {
$this->page = self::PAGE_FEED;
}
$this->feedOffset = 0;
if (isset($data['feedoffset'])) {
$this->feedOffset = $data['feedoffset'];
}
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$this->feedOffset = $request->getInt('off');
$this->questionOffset = $request->getInt('qoff');
$this->answerOffset = $request->getInt('aoff');
$side_nav = new AphrontSideNavView();
foreach (self::$pages as $pagename => $pagetitle) {
$class = "";
if ($pagename == $this->page) {
$class = 'aphront-side-nav-selected';
}
$linky = phutil_render_tag(
'a',
array(
'href' => '/ponder/'.$pagename .'/',
'class' => $class
),
phutil_escape_html($pagetitle)
);
$side_nav->addNavItem($linky);
}
switch ($this->page) {
case self::PAGE_FEED:
$data = PonderQuestionQuery::loadHottest(
$user,
$this->feedOffset,
self::FEED_PAGE_SIZE + 1);
$phids = array();
foreach ($data as $question) {
$phids[] = $question->getAuthorPHID();
}
$handles = id(new PhabricatorObjectHandleData($phids))
->loadHandles();
$side_nav->appendChild(
id(new PonderQuestionFeedView())
->setUser($user)
->setData($data)
->setHandles($handles)
->setOffset($this->feedOffset)
->setPageSize(self::FEED_PAGE_SIZE)
->setURI(new PhutilURI("/ponder/feed/"), "off")
);
break;
case self::PAGE_PROFILE:
$questions = PonderQuestionQuery::loadByAuthor(
$user,
$user->getPHID(),
$this->questionOffset,
self::PROFILE_QUESTION_PAGE_SIZE + 1
);
$answers = PonderAnswerQuery::loadByAuthorWithQuestions(
$user,
$user->getPHID(),
$this->answerOffset,
self::PROFILE_ANSWER_PAGE_SIZE + 1
);
$phids = array($user->getPHID());
$handles = id(new PhabricatorObjectHandleData($phids))
->loadHandles();
$side_nav->appendChild(
id(new PonderUserProfileView())
->setUser($user)
->setQuestions($questions)
->setAnswers($answers)
->setHandles($handles)
->setQuestionOffset($this->questionOffset)
->setAnswerOffset($this->answerOffset)
->setPageSize(self::PROFILE_QUESTION_PAGE_SIZE)
->setURI(new PhutilURI("/ponder/profile/"), "qoff", "aoff")
);
break;
}
return $this->buildStandardPageResponse(
$side_nav,
array(
'title' => self::$pages[$this->page]
));
}
}

View file

@ -0,0 +1,152 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderQuestionAskController extends PonderController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($request->isFormPost()) {
return $this->handlePost();
}
return $this->showForm();
}
private function handlePost() {
$request = $this->getRequest();
$user = $request->getUser();
$errors = array();
$title = $request->getStr('title');
$content = $request->getStr('content');
// form validation
if (phutil_utf8_strlen($title) < 1 || phutil_utf8_strlen($title) > 255) {
$errors[] = "Please enter a title (1-255 characters)";
}
if ($errors) {
return $this->showForm($errors, $title, $content);
}
// no validation errors -> save it
$content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_WEB,
array(
'ip' => $request->getRemoteAddr(),
));
$question = id(new PonderQuestion())
->setTitle($title)
->setContent($content)
->setAuthorPHID($user->getPHID())
->setContentSource($content_source)
->setVoteCount(0)
->setAnswerCount(0)
->setHeat(0.0)
->save();
PhabricatorSearchPonderIndexer::indexQuestion($question);
return id(new AphrontRedirectResponse())
->setURI('/Q'.$question->getID());
}
private function showForm(
$errors = null,
$title = "",
$content = "",
$id = null) {
require_celerity_resource('ponder-core-view-css');
require_celerity_resource('phabricator-remarkup-css');
require_celerity_resource('ponder-post-css');
$request = $this->getRequest();
$user = $request->getUser();
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle('Form Errors')
->setErrors($errors);
}
$form = new AphrontFormView();
$form->setUser($user);
$form->setAction('/ponder/question/ask/');
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Title')
->setName('title')
->setValue($title))
->appendChild(
id(new AphrontFormTextAreaControl())
->setName('content')
->setID('content')
->setValue($content)
->setLabel("Question")
->setCaption(phutil_render_tag(
'a',
array(
'href' => PhabricatorEnv::getDoclink(
'article/Remarkup_Reference.html'),
'tabindex' => '-1',
'target' => '_blank',
),
"Formatting Reference")))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Ask Away!'));
$panel = id(new AphrontPanelView())
->addClass("ponder-panel")
->setHeader("Your Question:")
->appendChild($error_view)
->appendChild($form);
$panel->appendChild(
'<div class="aphront-panel-flush">'.
'<div id="question-preview">'.
'<span class="aphront-panel-preview-loading-text">'.
'Loading question preview...'.
'</span>'.
'</div>'.
'</div>'
);
Javelin::initBehavior(
'ponder-feedback-preview',
array(
'uri' => '/ponder/question/preview/',
'content' => 'content',
'preview' => 'question-preview',
'question_id' => null
));
return $this->buildStandardPageResponse(
array($panel),
array('title' => 'Ask a Question')
);
}
}

View file

@ -0,0 +1,52 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderQuestionPreviewController
extends PonderController {
const VERB_ASKED = "asked";
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$author_phid = $user->getPHID();
$object_phids = array($author_phid);
$handles = id(new PhabricatorObjectHandleData($object_phids))
->loadHandles();
$question = new PonderQuestion();
$question->setContent($request->getStr('content'));
$question->setAuthorPHID($author_phid);
$view = new PonderCommentBodyView();
$view
->setQuestion($question)
->setTarget($question)
->setPreview(true)
->setUser($user)
->setHandles($handles)
->setAction(self::VERB_ASKED);
return id(new AphrontAjaxResponse())
->setContent($view->render());
}
}

View file

@ -0,0 +1,76 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderQuestionViewController extends PonderController {
private $questionID;
public function willProcessRequest(array $data) {
$this->questionID = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$question = PonderQuestionQuery::loadSingle($user, $this->questionID);
if (!$question) {
return new Aphront404Response();
}
$question->attachRelated($user->getPHID());
$answers = $question->getAnswers();
$object_phids = array($user->getPHID(), $question->getAuthorPHID());
foreach ($answers as $answer) {
$object_phids[] = $answer->getAuthorPHID();
}
$handles = id(new PhabricatorObjectHandleData($object_phids))
->loadHandles();
$detail_panel = new PonderQuestionDetailView();
$detail_panel
->setQuestion($question)
->setUser($user)
->setHandles($handles);
$responses_panel = new PonderAnswerListView();
$responses_panel
->setQuestion($question)
->setHandles($handles)
->setUser($user)
->setAnswers($answers);
$answer_add_panel = new PonderAddAnswerView();
$answer_add_panel
->setQuestion($question)
->setUser($user)
->setActionURI("/ponder/answer/add/");
return $this->buildStandardPageResponse(
array(
$detail_panel,
$responses_panel,
$answer_add_panel
),
array(
'title' => 'Q'.$question->getID().' '.$question->getTitle()
));
}
}

View file

@ -0,0 +1,58 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderVoteSaveController extends PonderController {
private $kind;
public function willProcessRequest(array $data) {
$this->kind = $data['kind'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$newvote = $request->getInt("vote");
$phid = $request->getStr("phid");
if (1 < $newvote || $newvote < -1) {
return new Aphront400Response();
}
$target = null;
if ($this->kind == "question") {
$target = PonderQuestionQuery::loadSingleByPHID($user, $phid);
}
else if ($this->kind == "answer") {
$target = PonderAnswerQuery::loadSingleByPHID($user, $phid);
}
if (!$target) {
return new Aphront404Response();
}
$editor = id(new PonderVoteEditor())
->setVotable($target)
->setUser($user)
->setVote($newvote)
->saveVote();
return id(new AphrontAjaxResponse())->setContent(".");
}
}

View file

@ -0,0 +1,64 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderAnswerEditor {
private $question;
private $answer;
public function setQuestion($question) {
$this->question = $question;
return $this;
}
public function setAnswer($answer) {
$this->answer = $answer;
return $this;
}
public function saveAnswer() {
if (!$this->question) {
throw new Exception("Must set question before saving vote");
}
if (!$this->answer) {
throw new Exception("Must set answer before saving vote");
}
$question = $this->question;
$answer = $this->answer;
$conn = $answer->establishConnection('w');
$trans = $conn->openTransaction();
$trans->beginReadLocking();
$question->reload();
queryfx($conn,
'UPDATE %T as t
SET t.`answerCount` = t.`answerCount` + 1
WHERE t.`PHID` = %s',
$question->getTableName(),
$question->getPHID());
$answer->setQuestionID($question->getID());
$answer->save();
$trans->endReadLocking();
$trans->saveTransaction();
}
}

View file

@ -0,0 +1,104 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderVoteEditor {
private $answer;
private $votable;
private $anwer;
private $user;
private $vote;
public function setAnswer($answer) {
$this->answer = $answer;
return $this;
}
public function setVotable($votable) {
$this->votable = $votable;
return $this;
}
public function setUser($user) {
$this->user = $user;
return $this;
}
public function setVote($vote) {
$this->vote = $vote;
return $this;
}
public function saveVote() {
if (!$this->votable) {
throw new Exception("Must set votable before saving vote");
}
if (!$this->user) {
throw new Exception("Must set user before saving vote");
}
$user = $this->user;
$votable = $this->votable;
$newvote = $this->vote;
// prepare vote add, or update if this user is amending an
// earlier vote
$editor = id(new PhabricatorEdgeEditor())
->setUser($user)
->addEdge(
$user->getPHID(),
$votable->getUserVoteEdgeType(),
$votable->getVotablePHID(),
array('data' => $newvote))
->removeEdge(
$user->getPHID(),
$votable->getUserVoteEdgeType(),
$votable->getVotablePHID());
$conn = $votable->establishConnection('w');
$trans = $conn->openTransaction();
$trans->beginReadLocking();
$votable->reload();
$curvote = (int)PhabricatorEdgeQuery::loadSingleEdgeData(
$user->getPHID(),
$votable->getUserVoteEdgeType(),
$votable->getVotablePHID());
if (!$curvote) {
$curvote = PonderConstants::NONE_VOTE;
}
// adjust votable's score by this much
$delta = $newvote - $curvote;
queryfx($conn,
'UPDATE %T as t
SET t.`voteCount` = t.`voteCount` + %d
WHERE t.`PHID` = %s',
$votable->getTableName(),
$delta,
$votable->getVotablePHID());
$editor->save();
$trans->endReadLocking();
$trans->saveTransaction();
}
}

View file

@ -0,0 +1,161 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderAnswerQuery extends PhabricatorOffsetPagedQuery {
private $id;
private $phid;
private $authorPHID;
private $orderNewest;
public function withID($qid) {
$this->id = $qid;
return $this;
}
public function withPHID($phid) {
$this->phid = $phid;
return $this;
}
public function withAuthorPHID($phid) {
$this->authorPHID = $phid;
return $this;
}
public function orderByNewest($usethis) {
$this->orderNewest = $usethis;
return $this;
}
public static function loadByAuthorWithQuestions(
$viewer,
$phid,
$offset,
$count) {
if (!$viewer) {
throw new Exception("Must set viewer when calling loadByAuthor...");
}
$answers = id(new PonderAnswerQuery())
->withAuthorPHID($phid)
->orderByNewest(true)
->setOffset($offset)
->setLimit($count)
->execute();
$answerset = new LiskDAOSet();
foreach ($answers as $answer) {
$answerset->addToSet($answer);
}
foreach ($answers as $answer) {
$question = $answer->loadOneRelative(
new PonderQuestion(),
'id',
'getQuestionID');
$answer->setQuestion($question);
}
return $answers;
}
public static function loadByAuthor($viewer, $author_phid, $offset, $count) {
if (!$viewer) {
throw new Exception("Must set viewer when calling loadByAuthor");
}
return id(new PonderAnswerQuery())
->withAuthorPHID($author_phid)
->setOffset($offset)
->setLimit($count)
->orderByNewest(true)
->execute();
}
public static function loadSingle($viewer, $id) {
if (!$viewer) {
throw new Exception("Must set viewer when calling loadSingle");
}
return idx(id(new PonderAnswerQuery())
->withID($id)
->execute(), $id);
}
public static function loadSingleByPHID($viewer, $phid) {
if (!$viewer) {
throw new Exception("Must set viewer when calling loadSingle");
}
return array_shift(id(new PonderAnswerQuery())
->withPHID($phid)
->execute());
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->id) {
$where[] = qsprintf($conn_r, '(id = %d)', $this->id);
}
if ($this->phid) {
$where[] = qsprintf($conn_r, '(phid = %s)', $this->phid);
}
if ($this->authorPHID) {
$where[] = qsprintf($conn_r, '(authorPHID = %s)', $this->authorPHID);
}
return $this->formatWhereClause($where);
}
private function buildOrderByClause($conn_r) {
$order = array();
if ($this->orderNewest) {
$order[] = qsprintf($conn_r, 'id DESC');
}
if (count($order) == 0) {
$order[] = qsprintf($conn_r, 'id ASC');
}
return ($order ? 'ORDER BY ' . implode(', ', $order) : '');
}
public function execute() {
$answer = new PonderAnswer();
$conn_r = $answer->establishConnection('r');
$select = qsprintf(
$conn_r,
'SELECT r.* FROM %T r',
$answer->getTableName());
$where = $this->buildWhereClause($conn_r);
$order_by = $this->buildOrderByClause($conn_r);
$limit = $this->buildLimitClause($conn_r);
return $answer->loadAllFromArray(
queryfx_all(
$conn_r,
'%Q %Q %Q %Q',
$select,
$where,
$order_by,
$limit));
}
}

View file

@ -0,0 +1,157 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderQuestionQuery extends PhabricatorOffsetPagedQuery {
private $id;
private $phids;
private $authorPHID;
private $orderHottest;
private $orderNewest;
public function withID($qid) {
$this->id = $qid;
return $this;
}
public function withPHID($phid) {
$this->phids = array($phid);
return $this;
}
public function withPHIDs($phids) {
$this->phids = $phids;
return $this;
}
public function withAuthorPHID($phid) {
$this->authorPHID = $phid;
return $this;
}
public function orderByHottest($usethis) {
$this->orderHottest = $usethis;
return $this;
}
public function orderByNewest($usethis) {
$this->orderNewest = $usethis;
return $this;
}
public static function loadHottest($viewer, $offset, $count) {
if (!$viewer) {
throw new Exception("Must set viewer when calling loadHottest");
}
return id(new PonderQuestionQuery())
->setOffset($offset)
->setLimit($count)
->orderByHottest(true)
->execute();
}
public static function loadByAuthor($viewer, $author_phid, $offset, $count) {
if (!$viewer) {
throw new Exception("Must set viewer when calling loadByAuthor");
}
return id(new PonderQuestionQuery())
->withAuthorPHID($author_phid)
->setOffset($offset)
->setLimit($count)
->orderByNewest(true)
->execute();
}
public static function loadSingle($viewer, $id) {
if (!$viewer) {
throw new Exception("Must set viewer when calling loadSingle");
}
return idx(id(new PonderQuestionQuery())
->withID($id)
->execute(), $id);
}
public static function loadSingleByPHID($viewer, $phid) {
if (!$viewer) {
throw new Exception("Must set viewer when calling loadSingle");
}
return array_shift(id(new PonderQuestionQuery())
->withPHID($phid)
->execute());
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->id) {
$where[] = qsprintf($conn_r, '(id = %d)', $this->id);
}
if ($this->phids) {
$where[] = qsprintf($conn_r, '(phid in (%Ls))', $this->phids);
}
if ($this->authorPHID) {
$where[] = qsprintf($conn_r, '(authorPHID = %s)', $this->authorPHID);
}
return ($where ? 'WHERE ' . implode(' AND ', $where) : '');
}
private function buildOrderByClause($conn_r) {
$order = array();
if ($this->orderHottest) {
$order[] = qsprintf($conn_r, 'heat DESC');
}
if ($this->orderNewest) {
$order[] = qsprintf($conn_r, 'id DESC');
}
if (count($order) == 0) {
$order[] = qsprintf($conn_r, 'id ASC');
}
return ($order ? 'ORDER BY ' . implode(', ', $order) : '');
}
public function execute() {
$question = new PonderQuestion();
$conn_r = $question->establishConnection('r');
$select = qsprintf(
$conn_r,
'SELECT r.* FROM %T r',
$question->getTableName());
$where = $this->buildWhereClause($conn_r);
$order_by = $this->buildOrderByClause($conn_r);
$limit = $this->buildLimitClause($conn_r);
return $question->loadAllFromArray(
queryfx_all(
$conn_r,
'%Q %Q %Q %Q',
$select,
$where,
$order_by,
$limit));
}
}

View file

@ -0,0 +1,56 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorSearchPonderIndexer
extends PhabricatorSearchDocumentIndexer {
public static function indexQuestion(PonderQuestion $question) {
$doc = new PhabricatorSearchAbstractDocument();
$doc->setPHID($question->getPHID());
$doc->setDocumentType(PhabricatorPHIDConstants::PHID_TYPE_QUES);
$doc->setDocumentTitle($question->getTitle());
$doc->setDocumentCreated($question->getDateCreated());
$doc->setDocumentModified($question->getDateModified());
$doc->addField(
PhabricatorSearchField::FIELD_BODY,
$question->getContent());
$doc->addRelationship(
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
$question->getAuthorPHID(),
PhabricatorPHIDConstants::PHID_TYPE_USER,
$question->getDateCreated());
$answers = $question->getAnswers();
$touches = array();
foreach ($answers as $comment) {
if (strlen($comment->getContent())) {
$doc->addField(
PhabricatorSearchField::FIELD_COMMENT,
$comment->getContent());
}
$author = $comment->getAuthorPHID();
$touches[$author] = $comment->getDateCreated();
}
self::reindexAbstractDocument($doc);
}
}

View file

@ -0,0 +1,127 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderAnswer extends PonderDAO
implements PhabricatorMarkupInterface, PonderVotableInterface {
const MARKUP_FIELD_CONTENT = 'markup:content';
protected $phid;
protected $authorPHID;
protected $questionID;
protected $content;
protected $contentSource;
protected $voteCount;
private $vote;
private $question = null;
public function setQuestion($question) {
$this->question = $question;
return $this;
}
public function getQuestion() {
return $this->question;
}
public function setUserVote($vote) {
$this->vote = $vote['data'];
if (!$this->vote) {
$this->vote = PonderConstants::NONE_VOTE;
}
return $this;
}
public function getUserVote() {
return $this->vote;
}
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function setTitle($title) {
$this->title = $title;
if (!$this->getID()) {
$this->originalTitle = $title;
}
return $this;
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPHIDConstants::PHID_TYPE_ANSW);
}
public function setContentSource(PhabricatorContentSource $content_source) {
$this->contentSource = $content_source->serialize();
return $this;
}
public function getContentSource() {
return PhabricatorContentSource::newFromSerialized($this->contentSource);
}
public function getAnswers() {
return $this->loadRelatives(new PonderAnswer(), "questionID");
}
public function getMarkupField() {
return self::MARKUP_FIELD_CONTENT;
}
// Markup interface
public function getMarkupFieldKey($field) {
$hash = PhabricatorHash::digest($this->getMarkupText($field));
$id = $this->getID();
return "ponder:A{$id}:{$field}:{$hash}";
}
public function getMarkupText($field) {
return $this->getContent();
}
public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::newPonderMarkupEngine();
}
public function didMarkupText(
$field,
$output,
PhutilMarkupEngine $engine) {
return $output;
}
public function shouldUseMarkupCache($field) {
return (bool)$this->getID();
}
// votable interface
public function getUserVoteEdgeType() {
return PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_ANSWER;
}
public function getVotablePHID() {
return $this->getPHID();
}
}

View file

@ -0,0 +1,25 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
abstract class PonderDAO extends PhabricatorLiskDAO {
public function getApplicationName() {
return 'ponder';
}
}

View file

@ -0,0 +1,141 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderQuestion extends PonderDAO
implements PhabricatorMarkupInterface, PonderVotableInterface {
const MARKUP_FIELD_CONTENT = 'markup:content';
protected $title;
protected $phid;
protected $authorPHID;
protected $content;
protected $contentSource;
protected $voteCount;
protected $answerCount;
protected $heat;
private $answers;
private $vote;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPHIDConstants::PHID_TYPE_QUES);
}
public function setContentSource(PhabricatorContentSource $content_source) {
$this->contentSource = $content_source->serialize();
return $this;
}
public function getContentSource() {
return PhabricatorContentSource::newFromSerialized($this->contentSource);
}
public function attachRelated($user_phid) {
$this->answers = $this->loadRelatives(new PonderAnswer(), "questionID");
$qa_phids = mpull($this->answers, 'getPHID') + array($this->getPHID());
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($user_phid))
->withDestinationPHIDs($qa_phids)
->withEdgeTypes(
array(
PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_QUESTION,
PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_ANSWER
))
->needEdgeData(true)
->execute();
$question_edge =
$edges[$user_phid][PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_QUESTION];
$answer_edges =
$edges[$user_phid][PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_ANSWER];
$edges = null;
$this->setUserVote(idx($question_edge, $this->getPHID()));
foreach ($this->answers as $answer) {
$answer->setQuestion($this);
$answer->setUserVote(idx($answer_edges, $answer->getPHID()));
}
}
public function setUserVote($vote) {
$this->vote = $vote['data'];
if (!$this->vote) {
$this->vote = PonderConstants::NONE_VOTE;
}
return $this;
}
public function getUserVote() {
return $this->vote;
}
public function getAnswers() {
return $this->answers;
}
public function getMarkupField() {
return self::MARKUP_FIELD_CONTENT;
}
// Markup interface
public function getMarkupFieldKey($field) {
$hash = PhabricatorHash::digest($this->getMarkupText($field));
$id = $this->getID();
return "ponder:Q{$id}:{$field}:{$hash}";
}
public function getMarkupText($field) {
return $this->getContent();
}
public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::newPonderMarkupEngine();
}
public function didMarkupText(
$field,
$output,
PhutilMarkupEngine $engine) {
return $output;
}
public function shouldUseMarkupCache($field) {
return (bool)$this->getID();
}
// votable interface
public function getUserVoteEdgeType() {
return PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_QUESTION;
}
public function getVotablePHID() {
return $this->getPHID();
}
}

View file

@ -0,0 +1,24 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
interface PonderVotableInterface {
public function getUserVoteEdgeType();
public function getVotablePHID();
}

View file

@ -0,0 +1,97 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderAddAnswerView extends AphrontView {
private $question;
private $user;
private $actionURI;
private $draft;
public function setQuestion($question) {
$this->question = $question;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function setActionURI($uri) {
$this->actionURI = $uri;
return $this;
}
public function render() {
require_celerity_resource('ponder-core-view-css');
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$question = $this->question;
$panel = id(new AphrontPanelView())
->addClass("ponder-panel")
->setHeader("Your Answer:");
$form = new AphrontFormView();
$form
->setUser($this->user)
->setAction($this->actionURI)
->addHiddenInput('question_id', $question->getID())
->appendChild(
id(new AphrontFormTextAreaControl())
->setName('answer')
->setID('answer-content')
->setEnableDragAndDropFileUploads(true)
->setCaption(phutil_render_tag(
'a',
array(
'href' => PhabricatorEnv::getDoclink(
'article/Remarkup_Reference.html'),
'tabindex' => '-1',
'target' => '_blank',
),
'Formatting Reference')))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($is_serious ? 'Submit' : 'Make it so.'));
$panel->appendChild($form);
$panel->appendChild(
'<div class="aphront-panel-flush">'.
'<div id="answer-preview">'.
'<span class="aphront-panel-preview-loading-text">'.
'Loading answer preview...'.
'</span>'.
'</div>'.
'</div>'
);
Javelin::initBehavior(
'ponder-feedback-preview',
array(
'uri' => '/ponder/answer/preview/',
'content' => 'answer-content',
'preview' => 'answer-preview',
'question_id' => $question->getID()
));
return $panel->render();
}
}

View file

@ -0,0 +1,89 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderAnswerListView extends AphrontView {
private $question;
private $handles;
private $user;
private $answers;
public function setQuestion($question) {
$this->question = $question;
return $this;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function setAnswers(array $answers) {
assert_instances_of($answers, 'PonderAnswer');
$this->answers = array();
// group by descreasing score, randomizing
// order within groups
$by_score = mgroup($answers, 'getVoteCount');
$scores = array_keys($by_score);
rsort($scores);
foreach ($scores as $score) {
$group = $by_score[$score];
shuffle($group);
foreach ($group as $cur_answer) {
$this->answers[] = $cur_answer;
}
}
return $this;
}
public function render() {
require_celerity_resource('ponder-post-css');
$question = $this->question;
$user = $this->user;
$handles = $this->handles;
$panel = id(new AphrontPanelView())
->addClass("ponder-panel")
->setHeader("Responses:");
foreach ($this->answers as $cur_answer) {
$view = new PonderCommentBodyView();
$view
->setQuestion($question)
->setTarget($cur_answer)
->setAction(PonderConstants::ANSWERED_LITERAL)
->setHandles($handles)
->setUser($user);
$panel->appendChild($view);
}
return $panel->render();
}
}

View file

@ -0,0 +1,93 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderAnswerSummaryView extends AphrontView {
private $user;
private $answer;
private $handles;
public function setAnswer($answer) {
$this->answer = $answer;
return $this;
}
public function setHandles($handles) {
$this->handles = $handles;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
private static function abbreviate($w) {
return phutil_utf8_shorten($w, 60);
}
public function render() {
require_celerity_resource('ponder-feed-view-css');
$user = $this->user;
$answer = $this->answer;
$question = $answer->getQuestion();
$author_phid = $question->getAuthorPHID();
$handles = $this->handles;
$votecount =
'<div class="ponder-summary-votes">'.
phutil_escape_html($answer->getVoteCount()).
'<div class="ponder-question-label">'.
'votes'.
'</div>'.
'</div>';
$title =
'<h2 class="ponder-question-title">'.
phutil_render_tag(
'a',
array(
"href" => id(new PhutilURI('/Q' . $question->getID()))
->setFragment('A' . $answer->getID())
),
phutil_escape_html('A' . $answer->getID() . ' ' .
self::abbreviate($answer->getContent())
)
).
'</h2>';
$rhs =
'<div class="ponder-metadata">'.
$title.
'<span class="ponder-small-metadata">'.
phutil_escape_html(
'answer to "'. self::abbreviate($question->getTitle()). '" on ' .
phabricator_datetime($answer->getDateCreated(), $user)
).
'</span>'.
'</div>';
$summary =
'<div class="ponder-answer-summary">'.
$votecount.
$rhs.
'</div>';
return $summary;
}
}

View file

@ -0,0 +1,149 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderCommentBodyView extends AphrontView {
private $target;
private $question;
private $handles;
private $preview;
private $anchorName;
private $user;
private $action;
public function setQuestion($question) {
$this->question = $question;
return $this;
}
public function setTarget($target) {
$this->target = $target;
return $this;
}
public function setAction($action) {
$this->action = $action;
return $this;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function setPreview($preview) {
$this->preview = $preview;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function render() {
if (!$this->user) {
throw new Exception("Call setUser() before rendering!");
}
require_celerity_resource('phabricator-remarkup-css');
require_celerity_resource('ponder-post-css');
$user = $this->user;
$question = $this->question;
$target = $this->target;
$content = $target->getContent();
$info = array();
$content = PhabricatorMarkupEngine::renderOneObject(
$target,
$target->getMarkupField());
$content =
'<div class="phabricator-remarkup">'.
$content.
'</div>';
$author = $this->handles[$target->getAuthorPHID()];
$actions = array($author->renderLink().' '.$this->action);
$author_link = $author->renderLink();
$xaction_view = id(new PhabricatorTransactionView())
->setUser($user)
->setImageURI($author->getImageURI())
->setContentSource($target->getContentSource())
->setActions($actions);
if ($this->target instanceof PonderAnswer) {
$xaction_view->addClass("ponder-answer");
}
else {
$xaction_view->addClass("ponder-question");
}
if ($this->preview) {
$xaction_view->setIsPreview($this->preview);
} else {
$xaction_view->setEpoch($target->getDateCreated());
if ($this->target instanceof PonderAnswer) {
$anchor_text = 'Q' . $question->getID(). '#A' . $target->getID();
$xaction_view->setAnchor('A'.$target->getID(), $anchor_text);
$xaction_view->addClass("ponder-answer");
}
}
$xaction_view->appendChild(
'<div class="ponder-post-core">'.
$content.
'</div>'
);
$outerview = $xaction_view;
if (!$this->preview) {
$outerview =
id(new PonderVotableView())
->setPHID($target->getPHID())
->setCount($target->getVoteCount())
->setVote($target->getUserVote());
if ($this->target instanceof PonderAnswer) {
$outerview->setURI('/ponder/answer/vote/');
}
else {
$outerview->setURI('/ponder/question/vote/');
}
$outerview->appendChild($xaction_view);
}
return $outerview->render();
}
private function renderHandleList(array $phids) {
$result = array();
foreach ($phids as $phid) {
$result[] = $this->handles[$phid]->renderLink();
}
return implode(', ', $result);
}
}

View file

@ -0,0 +1,72 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderQuestionDetailView extends AphrontView {
private $question;
private $user;
private $handles;
public function setQuestion($question) {
$this->question = $question;
return $this;
}
public function setUser($user) {
$this->user = $user;
return $this;
}
public function setHandles($handles) {
$this->handles = $handles;
return $this;
}
public function render() {
require_celerity_resource('ponder-core-view-css');
$question = $this->question;
$handles = $this->handles;
$user = $this->user;
$panel = id(new AphrontPanelView())
->addClass("ponder-panel")
->setHeader($this->renderObjectLink().' '.$question->getTitle());
$contentview = new PonderCommentBodyView();
$contentview
->setTarget($question)
->setQuestion($question)
->setUser($user)
->setHandles($handles)
->setAction(PonderConstants::ASKED_LITERAL);
$panel->appendChild($contentview);
return $panel->render();
}
private function renderObjectLink() {
return phutil_render_tag(
'a',
array('href' => '/Q' . $this->question->getID()),
"Q". phutil_escape_html($this->question->getID())
);
}
}

View file

@ -0,0 +1,102 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderQuestionFeedView extends AphrontView {
private $user;
private $offset;
private $data;
private $pagesize;
private $uri;
private $param;
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function setOffset($offset) {
$this->offset = $offset;
return $this;
}
public function setData($data) {
$this->data = $data;
return $this;
}
public function setPageSize($pagesize) {
$this->pagesize = $pagesize;
return $this;
}
public function setHandles($handles) {
$this->handles = $handles;
return $this;
}
public function setURI($uri, $param) {
$this->uri = $uri;
$this->param = $param;
return $this;
}
public function render() {
require_celerity_resource('ponder-core-view-css');
require_celerity_resource('ponder-feed-view-css');
$user = $this->user;
$offset = $this->offset;
$data = $this->data;
$handles = $this->handles;
$uri = $this->uri;
$param = $this->param;
$pagesize = $this->pagesize;
$panel = id(new AphrontPanelView())
->setHeader("Popular Questions")
->addClass("ponder-panel");
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => "/ponder/question/ask/",
'class' => 'green button',
),
"Ask a question"));
$pagebuttons = id(new AphrontPagerView())
->setPageSize($pagesize)
->setOffset($offset)
->setURI($uri, $param);
$data = $pagebuttons->sliceResults($data);
foreach ($data as $question) {
$cur = id(new PonderQuestionSummaryView())
->setUser($user)
->setQuestion($question)
->setHandles($handles);
$panel->appendChild($cur);
}
$panel->appendChild($pagebuttons);
return $panel->render();
}
}

View file

@ -0,0 +1,105 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderQuestionSummaryView extends AphrontView {
private $user;
private $question;
private $handles;
public function setQuestion($question) {
$this->question = $question;
return $this;
}
public function setHandles($handles) {
$this->handles = $handles;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function render() {
require_celerity_resource('ponder-feed-view-css');
$user = $this->user;
$question = $this->question;
$author_phid = $question->getAuthorPHID();
$handles = $this->handles;
$authorlink = $handles[$author_phid]
->renderLink();
$votecount =
'<div class="ponder-summary-votes">'.
phutil_escape_html($question->getVoteCount()).
'<div class="ponder-question-label">'.
'votes'.
'</div>'.
'</div>';
$answerclass = "ponder-summary-answers";
if ($question->getAnswercount() == 0) {
$answerclass .= " ponder-not-answered";
}
$answercount =
'<div class="ponder-summary-answers">'.
phutil_escape_html($question->getAnswerCount()).
'<div class="ponder-question-label">'.
'answers'.
'</div>'.
'</div>';
$title =
'<h2 class="ponder-question-title">'.
phutil_render_tag(
'a',
array(
"href" => '/Q' . $question->getID(),
),
phutil_escape_html(
'Q' . $question->getID() .
' ' . $question->getTitle()
)
) .
'</h2>';
$rhs =
'<div class="ponder-metadata">'.
$title.
'<span class="ponder-small-metadata">'.
'asked on '.
phabricator_datetime($question->getDateCreated(), $user).
' by ' . $authorlink.
'</span>'.
'</div>';
$summary =
'<div class="ponder-question-summary">'.
$votecount.
$answercount.
$rhs.
'</div>';
return $summary;
}
}

View file

@ -0,0 +1,159 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderUserProfileView extends AphrontView {
private $user;
private $questionoffset;
private $answeroffset;
private $questions;
private $answers;
private $pagesize;
private $uri;
private $qparam;
private $aparam;
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function setQuestionOffset($offset) {
$this->questionoffset = $offset;
return $this;
}
public function setAnswerOffset($offset) {
$this->answeroffset = $offset;
return $this;
}
public function setQuestions($data) {
$this->questions = $data;
return $this;
}
public function setAnswers($data) {
$this->answers = $data;
return $this;
}
public function setPageSize($pagesize) {
$this->pagesize = $pagesize;
return $this;
}
public function setHandles($handles) {
$this->handles = $handles;
return $this;
}
public function setURI($uri, $qparam, $aparam) {
$this->uri = $uri;
$this->qparam = $qparam;
$this->aparam = $aparam;
return $this;
}
public function render() {
require_celerity_resource('ponder-core-view-css');
require_celerity_resource('ponder-feed-view-css');
$user = $this->user;
$qoffset = $this->questionoffset;
$aoffset = $this->answeroffset;
$questions = $this->questions;
$answers = $this->answers;
$handles = $this->handles;
$uri = $this->uri;
$qparam = $this->qparam;
$aparam = $this->aparam;
$pagesize = $this->pagesize;
// display questions
$question_panel = id(new AphrontPanelView())
->setHeader("Your Questions")
->addClass("ponder-panel");
$question_panel->addButton(
phutil_render_tag(
'a',
array(
'href' => "/ponder/question/ask/",
'class' => 'green button',
),
"Ask a question"));
$qpagebuttons = id(new AphrontPagerView())
->setPageSize($pagesize)
->setOffset($qoffset)
->setURI(
$uri->alter(
$aparam,
$aoffset),
$qparam);
$questions = $qpagebuttons->sliceResults($questions);
foreach ($questions as $question) {
$cur = id(new PonderQuestionSummaryView())
->setUser($user)
->setQuestion($question)
->setHandles($handles);
$question_panel->appendChild($cur);
}
$question_panel->appendChild($qpagebuttons);
// display answers
$answer_panel = id(new AphrontPanelView())
->setHeader("Your Answers")
->addClass("ponder-panel")
->appendChild(
phutil_render_tag(
'a',
array('id' => 'answers'),
"")
);
$apagebuttons = id(new AphrontPagerView())
->setPageSize($pagesize)
->setOffset($aoffset)
->setURI(
$uri
->alter(
$qparam,
$qoffset)
->setFragment("answers"),
$aparam);
$answers = $apagebuttons->sliceResults($answers);
foreach ($answers as $answer) {
$cur = id(new PonderAnswerSummaryView())
->setUser($user)
->setAnswer($answer)
->setHandles($handles);
$answer_panel->appendChild($cur);
}
$answer_panel->appendChild($apagebuttons);
return $question_panel->render() . $answer_panel->render();
}
}

View file

@ -0,0 +1,70 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PonderVotableView extends AphrontView {
private $phid;
private $uri;
private $count;
private $vote;
public function setPHID($phid) {
$this->phid = $phid;
return $this;
}
public function setURI($uri) {
$this->uri = $uri;
return $this;
}
public function setCount($count) {
$this->count = $count;
return $this;
}
public function setVote($vote) {
$this->vote = $vote;
return $this;
}
public function render() {
require_celerity_resource('ponder-vote-css');
require_celerity_resource('javelin-behavior-ponder-votebox');
Javelin::initBehavior(
'ponder-votebox',
array(
'nodeid' => $this->phid,
'vote' => $this->vote,
'count' => $this->count,
'uri' => $this->uri
));
$content =
'<div class="ponder-votable">'.
'<div id="'.phutil_escape_html($this->phid).'" class="ponder-votebox">
</div>'.
$this->renderChildren().
'<div class="ponder-votable-bottom"></div>'.
'</div>';
return $content;
}
}

View file

@ -26,6 +26,7 @@ final class PhabricatorSearchScope {
const SCOPE_OPEN_TASKS = 'open-tasks';
const SCOPE_COMMITS = 'commits';
const SCOPE_WIKI = 'wiki';
const SCOPE_QUESTIONS = 'questions';
public static function getScopeOptions() {
return array(
@ -34,6 +35,7 @@ final class PhabricatorSearchScope {
self::SCOPE_WIKI => 'Wiki Documents',
self::SCOPE_OPEN_REVISIONS => 'Open Revisions',
self::SCOPE_COMMITS => 'Commits',
self::SCOPE_QUESTIONS => 'Ponder Questions',
);
}

View file

@ -37,6 +37,7 @@ final class PhabricatorSearchAbstractDocument {
PhabricatorPHIDConstants::PHID_TYPE_TASK => 'Maniphest Tasks',
PhabricatorPHIDConstants::PHID_TYPE_WIKI => 'Phriction Documents',
PhabricatorPHIDConstants::PHID_TYPE_USER => 'Phabricator Users',
PhabricatorPHIDConstants::PHID_TYPE_QUES => 'Ponder Questions',
) + $more;
}

View file

@ -44,6 +44,11 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
const TYPE_COMMIT_HAS_PROJECT = 15;
const TYPE_PROJECT_HAS_COMMIT = 16;
const TYPE_QUESTION_HAS_VOTING_USER = 17;
const TYPE_VOTING_USER_HAS_QUESTION = 18;
const TYPE_ANSWER_HAS_VOTING_USER = 19;
CONST TYPE_VOTING_USER_HAS_ANSWER = 20;
const TYPE_TEST_NO_CYCLE = 9000;
public static function getInverse($edge_type) {
@ -71,6 +76,12 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
self::TYPE_COMMIT_HAS_PROJECT => self::TYPE_PROJECT_HAS_COMMIT,
self::TYPE_PROJECT_HAS_COMMIT => self::TYPE_COMMIT_HAS_PROJECT,
self::TYPE_QUESTION_HAS_VOTING_USER =>
self::TYPE_VOTING_USER_HAS_QUESTION,
self::TYPE_VOTING_USER_HAS_QUESTION =>
self::TYPE_QUESTION_HAS_VOTING_USER,
self::TYPE_ANSWER_HAS_VOTING_USER => self::TYPE_VOTING_USER_HAS_ANSWER,
self::TYPE_VOTING_USER_HAS_ANSWER => self::TYPE_ANSWER_HAS_VOTING_USER,
);
return idx($map, $edge_type);
@ -98,6 +109,8 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
PhabricatorPHIDConstants::PHID_TYPE_TOBJ => 'HarbormasterObject',
PhabricatorPHIDConstants::PHID_TYPE_BLOG => 'PhameBlog',
PhabricatorPHIDConstants::PHID_TYPE_POST => 'PhamePost',
PhabricatorPHIDConstants::PHID_TYPE_QUES => 'PonderQuestion',
PhabricatorPHIDConstants::PHID_TYPE_ANSW => 'PonderAnswer',
);
$class = idx($class_map, $phid_type);

View file

@ -330,6 +330,11 @@ final class PhabricatorMarkupEngine {
}
public static function newPonderMarkupEngine(array $options = array()) {
return self::newMarkupEngine($options);
}
/**
* @task engine
*/
@ -406,6 +411,8 @@ final class PhabricatorMarkupEngine {
$rules[] = new PhabricatorRemarkupRuleManiphest();
$rules[] = new PhabricatorRemarkupRulePaste();
$rules[] = new PonderRuleQuestion();
if ($options['macros']) {
$rules[] = new PhabricatorRemarkupRuleImageMacro();
}

View file

@ -0,0 +1,29 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group markup
*/
final class PonderRuleQuestion
extends PhabricatorRemarkupRuleObjectName {
protected function getObjectNamePrefix() {
return 'Q';
}
}

View file

@ -163,6 +163,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'db',
'name' => 'fact',
),
'db.ponder' => array(
'type' => 'db',
'name' => 'ponder',
),
'0000.legacy.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('0000.legacy.sql'),
@ -952,6 +956,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'sql',
'name' => $this->getPatchPath('fact-raw.sql'),
),
'ponder.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('ponder.sql')
),
);
}

View file

@ -0,0 +1,25 @@
/**
* @provides ponder-core-view-css
*/
.ponder-primary-pane {
margin: 0 0 0 2em;
padding-bottom: 2em;
max-width: 800px;
}
.ponder-panel {
max-width: 800px;
font-family : antiqua, verdana, arial;
}
.ponder-panel h1 {
padding-bottom: 8px;
margin-bottom: 8px;
font-size : 1.5em;
}
.ponder-panel .aphront-form-view {
border : none;
background : none;
}

View file

@ -0,0 +1,74 @@
/**
* @provides ponder-feed-view-css
*/
.ponder-question-summary {
width : 100%;
background : #DFDFE3;
float : left;
clear : both;
margin-top : 1px;
padding : 1px;
}
.ponder-answer-summary {
width : 100%;
background : #DFDFE3;
float : left;
clear : both;
margin-top : 1px;
padding : 1px;
}
.ponder-summary-votes {
width : 50px;
height : 36pt;
font-size : 18pt;
text-align : center;
background : #EEE;
border : 1px solid #BBB;
float : left;
margin : 2px;
padding-top : 2px;
}
.ponder-summary-answers {
width : 50px;
height : 36pt;
font-size : 18pt;
text-align : center;
background : #EEE;
border : 1px solid #BBB;
float : left;
margin : 2px;
padding-top : 2px;
}
.ponder-question-label {
font-size : 6pt;
}
h2.ponder-question-title {
font-size : 12pt;
margin : 2px;
padding : 0;
}
h2.ponder-answer-title {
font-size : 12pt;
margin : 2px;
padding : 0;
}
.ponder-metadata {
padding-left : 5px;
width : 650px;
float : left;
}
.ponder-small-metadata {
font-size : 7.5pt;
color : #555;
margin : 0;
text-align : right;
}

View file

@ -0,0 +1,34 @@
/**
* @provides ponder-post-css
*/
.ponder-post-list {
max-width: 1162px;
}
.ponder-add-answer-panel {
max-width: 1162px;
}
.ponder-post-list .anchor-target {
background-color: #ffffdd;
border-color: #ffff00;
}
.ponder-post-core .phabricator-remarkup .remarkup-code-block {
width: 88ex;
width: 81ch;
}
.phabricator-transaction-view .ponder-question {
border-color: #777;
}
.phabricator-transaction-detail .ponder-question {
border-color: #777;
}
.phabricator-transaction-view .ponder-answer {
/* border-color: #203791; */
}

View file

@ -0,0 +1,74 @@
/**
* @provides ponder-vote-css
*/
.ponder-votable {
min-height : 120px;
}
.ponder-votable .phabricator-transaction-detail {
min-height : 90px;
}
.ponder-votebox {
float : left;
width : 32px;
height : 60px;
margin-top : 56px;
margin-left : 10px;
}
.ponder-upbutton {
border : none;
padding : 0;
margin : 0;
width : 32px;
height : 13px;
}
.ponder-downbutton {
border : none;
padding : 0;
margin : 0;
width : 32px;
height : 13px;
}
.ponder-votecount {
width : 32px;
height : 22pt;
padding : 0;
margin : 0;
overflow : visible;
text-align : center;
font-size : 15pt;
}
.ponder-upbutton:hover {
cursor : pointer;
}
.ponder-downbutton:hover {
cursor : pointer;
}
.ponder-votable-bottom {
clear : both;
}
.ponder-upbutton {
background : url(/rsrc/image/application/ponder/upvote.png) 0 -13px no-repeat;
}
.ponder-upbutton.ponder-vote-up {
background : url(/rsrc/image/application/ponder/upvote.png) 0 0 no-repeat;
}
.ponder-downbutton {
background : url(/rsrc/image/application/ponder/downvote.png)
0 -13px no-repeat;
}
.ponder-downbutton.ponder-vote-down {
background : url(/rsrc/image/application/ponder/downvote.png) 0 0 no-repeat;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 B

View file

@ -0,0 +1,32 @@
/**
* @provides javelin-behavior-ponder-feedback-preview
* @requires javelin-behavior
* javelin-dom
* javelin-util
* phabricator-shaped-request
*/
JX.behavior('ponder-feedback-preview', function(config) {
var content = JX.$(config.content);
var question_id = config.question_id;
var callback = function(r) {
JX.DOM.setContent(JX.$(config.preview), JX.$H(r));
};
var getdata = function() {
var data = {
content : content.value,
question_id : question_id
};
return data;
};
var request = new JX.PhabricatorShapedRequest(config.uri, callback, getdata);
var trigger = JX.bind(request, request.trigger);
JX.DOM.listen(content, 'keydown', null, trigger);
request.start();
});

View file

@ -0,0 +1,76 @@
/**
* @provides javelin-behavior-ponder-votebox
* @requires javelin-behavior
* javelin-dom
* javelin-util
* phabricator-shaped-request
*/
JX.behavior('ponder-votebox', function(config) {
var node = JX.$(config.nodeid);
var vote = config.vote;
var count = config.count | 0;
var targetURI = config.targetURI;
var upnode, countnode, downnode;
// this defines the behavior of the up/downvote
// buttons, e.g. clicking 'up' transitions from
// an 'up' vote to a 'none' vote
var votecycle = {
"1" : { up : "0", down : "-1" },
"0" : { up : "1", down : "-1" },
"-1" : { up : "1", down : "0" }
};
var voteclass = {
"0" : "ponder-vote-none",
"-1" : "ponder-vote-down",
"1" : "ponder-vote-up"
};
function decorate() {
upnode = JX.$N('div');
countnode = JX.$N('div');
downnode = JX.$N('div');
node.appendChild(upnode);
node.appendChild(countnode);
node.appendChild(downnode);
JX.DOM.alterClass(upnode, "ponder-upbutton " + voteclass[vote], true);
JX.DOM.alterClass(downnode, "ponder-downbutton " + voteclass[vote], true);
JX.DOM.alterClass(countnode, "ponder-votecount", true);
}
function update_state() {
upnode.className = "ponder-upbutton " + voteclass[vote];
downnode.className = "ponder-downbutton " + voteclass[vote];
JX.DOM.setContent(countnode, JX.$H(count.toString()));
}
function getdata() {
return { phid : config.nodeid, vote : vote };
}
var request = new JX.PhabricatorShapedRequest(config.uri, JX.id, getdata);
var trigger = JX.bind(request, request.trigger);
function handle_upvote(e) {
count += votecycle[vote].up - vote;
vote = votecycle[vote].up;
trigger();
update_state();
}
function handle_downvote(e) {
count += votecycle[vote].down - vote;
vote = votecycle[vote].down;
trigger();
update_state();
}
decorate();
update_state();
JX.DOM.listen(upnode, 'click', null, handle_upvote);
JX.DOM.listen(downnode, 'click', null, handle_downvote);
});