@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+3204 -51
+50
resources/sql/patches/ponder.sql
··· 1 + CREATE TABLE `{$NAMESPACE}_ponder`.`ponder_question` ( 2 + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 3 + `title` varchar(255) NOT NULL, 4 + `phid` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 5 + `voteCount` int(10) NOT NULL, 6 + `authorPHID` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 7 + `content` longtext CHARACTER SET utf8 NOT NULL, 8 + `dateCreated` int(10) unsigned NOT NULL, 9 + `dateModified` int(10) unsigned NOT NULL, 10 + `contentSource` varchar(255) DEFAULT NULL, 11 + `heat` float NOT NULL, 12 + `answerCount` int(10) unsigned NOT NULL, 13 + PRIMARY KEY (`id`), 14 + UNIQUE KEY `phid` (`phid`), 15 + KEY `authorPHID` (`authorPHID`), 16 + KEY `heat` (`heat`) 17 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 18 + 19 + CREATE TABLE `{$NAMESPACE}_ponder`.`ponder_answer` ( 20 + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 21 + `questionID` int(10) unsigned NOT NULL, 22 + `phid` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 23 + `voteCount` int(10) NOT NULL, 24 + `authorPHID` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 25 + `content` longtext CHARACTER SET utf8 NOT NULL, 26 + `dateCreated` int(10) unsigned NOT NULL, 27 + `dateModified` int(10) unsigned NOT NULL, 28 + `contentSource` varchar(255) DEFAULT NULL, 29 + PRIMARY KEY (`id`), 30 + UNIQUE KEY `phid` (`phid`), 31 + KEY `questionID` (`questionID`), 32 + KEY `authorPHID` (`authorPHID`) 33 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 34 + 35 + CREATE TABLE `{$NAMESPACE}_ponder`.`edge` ( 36 + `src` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 37 + `type` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 38 + `dst` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 39 + `dateCreated` int(10) unsigned NOT NULL, 40 + `seq` int(10) unsigned NOT NULL, 41 + `dataID` int(10) unsigned DEFAULT NULL, 42 + PRIMARY KEY (`src`,`type`,`dst`), 43 + KEY `src` (`src`,`type`,`dateCreated`,`seq`) 44 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 45 + 46 + CREATE TABLE `{$NAMESPACE}_ponder`.`edgedata` ( 47 + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 48 + `data` longtext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 49 + PRIMARY KEY (`id`) 50 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+133 -50
src/__celerity_resource_map__.php
··· 63 63 'disk' => '/rsrc/image/app/app_phriction.png', 64 64 'type' => 'png', 65 65 ), 66 + '/rsrc/image/app/app_ponder.png' => 67 + array( 68 + 'hash' => '9893ca3674af96884d3564ecbe101280', 69 + 'uri' => '/res/9893ca36/rsrc/image/app/app_ponder.png', 70 + 'disk' => '/rsrc/image/app/app_ponder.png', 71 + 'type' => 'png', 72 + ), 66 73 '/rsrc/image/app/app_settings.png' => 67 74 array( 68 75 'hash' => '095fa0e61ec11d3f7e543ba48f5a5adb', ··· 70 77 'disk' => '/rsrc/image/app/app_settings.png', 71 78 'type' => 'png', 72 79 ), 80 + '/rsrc/image/application/ponder/downvote.png' => 81 + array( 82 + 'hash' => '46c5644a0fccb9e237a3363e07f50487', 83 + 'uri' => '/res/46c5644a/rsrc/image/application/ponder/downvote.png', 84 + 'disk' => '/rsrc/image/application/ponder/downvote.png', 85 + 'type' => 'png', 86 + ), 87 + '/rsrc/image/application/ponder/upvote.png' => 88 + array( 89 + 'hash' => 'edd58ed3a09f3017c78601b1a5d0e7a7', 90 + 'uri' => '/res/edd58ed3/rsrc/image/application/ponder/upvote.png', 91 + 'disk' => '/rsrc/image/application/ponder/upvote.png', 92 + 'type' => 'png', 93 + ), 73 94 '/rsrc/image/apps.png' => 74 95 array( 75 - 'hash' => 'f7cb4abeb73245fea4098a02fd784653', 76 - 'uri' => '/res/f7cb4abe/rsrc/image/apps.png', 96 + 'hash' => '134f6ac3d63e24070bf7a11b294ca72c', 97 + 'uri' => '/res/134f6ac3/rsrc/image/apps.png', 77 98 'disk' => '/rsrc/image/apps.png', 78 99 'type' => 'png', 79 100 ), ··· 1608 1629 ), 1609 1630 'disk' => '/rsrc/js/application/phriction/phriction-document-preview.js', 1610 1631 ), 1632 + 'javelin-behavior-ponder-feedback-preview' => 1633 + array( 1634 + 'uri' => '/res/2e802dd9/rsrc/js/application/ponder/behavior-comment-preview.js', 1635 + 'type' => 'js', 1636 + 'requires' => 1637 + array( 1638 + 0 => 'javelin-behavior', 1639 + 1 => 'javelin-dom', 1640 + 2 => 'javelin-util', 1641 + 3 => 'phabricator-shaped-request', 1642 + ), 1643 + 'disk' => '/rsrc/js/application/ponder/behavior-comment-preview.js', 1644 + ), 1645 + 'javelin-behavior-ponder-votebox' => 1646 + array( 1647 + 'uri' => '/res/6517d3f5/rsrc/js/application/ponder/behavior-votebox.js', 1648 + 'type' => 'js', 1649 + 'requires' => 1650 + array( 1651 + 0 => 'javelin-behavior', 1652 + 1 => 'javelin-dom', 1653 + 2 => 'javelin-util', 1654 + 3 => 'phabricator-shaped-request', 1655 + ), 1656 + 'disk' => '/rsrc/js/application/ponder/behavior-votebox.js', 1657 + ), 1611 1658 'javelin-behavior-project-create' => 1612 1659 array( 1613 1660 'uri' => '/res/e91f3f8f/rsrc/js/application/projects/behavior-project-create.js', ··· 1635 1682 ), 1636 1683 'javelin-behavior-repository-crossreference' => 1637 1684 array( 1638 - 'uri' => '/res/e0d58d3b/rsrc/js/application/repository/repository-crossreference.js', 1685 + 'uri' => '/res/12700384/rsrc/js/application/repository/repository-crossreference.js', 1639 1686 'type' => 'js', 1640 1687 'requires' => 1641 1688 array( ··· 2200 2247 ), 2201 2248 'phabricator-app-buttons-css' => 2202 2249 array( 2203 - 'uri' => '/res/1e153463/rsrc/css/application/directory/phabricator-app-buttons.css', 2250 + 'uri' => '/res/2d5d414c/rsrc/css/application/directory/phabricator-app-buttons.css', 2204 2251 'type' => 'css', 2205 2252 'requires' => 2206 2253 array( ··· 2730 2777 ), 2731 2778 'disk' => '/rsrc/css/application/phriction/phriction-document-css.css', 2732 2779 ), 2780 + 'ponder-core-view-css' => 2781 + array( 2782 + 'uri' => '/res/4a6e2fc7/rsrc/css/application/ponder/core.css', 2783 + 'type' => 'css', 2784 + 'requires' => 2785 + array( 2786 + ), 2787 + 'disk' => '/rsrc/css/application/ponder/core.css', 2788 + ), 2789 + 'ponder-feed-view-css' => 2790 + array( 2791 + 'uri' => '/res/df22bd20/rsrc/css/application/ponder/feed.css', 2792 + 'type' => 'css', 2793 + 'requires' => 2794 + array( 2795 + ), 2796 + 'disk' => '/rsrc/css/application/ponder/feed.css', 2797 + ), 2798 + 'ponder-post-css' => 2799 + array( 2800 + 'uri' => '/res/32c960df/rsrc/css/application/ponder/post.css', 2801 + 'type' => 'css', 2802 + 'requires' => 2803 + array( 2804 + ), 2805 + 'disk' => '/rsrc/css/application/ponder/post.css', 2806 + ), 2807 + 'ponder-vote-css' => 2808 + array( 2809 + 'uri' => '/res/923bcf97/rsrc/css/application/ponder/vote.css', 2810 + 'type' => 'css', 2811 + 'requires' => 2812 + array( 2813 + ), 2814 + 'disk' => '/rsrc/css/application/ponder/vote.css', 2815 + ), 2733 2816 'raphael-core' => 2734 2817 array( 2735 2818 'uri' => '/res/3f48575a/rsrc/js/raphael/raphael.js', ··· 2777 2860 ), 2778 2861 'syntax-highlighting-css' => 2779 2862 array( 2780 - 'uri' => '/res/5669beb6/rsrc/css/core/syntax.css', 2863 + 'uri' => '/res/cb3b9dc0/rsrc/css/core/syntax.css', 2781 2864 'type' => 'css', 2782 2865 'requires' => 2783 2866 array( ··· 2787 2870 ), array( 2788 2871 'packages' => 2789 2872 array( 2790 - '8b65e80d' => 2873 + '04e1ab3e' => 2791 2874 array( 2792 2875 'name' => 'core.pkg.css', 2793 2876 'symbols' => ··· 2816 2899 21 => 'phabricator-flag-css', 2817 2900 22 => 'aphront-error-view-css', 2818 2901 ), 2819 - 'uri' => '/res/pkg/8b65e80d/core.pkg.css', 2902 + 'uri' => '/res/pkg/04e1ab3e/core.pkg.css', 2820 2903 'type' => 'css', 2821 2904 ), 2822 2905 '971b021e' => ··· 2867 2950 'uri' => '/res/pkg/96bc37d6/differential.pkg.css', 2868 2951 'type' => 'css', 2869 2952 ), 2870 - 'ace0431a' => 2953 + 93479628 => 2871 2954 array( 2872 2955 'name' => 'differential.pkg.js', 2873 2956 'symbols' => ··· 2891 2974 16 => 'javelin-behavior-differential-dropdown-menus', 2892 2975 17 => 'javelin-behavior-buoyant', 2893 2976 ), 2894 - 'uri' => '/res/pkg/ace0431a/differential.pkg.js', 2977 + 'uri' => '/res/pkg/93479628/differential.pkg.js', 2895 2978 'type' => 'js', 2896 2979 ), 2897 2980 'c8ce2d88' => ··· 2983 3066 'reverse' => 2984 3067 array( 2985 3068 'aphront-attached-file-view-css' => '7839ae2d', 2986 - 'aphront-crumbs-view-css' => '8b65e80d', 2987 - 'aphront-dialog-view-css' => '8b65e80d', 2988 - 'aphront-error-view-css' => '8b65e80d', 2989 - 'aphront-form-view-css' => '8b65e80d', 3069 + 'aphront-crumbs-view-css' => '04e1ab3e', 3070 + 'aphront-dialog-view-css' => '04e1ab3e', 3071 + 'aphront-error-view-css' => '04e1ab3e', 3072 + 'aphront-form-view-css' => '04e1ab3e', 2990 3073 'aphront-headsup-action-list-view-css' => '96bc37d6', 2991 - 'aphront-headsup-view-css' => '8b65e80d', 2992 - 'aphront-list-filter-view-css' => '8b65e80d', 2993 - 'aphront-pager-view-css' => '8b65e80d', 2994 - 'aphront-panel-view-css' => '8b65e80d', 2995 - 'aphront-side-nav-view-css' => '8b65e80d', 2996 - 'aphront-table-view-css' => '8b65e80d', 2997 - 'aphront-tokenizer-control-css' => '8b65e80d', 2998 - 'aphront-tooltip-css' => '8b65e80d', 2999 - 'aphront-typeahead-control-css' => '8b65e80d', 3074 + 'aphront-headsup-view-css' => '04e1ab3e', 3075 + 'aphront-list-filter-view-css' => '04e1ab3e', 3076 + 'aphront-pager-view-css' => '04e1ab3e', 3077 + 'aphront-panel-view-css' => '04e1ab3e', 3078 + 'aphront-side-nav-view-css' => '04e1ab3e', 3079 + 'aphront-table-view-css' => '04e1ab3e', 3080 + 'aphront-tokenizer-control-css' => '04e1ab3e', 3081 + 'aphront-tooltip-css' => '04e1ab3e', 3082 + 'aphront-typeahead-control-css' => '04e1ab3e', 3000 3083 'differential-changeset-view-css' => '96bc37d6', 3001 3084 'differential-core-view-css' => '96bc37d6', 3002 - 'differential-inline-comment-editor' => 'ace0431a', 3085 + 'differential-inline-comment-editor' => '93479628', 3003 3086 'differential-local-commits-view-css' => '96bc37d6', 3004 3087 'differential-results-table-css' => '96bc37d6', 3005 3088 'differential-revision-add-comment-css' => '96bc37d6', ··· 3012 3095 'inline-comment-summary-css' => '96bc37d6', 3013 3096 'javelin-behavior' => '6fb20113', 3014 3097 'javelin-behavior-aphront-basic-tokenizer' => '97f65640', 3015 - 'javelin-behavior-aphront-drag-and-drop' => 'ace0431a', 3016 - 'javelin-behavior-aphront-drag-and-drop-textarea' => 'ace0431a', 3098 + 'javelin-behavior-aphront-drag-and-drop' => '93479628', 3099 + 'javelin-behavior-aphront-drag-and-drop-textarea' => '93479628', 3017 3100 'javelin-behavior-aphront-form-disable-on-submit' => '971b021e', 3018 3101 'javelin-behavior-audit-preview' => '5e68be89', 3019 - 'javelin-behavior-buoyant' => 'ace0431a', 3020 - 'javelin-behavior-differential-accept-with-errors' => 'ace0431a', 3021 - 'javelin-behavior-differential-add-reviewers-and-ccs' => 'ace0431a', 3022 - 'javelin-behavior-differential-comment-jump' => 'ace0431a', 3023 - 'javelin-behavior-differential-diff-radios' => 'ace0431a', 3024 - 'javelin-behavior-differential-dropdown-menus' => 'ace0431a', 3025 - 'javelin-behavior-differential-edit-inline-comments' => 'ace0431a', 3026 - 'javelin-behavior-differential-feedback-preview' => 'ace0431a', 3027 - 'javelin-behavior-differential-keyboard-navigation' => 'ace0431a', 3028 - 'javelin-behavior-differential-populate' => 'ace0431a', 3029 - 'javelin-behavior-differential-show-more' => 'ace0431a', 3102 + 'javelin-behavior-buoyant' => '93479628', 3103 + 'javelin-behavior-differential-accept-with-errors' => '93479628', 3104 + 'javelin-behavior-differential-add-reviewers-and-ccs' => '93479628', 3105 + 'javelin-behavior-differential-comment-jump' => '93479628', 3106 + 'javelin-behavior-differential-diff-radios' => '93479628', 3107 + 'javelin-behavior-differential-dropdown-menus' => '93479628', 3108 + 'javelin-behavior-differential-edit-inline-comments' => '93479628', 3109 + 'javelin-behavior-differential-feedback-preview' => '93479628', 3110 + 'javelin-behavior-differential-keyboard-navigation' => '93479628', 3111 + 'javelin-behavior-differential-populate' => '93479628', 3112 + 'javelin-behavior-differential-show-more' => '93479628', 3030 3113 'javelin-behavior-diffusion-commit-graph' => '5e68be89', 3031 3114 'javelin-behavior-diffusion-pull-lastmodified' => '5e68be89', 3032 3115 'javelin-behavior-maniphest-batch-selector' => '7707de41', ··· 3036 3119 'javelin-behavior-maniphest-transaction-preview' => '7707de41', 3037 3120 'javelin-behavior-phabricator-autofocus' => '971b021e', 3038 3121 'javelin-behavior-phabricator-keyboard-shortcuts' => '971b021e', 3039 - 'javelin-behavior-phabricator-object-selector' => 'ace0431a', 3122 + 'javelin-behavior-phabricator-object-selector' => '93479628', 3040 3123 'javelin-behavior-phabricator-oncopy' => '971b021e', 3041 3124 'javelin-behavior-phabricator-tooltips' => '971b021e', 3042 3125 'javelin-behavior-phabricator-watch-anchor' => '971b021e', 3043 3126 'javelin-behavior-refresh-csrf' => '971b021e', 3044 - 'javelin-behavior-repository-crossreference' => 'ace0431a', 3127 + 'javelin-behavior-repository-crossreference' => '93479628', 3045 3128 'javelin-behavior-workflow' => '971b021e', 3046 3129 'javelin-dom' => '6fb20113', 3047 3130 'javelin-event' => '6fb20113', ··· 3062 3145 'javelin-workflow' => '971b021e', 3063 3146 'maniphest-task-summary-css' => '7839ae2d', 3064 3147 'maniphest-transaction-detail-css' => '7839ae2d', 3065 - 'phabricator-app-buttons-css' => '8b65e80d', 3148 + 'phabricator-app-buttons-css' => '04e1ab3e', 3066 3149 'phabricator-content-source-view-css' => '96bc37d6', 3067 - 'phabricator-core-buttons-css' => '8b65e80d', 3068 - 'phabricator-core-css' => '8b65e80d', 3069 - 'phabricator-directory-css' => '8b65e80d', 3070 - 'phabricator-drag-and-drop-file-upload' => 'ace0431a', 3150 + 'phabricator-core-buttons-css' => '04e1ab3e', 3151 + 'phabricator-core-css' => '04e1ab3e', 3152 + 'phabricator-directory-css' => '04e1ab3e', 3153 + 'phabricator-drag-and-drop-file-upload' => '93479628', 3071 3154 'phabricator-dropdown-menu' => '971b021e', 3072 - 'phabricator-flag-css' => '8b65e80d', 3073 - 'phabricator-jump-nav' => '8b65e80d', 3155 + 'phabricator-flag-css' => '04e1ab3e', 3156 + 'phabricator-jump-nav' => '04e1ab3e', 3074 3157 'phabricator-keyboard-shortcut' => '971b021e', 3075 3158 'phabricator-keyboard-shortcut-manager' => '971b021e', 3076 3159 'phabricator-menu-item' => '971b021e', ··· 3078 3161 'phabricator-paste-file-upload' => '971b021e', 3079 3162 'phabricator-prefab' => '971b021e', 3080 3163 'phabricator-project-tag-css' => '7839ae2d', 3081 - 'phabricator-remarkup-css' => '8b65e80d', 3082 - 'phabricator-shaped-request' => 'ace0431a', 3083 - 'phabricator-standard-page-view' => '8b65e80d', 3164 + 'phabricator-remarkup-css' => '04e1ab3e', 3165 + 'phabricator-shaped-request' => '93479628', 3166 + 'phabricator-standard-page-view' => '04e1ab3e', 3084 3167 'phabricator-tooltip' => '971b021e', 3085 - 'phabricator-transaction-view-css' => '8b65e80d', 3086 - 'syntax-highlighting-css' => '8b65e80d', 3168 + 'phabricator-transaction-view-css' => '04e1ab3e', 3169 + 'syntax-highlighting-css' => '04e1ab3e', 3087 3170 ), 3088 3171 ));
+66
src/__phutil_library_map__.php
··· 554 554 'PhabricatorApplicationManiphest' => 'applications/maniphest/application/PhabricatorApplicationManiphest.php', 555 555 'PhabricatorApplicationPeople' => 'applications/people/application/PhabricatorApplicationPeople.php', 556 556 'PhabricatorApplicationPhriction' => 'applications/phriction/application/PhabricatorApplicationPhriction.php', 557 + 'PhabricatorApplicationPonder' => 'applications/ponder/application/PhabricatorApplicationPonder.php', 557 558 'PhabricatorApplicationProject' => 'applications/project/application/PhabricatorApplicationProject.php', 558 559 'PhabricatorApplicationSettings' => 'applications/people/application/PhabricatorApplicationSettings.php', 559 560 'PhabricatorApplicationStatusView' => 'applications/meta/view/PhabricatorApplicationStatusView.php', ··· 990 991 'PhabricatorSearchIndexController' => 'applications/search/controller/PhabricatorSearchIndexController.php', 991 992 'PhabricatorSearchManiphestIndexer' => 'applications/search/index/indexer/PhabricatorSearchManiphestIndexer.php', 992 993 'PhabricatorSearchPhrictionIndexer' => 'applications/search/index/indexer/PhabricatorSearchPhrictionIndexer.php', 994 + 'PhabricatorSearchPonderIndexer' => 'applications/ponder/search/PhabricatorSearchPonderIndexer.php', 993 995 'PhabricatorSearchQuery' => 'applications/search/storage/PhabricatorSearchQuery.php', 994 996 'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php', 995 997 'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php', ··· 1137 1139 'PhrictionEditController' => 'applications/phriction/controller/PhrictionEditController.php', 1138 1140 'PhrictionHistoryController' => 'applications/phriction/controller/PhrictionHistoryController.php', 1139 1141 'PhrictionListController' => 'applications/phriction/controller/PhrictionListController.php', 1142 + 'PonderAddAnswerView' => 'applications/ponder/view/PonderAddAnswerView.php', 1143 + 'PonderAnswer' => 'applications/ponder/storage/PonderAnswer.php', 1144 + 'PonderAnswerEditor' => 'applications/ponder/editor/PonderAnswerEditor.php', 1145 + 'PonderAnswerListView' => 'applications/ponder/view/PonderAnswerListView.php', 1146 + 'PonderAnswerPreviewController' => 'applications/ponder/controller/PonderAnswerPreviewController.php', 1147 + 'PonderAnswerQuery' => 'applications/ponder/query/PonderAnswerQuery.php', 1148 + 'PonderAnswerSaveController' => 'applications/ponder/controller/PonderAnswerSaveController.php', 1149 + 'PonderAnswerSummaryView' => 'applications/ponder/view/PonderAnswerSummaryView.php', 1150 + 'PonderAnswerViewController' => 'applications/ponder/controller/PonderAnswerViewController.php', 1151 + 'PonderCommentBodyView' => 'applications/ponder/view/PonderCommentBodyView.php', 1152 + 'PonderConstants' => 'applications/ponder/PonderConstants.php', 1153 + 'PonderController' => 'applications/ponder/controller/PonderController.php', 1154 + 'PonderDAO' => 'applications/ponder/storage/PonderDAO.php', 1155 + 'PonderFeedController' => 'applications/ponder/controller/PonderFeedController.php', 1156 + 'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php', 1157 + 'PonderQuestionAskController' => 'applications/ponder/controller/PonderQuestionAskController.php', 1158 + 'PonderQuestionDetailView' => 'applications/ponder/view/PonderQuestionDetailView.php', 1159 + 'PonderQuestionFeedView' => 'applications/ponder/view/PonderQuestionFeedView.php', 1160 + 'PonderQuestionPreviewController' => 'applications/ponder/controller/PonderQuestionPreviewController.php', 1161 + 'PonderQuestionQuery' => 'applications/ponder/query/PonderQuestionQuery.php', 1162 + 'PonderQuestionSummaryView' => 'applications/ponder/view/PonderQuestionSummaryView.php', 1163 + 'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php', 1164 + 'PonderRuleQuestion' => 'infrastructure/markup/rule/PonderRuleQuestion.php', 1165 + 'PonderUserProfileView' => 'applications/ponder/view/PonderUserProfileView.php', 1166 + 'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php', 1167 + 'PonderVotableView' => 'applications/ponder/view/PonderVotableView.php', 1168 + 'PonderVoteEditor' => 'applications/ponder/editor/PonderVoteEditor.php', 1169 + 'PonderVoteSaveController' => 'applications/ponder/controller/PonderVoteSaveController.php', 1140 1170 'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php', 1141 1171 ), 1142 1172 'function' => ··· 1641 1671 'PhabricatorApplicationManiphest' => 'PhabricatorApplication', 1642 1672 'PhabricatorApplicationPeople' => 'PhabricatorApplication', 1643 1673 'PhabricatorApplicationPhriction' => 'PhabricatorApplication', 1674 + 'PhabricatorApplicationPonder' => 'PhabricatorApplication', 1644 1675 'PhabricatorApplicationProject' => 'PhabricatorApplication', 1645 1676 'PhabricatorApplicationSettings' => 'PhabricatorApplication', 1646 1677 'PhabricatorApplicationStatusView' => 'AphrontView', ··· 2029 2060 'PhabricatorSearchIndexController' => 'PhabricatorSearchBaseController', 2030 2061 'PhabricatorSearchManiphestIndexer' => 'PhabricatorSearchDocumentIndexer', 2031 2062 'PhabricatorSearchPhrictionIndexer' => 'PhabricatorSearchDocumentIndexer', 2063 + 'PhabricatorSearchPonderIndexer' => 'PhabricatorSearchDocumentIndexer', 2032 2064 'PhabricatorSearchQuery' => 'PhabricatorSearchDAO', 2033 2065 'PhabricatorSearchResultView' => 'AphrontView', 2034 2066 'PhabricatorSearchSelectController' => 'PhabricatorSearchBaseController', ··· 2169 2201 'PhrictionEditController' => 'PhrictionController', 2170 2202 'PhrictionHistoryController' => 'PhrictionController', 2171 2203 'PhrictionListController' => 'PhrictionController', 2204 + 'PonderAddAnswerView' => 'AphrontView', 2205 + 'PonderAnswer' => 2206 + array( 2207 + 0 => 'PonderDAO', 2208 + 1 => 'PhabricatorMarkupInterface', 2209 + 2 => 'PonderVotableInterface', 2210 + ), 2211 + 'PonderAnswerListView' => 'AphrontView', 2212 + 'PonderAnswerPreviewController' => 'PonderController', 2213 + 'PonderAnswerQuery' => 'PhabricatorOffsetPagedQuery', 2214 + 'PonderAnswerSaveController' => 'PonderController', 2215 + 'PonderAnswerSummaryView' => 'AphrontView', 2216 + 'PonderAnswerViewController' => 'PonderController', 2217 + 'PonderCommentBodyView' => 'AphrontView', 2218 + 'PonderController' => 'PhabricatorController', 2219 + 'PonderDAO' => 'PhabricatorLiskDAO', 2220 + 'PonderFeedController' => 'PonderController', 2221 + 'PonderQuestion' => 2222 + array( 2223 + 0 => 'PonderDAO', 2224 + 1 => 'PhabricatorMarkupInterface', 2225 + 2 => 'PonderVotableInterface', 2226 + ), 2227 + 'PonderQuestionAskController' => 'PonderController', 2228 + 'PonderQuestionDetailView' => 'AphrontView', 2229 + 'PonderQuestionFeedView' => 'AphrontView', 2230 + 'PonderQuestionPreviewController' => 'PonderController', 2231 + 'PonderQuestionQuery' => 'PhabricatorOffsetPagedQuery', 2232 + 'PonderQuestionSummaryView' => 'AphrontView', 2233 + 'PonderQuestionViewController' => 'PonderController', 2234 + 'PonderRuleQuestion' => 'PhabricatorRemarkupRuleObjectName', 2235 + 'PonderUserProfileView' => 'AphrontView', 2236 + 'PonderVotableView' => 'AphrontView', 2237 + 'PonderVoteSaveController' => 'PonderController', 2172 2238 'QueryFormattingTestCase' => 'PhabricatorTestCase', 2173 2239 ), 2174 2240 ));
-1
src/aphront/configuration/AphrontDefaultApplicationConfiguration.php
··· 320 320 321 321 '/emailverify/(?P<code>[^/]+)/' => 322 322 'PhabricatorEmailVerificationController', 323 - 324 323 ); 325 324 } 326 325
+2
src/applications/phid/PhabricatorPHIDConstants.php
··· 42 42 const PHID_TYPE_POST = 'POST'; 43 43 const PHID_TYPE_TOBJ = 'TOBJ'; 44 44 const PHID_TYPE_BLOG = 'BLOG'; 45 + const PHID_TYPE_QUES = 'QUES'; 46 + const PHID_TYPE_ANSW = 'ANSW'; 45 47 }
+21
src/applications/phid/handle/PhabricatorObjectHandleData.php
··· 439 439 $handles[$phid] = $handle; 440 440 } 441 441 break; 442 + case PhabricatorPHIDConstants::PHID_TYPE_QUES: 443 + $questions = id(new PonderQuestionQuery()) 444 + ->withPHIDs($phids) 445 + ->execute(); 446 + $questions = mpull($questions, 'getPHID'); 447 + 448 + foreach ($phids as $phid) { 449 + $handle = new PhabricatorObjectHandle(); 450 + $handle->setPHID($phid); 451 + $handle->setType($type); 452 + if (empty($questions[$phid])) { 453 + $handle->setName('Unknown Ponder Question'); 454 + } else { 455 + $question = $questions[$phid]; 456 + $handle->setName(phutil_utf8_shorten($question->getTitle(), 60)); 457 + $handle->setURI(new PhutilURI('Q' . $question->getID())); 458 + $handle->setComplete(true); 459 + } 460 + $handles[$phid] = $handle; 461 + } 462 + break; 442 463 default: 443 464 $loader = null; 444 465 if (isset($external_loaders[$type])) {
+26
src/applications/ponder/PonderConstants.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderConstants { 20 + const UP_VOTE = 1; 21 + const NONE_VOTE = 0; 22 + const DOWN_VOTE = -1; 23 + 24 + const ANSWERED_LITERAL = "answered"; 25 + const ASKED_LITERAL = "asked"; 26 + }
+61
src/applications/ponder/application/PhabricatorApplicationPonder.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PhabricatorApplicationPonder extends PhabricatorApplication { 20 + 21 + public function getBaseURI() { 22 + return '/ponder/'; 23 + } 24 + 25 + public function getShortDescription() { 26 + return 'Find Answers'; 27 + } 28 + 29 + public function getIconURI() { 30 + return celerity_get_resource_uri('/rsrc/image/app/app_ponder.png'); 31 + } 32 + 33 + public function getFactObjectsForAnalysis() { 34 + return array( 35 + new PonderQuestion(), 36 + ); 37 + } 38 + 39 + public function loadStatus(PhabricatorUser $user) { 40 + // replace with "x new unanswered questions" or some such 41 + $status = array(); 42 + 43 + return $status; 44 + } 45 + 46 + public function getroutes() { 47 + return array( 48 + '/Q(?P<id>\d+)' => 'PonderQuestionViewController', 49 + '/ponder/' => array( 50 + '(?P<page>feed/)?' => 'PonderFeedController', 51 + '(?P<page>profile)/' => 'PonderFeedController', 52 + 'answer/add/' => 'PonderAnswerSaveController', 53 + 'answer/preview/' => 'PonderAnswerPreviewController', 54 + 'question/ask/' => 'PonderQuestionAskController', 55 + 'question/preview/' => 'PonderQuestionPreviewController', 56 + '(?P<kind>question)/vote/' => 'PonderVoteSaveController', 57 + '(?P<kind>answer)/vote/' => 'PonderVoteSaveController' 58 + )); 59 + } 60 + } 61 +
+55
src/applications/ponder/controller/PonderAnswerPreviewController.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderAnswerPreviewController 20 + extends PonderController { 21 + 22 + public function processRequest() { 23 + 24 + $request = $this->getRequest(); 25 + $user = $request->getUser(); 26 + $question_id = $request->getInt('question_id'); 27 + 28 + $question = PonderQuestionQuery::loadSingle($user, $question_id); 29 + if (!$question) { 30 + return new Aphront404Response(); 31 + } 32 + 33 + $author_phid = $user->getPHID(); 34 + $object_phids = array($author_phid); 35 + $handles = id(new PhabricatorObjectHandleData($object_phids)) 36 + ->loadHandles(); 37 + 38 + $answer = new PonderAnswer(); 39 + $answer->setContent($request->getStr('content')); 40 + $answer->setAuthorPHID($author_phid); 41 + 42 + $view = new PonderCommentBodyView(); 43 + $view 44 + ->setQuestion($question) 45 + ->setTarget($answer) 46 + ->setPreview(true) 47 + ->setUser($user) 48 + ->setHandles($handles) 49 + ->setAction(PonderConstants::ANSWERED_LITERAL); 50 + 51 + return id(new AphrontAjaxResponse()) 52 + ->setContent($view->render()); 53 + } 54 + 55 + }
+63
src/applications/ponder/controller/PonderAnswerSaveController.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderAnswerSaveController extends PonderController { 20 + 21 + public function processRequest() { 22 + $request = $this->getRequest(); 23 + if (!$request->isFormPost()) { 24 + return new Aphront400Response(); 25 + } 26 + 27 + $user = $request->getUser(); 28 + $question_id = $request->getInt('question_id'); 29 + $question = PonderQuestionQuery::loadSingle($user, $question_id); 30 + 31 + if (!$question) { 32 + return new Aphront404Response(); 33 + } 34 + 35 + $answer = $request->getStr('answer'); 36 + 37 + $content_source = PhabricatorContentSource::newForSource( 38 + PhabricatorContentSource::SOURCE_WEB, 39 + array( 40 + 'ip' => $request->getRemoteAddr(), 41 + )); 42 + 43 + $res = new PonderAnswer(); 44 + $res 45 + ->setContent($answer) 46 + ->setAuthorPHID($user->getPHID()) 47 + ->setVoteCount(0) 48 + ->setQuestionID($question_id) 49 + ->setContentSource($content_source); 50 + 51 + id(new PonderAnswerEditor()) 52 + ->setQuestion($question) 53 + ->setAnswer($res) 54 + ->saveAnswer(); 55 + 56 + PhabricatorSearchPonderIndexer::indexQuestion($question); 57 + 58 + return id(new AphrontRedirectResponse()) 59 + ->setURI(id(new PhutilURI('/Q'. $question->getID())) 60 + ->setFragment('A'.$res->getID())); 61 + } 62 + 63 + }
+41
src/applications/ponder/controller/PonderAnswerViewController.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderAnswerViewController extends PonderController { 20 + 21 + private $answerID; 22 + 23 + public function willProcessRequest(array $data) { 24 + $this->answerID = $data['id']; 25 + } 26 + 27 + public function processRequest() { 28 + $request = $this->getRequest(); 29 + $answer = id(new PonderAnswer())->load($this->answerID); 30 + 31 + if (!$answer) { 32 + return new Aphront404Response(); 33 + } 34 + 35 + $question_id = $answer->getQuestionID(); 36 + 37 + return id(new AphrontRedirectResponse()) 38 + ->setURI('/Q'.$question_id . '#A' . $answer->getID()); 39 + } 40 + 41 + }
+35
src/applications/ponder/controller/PonderController.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + abstract class PonderController extends PhabricatorController { 20 + 21 + public function buildStandardPageResponse($view, array $data) { 22 + 23 + $page = $this->buildStandardPageView(); 24 + $page->setApplicationName('Ponder!'); 25 + $page->setBaseURI('/ponder/'); 26 + $page->setTitle(idx($data, 'title')); 27 + $page->setGlyph("\xE2\x97\xB3"); 28 + $page->appendChild($view); 29 + $page->setSearchDefaultScope(PhabricatorSearchScope::SCOPE_QUESTIONS); 30 + 31 + $response = new AphrontWebpageResponse(); 32 + return $response->setContent($page->render()); 33 + } 34 + 35 + }
+143
src/applications/ponder/controller/PonderFeedController.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderFeedController extends PonderController { 20 + private $page; 21 + private $feedOffset; 22 + private $questionOffset; 23 + private $answerOffset; 24 + 25 + const PAGE_FEED = "feed"; 26 + const PAGE_PROFILE = "profile"; 27 + 28 + const FEED_PAGE_SIZE = 20; 29 + const PROFILE_QUESTION_PAGE_SIZE = 10; 30 + const PROFILE_ANSWER_PAGE_SIZE = 10; 31 + 32 + static $pages = array( 33 + self::PAGE_FEED => "Popular Questions", 34 + self::PAGE_PROFILE => "User Profile" 35 + ); 36 + 37 + public function willProcessRequest(array $data) { 38 + if (isset($data['page'])) { 39 + $this->page = $data['page']; 40 + } 41 + 42 + if (!isset(self::$pages[$this->page])) { 43 + $this->page = self::PAGE_FEED; 44 + } 45 + 46 + $this->feedOffset = 0; 47 + if (isset($data['feedoffset'])) { 48 + $this->feedOffset = $data['feedoffset']; 49 + } 50 + } 51 + 52 + public function processRequest() { 53 + $request = $this->getRequest(); 54 + $user = $request->getUser(); 55 + $this->feedOffset = $request->getInt('off'); 56 + $this->questionOffset = $request->getInt('qoff'); 57 + $this->answerOffset = $request->getInt('aoff'); 58 + 59 + $side_nav = new AphrontSideNavView(); 60 + foreach (self::$pages as $pagename => $pagetitle) { 61 + $class = ""; 62 + if ($pagename == $this->page) { 63 + $class = 'aphront-side-nav-selected'; 64 + } 65 + 66 + $linky = phutil_render_tag( 67 + 'a', 68 + array( 69 + 'href' => '/ponder/'.$pagename .'/', 70 + 'class' => $class 71 + ), 72 + phutil_escape_html($pagetitle) 73 + ); 74 + 75 + $side_nav->addNavItem($linky); 76 + } 77 + 78 + switch ($this->page) { 79 + case self::PAGE_FEED: 80 + $data = PonderQuestionQuery::loadHottest( 81 + $user, 82 + $this->feedOffset, 83 + self::FEED_PAGE_SIZE + 1); 84 + 85 + $phids = array(); 86 + foreach ($data as $question) { 87 + $phids[] = $question->getAuthorPHID(); 88 + } 89 + $handles = id(new PhabricatorObjectHandleData($phids)) 90 + ->loadHandles(); 91 + 92 + $side_nav->appendChild( 93 + id(new PonderQuestionFeedView()) 94 + ->setUser($user) 95 + ->setData($data) 96 + ->setHandles($handles) 97 + ->setOffset($this->feedOffset) 98 + ->setPageSize(self::FEED_PAGE_SIZE) 99 + ->setURI(new PhutilURI("/ponder/feed/"), "off") 100 + ); 101 + break; 102 + case self::PAGE_PROFILE: 103 + $questions = PonderQuestionQuery::loadByAuthor( 104 + $user, 105 + $user->getPHID(), 106 + $this->questionOffset, 107 + self::PROFILE_QUESTION_PAGE_SIZE + 1 108 + ); 109 + 110 + $answers = PonderAnswerQuery::loadByAuthorWithQuestions( 111 + $user, 112 + $user->getPHID(), 113 + $this->answerOffset, 114 + self::PROFILE_ANSWER_PAGE_SIZE + 1 115 + ); 116 + 117 + $phids = array($user->getPHID()); 118 + $handles = id(new PhabricatorObjectHandleData($phids)) 119 + ->loadHandles(); 120 + 121 + $side_nav->appendChild( 122 + id(new PonderUserProfileView()) 123 + ->setUser($user) 124 + ->setQuestions($questions) 125 + ->setAnswers($answers) 126 + ->setHandles($handles) 127 + ->setQuestionOffset($this->questionOffset) 128 + ->setAnswerOffset($this->answerOffset) 129 + ->setPageSize(self::PROFILE_QUESTION_PAGE_SIZE) 130 + ->setURI(new PhutilURI("/ponder/profile/"), "qoff", "aoff") 131 + ); 132 + break; 133 + } 134 + 135 + 136 + return $this->buildStandardPageResponse( 137 + $side_nav, 138 + array( 139 + 'title' => self::$pages[$this->page] 140 + )); 141 + } 142 + 143 + }
+152
src/applications/ponder/controller/PonderQuestionAskController.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderQuestionAskController extends PonderController { 20 + 21 + public function processRequest() { 22 + $request = $this->getRequest(); 23 + $user = $request->getUser(); 24 + 25 + if ($request->isFormPost()) { 26 + return $this->handlePost(); 27 + } 28 + 29 + return $this->showForm(); 30 + } 31 + 32 + private function handlePost() { 33 + $request = $this->getRequest(); 34 + $user = $request->getUser(); 35 + 36 + $errors = array(); 37 + $title = $request->getStr('title'); 38 + $content = $request->getStr('content'); 39 + 40 + // form validation 41 + if (phutil_utf8_strlen($title) < 1 || phutil_utf8_strlen($title) > 255) { 42 + $errors[] = "Please enter a title (1-255 characters)"; 43 + } 44 + 45 + if ($errors) { 46 + return $this->showForm($errors, $title, $content); 47 + } 48 + 49 + // no validation errors -> save it 50 + 51 + $content_source = PhabricatorContentSource::newForSource( 52 + PhabricatorContentSource::SOURCE_WEB, 53 + array( 54 + 'ip' => $request->getRemoteAddr(), 55 + )); 56 + 57 + $question = id(new PonderQuestion()) 58 + ->setTitle($title) 59 + ->setContent($content) 60 + ->setAuthorPHID($user->getPHID()) 61 + ->setContentSource($content_source) 62 + ->setVoteCount(0) 63 + ->setAnswerCount(0) 64 + ->setHeat(0.0) 65 + ->save(); 66 + 67 + PhabricatorSearchPonderIndexer::indexQuestion($question); 68 + 69 + return id(new AphrontRedirectResponse()) 70 + ->setURI('/Q'.$question->getID()); 71 + } 72 + 73 + private function showForm( 74 + $errors = null, 75 + $title = "", 76 + $content = "", 77 + $id = null) { 78 + 79 + require_celerity_resource('ponder-core-view-css'); 80 + require_celerity_resource('phabricator-remarkup-css'); 81 + require_celerity_resource('ponder-post-css'); 82 + 83 + $request = $this->getRequest(); 84 + $user = $request->getUser(); 85 + $error_view = null; 86 + 87 + if ($errors) { 88 + $error_view = id(new AphrontErrorView()) 89 + ->setTitle('Form Errors') 90 + ->setErrors($errors); 91 + } 92 + 93 + $form = new AphrontFormView(); 94 + $form->setUser($user); 95 + $form->setAction('/ponder/question/ask/'); 96 + $form 97 + ->appendChild( 98 + id(new AphrontFormTextControl()) 99 + ->setLabel('Title') 100 + ->setName('title') 101 + ->setValue($title)) 102 + ->appendChild( 103 + id(new AphrontFormTextAreaControl()) 104 + ->setName('content') 105 + ->setID('content') 106 + ->setValue($content) 107 + ->setLabel("Question") 108 + ->setCaption(phutil_render_tag( 109 + 'a', 110 + array( 111 + 'href' => PhabricatorEnv::getDoclink( 112 + 'article/Remarkup_Reference.html'), 113 + 'tabindex' => '-1', 114 + 'target' => '_blank', 115 + ), 116 + "Formatting Reference"))) 117 + ->appendChild( 118 + id(new AphrontFormSubmitControl()) 119 + ->setValue('Ask Away!')); 120 + 121 + $panel = id(new AphrontPanelView()) 122 + ->addClass("ponder-panel") 123 + ->setHeader("Your Question:") 124 + ->appendChild($error_view) 125 + ->appendChild($form); 126 + 127 + $panel->appendChild( 128 + '<div class="aphront-panel-flush">'. 129 + '<div id="question-preview">'. 130 + '<span class="aphront-panel-preview-loading-text">'. 131 + 'Loading question preview...'. 132 + '</span>'. 133 + '</div>'. 134 + '</div>' 135 + ); 136 + 137 + Javelin::initBehavior( 138 + 'ponder-feedback-preview', 139 + array( 140 + 'uri' => '/ponder/question/preview/', 141 + 'content' => 'content', 142 + 'preview' => 'question-preview', 143 + 'question_id' => null 144 + )); 145 + 146 + return $this->buildStandardPageResponse( 147 + array($panel), 148 + array('title' => 'Ask a Question') 149 + ); 150 + } 151 + 152 + }
+52
src/applications/ponder/controller/PonderQuestionPreviewController.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderQuestionPreviewController 20 + extends PonderController { 21 + 22 + const VERB_ASKED = "asked"; 23 + 24 + public function processRequest() { 25 + 26 + $request = $this->getRequest(); 27 + 28 + $user = $request->getUser(); 29 + $author_phid = $user->getPHID(); 30 + 31 + $object_phids = array($author_phid); 32 + $handles = id(new PhabricatorObjectHandleData($object_phids)) 33 + ->loadHandles(); 34 + 35 + $question = new PonderQuestion(); 36 + $question->setContent($request->getStr('content')); 37 + $question->setAuthorPHID($author_phid); 38 + 39 + $view = new PonderCommentBodyView(); 40 + $view 41 + ->setQuestion($question) 42 + ->setTarget($question) 43 + ->setPreview(true) 44 + ->setUser($user) 45 + ->setHandles($handles) 46 + ->setAction(self::VERB_ASKED); 47 + 48 + return id(new AphrontAjaxResponse()) 49 + ->setContent($view->render()); 50 + } 51 + 52 + }
+76
src/applications/ponder/controller/PonderQuestionViewController.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderQuestionViewController extends PonderController { 20 + 21 + private $questionID; 22 + 23 + public function willProcessRequest(array $data) { 24 + $this->questionID = $data['id']; 25 + } 26 + 27 + public function processRequest() { 28 + 29 + $request = $this->getRequest(); 30 + $user = $request->getUser(); 31 + 32 + $question = PonderQuestionQuery::loadSingle($user, $this->questionID); 33 + if (!$question) { 34 + return new Aphront404Response(); 35 + } 36 + $question->attachRelated($user->getPHID()); 37 + $answers = $question->getAnswers(); 38 + 39 + $object_phids = array($user->getPHID(), $question->getAuthorPHID()); 40 + foreach ($answers as $answer) { 41 + $object_phids[] = $answer->getAuthorPHID(); 42 + } 43 + 44 + $handles = id(new PhabricatorObjectHandleData($object_phids)) 45 + ->loadHandles(); 46 + 47 + $detail_panel = new PonderQuestionDetailView(); 48 + $detail_panel 49 + ->setQuestion($question) 50 + ->setUser($user) 51 + ->setHandles($handles); 52 + 53 + $responses_panel = new PonderAnswerListView(); 54 + $responses_panel 55 + ->setQuestion($question) 56 + ->setHandles($handles) 57 + ->setUser($user) 58 + ->setAnswers($answers); 59 + 60 + $answer_add_panel = new PonderAddAnswerView(); 61 + $answer_add_panel 62 + ->setQuestion($question) 63 + ->setUser($user) 64 + ->setActionURI("/ponder/answer/add/"); 65 + 66 + return $this->buildStandardPageResponse( 67 + array( 68 + $detail_panel, 69 + $responses_panel, 70 + $answer_add_panel 71 + ), 72 + array( 73 + 'title' => 'Q'.$question->getID().' '.$question->getTitle() 74 + )); 75 + } 76 + }
+58
src/applications/ponder/controller/PonderVoteSaveController.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderVoteSaveController extends PonderController { 20 + 21 + private $kind; 22 + 23 + public function willProcessRequest(array $data) { 24 + $this->kind = $data['kind']; 25 + } 26 + 27 + public function processRequest() { 28 + $request = $this->getRequest(); 29 + $user = $request->getUser(); 30 + $newvote = $request->getInt("vote"); 31 + $phid = $request->getStr("phid"); 32 + 33 + if (1 < $newvote || $newvote < -1) { 34 + return new Aphront400Response(); 35 + } 36 + 37 + $target = null; 38 + 39 + if ($this->kind == "question") { 40 + $target = PonderQuestionQuery::loadSingleByPHID($user, $phid); 41 + } 42 + else if ($this->kind == "answer") { 43 + $target = PonderAnswerQuery::loadSingleByPHID($user, $phid); 44 + } 45 + 46 + if (!$target) { 47 + return new Aphront404Response(); 48 + } 49 + 50 + $editor = id(new PonderVoteEditor()) 51 + ->setVotable($target) 52 + ->setUser($user) 53 + ->setVote($newvote) 54 + ->saveVote(); 55 + 56 + return id(new AphrontAjaxResponse())->setContent("."); 57 + } 58 + }
+64
src/applications/ponder/editor/PonderAnswerEditor.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + 20 + final class PonderAnswerEditor { 21 + 22 + private $question; 23 + private $answer; 24 + 25 + public function setQuestion($question) { 26 + $this->question = $question; 27 + return $this; 28 + } 29 + 30 + public function setAnswer($answer) { 31 + $this->answer = $answer; 32 + return $this; 33 + } 34 + 35 + public function saveAnswer() { 36 + if (!$this->question) { 37 + throw new Exception("Must set question before saving vote"); 38 + } 39 + if (!$this->answer) { 40 + throw new Exception("Must set answer before saving vote"); 41 + } 42 + 43 + $question = $this->question; 44 + $answer = $this->answer; 45 + $conn = $answer->establishConnection('w'); 46 + $trans = $conn->openTransaction(); 47 + $trans->beginReadLocking(); 48 + 49 + $question->reload(); 50 + 51 + queryfx($conn, 52 + 'UPDATE %T as t 53 + SET t.`answerCount` = t.`answerCount` + 1 54 + WHERE t.`PHID` = %s', 55 + $question->getTableName(), 56 + $question->getPHID()); 57 + 58 + $answer->setQuestionID($question->getID()); 59 + $answer->save(); 60 + 61 + $trans->endReadLocking(); 62 + $trans->saveTransaction(); 63 + } 64 + }
+104
src/applications/ponder/editor/PonderVoteEditor.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + 20 + final class PonderVoteEditor { 21 + 22 + private $answer; 23 + private $votable; 24 + private $anwer; 25 + private $user; 26 + private $vote; 27 + 28 + public function setAnswer($answer) { 29 + $this->answer = $answer; 30 + return $this; 31 + } 32 + 33 + public function setVotable($votable) { 34 + $this->votable = $votable; 35 + return $this; 36 + } 37 + 38 + public function setUser($user) { 39 + $this->user = $user; 40 + return $this; 41 + } 42 + 43 + public function setVote($vote) { 44 + $this->vote = $vote; 45 + return $this; 46 + } 47 + 48 + public function saveVote() { 49 + if (!$this->votable) { 50 + throw new Exception("Must set votable before saving vote"); 51 + } 52 + if (!$this->user) { 53 + throw new Exception("Must set user before saving vote"); 54 + } 55 + 56 + $user = $this->user; 57 + $votable = $this->votable; 58 + $newvote = $this->vote; 59 + 60 + // prepare vote add, or update if this user is amending an 61 + // earlier vote 62 + $editor = id(new PhabricatorEdgeEditor()) 63 + ->setUser($user) 64 + ->addEdge( 65 + $user->getPHID(), 66 + $votable->getUserVoteEdgeType(), 67 + $votable->getVotablePHID(), 68 + array('data' => $newvote)) 69 + ->removeEdge( 70 + $user->getPHID(), 71 + $votable->getUserVoteEdgeType(), 72 + $votable->getVotablePHID()); 73 + 74 + $conn = $votable->establishConnection('w'); 75 + $trans = $conn->openTransaction(); 76 + $trans->beginReadLocking(); 77 + 78 + $votable->reload(); 79 + $curvote = (int)PhabricatorEdgeQuery::loadSingleEdgeData( 80 + $user->getPHID(), 81 + $votable->getUserVoteEdgeType(), 82 + $votable->getVotablePHID()); 83 + 84 + if (!$curvote) { 85 + $curvote = PonderConstants::NONE_VOTE; 86 + } 87 + 88 + // adjust votable's score by this much 89 + $delta = $newvote - $curvote; 90 + 91 + queryfx($conn, 92 + 'UPDATE %T as t 93 + SET t.`voteCount` = t.`voteCount` + %d 94 + WHERE t.`PHID` = %s', 95 + $votable->getTableName(), 96 + $delta, 97 + $votable->getVotablePHID()); 98 + 99 + $editor->save(); 100 + 101 + $trans->endReadLocking(); 102 + $trans->saveTransaction(); 103 + } 104 + }
+161
src/applications/ponder/query/PonderAnswerQuery.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderAnswerQuery extends PhabricatorOffsetPagedQuery { 20 + 21 + private $id; 22 + private $phid; 23 + private $authorPHID; 24 + private $orderNewest; 25 + 26 + public function withID($qid) { 27 + $this->id = $qid; 28 + return $this; 29 + } 30 + 31 + public function withPHID($phid) { 32 + $this->phid = $phid; 33 + return $this; 34 + } 35 + 36 + public function withAuthorPHID($phid) { 37 + $this->authorPHID = $phid; 38 + return $this; 39 + } 40 + 41 + public function orderByNewest($usethis) { 42 + $this->orderNewest = $usethis; 43 + return $this; 44 + } 45 + 46 + public static function loadByAuthorWithQuestions( 47 + $viewer, 48 + $phid, 49 + $offset, 50 + $count) { 51 + 52 + if (!$viewer) { 53 + throw new Exception("Must set viewer when calling loadByAuthor..."); 54 + } 55 + 56 + $answers = id(new PonderAnswerQuery()) 57 + ->withAuthorPHID($phid) 58 + ->orderByNewest(true) 59 + ->setOffset($offset) 60 + ->setLimit($count) 61 + ->execute(); 62 + 63 + $answerset = new LiskDAOSet(); 64 + foreach ($answers as $answer) { 65 + $answerset->addToSet($answer); 66 + } 67 + 68 + foreach ($answers as $answer) { 69 + $question = $answer->loadOneRelative( 70 + new PonderQuestion(), 71 + 'id', 72 + 'getQuestionID'); 73 + $answer->setQuestion($question); 74 + } 75 + 76 + return $answers; 77 + } 78 + 79 + public static function loadByAuthor($viewer, $author_phid, $offset, $count) { 80 + if (!$viewer) { 81 + throw new Exception("Must set viewer when calling loadByAuthor"); 82 + } 83 + 84 + return id(new PonderAnswerQuery()) 85 + ->withAuthorPHID($author_phid) 86 + ->setOffset($offset) 87 + ->setLimit($count) 88 + ->orderByNewest(true) 89 + ->execute(); 90 + } 91 + 92 + public static function loadSingle($viewer, $id) { 93 + if (!$viewer) { 94 + throw new Exception("Must set viewer when calling loadSingle"); 95 + } 96 + return idx(id(new PonderAnswerQuery()) 97 + ->withID($id) 98 + ->execute(), $id); 99 + } 100 + 101 + public static function loadSingleByPHID($viewer, $phid) { 102 + if (!$viewer) { 103 + throw new Exception("Must set viewer when calling loadSingle"); 104 + } 105 + 106 + return array_shift(id(new PonderAnswerQuery()) 107 + ->withPHID($phid) 108 + ->execute()); 109 + } 110 + 111 + private function buildWhereClause($conn_r) { 112 + $where = array(); 113 + if ($this->id) { 114 + $where[] = qsprintf($conn_r, '(id = %d)', $this->id); 115 + } 116 + if ($this->phid) { 117 + $where[] = qsprintf($conn_r, '(phid = %s)', $this->phid); 118 + } 119 + if ($this->authorPHID) { 120 + $where[] = qsprintf($conn_r, '(authorPHID = %s)', $this->authorPHID); 121 + } 122 + 123 + return $this->formatWhereClause($where); 124 + } 125 + 126 + private function buildOrderByClause($conn_r) { 127 + $order = array(); 128 + if ($this->orderNewest) { 129 + $order[] = qsprintf($conn_r, 'id DESC'); 130 + } 131 + 132 + if (count($order) == 0) { 133 + $order[] = qsprintf($conn_r, 'id ASC'); 134 + } 135 + 136 + return ($order ? 'ORDER BY ' . implode(', ', $order) : ''); 137 + } 138 + 139 + public function execute() { 140 + $answer = new PonderAnswer(); 141 + $conn_r = $answer->establishConnection('r'); 142 + 143 + $select = qsprintf( 144 + $conn_r, 145 + 'SELECT r.* FROM %T r', 146 + $answer->getTableName()); 147 + 148 + $where = $this->buildWhereClause($conn_r); 149 + $order_by = $this->buildOrderByClause($conn_r); 150 + $limit = $this->buildLimitClause($conn_r); 151 + 152 + return $answer->loadAllFromArray( 153 + queryfx_all( 154 + $conn_r, 155 + '%Q %Q %Q %Q', 156 + $select, 157 + $where, 158 + $order_by, 159 + $limit)); 160 + } 161 + }
+157
src/applications/ponder/query/PonderQuestionQuery.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderQuestionQuery extends PhabricatorOffsetPagedQuery { 20 + 21 + private $id; 22 + private $phids; 23 + private $authorPHID; 24 + private $orderHottest; 25 + private $orderNewest; 26 + 27 + public function withID($qid) { 28 + $this->id = $qid; 29 + return $this; 30 + } 31 + 32 + public function withPHID($phid) { 33 + $this->phids = array($phid); 34 + return $this; 35 + } 36 + 37 + public function withPHIDs($phids) { 38 + $this->phids = $phids; 39 + return $this; 40 + } 41 + 42 + public function withAuthorPHID($phid) { 43 + $this->authorPHID = $phid; 44 + return $this; 45 + } 46 + 47 + public function orderByHottest($usethis) { 48 + $this->orderHottest = $usethis; 49 + return $this; 50 + } 51 + 52 + public function orderByNewest($usethis) { 53 + $this->orderNewest = $usethis; 54 + return $this; 55 + } 56 + 57 + public static function loadHottest($viewer, $offset, $count) { 58 + if (!$viewer) { 59 + throw new Exception("Must set viewer when calling loadHottest"); 60 + } 61 + 62 + return id(new PonderQuestionQuery()) 63 + ->setOffset($offset) 64 + ->setLimit($count) 65 + ->orderByHottest(true) 66 + ->execute(); 67 + } 68 + 69 + public static function loadByAuthor($viewer, $author_phid, $offset, $count) { 70 + if (!$viewer) { 71 + throw new Exception("Must set viewer when calling loadByAuthor"); 72 + } 73 + 74 + return id(new PonderQuestionQuery()) 75 + ->withAuthorPHID($author_phid) 76 + ->setOffset($offset) 77 + ->setLimit($count) 78 + ->orderByNewest(true) 79 + ->execute(); 80 + } 81 + 82 + public static function loadSingle($viewer, $id) { 83 + if (!$viewer) { 84 + throw new Exception("Must set viewer when calling loadSingle"); 85 + } 86 + 87 + return idx(id(new PonderQuestionQuery()) 88 + ->withID($id) 89 + ->execute(), $id); 90 + } 91 + 92 + public static function loadSingleByPHID($viewer, $phid) { 93 + if (!$viewer) { 94 + throw new Exception("Must set viewer when calling loadSingle"); 95 + } 96 + 97 + return array_shift(id(new PonderQuestionQuery()) 98 + ->withPHID($phid) 99 + ->execute()); 100 + } 101 + 102 + private function buildWhereClause($conn_r) { 103 + $where = array(); 104 + if ($this->id) { 105 + $where[] = qsprintf($conn_r, '(id = %d)', $this->id); 106 + } 107 + if ($this->phids) { 108 + $where[] = qsprintf($conn_r, '(phid in (%Ls))', $this->phids); 109 + } 110 + if ($this->authorPHID) { 111 + $where[] = qsprintf($conn_r, '(authorPHID = %s)', $this->authorPHID); 112 + } 113 + 114 + return ($where ? 'WHERE ' . implode(' AND ', $where) : ''); 115 + } 116 + 117 + private function buildOrderByClause($conn_r) { 118 + $order = array(); 119 + if ($this->orderHottest) { 120 + $order[] = qsprintf($conn_r, 'heat DESC'); 121 + } 122 + if ($this->orderNewest) { 123 + $order[] = qsprintf($conn_r, 'id DESC'); 124 + } 125 + 126 + if (count($order) == 0) { 127 + $order[] = qsprintf($conn_r, 'id ASC'); 128 + } 129 + 130 + return ($order ? 'ORDER BY ' . implode(', ', $order) : ''); 131 + } 132 + 133 + public function execute() { 134 + $question = new PonderQuestion(); 135 + $conn_r = $question->establishConnection('r'); 136 + 137 + $select = qsprintf( 138 + $conn_r, 139 + 'SELECT r.* FROM %T r', 140 + $question->getTableName()); 141 + 142 + $where = $this->buildWhereClause($conn_r); 143 + $order_by = $this->buildOrderByClause($conn_r); 144 + $limit = $this->buildLimitClause($conn_r); 145 + 146 + return $question->loadAllFromArray( 147 + queryfx_all( 148 + $conn_r, 149 + '%Q %Q %Q %Q', 150 + $select, 151 + $where, 152 + $order_by, 153 + $limit)); 154 + } 155 + 156 + 157 + }
+56
src/applications/ponder/search/PhabricatorSearchPonderIndexer.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PhabricatorSearchPonderIndexer 20 + extends PhabricatorSearchDocumentIndexer { 21 + 22 + public static function indexQuestion(PonderQuestion $question) { 23 + 24 + $doc = new PhabricatorSearchAbstractDocument(); 25 + $doc->setPHID($question->getPHID()); 26 + $doc->setDocumentType(PhabricatorPHIDConstants::PHID_TYPE_QUES); 27 + $doc->setDocumentTitle($question->getTitle()); 28 + $doc->setDocumentCreated($question->getDateCreated()); 29 + $doc->setDocumentModified($question->getDateModified()); 30 + 31 + $doc->addField( 32 + PhabricatorSearchField::FIELD_BODY, 33 + $question->getContent()); 34 + 35 + $doc->addRelationship( 36 + PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR, 37 + $question->getAuthorPHID(), 38 + PhabricatorPHIDConstants::PHID_TYPE_USER, 39 + $question->getDateCreated()); 40 + 41 + $answers = $question->getAnswers(); 42 + $touches = array(); 43 + foreach ($answers as $comment) { 44 + if (strlen($comment->getContent())) { 45 + $doc->addField( 46 + PhabricatorSearchField::FIELD_COMMENT, 47 + $comment->getContent()); 48 + } 49 + 50 + $author = $comment->getAuthorPHID(); 51 + $touches[$author] = $comment->getDateCreated(); 52 + } 53 + 54 + self::reindexAbstractDocument($doc); 55 + } 56 + }
+127
src/applications/ponder/storage/PonderAnswer.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderAnswer extends PonderDAO 20 + implements PhabricatorMarkupInterface, PonderVotableInterface { 21 + 22 + const MARKUP_FIELD_CONTENT = 'markup:content'; 23 + 24 + protected $phid; 25 + protected $authorPHID; 26 + protected $questionID; 27 + 28 + protected $content; 29 + protected $contentSource; 30 + 31 + protected $voteCount; 32 + private $vote; 33 + private $question = null; 34 + 35 + public function setQuestion($question) { 36 + $this->question = $question; 37 + return $this; 38 + } 39 + 40 + public function getQuestion() { 41 + return $this->question; 42 + } 43 + 44 + public function setUserVote($vote) { 45 + $this->vote = $vote['data']; 46 + if (!$this->vote) { 47 + $this->vote = PonderConstants::NONE_VOTE; 48 + } 49 + return $this; 50 + } 51 + 52 + public function getUserVote() { 53 + return $this->vote; 54 + } 55 + 56 + public function getConfiguration() { 57 + return array( 58 + self::CONFIG_AUX_PHID => true, 59 + ) + parent::getConfiguration(); 60 + } 61 + 62 + public function setTitle($title) { 63 + $this->title = $title; 64 + if (!$this->getID()) { 65 + $this->originalTitle = $title; 66 + } 67 + return $this; 68 + } 69 + 70 + public function generatePHID() { 71 + return PhabricatorPHID::generateNewPHID( 72 + PhabricatorPHIDConstants::PHID_TYPE_ANSW); 73 + } 74 + 75 + public function setContentSource(PhabricatorContentSource $content_source) { 76 + $this->contentSource = $content_source->serialize(); 77 + return $this; 78 + } 79 + 80 + public function getContentSource() { 81 + return PhabricatorContentSource::newFromSerialized($this->contentSource); 82 + } 83 + 84 + public function getAnswers() { 85 + return $this->loadRelatives(new PonderAnswer(), "questionID"); 86 + } 87 + 88 + public function getMarkupField() { 89 + return self::MARKUP_FIELD_CONTENT; 90 + } 91 + 92 + // Markup interface 93 + 94 + public function getMarkupFieldKey($field) { 95 + $hash = PhabricatorHash::digest($this->getMarkupText($field)); 96 + $id = $this->getID(); 97 + return "ponder:A{$id}:{$field}:{$hash}"; 98 + } 99 + 100 + public function getMarkupText($field) { 101 + return $this->getContent(); 102 + } 103 + 104 + public function newMarkupEngine($field) { 105 + return PhabricatorMarkupEngine::newPonderMarkupEngine(); 106 + } 107 + 108 + public function didMarkupText( 109 + $field, 110 + $output, 111 + PhutilMarkupEngine $engine) { 112 + return $output; 113 + } 114 + 115 + public function shouldUseMarkupCache($field) { 116 + return (bool)$this->getID(); 117 + } 118 + 119 + // votable interface 120 + public function getUserVoteEdgeType() { 121 + return PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_ANSWER; 122 + } 123 + 124 + public function getVotablePHID() { 125 + return $this->getPHID(); 126 + } 127 + }
+25
src/applications/ponder/storage/PonderDAO.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + abstract class PonderDAO extends PhabricatorLiskDAO { 20 + 21 + public function getApplicationName() { 22 + return 'ponder'; 23 + } 24 + 25 + }
+141
src/applications/ponder/storage/PonderQuestion.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderQuestion extends PonderDAO 20 + implements PhabricatorMarkupInterface, PonderVotableInterface { 21 + 22 + const MARKUP_FIELD_CONTENT = 'markup:content'; 23 + 24 + protected $title; 25 + protected $phid; 26 + 27 + protected $authorPHID; 28 + protected $content; 29 + protected $contentSource; 30 + 31 + protected $voteCount; 32 + protected $answerCount; 33 + protected $heat; 34 + 35 + private $answers; 36 + private $vote; 37 + 38 + public function getConfiguration() { 39 + return array( 40 + self::CONFIG_AUX_PHID => true, 41 + ) + parent::getConfiguration(); 42 + } 43 + 44 + public function generatePHID() { 45 + return PhabricatorPHID::generateNewPHID( 46 + PhabricatorPHIDConstants::PHID_TYPE_QUES); 47 + } 48 + 49 + public function setContentSource(PhabricatorContentSource $content_source) { 50 + $this->contentSource = $content_source->serialize(); 51 + return $this; 52 + } 53 + 54 + public function getContentSource() { 55 + return PhabricatorContentSource::newFromSerialized($this->contentSource); 56 + } 57 + 58 + public function attachRelated($user_phid) { 59 + $this->answers = $this->loadRelatives(new PonderAnswer(), "questionID"); 60 + $qa_phids = mpull($this->answers, 'getPHID') + array($this->getPHID()); 61 + 62 + $edges = id(new PhabricatorEdgeQuery()) 63 + ->withSourcePHIDs(array($user_phid)) 64 + ->withDestinationPHIDs($qa_phids) 65 + ->withEdgeTypes( 66 + array( 67 + PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_QUESTION, 68 + PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_ANSWER 69 + )) 70 + ->needEdgeData(true) 71 + ->execute(); 72 + 73 + $question_edge = 74 + $edges[$user_phid][PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_QUESTION]; 75 + $answer_edges = 76 + $edges[$user_phid][PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_ANSWER]; 77 + $edges = null; 78 + 79 + $this->setUserVote(idx($question_edge, $this->getPHID())); 80 + foreach ($this->answers as $answer) { 81 + $answer->setQuestion($this); 82 + $answer->setUserVote(idx($answer_edges, $answer->getPHID())); 83 + } 84 + } 85 + 86 + public function setUserVote($vote) { 87 + $this->vote = $vote['data']; 88 + if (!$this->vote) { 89 + $this->vote = PonderConstants::NONE_VOTE; 90 + } 91 + return $this; 92 + } 93 + 94 + public function getUserVote() { 95 + return $this->vote; 96 + } 97 + 98 + public function getAnswers() { 99 + return $this->answers; 100 + } 101 + 102 + public function getMarkupField() { 103 + return self::MARKUP_FIELD_CONTENT; 104 + } 105 + 106 + // Markup interface 107 + 108 + public function getMarkupFieldKey($field) { 109 + $hash = PhabricatorHash::digest($this->getMarkupText($field)); 110 + $id = $this->getID(); 111 + return "ponder:Q{$id}:{$field}:{$hash}"; 112 + } 113 + 114 + public function getMarkupText($field) { 115 + return $this->getContent(); 116 + } 117 + 118 + public function newMarkupEngine($field) { 119 + return PhabricatorMarkupEngine::newPonderMarkupEngine(); 120 + } 121 + 122 + public function didMarkupText( 123 + $field, 124 + $output, 125 + PhutilMarkupEngine $engine) { 126 + return $output; 127 + } 128 + 129 + public function shouldUseMarkupCache($field) { 130 + return (bool)$this->getID(); 131 + } 132 + 133 + // votable interface 134 + public function getUserVoteEdgeType() { 135 + return PhabricatorEdgeConfig::TYPE_VOTING_USER_HAS_QUESTION; 136 + } 137 + 138 + public function getVotablePHID() { 139 + return $this->getPHID(); 140 + } 141 + }
+24
src/applications/ponder/storage/PonderVotableInterface.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + interface PonderVotableInterface { 20 + 21 + public function getUserVoteEdgeType(); 22 + public function getVotablePHID(); 23 + 24 + }
+97
src/applications/ponder/view/PonderAddAnswerView.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderAddAnswerView extends AphrontView { 20 + 21 + private $question; 22 + private $user; 23 + private $actionURI; 24 + private $draft; 25 + 26 + public function setQuestion($question) { 27 + $this->question = $question; 28 + return $this; 29 + } 30 + 31 + public function setUser(PhabricatorUser $user) { 32 + $this->user = $user; 33 + return $this; 34 + } 35 + 36 + public function setActionURI($uri) { 37 + $this->actionURI = $uri; 38 + return $this; 39 + } 40 + 41 + public function render() { 42 + require_celerity_resource('ponder-core-view-css'); 43 + 44 + $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); 45 + 46 + $question = $this->question; 47 + 48 + $panel = id(new AphrontPanelView()) 49 + ->addClass("ponder-panel") 50 + ->setHeader("Your Answer:"); 51 + 52 + $form = new AphrontFormView(); 53 + $form 54 + ->setUser($this->user) 55 + ->setAction($this->actionURI) 56 + ->addHiddenInput('question_id', $question->getID()) 57 + ->appendChild( 58 + id(new AphrontFormTextAreaControl()) 59 + ->setName('answer') 60 + ->setID('answer-content') 61 + ->setEnableDragAndDropFileUploads(true) 62 + ->setCaption(phutil_render_tag( 63 + 'a', 64 + array( 65 + 'href' => PhabricatorEnv::getDoclink( 66 + 'article/Remarkup_Reference.html'), 67 + 'tabindex' => '-1', 68 + 'target' => '_blank', 69 + ), 70 + 'Formatting Reference'))) 71 + ->appendChild( 72 + id(new AphrontFormSubmitControl()) 73 + ->setValue($is_serious ? 'Submit' : 'Make it so.')); 74 + 75 + $panel->appendChild($form); 76 + $panel->appendChild( 77 + '<div class="aphront-panel-flush">'. 78 + '<div id="answer-preview">'. 79 + '<span class="aphront-panel-preview-loading-text">'. 80 + 'Loading answer preview...'. 81 + '</span>'. 82 + '</div>'. 83 + '</div>' 84 + ); 85 + 86 + Javelin::initBehavior( 87 + 'ponder-feedback-preview', 88 + array( 89 + 'uri' => '/ponder/answer/preview/', 90 + 'content' => 'answer-content', 91 + 'preview' => 'answer-preview', 92 + 'question_id' => $question->getID() 93 + )); 94 + 95 + return $panel->render(); 96 + } 97 + }
+89
src/applications/ponder/view/PonderAnswerListView.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderAnswerListView extends AphrontView { 20 + 21 + private $question; 22 + private $handles; 23 + private $user; 24 + private $answers; 25 + 26 + public function setQuestion($question) { 27 + $this->question = $question; 28 + return $this; 29 + } 30 + 31 + public function setHandles(array $handles) { 32 + assert_instances_of($handles, 'PhabricatorObjectHandle'); 33 + $this->handles = $handles; 34 + return $this; 35 + } 36 + 37 + public function setUser(PhabricatorUser $user) { 38 + $this->user = $user; 39 + return $this; 40 + } 41 + 42 + public function setAnswers(array $answers) { 43 + assert_instances_of($answers, 'PonderAnswer'); 44 + 45 + $this->answers = array(); 46 + 47 + // group by descreasing score, randomizing 48 + // order within groups 49 + $by_score = mgroup($answers, 'getVoteCount'); 50 + $scores = array_keys($by_score); 51 + rsort($scores); 52 + 53 + foreach ($scores as $score) { 54 + $group = $by_score[$score]; 55 + shuffle($group); 56 + foreach ($group as $cur_answer) { 57 + $this->answers[] = $cur_answer; 58 + } 59 + } 60 + 61 + return $this; 62 + } 63 + 64 + public function render() { 65 + require_celerity_resource('ponder-post-css'); 66 + 67 + $question = $this->question; 68 + $user = $this->user; 69 + $handles = $this->handles; 70 + 71 + $panel = id(new AphrontPanelView()) 72 + ->addClass("ponder-panel") 73 + ->setHeader("Responses:"); 74 + 75 + foreach ($this->answers as $cur_answer) { 76 + $view = new PonderCommentBodyView(); 77 + $view 78 + ->setQuestion($question) 79 + ->setTarget($cur_answer) 80 + ->setAction(PonderConstants::ANSWERED_LITERAL) 81 + ->setHandles($handles) 82 + ->setUser($user); 83 + 84 + $panel->appendChild($view); 85 + } 86 + 87 + return $panel->render(); 88 + } 89 + }
+93
src/applications/ponder/view/PonderAnswerSummaryView.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderAnswerSummaryView extends AphrontView { 20 + private $user; 21 + private $answer; 22 + private $handles; 23 + 24 + public function setAnswer($answer) { 25 + $this->answer = $answer; 26 + return $this; 27 + } 28 + 29 + public function setHandles($handles) { 30 + $this->handles = $handles; 31 + return $this; 32 + } 33 + 34 + public function setUser(PhabricatorUser $user) { 35 + $this->user = $user; 36 + return $this; 37 + } 38 + 39 + private static function abbreviate($w) { 40 + return phutil_utf8_shorten($w, 60); 41 + } 42 + 43 + public function render() { 44 + require_celerity_resource('ponder-feed-view-css'); 45 + 46 + $user = $this->user; 47 + $answer = $this->answer; 48 + $question = $answer->getQuestion(); 49 + $author_phid = $question->getAuthorPHID(); 50 + $handles = $this->handles; 51 + 52 + $votecount = 53 + '<div class="ponder-summary-votes">'. 54 + phutil_escape_html($answer->getVoteCount()). 55 + '<div class="ponder-question-label">'. 56 + 'votes'. 57 + '</div>'. 58 + '</div>'; 59 + 60 + $title = 61 + '<h2 class="ponder-question-title">'. 62 + phutil_render_tag( 63 + 'a', 64 + array( 65 + "href" => id(new PhutilURI('/Q' . $question->getID())) 66 + ->setFragment('A' . $answer->getID()) 67 + ), 68 + phutil_escape_html('A' . $answer->getID() . ' ' . 69 + self::abbreviate($answer->getContent()) 70 + ) 71 + ). 72 + '</h2>'; 73 + 74 + $rhs = 75 + '<div class="ponder-metadata">'. 76 + $title. 77 + '<span class="ponder-small-metadata">'. 78 + phutil_escape_html( 79 + 'answer to "'. self::abbreviate($question->getTitle()). '" on ' . 80 + phabricator_datetime($answer->getDateCreated(), $user) 81 + ). 82 + '</span>'. 83 + '</div>'; 84 + 85 + $summary = 86 + '<div class="ponder-answer-summary">'. 87 + $votecount. 88 + $rhs. 89 + '</div>'; 90 + 91 + return $summary; 92 + } 93 + }
+149
src/applications/ponder/view/PonderCommentBodyView.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderCommentBodyView extends AphrontView { 20 + 21 + private $target; 22 + private $question; 23 + private $handles; 24 + private $preview; 25 + private $anchorName; 26 + private $user; 27 + private $action; 28 + 29 + public function setQuestion($question) { 30 + $this->question = $question; 31 + return $this; 32 + } 33 + 34 + public function setTarget($target) { 35 + $this->target = $target; 36 + return $this; 37 + } 38 + 39 + public function setAction($action) { 40 + $this->action = $action; 41 + return $this; 42 + } 43 + 44 + public function setHandles(array $handles) { 45 + assert_instances_of($handles, 'PhabricatorObjectHandle'); 46 + $this->handles = $handles; 47 + return $this; 48 + } 49 + 50 + public function setPreview($preview) { 51 + $this->preview = $preview; 52 + return $this; 53 + } 54 + 55 + public function setUser(PhabricatorUser $user) { 56 + $this->user = $user; 57 + return $this; 58 + } 59 + 60 + public function render() { 61 + 62 + if (!$this->user) { 63 + throw new Exception("Call setUser() before rendering!"); 64 + } 65 + 66 + require_celerity_resource('phabricator-remarkup-css'); 67 + require_celerity_resource('ponder-post-css'); 68 + 69 + $user = $this->user; 70 + $question = $this->question; 71 + $target = $this->target; 72 + $content = $target->getContent(); 73 + $info = array(); 74 + 75 + 76 + $content = PhabricatorMarkupEngine::renderOneObject( 77 + $target, 78 + $target->getMarkupField()); 79 + 80 + $content = 81 + '<div class="phabricator-remarkup">'. 82 + $content. 83 + '</div>'; 84 + 85 + $author = $this->handles[$target->getAuthorPHID()]; 86 + $actions = array($author->renderLink().' '.$this->action); 87 + $author_link = $author->renderLink(); 88 + $xaction_view = id(new PhabricatorTransactionView()) 89 + ->setUser($user) 90 + ->setImageURI($author->getImageURI()) 91 + ->setContentSource($target->getContentSource()) 92 + ->setActions($actions); 93 + 94 + if ($this->target instanceof PonderAnswer) { 95 + $xaction_view->addClass("ponder-answer"); 96 + } 97 + else { 98 + $xaction_view->addClass("ponder-question"); 99 + } 100 + 101 + if ($this->preview) { 102 + $xaction_view->setIsPreview($this->preview); 103 + } else { 104 + $xaction_view->setEpoch($target->getDateCreated()); 105 + if ($this->target instanceof PonderAnswer) { 106 + $anchor_text = 'Q' . $question->getID(). '#A' . $target->getID(); 107 + $xaction_view->setAnchor('A'.$target->getID(), $anchor_text); 108 + $xaction_view->addClass("ponder-answer"); 109 + } 110 + } 111 + 112 + $xaction_view->appendChild( 113 + '<div class="ponder-post-core">'. 114 + $content. 115 + '</div>' 116 + ); 117 + 118 + $outerview = $xaction_view; 119 + if (!$this->preview) { 120 + $outerview = 121 + id(new PonderVotableView()) 122 + ->setPHID($target->getPHID()) 123 + ->setCount($target->getVoteCount()) 124 + ->setVote($target->getUserVote()); 125 + 126 + if ($this->target instanceof PonderAnswer) { 127 + $outerview->setURI('/ponder/answer/vote/'); 128 + } 129 + else { 130 + $outerview->setURI('/ponder/question/vote/'); 131 + } 132 + 133 + $outerview->appendChild($xaction_view); 134 + } 135 + 136 + return $outerview->render(); 137 + } 138 + 139 + private function renderHandleList(array $phids) { 140 + $result = array(); 141 + foreach ($phids as $phid) { 142 + $result[] = $this->handles[$phid]->renderLink(); 143 + } 144 + return implode(', ', $result); 145 + } 146 + 147 + 148 + 149 + }
+72
src/applications/ponder/view/PonderQuestionDetailView.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderQuestionDetailView extends AphrontView { 20 + 21 + private $question; 22 + private $user; 23 + private $handles; 24 + 25 + public function setQuestion($question) { 26 + $this->question = $question; 27 + return $this; 28 + } 29 + 30 + public function setUser($user) { 31 + $this->user = $user; 32 + return $this; 33 + } 34 + 35 + public function setHandles($handles) { 36 + $this->handles = $handles; 37 + return $this; 38 + } 39 + 40 + public function render() { 41 + require_celerity_resource('ponder-core-view-css'); 42 + 43 + $question = $this->question; 44 + $handles = $this->handles; 45 + $user = $this->user; 46 + 47 + $panel = id(new AphrontPanelView()) 48 + ->addClass("ponder-panel") 49 + ->setHeader($this->renderObjectLink().' '.$question->getTitle()); 50 + 51 + $contentview = new PonderCommentBodyView(); 52 + $contentview 53 + ->setTarget($question) 54 + ->setQuestion($question) 55 + ->setUser($user) 56 + ->setHandles($handles) 57 + ->setAction(PonderConstants::ASKED_LITERAL); 58 + 59 + $panel->appendChild($contentview); 60 + 61 + return $panel->render(); 62 + } 63 + 64 + private function renderObjectLink() { 65 + return phutil_render_tag( 66 + 'a', 67 + array('href' => '/Q' . $this->question->getID()), 68 + "Q". phutil_escape_html($this->question->getID()) 69 + ); 70 + } 71 + 72 + }
+102
src/applications/ponder/view/PonderQuestionFeedView.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderQuestionFeedView extends AphrontView { 20 + private $user; 21 + private $offset; 22 + private $data; 23 + private $pagesize; 24 + private $uri; 25 + private $param; 26 + 27 + public function setUser(PhabricatorUser $user) { 28 + $this->user = $user; 29 + return $this; 30 + } 31 + 32 + public function setOffset($offset) { 33 + $this->offset = $offset; 34 + return $this; 35 + } 36 + 37 + public function setData($data) { 38 + $this->data = $data; 39 + return $this; 40 + } 41 + 42 + public function setPageSize($pagesize) { 43 + $this->pagesize = $pagesize; 44 + return $this; 45 + } 46 + 47 + public function setHandles($handles) { 48 + $this->handles = $handles; 49 + return $this; 50 + } 51 + 52 + public function setURI($uri, $param) { 53 + $this->uri = $uri; 54 + $this->param = $param; 55 + return $this; 56 + } 57 + 58 + public function render() { 59 + require_celerity_resource('ponder-core-view-css'); 60 + require_celerity_resource('ponder-feed-view-css'); 61 + 62 + $user = $this->user; 63 + $offset = $this->offset; 64 + $data = $this->data; 65 + $handles = $this->handles; 66 + $uri = $this->uri; 67 + $param = $this->param; 68 + $pagesize = $this->pagesize; 69 + 70 + $panel = id(new AphrontPanelView()) 71 + ->setHeader("Popular Questions") 72 + ->addClass("ponder-panel"); 73 + 74 + $panel->addButton( 75 + phutil_render_tag( 76 + 'a', 77 + array( 78 + 'href' => "/ponder/question/ask/", 79 + 'class' => 'green button', 80 + ), 81 + "Ask a question")); 82 + 83 + $pagebuttons = id(new AphrontPagerView()) 84 + ->setPageSize($pagesize) 85 + ->setOffset($offset) 86 + ->setURI($uri, $param); 87 + 88 + $data = $pagebuttons->sliceResults($data); 89 + 90 + 91 + foreach ($data as $question) { 92 + $cur = id(new PonderQuestionSummaryView()) 93 + ->setUser($user) 94 + ->setQuestion($question) 95 + ->setHandles($handles); 96 + $panel->appendChild($cur); 97 + } 98 + 99 + $panel->appendChild($pagebuttons); 100 + return $panel->render(); 101 + } 102 + }
+105
src/applications/ponder/view/PonderQuestionSummaryView.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderQuestionSummaryView extends AphrontView { 20 + private $user; 21 + private $question; 22 + private $handles; 23 + 24 + public function setQuestion($question) { 25 + $this->question = $question; 26 + return $this; 27 + } 28 + 29 + public function setHandles($handles) { 30 + $this->handles = $handles; 31 + return $this; 32 + } 33 + 34 + public function setUser(PhabricatorUser $user) { 35 + $this->user = $user; 36 + return $this; 37 + } 38 + 39 + public function render() { 40 + require_celerity_resource('ponder-feed-view-css'); 41 + 42 + $user = $this->user; 43 + $question = $this->question; 44 + $author_phid = $question->getAuthorPHID(); 45 + $handles = $this->handles; 46 + 47 + $authorlink = $handles[$author_phid] 48 + ->renderLink(); 49 + 50 + $votecount = 51 + '<div class="ponder-summary-votes">'. 52 + phutil_escape_html($question->getVoteCount()). 53 + '<div class="ponder-question-label">'. 54 + 'votes'. 55 + '</div>'. 56 + '</div>'; 57 + 58 + $answerclass = "ponder-summary-answers"; 59 + if ($question->getAnswercount() == 0) { 60 + $answerclass .= " ponder-not-answered"; 61 + } 62 + $answercount = 63 + '<div class="ponder-summary-answers">'. 64 + phutil_escape_html($question->getAnswerCount()). 65 + '<div class="ponder-question-label">'. 66 + 'answers'. 67 + '</div>'. 68 + '</div>'; 69 + 70 + 71 + $title = 72 + '<h2 class="ponder-question-title">'. 73 + phutil_render_tag( 74 + 'a', 75 + array( 76 + "href" => '/Q' . $question->getID(), 77 + ), 78 + phutil_escape_html( 79 + 'Q' . $question->getID() . 80 + ' ' . $question->getTitle() 81 + ) 82 + ) . 83 + '</h2>'; 84 + 85 + $rhs = 86 + '<div class="ponder-metadata">'. 87 + $title. 88 + '<span class="ponder-small-metadata">'. 89 + 'asked on '. 90 + phabricator_datetime($question->getDateCreated(), $user). 91 + ' by ' . $authorlink. 92 + '</span>'. 93 + '</div>'; 94 + 95 + $summary = 96 + '<div class="ponder-question-summary">'. 97 + $votecount. 98 + $answercount. 99 + $rhs. 100 + '</div>'; 101 + 102 + 103 + return $summary; 104 + } 105 + }
+159
src/applications/ponder/view/PonderUserProfileView.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderUserProfileView extends AphrontView { 20 + private $user; 21 + private $questionoffset; 22 + private $answeroffset; 23 + private $questions; 24 + private $answers; 25 + private $pagesize; 26 + private $uri; 27 + private $qparam; 28 + private $aparam; 29 + 30 + public function setUser(PhabricatorUser $user) { 31 + $this->user = $user; 32 + return $this; 33 + } 34 + 35 + public function setQuestionOffset($offset) { 36 + $this->questionoffset = $offset; 37 + return $this; 38 + } 39 + 40 + public function setAnswerOffset($offset) { 41 + $this->answeroffset = $offset; 42 + return $this; 43 + } 44 + 45 + public function setQuestions($data) { 46 + $this->questions = $data; 47 + return $this; 48 + } 49 + 50 + public function setAnswers($data) { 51 + $this->answers = $data; 52 + return $this; 53 + } 54 + 55 + public function setPageSize($pagesize) { 56 + $this->pagesize = $pagesize; 57 + return $this; 58 + } 59 + 60 + public function setHandles($handles) { 61 + $this->handles = $handles; 62 + return $this; 63 + } 64 + 65 + public function setURI($uri, $qparam, $aparam) { 66 + $this->uri = $uri; 67 + $this->qparam = $qparam; 68 + $this->aparam = $aparam; 69 + return $this; 70 + } 71 + 72 + public function render() { 73 + require_celerity_resource('ponder-core-view-css'); 74 + require_celerity_resource('ponder-feed-view-css'); 75 + 76 + $user = $this->user; 77 + $qoffset = $this->questionoffset; 78 + $aoffset = $this->answeroffset; 79 + $questions = $this->questions; 80 + $answers = $this->answers; 81 + $handles = $this->handles; 82 + $uri = $this->uri; 83 + $qparam = $this->qparam; 84 + $aparam = $this->aparam; 85 + $pagesize = $this->pagesize; 86 + 87 + 88 + // display questions 89 + $question_panel = id(new AphrontPanelView()) 90 + ->setHeader("Your Questions") 91 + ->addClass("ponder-panel"); 92 + 93 + $question_panel->addButton( 94 + phutil_render_tag( 95 + 'a', 96 + array( 97 + 'href' => "/ponder/question/ask/", 98 + 'class' => 'green button', 99 + ), 100 + "Ask a question")); 101 + 102 + $qpagebuttons = id(new AphrontPagerView()) 103 + ->setPageSize($pagesize) 104 + ->setOffset($qoffset) 105 + ->setURI( 106 + $uri->alter( 107 + $aparam, 108 + $aoffset), 109 + $qparam); 110 + 111 + $questions = $qpagebuttons->sliceResults($questions); 112 + 113 + foreach ($questions as $question) { 114 + $cur = id(new PonderQuestionSummaryView()) 115 + ->setUser($user) 116 + ->setQuestion($question) 117 + ->setHandles($handles); 118 + $question_panel->appendChild($cur); 119 + } 120 + 121 + $question_panel->appendChild($qpagebuttons); 122 + 123 + // display answers 124 + $answer_panel = id(new AphrontPanelView()) 125 + ->setHeader("Your Answers") 126 + ->addClass("ponder-panel") 127 + ->appendChild( 128 + phutil_render_tag( 129 + 'a', 130 + array('id' => 'answers'), 131 + "") 132 + ); 133 + 134 + $apagebuttons = id(new AphrontPagerView()) 135 + ->setPageSize($pagesize) 136 + ->setOffset($aoffset) 137 + ->setURI( 138 + $uri 139 + ->alter( 140 + $qparam, 141 + $qoffset) 142 + ->setFragment("answers"), 143 + $aparam); 144 + 145 + $answers = $apagebuttons->sliceResults($answers); 146 + 147 + foreach ($answers as $answer) { 148 + $cur = id(new PonderAnswerSummaryView()) 149 + ->setUser($user) 150 + ->setAnswer($answer) 151 + ->setHandles($handles); 152 + $answer_panel->appendChild($cur); 153 + } 154 + 155 + $answer_panel->appendChild($apagebuttons); 156 + 157 + return $question_panel->render() . $answer_panel->render(); 158 + } 159 + }
+70
src/applications/ponder/view/PonderVotableView.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PonderVotableView extends AphrontView { 20 + 21 + private $phid; 22 + private $uri; 23 + private $count; 24 + private $vote; 25 + 26 + public function setPHID($phid) { 27 + $this->phid = $phid; 28 + return $this; 29 + } 30 + 31 + public function setURI($uri) { 32 + $this->uri = $uri; 33 + return $this; 34 + } 35 + 36 + public function setCount($count) { 37 + $this->count = $count; 38 + return $this; 39 + } 40 + 41 + public function setVote($vote) { 42 + $this->vote = $vote; 43 + return $this; 44 + } 45 + 46 + public function render() { 47 + require_celerity_resource('ponder-vote-css'); 48 + require_celerity_resource('javelin-behavior-ponder-votebox'); 49 + 50 + Javelin::initBehavior( 51 + 'ponder-votebox', 52 + array( 53 + 'nodeid' => $this->phid, 54 + 'vote' => $this->vote, 55 + 'count' => $this->count, 56 + 'uri' => $this->uri 57 + )); 58 + 59 + $content = 60 + '<div class="ponder-votable">'. 61 + '<div id="'.phutil_escape_html($this->phid).'" class="ponder-votebox"> 62 + </div>'. 63 + $this->renderChildren(). 64 + '<div class="ponder-votable-bottom"></div>'. 65 + '</div>'; 66 + 67 + return $content; 68 + } 69 + 70 + }
+2
src/applications/search/constants/PhabricatorSearchScope.php
··· 26 26 const SCOPE_OPEN_TASKS = 'open-tasks'; 27 27 const SCOPE_COMMITS = 'commits'; 28 28 const SCOPE_WIKI = 'wiki'; 29 + const SCOPE_QUESTIONS = 'questions'; 29 30 30 31 public static function getScopeOptions() { 31 32 return array( ··· 34 35 self::SCOPE_WIKI => 'Wiki Documents', 35 36 self::SCOPE_OPEN_REVISIONS => 'Open Revisions', 36 37 self::SCOPE_COMMITS => 'Commits', 38 + self::SCOPE_QUESTIONS => 'Ponder Questions', 37 39 ); 38 40 } 39 41
+1
src/applications/search/index/PhabricatorSearchAbstractDocument.php
··· 37 37 PhabricatorPHIDConstants::PHID_TYPE_TASK => 'Maniphest Tasks', 38 38 PhabricatorPHIDConstants::PHID_TYPE_WIKI => 'Phriction Documents', 39 39 PhabricatorPHIDConstants::PHID_TYPE_USER => 'Phabricator Users', 40 + PhabricatorPHIDConstants::PHID_TYPE_QUES => 'Ponder Questions', 40 41 ) + $more; 41 42 } 42 43
+13
src/infrastructure/edges/constants/PhabricatorEdgeConfig.php
··· 44 44 const TYPE_COMMIT_HAS_PROJECT = 15; 45 45 const TYPE_PROJECT_HAS_COMMIT = 16; 46 46 47 + const TYPE_QUESTION_HAS_VOTING_USER = 17; 48 + const TYPE_VOTING_USER_HAS_QUESTION = 18; 49 + const TYPE_ANSWER_HAS_VOTING_USER = 19; 50 + CONST TYPE_VOTING_USER_HAS_ANSWER = 20; 51 + 47 52 const TYPE_TEST_NO_CYCLE = 9000; 48 53 49 54 public static function getInverse($edge_type) { ··· 71 76 self::TYPE_COMMIT_HAS_PROJECT => self::TYPE_PROJECT_HAS_COMMIT, 72 77 self::TYPE_PROJECT_HAS_COMMIT => self::TYPE_COMMIT_HAS_PROJECT, 73 78 79 + self::TYPE_QUESTION_HAS_VOTING_USER => 80 + self::TYPE_VOTING_USER_HAS_QUESTION, 81 + self::TYPE_VOTING_USER_HAS_QUESTION => 82 + self::TYPE_QUESTION_HAS_VOTING_USER, 83 + self::TYPE_ANSWER_HAS_VOTING_USER => self::TYPE_VOTING_USER_HAS_ANSWER, 84 + self::TYPE_VOTING_USER_HAS_ANSWER => self::TYPE_ANSWER_HAS_VOTING_USER, 74 85 ); 75 86 76 87 return idx($map, $edge_type); ··· 98 109 PhabricatorPHIDConstants::PHID_TYPE_TOBJ => 'HarbormasterObject', 99 110 PhabricatorPHIDConstants::PHID_TYPE_BLOG => 'PhameBlog', 100 111 PhabricatorPHIDConstants::PHID_TYPE_POST => 'PhamePost', 112 + PhabricatorPHIDConstants::PHID_TYPE_QUES => 'PonderQuestion', 113 + PhabricatorPHIDConstants::PHID_TYPE_ANSW => 'PonderAnswer', 101 114 ); 102 115 103 116 $class = idx($class_map, $phid_type);
+7
src/infrastructure/markup/PhabricatorMarkupEngine.php
··· 330 330 } 331 331 332 332 333 + public static function newPonderMarkupEngine(array $options = array()) { 334 + return self::newMarkupEngine($options); 335 + } 336 + 337 + 333 338 /** 334 339 * @task engine 335 340 */ ··· 405 410 $rules[] = new PhabricatorRemarkupRuleDiffusion(); 406 411 $rules[] = new PhabricatorRemarkupRuleManiphest(); 407 412 $rules[] = new PhabricatorRemarkupRulePaste(); 413 + 414 + $rules[] = new PonderRuleQuestion(); 408 415 409 416 if ($options['macros']) { 410 417 $rules[] = new PhabricatorRemarkupRuleImageMacro();
+29
src/infrastructure/markup/rule/PonderRuleQuestion.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + /** 20 + * @group markup 21 + */ 22 + final class PonderRuleQuestion 23 + extends PhabricatorRemarkupRuleObjectName { 24 + 25 + protected function getObjectNamePrefix() { 26 + return 'Q'; 27 + } 28 + } 29 +
+8
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 163 163 'type' => 'db', 164 164 'name' => 'fact', 165 165 ), 166 + 'db.ponder' => array( 167 + 'type' => 'db', 168 + 'name' => 'ponder', 169 + ), 166 170 '0000.legacy.sql' => array( 167 171 'type' => 'sql', 168 172 'name' => $this->getPatchPath('0000.legacy.sql'), ··· 951 955 'fact-raw.sql' => array( 952 956 'type' => 'sql', 953 957 'name' => $this->getPatchPath('fact-raw.sql'), 958 + ), 959 + 'ponder.sql' => array( 960 + 'type' => 'sql', 961 + 'name' => $this->getPatchPath('ponder.sql') 954 962 ), 955 963 ); 956 964 }
+25
webroot/rsrc/css/application/ponder/core.css
··· 1 + /** 2 + * @provides ponder-core-view-css 3 + */ 4 + 5 + .ponder-primary-pane { 6 + margin: 0 0 0 2em; 7 + padding-bottom: 2em; 8 + max-width: 800px; 9 + } 10 + 11 + .ponder-panel { 12 + max-width: 800px; 13 + font-family : antiqua, verdana, arial; 14 + } 15 + 16 + .ponder-panel h1 { 17 + padding-bottom: 8px; 18 + margin-bottom: 8px; 19 + font-size : 1.5em; 20 + } 21 + 22 + .ponder-panel .aphront-form-view { 23 + border : none; 24 + background : none; 25 + }
+74
webroot/rsrc/css/application/ponder/feed.css
··· 1 + /** 2 + * @provides ponder-feed-view-css 3 + */ 4 + 5 + .ponder-question-summary { 6 + width : 100%; 7 + background : #DFDFE3; 8 + float : left; 9 + clear : both; 10 + margin-top : 1px; 11 + padding : 1px; 12 + } 13 + 14 + .ponder-answer-summary { 15 + width : 100%; 16 + background : #DFDFE3; 17 + float : left; 18 + clear : both; 19 + margin-top : 1px; 20 + padding : 1px; 21 + } 22 + 23 + .ponder-summary-votes { 24 + width : 50px; 25 + height : 36pt; 26 + font-size : 18pt; 27 + text-align : center; 28 + background : #EEE; 29 + border : 1px solid #BBB; 30 + float : left; 31 + margin : 2px; 32 + padding-top : 2px; 33 + } 34 + 35 + .ponder-summary-answers { 36 + width : 50px; 37 + height : 36pt; 38 + font-size : 18pt; 39 + text-align : center; 40 + background : #EEE; 41 + border : 1px solid #BBB; 42 + float : left; 43 + margin : 2px; 44 + padding-top : 2px; 45 + } 46 + 47 + .ponder-question-label { 48 + font-size : 6pt; 49 + } 50 + 51 + h2.ponder-question-title { 52 + font-size : 12pt; 53 + margin : 2px; 54 + padding : 0; 55 + } 56 + 57 + h2.ponder-answer-title { 58 + font-size : 12pt; 59 + margin : 2px; 60 + padding : 0; 61 + } 62 + 63 + .ponder-metadata { 64 + padding-left : 5px; 65 + width : 650px; 66 + float : left; 67 + } 68 + 69 + .ponder-small-metadata { 70 + font-size : 7.5pt; 71 + color : #555; 72 + margin : 0; 73 + text-align : right; 74 + }
+34
webroot/rsrc/css/application/ponder/post.css
··· 1 + /** 2 + * @provides ponder-post-css 3 + */ 4 + 5 + .ponder-post-list { 6 + max-width: 1162px; 7 + } 8 + 9 + .ponder-add-answer-panel { 10 + max-width: 1162px; 11 + } 12 + 13 + .ponder-post-list .anchor-target { 14 + background-color: #ffffdd; 15 + border-color: #ffff00; 16 + } 17 + 18 + .ponder-post-core .phabricator-remarkup .remarkup-code-block { 19 + width: 88ex; 20 + width: 81ch; 21 + } 22 + 23 + .phabricator-transaction-view .ponder-question { 24 + border-color: #777; 25 + } 26 + 27 + .phabricator-transaction-detail .ponder-question { 28 + border-color: #777; 29 + } 30 + 31 + 32 + .phabricator-transaction-view .ponder-answer { 33 + /* border-color: #203791; */ 34 + }
+74
webroot/rsrc/css/application/ponder/vote.css
··· 1 + /** 2 + * @provides ponder-vote-css 3 + */ 4 + 5 + .ponder-votable { 6 + min-height : 120px; 7 + } 8 + 9 + .ponder-votable .phabricator-transaction-detail { 10 + min-height : 90px; 11 + } 12 + 13 + .ponder-votebox { 14 + float : left; 15 + width : 32px; 16 + height : 60px; 17 + margin-top : 56px; 18 + margin-left : 10px; 19 + } 20 + 21 + .ponder-upbutton { 22 + border : none; 23 + padding : 0; 24 + margin : 0; 25 + width : 32px; 26 + height : 13px; 27 + } 28 + 29 + .ponder-downbutton { 30 + border : none; 31 + padding : 0; 32 + margin : 0; 33 + width : 32px; 34 + height : 13px; 35 + } 36 + 37 + .ponder-votecount { 38 + width : 32px; 39 + height : 22pt; 40 + padding : 0; 41 + margin : 0; 42 + overflow : visible; 43 + text-align : center; 44 + font-size : 15pt; 45 + } 46 + 47 + .ponder-upbutton:hover { 48 + cursor : pointer; 49 + } 50 + 51 + .ponder-downbutton:hover { 52 + cursor : pointer; 53 + } 54 + 55 + .ponder-votable-bottom { 56 + clear : both; 57 + } 58 + 59 + .ponder-upbutton { 60 + background : url(/rsrc/image/application/ponder/upvote.png) 0 -13px no-repeat; 61 + } 62 + 63 + .ponder-upbutton.ponder-vote-up { 64 + background : url(/rsrc/image/application/ponder/upvote.png) 0 0 no-repeat; 65 + } 66 + 67 + .ponder-downbutton { 68 + background : url(/rsrc/image/application/ponder/downvote.png) 69 + 0 -13px no-repeat; 70 + } 71 + 72 + .ponder-downbutton.ponder-vote-down { 73 + background : url(/rsrc/image/application/ponder/downvote.png) 0 0 no-repeat; 74 + }
webroot/rsrc/image/app/app_ponder.png

This is a binary file and will not be displayed.

webroot/rsrc/image/application/ponder/downvote.png

This is a binary file and will not be displayed.

webroot/rsrc/image/application/ponder/upvote.png

This is a binary file and will not be displayed.

+32
webroot/rsrc/js/application/ponder/behavior-comment-preview.js
··· 1 + /** 2 + * @provides javelin-behavior-ponder-feedback-preview 3 + * @requires javelin-behavior 4 + * javelin-dom 5 + * javelin-util 6 + * phabricator-shaped-request 7 + */ 8 + 9 + JX.behavior('ponder-feedback-preview', function(config) { 10 + 11 + var content = JX.$(config.content); 12 + var question_id = config.question_id; 13 + 14 + var callback = function(r) { 15 + JX.DOM.setContent(JX.$(config.preview), JX.$H(r)); 16 + }; 17 + 18 + var getdata = function() { 19 + var data = { 20 + content : content.value, 21 + question_id : question_id 22 + }; 23 + return data; 24 + }; 25 + 26 + var request = new JX.PhabricatorShapedRequest(config.uri, callback, getdata); 27 + var trigger = JX.bind(request, request.trigger); 28 + 29 + JX.DOM.listen(content, 'keydown', null, trigger); 30 + 31 + request.start(); 32 + });
+76
webroot/rsrc/js/application/ponder/behavior-votebox.js
··· 1 + /** 2 + * @provides javelin-behavior-ponder-votebox 3 + * @requires javelin-behavior 4 + * javelin-dom 5 + * javelin-util 6 + * phabricator-shaped-request 7 + */ 8 + 9 + JX.behavior('ponder-votebox', function(config) { 10 + 11 + var node = JX.$(config.nodeid); 12 + var vote = config.vote; 13 + var count = config.count | 0; 14 + var targetURI = config.targetURI; 15 + 16 + var upnode, countnode, downnode; 17 + 18 + // this defines the behavior of the up/downvote 19 + // buttons, e.g. clicking 'up' transitions from 20 + // an 'up' vote to a 'none' vote 21 + var votecycle = { 22 + "1" : { up : "0", down : "-1" }, 23 + "0" : { up : "1", down : "-1" }, 24 + "-1" : { up : "1", down : "0" } 25 + }; 26 + 27 + var voteclass = { 28 + "0" : "ponder-vote-none", 29 + "-1" : "ponder-vote-down", 30 + "1" : "ponder-vote-up" 31 + }; 32 + 33 + function decorate() { 34 + upnode = JX.$N('div'); 35 + countnode = JX.$N('div'); 36 + downnode = JX.$N('div'); 37 + node.appendChild(upnode); 38 + node.appendChild(countnode); 39 + node.appendChild(downnode); 40 + JX.DOM.alterClass(upnode, "ponder-upbutton " + voteclass[vote], true); 41 + JX.DOM.alterClass(downnode, "ponder-downbutton " + voteclass[vote], true); 42 + JX.DOM.alterClass(countnode, "ponder-votecount", true); 43 + } 44 + 45 + function update_state() { 46 + upnode.className = "ponder-upbutton " + voteclass[vote]; 47 + downnode.className = "ponder-downbutton " + voteclass[vote]; 48 + JX.DOM.setContent(countnode, JX.$H(count.toString())); 49 + } 50 + 51 + function getdata() { 52 + return { phid : config.nodeid, vote : vote }; 53 + } 54 + 55 + var request = new JX.PhabricatorShapedRequest(config.uri, JX.id, getdata); 56 + var trigger = JX.bind(request, request.trigger); 57 + 58 + function handle_upvote(e) { 59 + count += votecycle[vote].up - vote; 60 + vote = votecycle[vote].up; 61 + trigger(); 62 + update_state(); 63 + } 64 + 65 + function handle_downvote(e) { 66 + count += votecycle[vote].down - vote; 67 + vote = votecycle[vote].down; 68 + trigger(); 69 + update_state(); 70 + } 71 + 72 + decorate(); 73 + update_state(); 74 + JX.DOM.listen(upnode, 'click', null, handle_upvote); 75 + JX.DOM.listen(downnode, 'click', null, handle_downvote); 76 + });