Index: trunk/extensions/EducationProgram/EducationProgram.settings.php |
— | — | @@ -34,7 +34,8 @@ |
35 | 35 | ), |
36 | 36 | 'ambassadorCommonsUrl' => 'https://commons.wikimedia.org/wiki/Special:UploadWizard', |
37 | 37 | 'citylessCountries' => array( 'BT', 'BV', 'IO', 'VG', 'TD', 'CX', 'CC', 'KM', 'DJ', 'GQ', 'FK', 'FX', 'TF', 'GW', 'HM', 'KI', 'YT', 'MS', 'NR', 'NU', 'NF', 'PN', 'SH', 'PM', 'WS', 'SC', 'GS', 'SJ', 'TK', 'TP', 'TV', 'UM', 'VU', 'EH' ), |
38 | | - 'ambassadorImgWidth' => 140 |
| 38 | + 'ambassadorImgWidth' => 140, |
| 39 | + 'recentActivityLimit' => 24 * 60 * 60, |
39 | 40 | ); |
40 | 41 | } |
41 | 42 | |
Index: trunk/extensions/EducationProgram/EducationProgram.php |
— | — | @@ -142,6 +142,7 @@ |
143 | 143 | $wgAutoloadClasses['SpecialOAProfile'] = dirname( __FILE__ ) . '/specials/SpecialOAProfile.php'; |
144 | 144 | $wgAutoloadClasses['SpecialCAProfile'] = dirname( __FILE__ ) . '/specials/SpecialCAProfile.php'; |
145 | 145 | $wgAutoloadClasses['SpecialAmbassadorProfile'] = dirname( __FILE__ ) . '/specials/SpecialAmbassadorProfile.php'; |
| 146 | +$wgAutoloadClasses['SpecialStudentActivity'] = dirname( __FILE__ ) . '/specials/SpecialStudentActivity.php'; |
146 | 147 | |
147 | 148 | // Special pages |
148 | 149 | $wgSpecialPages['MyCourses'] = 'SpecialMyCourses'; |
— | — | @@ -156,6 +157,7 @@ |
157 | 158 | $wgSpecialPages['OnlineAmbassadors'] = 'SpecialOAs'; |
158 | 159 | $wgSpecialPages['CampusAmbassadorProfile'] = 'SpecialCAProfile'; |
159 | 160 | $wgSpecialPages['OnlineAmbassadorProfile'] = 'SpecialOAProfile'; |
| 161 | +$wgSpecialPages['StudentActivity'] = 'SpecialStudentActivity'; |
160 | 162 | |
161 | 163 | $wgSpecialPageGroups['MyCourses'] = 'education'; |
162 | 164 | $wgSpecialPageGroups['Institutions'] = 'education'; |
— | — | @@ -169,6 +171,7 @@ |
170 | 172 | $wgSpecialPageGroups['OnlineAmbassadorProfile'] = 'education'; |
171 | 173 | $wgSpecialPageGroups['Enroll'] = 'education'; |
172 | 174 | $wgSpecialPageGroups['Disenroll'] = 'education'; |
| 175 | +$wgSpecialPageGroups['StudentActivity'] = 'education'; |
173 | 176 | |
174 | 177 | define( 'EP_STUDENT', 0 ); |
175 | 178 | define( 'EP_INSTRUCTOR', 1 ); |
Index: trunk/extensions/EducationProgram/sql/EducationProgram.sql |
— | — | @@ -100,28 +100,36 @@ |
101 | 101 | CREATE TABLE IF NOT EXISTS /*_*/ep_users_per_course ( |
102 | 102 | upc_user_id INT unsigned NOT NULL, -- Foreign key on ep_user.user_id |
103 | 103 | upc_course_id INT unsigned NOT NULL, -- Foreign key on ep_courses.course_id |
104 | | - upc_role TINYINT unsigned NOT NULL -- The role the user has for the course |
| 104 | + upc_role TINYINT unsigned NOT NULL, -- The role the user has for the course |
| 105 | + upc_time varbinary(14) NOT NULL -- Time at which the link was made |
105 | 106 | ) /*$wgDBTableOptions*/; |
106 | 107 | |
107 | 108 | CREATE UNIQUE INDEX /*i*/ep_users_per_course ON /*_*/ep_users_per_course (upc_user_id, upc_course_id, upc_role); |
108 | 109 | CREATE INDEX /*i*/ep_upc_course_id ON /*_*/ep_users_per_course (upc_course_id); |
109 | 110 | CREATE INDEX /*i*/ep_upc_role ON /*_*/ep_users_per_course (upc_role); |
| 111 | +CREATE INDEX /*i*/ep_upc_time ON /*_*/ep_users_per_course (upc_time); |
110 | 112 | |
111 | 113 | |
112 | 114 | |
113 | 115 | -- Students. In essence this is an extension to the user table. |
114 | 116 | CREATE TABLE IF NOT EXISTS /*_*/ep_students ( |
115 | 117 | student_id INT unsigned NOT NULL auto_increment PRIMARY KEY, |
| 118 | + student_user_id INT unsigned NOT NULL, -- Foreign key on user.user_id |
116 | 119 | |
117 | | - student_user_id INT unsigned NOT NULL, -- Foreign key on user.user_id |
| 120 | + -- Summary fields - cahing data or computations on data stored elswhere |
118 | 121 | student_first_enroll varbinary(14) NOT NULL, -- Time of first enrollment |
119 | | - |
120 | | - student_last_active varbinary(14) NOT NULL, -- Time of last activity |
| 122 | + student_first_course INT unsigned NOT NULL, -- First course the user enrolled in |
| 123 | + student_last_enroll varbinary(14) NOT NULL, -- Time of last enrollment |
| 124 | + student_last_course INT unsigned NOT NULL, -- Last course the user enrolled in |
| 125 | + student_last_active varbinary(14) NOT NULL, -- Time of last activity in article NS |
121 | 126 | student_active_enroll TINYINT unsigned NOT NULL -- If the student is enrolled in any active courses |
122 | 127 | ) /*$wgDBTableOptions*/; |
123 | 128 | |
124 | 129 | CREATE UNIQUE INDEX /*i*/ep_students_user_id ON /*_*/ep_students (student_user_id); |
125 | 130 | CREATE INDEX /*i*/ep_students_first_enroll ON /*_*/ep_students (student_first_enroll); |
| 131 | +CREATE INDEX /*i*/ep_students_first_course ON /*_*/ep_students (student_first_course); |
| 132 | +CREATE INDEX /*i*/ep_students_last_enroll ON /*_*/ep_students (student_last_enroll); |
| 133 | +CREATE INDEX /*i*/ep_students_last_course ON /*_*/ep_students (student_last_course); |
126 | 134 | CREATE INDEX /*i*/ep_students_last_active ON /*_*/ep_students (student_last_active); |
127 | 135 | CREATE INDEX /*i*/ep_students_active_enroll ON /*_*/ep_students (student_active_enroll); |
128 | 136 | |
Index: trunk/extensions/EducationProgram/sql/AddActivityStuff.sql |
— | — | @@ -0,0 +1,20 @@ |
| 2 | +-- MySQL patch for the Education Program extension. |
| 3 | +-- Licence: GNU GPL v3+ |
| 4 | +-- Author: Jeroen De Dauw < jeroendedauw@gmail.com > |
| 5 | + |
| 6 | +ALTER TABLE /*_*/ep_users_per_course ADD COLUMN upc_time varbinary(14) NOT NULL; |
| 7 | + |
| 8 | +CREATE INDEX /*i*/ep_upc_time ON /*_*/ep_users_per_course (upc_time); |
| 9 | + |
| 10 | +ALTER TABLE /*_*/ep_students ADD COLUMN student_last_enroll varbinary(14) NOT NULL; |
| 11 | +ALTER TABLE /*_*/ep_students ADD COLUMN student_last_course INT unsigned NOT NULL; |
| 12 | +ALTER TABLE /*_*/ep_students ADD COLUMN student_first_course INT unsigned NOT NULL; |
| 13 | + |
| 14 | +CREATE INDEX /*i*/ep_students_first_course ON /*_*/ep_students (student_first_course); |
| 15 | +CREATE INDEX /*i*/ep_students_last_enroll ON /*_*/ep_students (student_last_enroll); |
| 16 | +CREATE INDEX /*i*/ep_students_last_course ON /*_*/ep_students (student_last_course); |
| 17 | + |
| 18 | +UPDATE /*_*/ep_users_per_course SET upc_time = '20120315224638'; |
| 19 | +UPDATE /*_*/ep_students SET student_last_enroll = '20120315224638'; |
| 20 | +UPDATE /*_*/ep_students SET student_last_active = '20120315224638'; |
| 21 | + |
Index: trunk/extensions/EducationProgram/EducationProgram.i18n.alias.php |
— | — | @@ -26,10 +26,9 @@ |
27 | 27 | 'Disenroll' => array( 'Disenroll' ), |
28 | 28 | 'CampusAmbassadors' => array( 'CampusAmbassadors' ), |
29 | 29 | 'OnlineAmbassadors' => array( 'OnlineAmbassadors' ), |
30 | | -// 'CampusAmbassador' => array( 'CampusAmbassador' ), |
31 | | -// 'OnlineAmbassador' => array( 'OnlineAmbassador' ), |
32 | 30 | 'OnlineAmbassadorProfile' => array( 'OnlineAmbassadorProfile' ), |
33 | 31 | 'CampusAmbassadorProfile' => array( 'CampusAmbassadorProfile' ), |
| 32 | + 'StudentActivity' => array( 'StudentActivity' ), |
34 | 33 | ); |
35 | 34 | |
36 | 35 | /** Dutch (Nederlands) */ |
Index: trunk/extensions/EducationProgram/specials/SpecialStudentActivity.php |
— | — | @@ -0,0 +1,237 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Page listing recent student activity. |
| 6 | + * |
| 7 | + * @since 0.1 |
| 8 | + * |
| 9 | + * @file SpecialStudentActivity.php |
| 10 | + * @ingroup EducationProgram |
| 11 | + * |
| 12 | + * @licence GNU GPL v3 or later |
| 13 | + * @author Jeroen De Dauw < jeroendedauw@gmail.com > |
| 14 | + */ |
| 15 | +class SpecialStudentActivity extends SpecialEPPage { |
| 16 | + |
| 17 | + /** |
| 18 | + * Constructor. |
| 19 | + * |
| 20 | + * @since 0.1 |
| 21 | + */ |
| 22 | + public function __construct() { |
| 23 | + parent::__construct( 'StudentActivity' ); |
| 24 | + } |
| 25 | + |
| 26 | + /** |
| 27 | + * Main method. |
| 28 | + * |
| 29 | + * @since 0.1 |
| 30 | + * |
| 31 | + * @param string $subPage |
| 32 | + */ |
| 33 | + public function execute( $subPage ) { |
| 34 | + parent::execute( $subPage ); |
| 35 | + |
| 36 | + $this->displayNavigation(); |
| 37 | + |
| 38 | + $out = $this->getOutput(); |
| 39 | + |
| 40 | + $pager = new EPStudentActivityPager( $this->getContext(), array() ); |
| 41 | + |
| 42 | + if ( $pager->getNumRows() ) { |
| 43 | + $out->addHTML( |
| 44 | + $pager->getFilterControl() . |
| 45 | + $pager->getNavigationBar() . |
| 46 | + $pager->getBody() . |
| 47 | + $pager->getNavigationBar() . |
| 48 | + $pager->getMultipleItemControl() |
| 49 | + ); |
| 50 | + } |
| 51 | + else { |
| 52 | + $out->addHTML( $pager->getFilterControl( true ) ); |
| 53 | + $out->addWikiMsg( 'ep-studentactivity-noresults' ); |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | +} |
| 58 | + |
| 59 | + |
| 60 | +/** |
| 61 | + * Student pager, primarily for Special:Students. |
| 62 | + * |
| 63 | + * @since 0.1 |
| 64 | + * |
| 65 | + * @file EPStudentPager.php |
| 66 | + * @ingroup EductaionProgram |
| 67 | + * |
| 68 | + * @licence GNU GPL v3 or later |
| 69 | + * @author Jeroen De Dauw < jeroendedauw@gmail.com > |
| 70 | + */ |
| 71 | +class EPStudentActivityPager extends EPPager { |
| 72 | + |
| 73 | + /** |
| 74 | + * List of user ids mapped to user names and real names, set in doBatchLookups. |
| 75 | + * The real names will just hold the user name when no real name is set. |
| 76 | + * user id => array( user name, real name ) |
| 77 | + * |
| 78 | + * @since 0.1 |
| 79 | + * @var array |
| 80 | + */ |
| 81 | + protected $userNames = array(); |
| 82 | + |
| 83 | + /** |
| 84 | + * List of user ids with the names of their associated courses. |
| 85 | + * user id => array( course name 0, ... ) |
| 86 | + * |
| 87 | + * @since 0.1 |
| 88 | + * @var array |
| 89 | + */ |
| 90 | + protected $courseNames = array(); |
| 91 | + |
| 92 | + /** |
| 93 | + * Constructor. |
| 94 | + * |
| 95 | + * @param IContextSource $context |
| 96 | + * @param array $conds |
| 97 | + */ |
| 98 | + public function __construct( IContextSource $context, array $conds = array() ) { |
| 99 | + $this->mDefaultDirection = true; |
| 100 | + |
| 101 | +// $conds[] = 'last_active > ' . wfGetDB( DB_SLAVE )->addQuotes( |
| 102 | +// wfTimestamp( TS_MW, time() - ( EPSettings::get( 'recentActivityLimit' ) ) ) |
| 103 | +// ); |
| 104 | + |
| 105 | + parent::__construct( $context, $conds, EPStudents::singleton() ); |
| 106 | + } |
| 107 | + |
| 108 | + /** |
| 109 | + * (non-PHPdoc) |
| 110 | + * @see EPPager::getFields() |
| 111 | + */ |
| 112 | + public function getFields() { |
| 113 | + return array( |
| 114 | + 'id', |
| 115 | + 'user_id', |
| 116 | + 'last_course', |
| 117 | + 'last_active', |
| 118 | + ); |
| 119 | + } |
| 120 | + |
| 121 | + /** |
| 122 | + * (non-PHPdoc) |
| 123 | + * @see TablePager::getRowClass() |
| 124 | + */ |
| 125 | + function getRowClass( $row ) { |
| 126 | + return 'ep-studentactivity-row'; |
| 127 | + } |
| 128 | + |
| 129 | + /** |
| 130 | + * (non-PHPdoc) |
| 131 | + * @see TablePager::getTableClass() |
| 132 | + */ |
| 133 | + public function getTableClass() { |
| 134 | + return 'TablePager ep-studentactivity'; |
| 135 | + } |
| 136 | + |
| 137 | + /** |
| 138 | + * (non-PHPdoc) |
| 139 | + * @see EPPager::getFormattedValue() |
| 140 | + */ |
| 141 | + protected function getFormattedValue( $name, $value ) { |
| 142 | + switch ( $name ) { |
| 143 | + case 'user_id': |
| 144 | + if ( array_key_exists( $value, $this->userNames ) ) { |
| 145 | + list( $userName, $realName ) = $this->userNames[$value]; |
| 146 | + $value = Linker::userLink( $value, $userName, $realName ) . Linker::userToolLinks( $value, $userName ); |
| 147 | + } |
| 148 | + else { |
| 149 | + wfWarn( 'User id not in $this->userNames in ' . __METHOD__ ); |
| 150 | + } |
| 151 | + break; |
| 152 | + case 'first_enroll': case 'last_active': |
| 153 | + $value = htmlspecialchars( $this->getLanguage()->date( $value ) ); |
| 154 | + break; |
| 155 | + } |
| 156 | + |
| 157 | + return $value; |
| 158 | + } |
| 159 | + |
| 160 | + /** |
| 161 | + * (non-PHPdoc) |
| 162 | + * @see EPPager::getSortableFields() |
| 163 | + */ |
| 164 | + protected function getSortableFields() { |
| 165 | + return array( |
| 166 | + ); |
| 167 | + } |
| 168 | + |
| 169 | + /** |
| 170 | + * (non-PHPdoc) |
| 171 | + * @see EPPager::hasActionsColumn() |
| 172 | + */ |
| 173 | + protected function hasActionsColumn() { |
| 174 | + return false; |
| 175 | + } |
| 176 | + |
| 177 | + /** |
| 178 | + * (non-PHPdoc) |
| 179 | + * @see IndexPager::getDefaultSort() |
| 180 | + */ |
| 181 | + function getDefaultSort() { |
| 182 | + return 'last_active'; |
| 183 | + } |
| 184 | + |
| 185 | + /** |
| 186 | + * (non-PHPdoc) |
| 187 | + * @see EPPager::getFieldNames() |
| 188 | + */ |
| 189 | + public function getFieldNames() { |
| 190 | + $fields = parent::getFieldNames(); |
| 191 | + |
| 192 | + unset( $fields['id'] ); |
| 193 | + |
| 194 | + $fields = wfArrayInsertAfter( $fields, array( 'org_id' => 'org-id' ), 'user_id' ); |
| 195 | + |
| 196 | + return $fields; |
| 197 | + } |
| 198 | + |
| 199 | + /** |
| 200 | + * (non-PHPdoc) |
| 201 | + * @see IndexPager::doBatchLookups() |
| 202 | + */ |
| 203 | + protected function doBatchLookups() { |
| 204 | + $userIds = array(); |
| 205 | + $field = $this->table->getPrefixedField( 'user_id' ); |
| 206 | + |
| 207 | + while( $student = $this->mResult->fetchObject() ) { |
| 208 | + $userIds[] = (int)$student->$field; |
| 209 | + } |
| 210 | + |
| 211 | + if ( !empty( $userIds ) ) { |
| 212 | + $result = wfGetDB( DB_SLAVE )->select( |
| 213 | + 'user', |
| 214 | + array( 'user_id', 'user_name', 'user_real_name' ), |
| 215 | + array( 'user_id' => $userIds ), |
| 216 | + __METHOD__ |
| 217 | + ); |
| 218 | + |
| 219 | + while( $user = $result->fetchObject() ) { |
| 220 | + $real = $user->user_real_name === '' ? $user->user_name : $user->user_real_name; |
| 221 | + $this->userNames[$user->user_id] = array( $user->user_name, $real ); |
| 222 | + } |
| 223 | + |
| 224 | + $courseNameField = EPCourses::singleton()->getPrefixedField( 'name' ); |
| 225 | + |
| 226 | + // TODO: $this->courseNames[] = |
| 227 | + } |
| 228 | + } |
| 229 | + |
| 230 | + /** |
| 231 | + * (non-PHPdoc) |
| 232 | + * @see EPPager::getMsg() |
| 233 | + */ |
| 234 | + protected function getMsg( $messageKey ) { |
| 235 | + return wfMsg( strtolower( get_called_class() ) . '-' . str_replace( '_', '-', $messageKey ) ); |
| 236 | + } |
| 237 | + |
| 238 | +} |
\ No newline at end of file |
Index: trunk/extensions/EducationProgram/EducationProgram.i18n.php |
— | — | @@ -200,6 +200,7 @@ |
201 | 201 | 'special-onlineambassador' => 'Online ambassador', |
202 | 202 | 'special-campusambassador' => 'Campus ambassador', |
203 | 203 | 'special-disenroll' => 'Disenroll', |
| 204 | + 'special-studentactivity' => 'Student activity', |
204 | 205 | |
205 | 206 | // Course statuses |
206 | 207 | 'ep-course-status-passed' => 'Passed', |
— | — | @@ -362,6 +363,12 @@ |
363 | 364 | 'epoapager-header-courses' => 'Current courses', |
364 | 365 | 'ep-oa-noresults' => 'There are no Online Ambassadors to list.', |
365 | 366 | |
| 367 | + // Student activity pager |
| 368 | + 'epstudentactivitypager-header-user-id' => 'Student', |
| 369 | + 'epstudentactivitypager-header-org-id' => 'Institution', |
| 370 | + 'epstudentactivitypager-header-last-course' => 'Course', |
| 371 | + 'epstudentactivitypager-header-last-active' => 'Last activity', |
| 372 | + |
366 | 373 | // Institution editing |
367 | 374 | 'editinstitution-text' => 'Enter the institution details below and click submit to save your changes.', |
368 | 375 | 'educationprogram-org-edit-name' => 'Institution name', |
— | — | @@ -825,7 +832,6 @@ |
826 | 833 | 'special-onlineambassador' => '{{doc-special|onlineambassador}}', |
827 | 834 | 'special-campusambassador' => '{{doc-special|campusambassador}}', |
828 | 835 | 'special-disenroll' => '{{doc-special|disenroll}}', |
| 836 | + 'special-studentactivity' => '{{doc-special|studentactivity}}', |
829 | 837 | |
830 | | - |
831 | | - |
832 | 838 | ); |
Index: trunk/extensions/EducationProgram/EducationProgram.hooks.php |
— | — | @@ -53,6 +53,14 @@ |
54 | 54 | true |
55 | 55 | ) ); |
56 | 56 | |
| 57 | + $updater->addExtensionUpdate( array( |
| 58 | + 'addField', |
| 59 | + 'ep_users_per_course', |
| 60 | + 'upc_time', |
| 61 | + dirname( __FILE__ ) . '/sql/AddActivityStuff.sql', |
| 62 | + true |
| 63 | + ) ); |
| 64 | + |
57 | 65 | return true; |
58 | 66 | } |
59 | 67 | |