├── assets ├── index.html ├── screen1.png ├── screen2.png ├── screen3.png ├── screen4.png ├── module_image.png ├── reputation_space_config.png ├── reputation_user_settings.png ├── reputation_spaceadmin_view.png └── reputation_space_view_new_sorting.png ├── messages └── de │ ├── forms_exportForm.php │ ├── widgets_views_stream.php │ ├── views_adminReputation_setting.php │ ├── views_adminReputation_exportCsv.php │ ├── base.php │ ├── widgets_views_spaceUserReputationWidget.php │ ├── views_profileReputation_show.php │ ├── views_adminReputation_show.php │ └── forms_adminController_settings.php ├── Assets.php ├── migrations ├── uninstall.php ├── m160403_114443_reputation_content.php └── m160403_114257_initial.php ├── module.json ├── views ├── space │ ├── index.php │ ├── stats.php │ └── settings.php ├── admin │ └── index.php └── profile │ └── config.php ├── widgets ├── ReputationStream.php ├── SpaceUserReputationWidget.php └── views │ ├── stream.php │ └── spaceUserReputationWidget.php ├── models ├── DailyReputation.php ├── SpaceSettings.php ├── ReputationContent.php ├── ReputationBase.php └── ReputationUser.php ├── config.php ├── Module.php ├── components └── StreamAction.php ├── controllers ├── AdminController.php ├── ProfileController.php └── SpaceController.php ├── README.md └── Events.php /assets/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/HEAD/assets/screen1.png -------------------------------------------------------------------------------- /assets/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/HEAD/assets/screen2.png -------------------------------------------------------------------------------- /assets/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/HEAD/assets/screen3.png -------------------------------------------------------------------------------- /assets/screen4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/HEAD/assets/screen4.png -------------------------------------------------------------------------------- /assets/module_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/HEAD/assets/module_image.png -------------------------------------------------------------------------------- /assets/reputation_space_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/HEAD/assets/reputation_space_config.png -------------------------------------------------------------------------------- /assets/reputation_user_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/HEAD/assets/reputation_user_settings.png -------------------------------------------------------------------------------- /assets/reputation_spaceadmin_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/HEAD/assets/reputation_spaceadmin_view.png -------------------------------------------------------------------------------- /messages/de/forms_exportForm.php: -------------------------------------------------------------------------------- 1 | "Gewichtung", 7 | ); -------------------------------------------------------------------------------- /assets/reputation_space_view_new_sorting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/HEAD/assets/reputation_space_view_new_sorting.png -------------------------------------------------------------------------------- /messages/de/widgets_views_stream.php: -------------------------------------------------------------------------------- 1 | 'Beliebt', 7 | 'Top' => 'Top', 8 | 'New' => 'Neu', 9 | 'Rising' => 'Aufsteigend' 10 | ); -------------------------------------------------------------------------------- /Assets.php: -------------------------------------------------------------------------------- 1 | sourcePath = dirname(__FILE__) . '/assets'; 15 | parent::init(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /migrations/uninstall.php: -------------------------------------------------------------------------------- 1 | dropTable('reputation_user'); 11 | $this->dropTable('reputation_content'); 12 | } 13 | 14 | public function down() 15 | { 16 | echo "uninstall does not support migration down.\n"; 17 | return false; 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /module.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "reputation", 3 | "name": "Reputation", 4 | "description": "This Module Integrates A Reputation System Into HumHub. It Works With HumHub 1.1.x +.", 5 | "keywords": ["content", "reputation", "space", "user"], 6 | "version": "0.6.0", 7 | "time": "2017-02-05", 8 | "authors": [ 9 | { 10 | "name": "Anton Kurnitzky", 11 | "name": "Philipp Horna" 12 | } 13 | ], 14 | "humhub": { 15 | "minVersion": "1.1.0" 16 | } 17 | } -------------------------------------------------------------------------------- /messages/de/views_adminReputation_setting.php: -------------------------------------------------------------------------------- 1 | 'Konfiguration des Reputations-Moduls für diesen Space', 7 | 'Advanced Settings' => 'Erweiterte Einstellungen', 8 | 'Weightings' => 'Gewichtungen', 9 | 'Please read the documentation before changing the settings: TODO URL.' => 'Bitte Lesen sie erst die Dokumentation bevor sie Einstellungen vornehmen: TODO URL.', 10 | ' minutes ago' => ' Minuten' 11 | ); -------------------------------------------------------------------------------- /messages/de/views_adminReputation_exportCsv.php: -------------------------------------------------------------------------------- 1 | 'Dieser Space nutzt die Logarithmus Funktion. Zum Export wird ein Wert benötigt, der mit dem Prozentwert multipliziert wird. ', 7 | 'Weighting' => 'Gewichtung', 8 | 'Export as CSV' => 'CSV-Export', 9 | 'Generate CSV' => 'CSV erzeugen', 10 | 'Close' => 'Schließen', 11 | ); -------------------------------------------------------------------------------- /messages/de/base.php: -------------------------------------------------------------------------------- 1 | 'Reputation', 8 | 'This Module Integrates A Reputation System Into HumHub. It Works With HumHub 1.1.x +.' => 'Dieses Modul integriert ein Reputations-System. Es funktioniert ab Humhub 1.1.x und größer!', 9 | 'User Reputation' => 'Reputation', 10 | 'Save' => 'Speichern', 11 | 'Logarithmic' => 'Logarithmisch', 12 | 'Linear' => 'Linear', 13 | 'Hot' => 'Beliebt', 14 | 'Yes' => 'Ja', 15 | 'No' => 'Nein', 16 | 'E-mail address' => 'E-Mail-Adresse', 17 | ); 18 | -------------------------------------------------------------------------------- /messages/de/widgets_views_spaceUserReputationWidget.php: -------------------------------------------------------------------------------- 1 | Space User Reputation' => 'Space Mitglieder Reputation', 7 | 'Show all' => 'Alle anzeigen', 8 | 'Settings' => 'Einstellungen', 9 | 'No Reputation found!' => 'Keine Reputation gefunden!', 10 | 'You can only see reputation the user shares.' => 'Es wird nur Reputation angezeigt, die der Benutzer teilt.', 11 | 'Change your settings to share your Reputation on this Space' => 'Ändern Sie Ihre Einstellungen um Ihre Reputation in diesem Space zu teilen.', 12 | 13 | ); -------------------------------------------------------------------------------- /messages/de/views_profileReputation_show.php: -------------------------------------------------------------------------------- 1 | Space reputation' => 'Space-Reputation', 7 | 'In the area below, you see how much reputation this user gained inside each space.' => 'Im unteren Bereich ist sichtbar, wie viel Reputation dieser Benutzer innerhalb verschiedener Spaces erreicht hat.', 8 | 'You can only see reputation the user shares.' => 'Es wird nur Reputation angezeigt, die der Benutzer teilt.', 9 | 'Updated' => 'Aktualisiert', 10 | 'Visible for other users.' => 'Sichtbar für andere Benutzer.', 11 | 'Share' => 'Teilen', 12 | 'Save' => 'Speichern' 13 | ); -------------------------------------------------------------------------------- /messages/de/views_adminReputation_show.php: -------------------------------------------------------------------------------- 1 | Space member reputation' => "Space-Mitglieder Reputation", 7 | 'In the area below, you see how much reputation each member inside this space has gained.' => "Im unteren Bereich ist sichtbar, wie viel Reputation die Benutzer innerhalb dieses Spaces erreicht haben.", 8 | 'User' => "Benutzer", 9 | 'Reputation' => "Reputation", 10 | 'Reputation score of this user' => 'Reputation dieses Nutzers', 11 | 'Export as CSV' => "Export als CSV", 12 | 'Update' => 'Aktualisieren', 13 | 'Configuration' => 'Konfiguration', 14 | 'Last Update: ' => 'Letzte Aktualisierung vor: ', 15 | ' minutes ago' => ' Minuten' 16 | ); -------------------------------------------------------------------------------- /views/space/index.php: -------------------------------------------------------------------------------- 1 | $space]); ?> 2 | 3 | This space is still empty!
Start by posting something here...'); 7 | } elseif ($isMember) { 8 | $emptyMessage = Yii::t('SpaceModule.views_space_index', 'This space is still empty!'); 9 | } else { 10 | $emptyMessage = Yii::t('SpaceModule.views_space_index', 'You are not member of this space and there is no public content, yet!'); 11 | } 12 | 13 | echo \humhub\modules\reputation\widgets\ReputationStream::widget(array( 14 | 'contentContainer' => $space, 15 | 'streamAction' => '/reputation/space/stream', 16 | 'messageStreamEmpty' => $emptyMessage, 17 | 'messageStreamEmptyWithFiltersCss' => $emptyMessage, 18 | )); 19 | ?> -------------------------------------------------------------------------------- /messages/de/forms_adminController_settings.php: -------------------------------------------------------------------------------- 1 | "Funktion", 7 | 'Logarithm base' => "Basis des Logarithmus", 8 | 'Creating posts or comments' => "Erzeugen von Beiträgen oder Kommentaren", 9 | 'Somebody liked the post' => "Jemand gefällt der Beitrag", 10 | 'Somebody marked the post as favorite' => "Jemand markiert den Beitrag als Favorit", 11 | 'Somebody comments the post' => "Jemand kommentiert den Beitrag", 12 | 'Daily limit for Users' => "Tageslimit für Benutzer", 13 | 'Update reputation data on hourly cron job' => 'Aktualisiere Reputation im stündlichem Cron-Job', 14 | 'Decrease weighting per post' => 'Abnehmende Gewichtung pro Beitrag', 15 | 'Exponential decrease for Ranking Rising' => 'Expotentielle Degression für die Sortierung "Aufsteigend"', 16 | 'Exponential decrease for Ranking Hot' => 'Expotentielle Degression für die Sortierung "Beliebt"', 17 | 'Time in hours for the "NEW" filter to show Content' => 'Zeit in Stunden für den Filter "NEU", um Inhalte anzuzeigen', 18 | ); -------------------------------------------------------------------------------- /migrations/m160403_114443_reputation_content.php: -------------------------------------------------------------------------------- 1 | createTable('reputation_content', array( 9 | 'id' => 'pk', 10 | 'score' => 'int(11) NOT NULL', 11 | 'score_short' => 'float(11) NOT NULL', 12 | 'score_long' => 'float(11) NOT NULL', 13 | 'content_id' => 'int(11) DEFAULT NULL', 14 | 'created_at' => 'datetime DEFAULT NULL', 15 | 'created_by' => 'int(11) DEFAULT NULL', 16 | 'updated_at' => 'datetime DEFAULT NULL', 17 | 'updated_by' => 'int(11) DEFAULT NULL', 18 | ), ''); 19 | } 20 | 21 | public function down() { 22 | echo "m160403_114443_reputation_content cannot be reverted.\n"; 23 | 24 | return false; 25 | } 26 | 27 | /* 28 | // Use safeUp/safeDown to run migration code within a transaction 29 | public function safeUp() 30 | { 31 | } 32 | 33 | public function safeDown() 34 | { 35 | } 36 | */ 37 | } 38 | -------------------------------------------------------------------------------- /migrations/m160403_114257_initial.php: -------------------------------------------------------------------------------- 1 | createTable('reputation_user', array( 9 | 'id' => 'pk', 10 | 'value' => 'int(11) NOT NULL', 11 | 'visibility' => 'tinyint(4) NOT NULL', 12 | 'user_id' => 'int(11) NOT NULL', 13 | 'space_id' => 'int(11) NOT NULL', 14 | 'wall_id' => 'int(11) NOT NULL', 15 | 'created_at' => 'datetime DEFAULT NULL', 16 | 'created_by' => 'int(11) DEFAULT NULL', 17 | 'updated_at' => 'datetime DEFAULT NULL', 18 | 'updated_by' => 'int(11) DEFAULT NULL', 19 | ), ''); 20 | } 21 | 22 | public function down() { 23 | echo "m160403_114257_initial cannot be reverted.\n"; 24 | 25 | return false; 26 | } 27 | 28 | /* 29 | // Use safeUp/safeDown to run migration code within a transaction 30 | public function safeUp() 31 | { 32 | } 33 | 34 | public function safeDown() 35 | { 36 | } 37 | */ 38 | } 39 | -------------------------------------------------------------------------------- /widgets/ReputationStream.php: -------------------------------------------------------------------------------- 1 | '-limit-', 19 | 'filters' => '-filter-', 20 | 'sort' => '-sort-', 21 | 'from' => '-from-', 22 | 'mode' => \humhub\modules\reputation\components\StreamAction::MODE_HOT 23 | ], $this->streamActionParams); 24 | 25 | if ($this->contentContainer) { 26 | return $this->contentContainer->createUrl($this->streamAction, $params); 27 | } else { 28 | array_unshift($params, $this->streamAction); 29 | return Url::to($params); 30 | } 31 | } 32 | 33 | public function run() { 34 | return $this->render('stream', ['streamUrl' => $this->getStreamUrl(), 'showFilters' => $this->showFilters, 'filters' => $this->filters]); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /widgets/SpaceUserReputationWidget.php: -------------------------------------------------------------------------------- 1 | getModule('reputation'); 27 | $function = $module->settings->space()->get('functions', ReputationBase::DEFAULT_FUNCTION); 28 | $query = ReputationUser::find()->where('space_id = :spaceId AND visibility = 1', [':spaceId' => $this->contentContainer->id])->orderBy("value DESC"); 29 | $query->limit($this->maxUsers); 30 | return $this->render('spaceUserReputationWidget', ['contentContainer' => $this->contentContainer, 'maxUsers' => $this->maxUsers, 'users' => $query->all(), 'function' => $function]); 31 | } 32 | 33 | } 34 | 35 | ?> -------------------------------------------------------------------------------- /models/DailyReputation.php: -------------------------------------------------------------------------------- 1 | daily_limit = PHP_INT_MAX; 20 | } else { 21 | $this->daily_limit = $daily_limit; 22 | } 23 | 24 | 25 | if ($score <= $daily_limit) { 26 | $this->score = $score; 27 | } else { 28 | $this->score = $daily_limit; 29 | } 30 | } 31 | 32 | /** 33 | * @return int: current score 34 | */ 35 | public function getScore() 36 | { 37 | return $this->score; 38 | } 39 | 40 | /** 41 | * @param $score : score to add 42 | */ 43 | public function addScore($score) 44 | { 45 | if ($this->score < $this->daily_limit) { 46 | $this->score += $score; 47 | } 48 | 49 | if ($this->score > $this->daily_limit) { 50 | $this->score = $this->daily_limit; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | 'reputation', 19 | 'class' => 'humhub\modules\reputation\Module', 20 | 'namespace' => 'humhub\modules\reputation', 21 | 'events' => [ 22 | ['class' => CronController::className(), 'event' => CronController::EVENT_ON_HOURLY_RUN, 'callback' => ['humhub\modules\reputation\Events', 'onCronHourlyRun']], 23 | ['class' => Content::className(), 'event' => Content::EVENT_BEFORE_DELETE, 'callback' => ['humhub\modules\reputation\Events', 'onContentDelete']], 24 | ['class' => IntegrityController::className(), 'event' => IntegrityController::EVENT_ON_RUN, 'callback' => ['humhub\modules\reputation\Events', 'onIntegrityCheck']], 25 | ['class' => User::className(), 'event' => User::EVENT_BEFORE_DELETE, 'callback' => ['humhub\modules\reputation\Events', 'onUserDelete']], 26 | ['class' => Membership::className(), 'event' => Membership::EVENT_BEFORE_DELETE, 'callback' => ['humhub\modules\reputation\Events', 'onSpaceMembershipDelete']], 27 | ['class' => Space::className(), 'event' => Space::EVENT_BEFORE_DELETE, 'callback' => ['humhub\modules\reputation\Events', 'onSpaceDelete']], 28 | ['class' => Sidebar::className(), 'event' => Sidebar::EVENT_RUN, 'callback' => ['humhub\modules\reputation\Events', 'onSpaceSidebar']], 29 | ['class' => ProfileMenu::className(), 'event' => ProfileMenu::EVENT_INIT, 'callback' => ['humhub\modules\reputation\Events', 'onProfileMenuInit']], 30 | ['class' => DefaultMenu::className(), 'event' => DefaultMenu::EVENT_INIT, 'callback' => ['humhub\modules\reputation\Events', 'onSpaceAdminMenuWidgetInit']], 31 | ['class' => Menu::className(), 'event' => Menu::EVENT_INIT, 'callback' => ['humhub\modules\reputation\Events', 'onSpaceMenuInit']], 32 | ], 33 | ]; 34 | ?> -------------------------------------------------------------------------------- /Module.php: -------------------------------------------------------------------------------- 1 | createUrl('/reputation/space/settings'); 54 | } 55 | } 56 | 57 | /** 58 | * @inheritdoc 59 | */ 60 | public function disableContentContainer(ContentContainerActiveRecord $container) { 61 | $this->settings->contentContainer($container)->deleteAll(); 62 | foreach (ReputationUser::findAll(['wall_id' => $container->wall_id]) as $reputationSpace) { 63 | $reputationSpace->delete(); 64 | } 65 | } 66 | 67 | /** 68 | * @inheritdoc 69 | */ 70 | public function getContentContainerName(ContentContainerActiveRecord $container) { 71 | return Yii::t('ReputationModule.base', 'Reputation'); 72 | } 73 | 74 | /** 75 | * @inheritdoc 76 | */ 77 | public function getContentContainerDescription(ContentContainerActiveRecord $container) { 78 | return Yii::t('ReputationModule.base', 'This Module Integrates A Reputation System Into HumHub. It Works With HumHub 1.1.x +.'); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /views/space/stats.php: -------------------------------------------------------------------------------- 1 | 10 |
11 |
12 | Space member reputation'); ?> 13 |
14 |
15 | 16 |

17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | $reputation->user_id]); 33 | if ($user == null)continue; 34 | ?> 35 | 36 | 44 | 48 | 49 | 60 | 61 | 62 | 63 | 64 |
23 | 25 |
37 | 38 | 32x32 42 | 43 | 45 | getDisplayName(), $user->getUrl()); ?> 46 |
47 |
50 | 51 | value); 54 | } else { 55 | echo Html::encode($reputation->value) . '%'; 56 | } 57 | ?> 58 | 59 |
65 |
66 | $pagination]); ?> 67 |
68 |
69 |
-------------------------------------------------------------------------------- /models/SpaceSettings.php: -------------------------------------------------------------------------------- 1 | 1), 37 | array('create_content', 'required'), 38 | array('create_content', 'number', 'min' => 0), 39 | array('smb_likes_content', 'required'), 40 | array('smb_likes_content', 'number', 'min' => 0), 41 | array('smb_favorites_content', 'required'), 42 | array('smb_favorites_content', 'number', 'min' => 0), 43 | array('smb_comments_content', 'required'), 44 | array('smb_comments_content', 'number', 'min' => 0), 45 | array('daily_limit', 'required'), 46 | array('smb_comments_content', 'number', 'min' => 0), 47 | array('decrease_weighting', 'required'), 48 | array('cron_job', 'required'), 49 | array('lambda_long', 'required'), 50 | array('lambda_long', 'double'), 51 | array('lambda_short', 'required'), 52 | array('lambda_short', 'double'), 53 | array('ranking_new_period', 'required'), 54 | array('ranking_new_period', 'number', 'min' => 1) 55 | ); 56 | } 57 | 58 | /** 59 | * Declares customized attribute labels. 60 | * If not declared here, an attribute would have a label that is 61 | * the same as its name with the first letter in upper case. 62 | */ 63 | public function attributeLabels() { 64 | return array( 65 | 'functions' => Yii::t('ReputationModule.forms_adminController_settings', 'Function'), 66 | 'logarithm_base' => Yii::t('ReputationModule.forms_adminController_settings', 'Logarithm base'), 67 | 'create_content' => Yii::t('ReputationModule.forms_adminController_settings', 'Creating posts or comments'), 68 | 'smb_likes_content' => Yii::t('ReputationModule.forms_adminController_settings', 'Somebody liked the post'), 69 | 'smb_favorites_content' => Yii::t('ReputationModule.forms_adminController_settings', 'Somebody marked the post as favorite'), 70 | 'smb_comments_content' => Yii::t('ReputationModule.forms_adminController_settings', 'Somebody comments the post'), 71 | 'daily_limit' => Yii::t('ReputationModule.forms_adminController_settings', 'Daily limit for Users'), 72 | 'decrease_weighting' => Yii::t('ReputationModule.forms_adminController_settings', 'Decrease weighting per post'), 73 | 'cron_job' => Yii::t('ReputationModule.forms_adminController_settings', 'Update reputation data on hourly cron job'), 74 | 'lambda_long' => Yii::t('ReputationModule.forms_adminController_settings', 'Exponential decrease for Ranking Hot'), 75 | 'lambda_short' => Yii::t('ReputationModule.forms_adminController_settings', 'Exponential decrease for Ranking Rising'), 76 | 'ranking_new_period' => Yii::t('ReputationModule.forms_adminController_settings', 'Time in hours for the "NEW" filter to show Content'), 77 | ); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /components/StreamAction.php: -------------------------------------------------------------------------------- 1 | getRequest()->get('sort', self::MODE_HOT); 47 | if ($sort === self::MODE_HOT) { 48 | $this->sort = $sort; 49 | } elseif ($sort === self::MODE_NEW) { 50 | $this->sort = $sort; 51 | } elseif ($sort === self::MODE_TOP) { 52 | $this->sort = $sort; 53 | } elseif ($sort === self::MODE_RISING) { 54 | $this->sort = $sort; 55 | } 56 | $this->setupCriteria(); 57 | } 58 | 59 | public function setupCriteria() { 60 | parent::setupCriteria(); 61 | /** 62 | * Setup Sorting 63 | */ 64 | if ($this->sort === self::MODE_HOT) { 65 | $this->activeQuery->leftJoin('reputation_content AS rc', 'rc.content_id = wall_entry.content_id'); 66 | if ($this->from != "") { 67 | $params = array(':from' => $this->from); 68 | $this->activeQuery->andWhere('wall_entry.content_id = :from', $params); 69 | } 70 | $this->activeQuery->orderBy('rc.score_long DESC'); 71 | } elseif ($this->sort === self::MODE_NEW) { 72 | $this->activeQuery->leftJoin('reputation_content AS rc', 'rc.content_id = wall_entry.content_id'); 73 | if ($this->from != "") { 74 | $params = array(':from' => $this->from); 75 | $this->activeQuery->andWhere('wall_entry.content_id = :from', $params); 76 | } 77 | $this->activeQuery->andWhere("content.created_at >= DATE_SUB(NOW(), INTERVAL :newRanking HOUR)", [':newRanking' => $this->spaceSettings['ranking_new_period']]); 78 | $this->activeQuery->orderBy('rc.score DESC'); 79 | } elseif ($this->sort === self::MODE_TOP) { 80 | $this->activeQuery->leftJoin('reputation_content AS rc', 'rc.content_id = wall_entry.content_id'); 81 | if ($this->from != "") { 82 | $params = array(':from' => $this->from); 83 | $this->activeQuery->andWhere('wall_entry.content_id = :from', $params); 84 | } 85 | $this->activeQuery->orderBy('rc.score DESC'); 86 | } elseif ($this->sort === self::MODE_RISING) { 87 | $this->activeQuery->leftJoin('reputation_content AS rc', 'rc.content_id = wall_entry.content_id'); 88 | if ($this->from != "") { 89 | $params = array(':from' => $this->from); 90 | $this->activeQuery->andWhere('wall_entry.content_id = :from', $params); 91 | } 92 | $this->activeQuery->orderBy('rc.score_short DESC'); 93 | } 94 | } 95 | 96 | } 97 | 98 | ?> -------------------------------------------------------------------------------- /controllers/AdminController.php: -------------------------------------------------------------------------------- 1 | no write access / 1 -> create links and edit own links / 2 -> full write access. * */ 24 | // public $accessLevel = 0; 25 | // 26 | // /** 27 | // * Automatically loads the underlying contentContainer (User/Space) by using 28 | // * the uguid/sguid request parameter 29 | // * 30 | // * @return boolean 31 | // */ 32 | // public function init() { 33 | // $retVal = parent::init(); 34 | // $this->accessLevel = $this->getAccessLevel(); 35 | // return $retVal; 36 | // } 37 | // 38 | // /** 39 | // * @return array action filters 40 | // */ 41 | // public function filters() { 42 | // return array( 43 | // 'accessControl', // perform access control for CRUD operations 44 | // ); 45 | // } 46 | // 47 | // /** 48 | // * Get the acces level to the linklist of the currently logged in user. 49 | // * @return number 0 -> no write access / 1 -> create links and edit own links / 2 -> full write access 50 | // */ 51 | // private function getAccessLevel() { 52 | // if ($this->contentContainer instanceof User) { 53 | // return $this->contentContainer->id == Yii::$app->user->id ? 2 : 0; 54 | // } else if ($this->contentContainer instanceof Space) { 55 | // return $this->contentContainer->isAdmin(Yii::$app->user->id) ? 2 : 1; 56 | // } 57 | // } 58 | 59 | /* 60 | * Allow only space admins to see configuration 61 | */ 62 | public function beforeAction($action) { 63 | if (!$this->contentContainer->permissionManager->can(new \humhub\modules\content\permissions\ManageContent())) { 64 | throw new HttpException(400, 'Access denied!'); 65 | } 66 | return parent::beforeAction($action); 67 | } 68 | 69 | /* 70 | * Initialize user reputation overview 71 | * 72 | * force update can be triggered by appending &forceUpdate at the end of the url 73 | * otherwise cache is used 74 | */ 75 | 76 | public function actionIndex() { 77 | $forceUpdate = false; 78 | if (Yii::$app->request->get('forceUpdate') === 1) { 79 | $forceUpdate = true; 80 | } 81 | $space = $this->contentContainer; 82 | ReputationUser::updateUserReputation($space, $forceUpdate); 83 | $params = [':spaceId' => $space->id]; 84 | $query = ReputationUser::find(); 85 | $query->where('space_id=:spaceId', $params); 86 | $query->orderBy('reputation_user.value DESC'); 87 | 88 | $countQuery = clone $query; 89 | $itemCount = $countQuery->count(); 90 | $pagination = new \yii\data\Pagination(['totalCount' => $itemCount, 'pageSize' => 10]); 91 | $query->offset($pagination->offset)->limit($pagination->limit); 92 | 93 | $reputations = $query->all(); 94 | 95 | $module = Yii::$app->getModule('reputation'); 96 | $function = $module->settings->space()->get('functions', ReputationBase::DEFAULT_FUNCTION); 97 | 98 | $lastUpdatedBefore = $this->GetLastUpdateTimeInMinutes($reputations); 99 | 100 | return $this->render('index', array( 101 | 'function' => $function, 102 | 'space' => $space, 103 | 'reputations' => $reputations, 104 | 'pagination' => $pagination, 105 | 'lastUpdatedBefore' => $lastUpdatedBefore, 106 | )); 107 | } 108 | 109 | /** 110 | * Get time in minutes since last update occurred 111 | * 112 | * @param $criteria 113 | * @return string: The time elapsed since the last update 114 | */ 115 | private function GetLastUpdateTimeInMinutes($criteria) { 116 | $now = new \DateTime(); 117 | $lastUpdateTime = new \DateTime($criteria[0]->updated_at); 118 | $lastUpdatedBefore = $lastUpdateTime->diff($now)->format('%i'); 119 | 120 | return $lastUpdatedBefore; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /controllers/ProfileController.php: -------------------------------------------------------------------------------- 1 | no write access / 1 -> create links and edit own links / 2 -> full write access. * */ 18 | // public $accessLevel = 0; 19 | // 20 | // /** 21 | // * Automatically loads the underlying contentContainer (User/Space) by using 22 | // * the uguid/sguid request parameter 23 | // * 24 | // * @return boolean 25 | // */ 26 | // public function init() { 27 | // $retVal = parent::init(); 28 | // $this->accessLevel = $this->getAccessLevel(); 29 | // return $retVal; 30 | // } 31 | // 32 | // /** 33 | // * @return array action filters 34 | // */ 35 | // public function filters() { 36 | // return array( 37 | // 'accessControl', // perform access control for CRUD operations 38 | // ); 39 | // } 40 | // 41 | // /** 42 | // * Get the acces level to the linklist of the currently logged in user. 43 | // * @return number 0 -> no write access / 1 -> create links and edit own links / 2 -> full write access 44 | // */ 45 | // private function getAccessLevel() { 46 | // if ($this->contentContainer instanceof User) { 47 | // return $this->contentContainer->id == Yii::$app->user->id ? 2 : 0; 48 | // } else if ($this->contentContainer instanceof Space) { 49 | // return $this->contentContainer->isAdmin(Yii::$app->user->id) ? 2 : 1; 50 | // } 51 | // } 52 | // 53 | // /** 54 | // * Specifies the access control rules. 55 | // * This method is used by the 'accessControl' filter. 56 | // * @return array access control rules 57 | // */ 58 | // public function accessRules() { 59 | // return array( 60 | // array('allow', // allow authenticated user to perform 'create' and 'update' actions 61 | // 'users' => array('@'), 62 | // ), 63 | // array('deny', // deny all users 64 | // 'users' => array('*'), 65 | // ), 66 | // ); 67 | // } 68 | 69 | /** 70 | * Action that renders the list view. 71 | * @see views/profile/config.php 72 | */ 73 | public function actionConfig() { 74 | 75 | if (isset($_POST['reputationUsers'])) { 76 | $user = User::findIdentityByAccessToken(Yii::$app->getRequest()->get('uguid')); 77 | $userSpaces = Membership::GetUserSpaces($user->id); 78 | foreach ($userSpaces as $space) { 79 | $getPost = Yii::$app->getRequest()->post('reputationUser_' . $space->id); 80 | if (isset($getPost)) { 81 | $userSettings = $getPost; 82 | $params = [':spaceId' => $space->id, ':userId' => $user->id]; 83 | $query = ReputationUser::find(); 84 | $query->where('space_id=:spaceId AND user_id=:userId', $params); 85 | $result = $query->one(); 86 | if ($result != null) { 87 | $result->visibility = (isset($userSettings['visibility']) && $userSettings['visibility'] == 1) ? 1 : 0; 88 | $result->save(); 89 | } 90 | } 91 | } 92 | } 93 | // TODO was ist das?! 94 | //Yii::$app->user->setFlash('data-saved', Yii::t('SpaceModule.controllers_AdminController', 'Saved')); 95 | 96 | $user = User::findIdentityByAccessToken(Yii::$app->getRequest()->get('uguid')); 97 | $params = [':userId' => $user->id]; 98 | $query = ReputationUser::find(); 99 | $query->where('user_id=:userId', $params); 100 | $query->leftJoin('space', 'space.id = reputation_user.space_id'); 101 | $query->orderBy('reputation_user.space_id ASC'); 102 | $result = $query->all(); 103 | 104 | $itemCount = count($result); 105 | 106 | $pagination = new \yii\data\Pagination(['totalCount' => $itemCount]); 107 | return $this->render('config', array( 108 | 'contentContainer' => $this->contentContainer, 109 | 'user' => $user, 110 | 'reputations' => $result, 111 | 'pagination' => $pagination, 112 | )); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /widgets/views/stream.php: -------------------------------------------------------------------------------- 1 | registerJs('var streamUrl="' . $streamUrl . '"', View::POS_BEGIN); 7 | 8 | $jsLoadWall = "s = new Stream('#wallStream');\n"; 9 | $wallEntryId = (int) Yii::$app->request->getQueryParam('wallEntryId'); 10 | if ($wallEntryId != "") { 11 | $jsLoadWall .= "s.showItem(" . $wallEntryId . ");\n"; 12 | } else { 13 | $jsLoadWall .= "s.showStream();\n"; 14 | } 15 | $jsLoadWall .= "currentStream = s;\n"; 16 | $jsLoadWall .= "mainStream = s;\n"; 17 | $jsLoadWall .= "$('#btn-load-more').click(function() { currentStream.loadMore(); })\n"; 18 | $this->registerJs($jsLoadWall, View::POS_READY); 19 | 20 | if (Yii::$app->settings->get('horImageScrollOnMobile')) 21 | $this->registerJs(new \yii\web\JsExpression(" 22 | if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent)) { 23 | $('#wallStream').addClass('mobile'); 24 | }"), View::POS_READY); 25 | 26 | $this->registerJsVar('defaultStreamSort', 'h'); 27 | ?> 28 | 29 | context->showFilters) { ?> 30 | 61 | 62 | 63 |
64 | 65 | 66 | 94 | 95 | 96 | 107 |
108 | 109 | 110 |
111 | 112 |

113 |
-------------------------------------------------------------------------------- /widgets/views/spaceUserReputationWidget.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | 'space-members-panel']); ?> 14 |
Space User Reputation'); ?>
15 |
16 | 0) : ?> 17 | 66 | 67 |
68 |
69 |

70 | 72 |

73 |
74 |
75 |
76 | 77 |
78 | 79 |
80 | 81 | 82 |
83 | Yii::$app->user->guid), array('class' => 'btn btn-default btn-sm')); ?> 84 |
85 |
86 |
87 |
88 | -------------------------------------------------------------------------------- /controllers/SpaceController.php: -------------------------------------------------------------------------------- 1 | contentContainer); 27 | return array( 28 | 'stream' => array( 29 | 'class' => \humhub\modules\reputation\components\StreamAction::className(), 30 | 'sort' => \humhub\modules\reputation\components\StreamAction::MODE_HOT, 31 | 'contentContainer' => $this->contentContainer, 32 | 'spaceSettings' => $spaceSettings 33 | ), 34 | ); 35 | } 36 | 37 | /** 38 | * Shows the reputation_content space 39 | */ 40 | public function actionIndex() { 41 | $forceUpdate = false; 42 | if (isset($_GET['forceUpdate'])) { 43 | $forceUpdate = true; 44 | } 45 | 46 | $space = $this->contentContainer; 47 | $canCreatePosts = $space->permissionManager->can(new \humhub\modules\post\permissions\CreatePost()); 48 | $isMember = $space->isMember(); 49 | 50 | ReputationContent::updateContentReputation($space, $forceUpdate); 51 | return $this->render('index', [ 52 | 'space' => $space, 53 | 'canCreatePosts' => $canCreatePosts, 54 | 'isMember' => $isMember 55 | ]); 56 | } 57 | 58 | /** 59 | * Initialize settings view 60 | * Allows the user to set a bunch of parameters for reputation settings inside this space 61 | */ 62 | public function actionSettings() { 63 | if (!$this->contentContainer->permissionManager->can(new \humhub\modules\content\permissions\ManageContent())) { 64 | throw new HttpException(400, 'Access denied!'); 65 | } 66 | $space = $this->getSpace(); 67 | $module = Yii::$app->getModule('reputation'); 68 | $form = new SpaceSettings(); 69 | 70 | if ($form->load(Yii::$app->request->post()) && $form->validate()) { 71 | $form->functions = $module->settings->space()->set('functions', $form->functions); 72 | $form->logarithm_base = $module->settings->space()->set('logarithm_base', $form->logarithm_base); 73 | $form->create_content = $module->settings->space()->set('create_content', $form->create_content); 74 | $form->smb_likes_content = $module->settings->space()->set('smb_likes_content', $form->smb_likes_content); 75 | $form->smb_favorites_content = $module->settings->space()->set('smb_favorites_content', $form->smb_favorites_content); 76 | $form->smb_comments_content = $module->settings->space()->set('smb_comments_content', $form->smb_comments_content); 77 | $form->daily_limit = $module->settings->space()->set('daily_limit', $form->daily_limit); 78 | $form->decrease_weighting = $module->settings->space()->set('decrease_weighting', $form->decrease_weighting); 79 | $form->cron_job = $module->settings->space()->set('cron_job', $form->cron_job); 80 | $form->ranking_new_period = $module->settings->space()->set('ranking_new_period', $form->ranking_new_period); 81 | $form->lambda_short = $module->settings->space()->set('lambda_short', $form->lambda_short); 82 | $form->lambda_long = $module->settings->space()->set('lambda_long', $form->lambda_long); 83 | 84 | ReputationContent::updateContentReputation($space, true); 85 | ReputationUser::updateUserReputation($space, true); 86 | 87 | $this->redirect(['/reputation/space/settings', 'sguid' => $space->guid]); 88 | } else { 89 | $spaceSettings = ReputationBase::getSpaceSettings($space); 90 | $form->functions = $spaceSettings['functions']; 91 | $form->logarithm_base = $spaceSettings['logarithm_base']; 92 | $form->create_content = $spaceSettings['create_content']; 93 | $form->smb_likes_content = $spaceSettings['smb_likes_content']; 94 | $form->smb_favorites_content = $spaceSettings['smb_favorites_content']; 95 | $form->smb_comments_content = $spaceSettings['smb_comments_content']; 96 | $form->daily_limit = $spaceSettings['daily_limit']; 97 | $form->decrease_weighting = $spaceSettings['decrease_weighting']; 98 | $form->cron_job = $spaceSettings['cron_job']; 99 | $form->ranking_new_period = $spaceSettings['ranking_new_period']; 100 | $form->lambda_short = $spaceSettings['lambda_short']; 101 | $form->lambda_long = $spaceSettings['lambda_long']; 102 | } 103 | return $this->render('settings', array('model' => $form, 'space' => $space)); 104 | } 105 | 106 | public function actionStats() { 107 | $space = $this->getSpace(); 108 | ReputationUser::updateUserReputation($space); 109 | $params = [':spaceId' => $space->id]; 110 | $reputations = ReputationUser::find()->where('space_id=:spaceId AND visibility = 1', $params)->all(); 111 | $itemCount = count($reputations); 112 | $pagination = new \yii\data\Pagination(['totalCount' => $itemCount]); 113 | $module = Yii::$app->getModule('reputation'); 114 | $function = $module->settings->space()->get('functions', ReputationBase::DEFAULT_FUNCTION); 115 | return $this->render('stats', array( 116 | 'function' => $function, 117 | 'space' => $space, 118 | 'reputations' => $reputations, 119 | 'pagination' => $pagination, 120 | )); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Reputation module for HumHub [working on 1.1.x (Yii2)] 2 | ### Under Development - please report issues! 3 | 4 | This module integrates a reputation system into HumHub. 5 | 6 | ##About 7 | __Author:__ Anton Kurnitzky / Philipp Horna (Port to Humhub 1.1.x) 8 | 9 | This module was developed as part of my master thesis at the [Technische Hochschule Nürnberg](http://www.th-nuernberg.eu). 10 | It's default settings are designed to work for a social network in an educational context. By customizing the settings you can probably use this in any other context too. 11 | ##Installation 12 | * Copy the module to your HumHub module folder (.../humhub/protected/modules/reputation) 13 | * The folder name must be "reputation" 14 | * Activate the module on the HumHub admin page. Set it as default for space to activate it in all spaces. You should set is as default for user profiles so users can see their own reputation. 15 | * If not set to default for spaces, activate the module on each space you want to get an entry in the space navigation menu 16 | * If not set to default for user profiles, a user has to manually activate this module on their profile page. 17 | 18 | ##Usage 19 | ### Everybody 20 | Inside the space menu you will see a new menu called "Hot". This menu provides four new sorting options that are possible trough the reputation system. The ranking of the posts depends on the sorting option and your module configuration. 21 | ![Ranking](https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/master/assets/reputation_space_view_new_sorting.png) 22 | 23 | * Hot: The default sorting option. This option prioritizes posts that are younger than on week and have a high number of likes, favorites and comments. 24 | * Top: This option shows you the best rated posts at the top. 25 | * New: Show posts created in the last 36 hours. Sorted by best rating. 26 | * Rising: Similar to hot. But it prioritizes posts that are younger than 24 hours. 27 | 28 | ### Space-Admins 29 | As Space-Admin you will get a new menu item called "User Reputation". Inside this menu you will see a list of all space members and their respective reputation score. 30 | ![UserReputation](https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/master/assets/reputation_spaceadmin_view.png) 31 | At the bottom you have three actions: 32 | 33 | * Update: Refresh the data 34 | * Configuration: Opens the module configuration for this space. [More info](#Configuration). 35 | 36 | ### Users 37 | Inside your Profile menu you will see a new menu "Reputation". 38 | ![ProfileReputation](https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/master/assets/reputation_user_settings.png) 39 | Here you see all the spaces you're a member of and that have the reputation module enabled. 40 | You can see your reputation score and the time this score was last updated. 41 | To share your reputation with other users you can enable this per space. If you enable this every other user inside the HumHub installation can see your reputation. This is an explicit opt-in option. To set this to enabled for all users you have to set the visible column in the table reputation_user to 1. 42 | 43 | ##Configuration 44 | ![ReputationSettings](https://raw.githubusercontent.com/globeFrEak/humhub-modules-reputation/master/assets/reputation_space_config.png) 45 | 46 | * Function: Use a linear function that just show the score the user reached or use a logarithmic function. The logarithmic function is a good motivator because you reach a high score with little effort. For pure comparision the linear setting should be fine. **Default** is Logarithmic. 47 | * Base: This is only visible if you use the logarithmic function. Set the base for the logarithmic function. When you set it to 1000 a user needs 1000 points to get a value of 100%. But he only needs 32 points to get above 50%. **Default** is 100. 48 | * Creating posts or comments: How much points should a user get for creating a post or comment. **Default** is 1. 49 | * Somebody liked the post: How much points should the post creator get when somebody likes the post. The user who clicks like doesn't get any points. **Default** is 2. 50 | * Somebody marked the post as favorite: Same as "Somebody liked the post". **Default** is 2. 51 | * Somebody comments the post: How much points should the creator of the original post get if somebody (other) posts a comment. The posts of the comment also gets points according to the setting "Creating posts or comments". **Default** is 3. 52 | * Daily limit: Set a limit of points a user can reach on a single day. Set this to zero if you don't want to use a daily limit. **Default** is 15. 53 | * Decrease weighting: This setting decreases the weighting of additional likes, comments, favorites on a post. It divides any additional like by the count of likes. So for example 2 likes with the "Somebody liked the post" setting set to 2 generates only 3 points for the post creator. 2 + 2/2. 5 likes is 2 + 2/2 + 2/3 + 2/4 + 2/5 = 4.56. The exact formula is: ![equation](http://www.sciweavers.org/tex2img.php?eq=%20%5Csum_1%5En%20%20%5Cfrac%7Bn%7D%7Bweighting%7D%20&bc=White&fc=Black&im=jpg&fs=12&ff=arev&edit=0) The purpose of this setting is to make it really hard to get a high reputation score from just one post. **Default** is Yes. 54 | 55 | ##Technical 56 | ### Update interval 57 | This module uses the [hourly cron job of HumHub](https://www.humhub.org/docs/guide-admin-installation.html#enable-cron-jobs). So make sure you properly configured the cron job on your system. If you want more up to date data you should change the cron schedule. 58 | 59 | It also uses [Yii Caching](http://www.yiiframework.com/doc/guide/1.1/en/caching.overview) to improve loading times. The cache is set to 15minutes. So if you open a view (e.g. "User Reptuation") it will use cached data if it's newer than 15 minutes. Otherwise it will refresh the data before displaying the view. 60 | If you want to see more up to date data you can always click on the "Update" button. 61 | 62 | That means you should never see data that's older than 15 minutes. 63 | 64 | ### Reputation sources 65 | Posts, Likes, Favorites and comments are used as reputation sources. Therefore this module uses the associated database tables (content, post, like, comment, favorite, etc.). 66 | 67 | ### Calculation 68 | The reputation calculation is performed dynamically. This means that on every calculation everything will be reconsidered. 69 | That gives you the advantage that you can change the module settings any time and the recalculated values will take the new settings into account. So you could just test a bunch of different settings and see their outcome. 70 | -------------------------------------------------------------------------------- /views/admin/index.php: -------------------------------------------------------------------------------- 1 | 12 | 13 |
14 |
15 | Space member reputation'); ?> 16 |
17 | $space]); ?> 18 |
19 | 20 | 21 | ' . $lastUpdatedBefore . '' ?> 22 | 23 |
' ?> 24 | 25 |

26 | 0) : ?> 27 | 88 | 89 |
90 |
91 |

92 | 94 |

95 |
96 |
97 | 98 | 99 |
100 | $pagination]); ?> 101 |
102 | 103 |
104 | $space->guid, 'forceUpdate' => 1), array('class' => 'btn btn-primary')); ?> 105 | 106 |
107 | $space->guid), array('class' => 'btn btn-warning')); ?> 108 |
109 |
110 |
111 | -------------------------------------------------------------------------------- /views/profile/config.php: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 |
13 | Space reputation'); ?> 14 |
15 |
16 | 17 | 18 |
19 | user->id != $user->id) echo Yii::t('ReputationModule.views_profileReputation_show', 'You can only see reputation the user shares.'); ?> 20 | 21 |

22 | 23 | user->id === $user->id)): ?> 24 | ['config', 'uguid' => $user->guid], 27 | 'method' => 'post', 28 | 'id' => 'configure-form', 29 | 'enableAjaxValidation' => false, 30 | ]); 31 | ?> 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | user->id == $user->id): ?> 40 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | $reputation->space_id]); 53 | 54 | if ($space == null || ($reputation->visibility == 0 && Yii::$app->user->id != $reputation->user_id)) 55 | continue; 56 | 57 | // Hidden input to get users on this page 58 | echo Html::hiddenInput("reputationUsers[" . $reputation->space_id . "]", $reputation->space_id); 59 | 60 | // Hidden field to get users on this page 61 | echo Html::hiddenInput('reputationUser_' . $reputation->space_id . "[placeholder]", 1); 62 | 63 | $module = Yii::$app->getModule('reputation'); 64 | $setting_function = $module->settings->contentContainer($contentContainer)->get('functions', ReputationBase::DEFAULT_FUNCTION); 65 | ?> 66 | 67 | 68 | 81 | 85 | 86 | 97 | user->id == $reputation->user_id): ?> 98 | 112 | 113 | 116 | 117 | 118 | 119 | 120 | 121 |
41 | 43 |
69 | $space, 73 | 'width' => 32, 74 | 'htmlOptions' => [ 75 | 'class' => 'current-space-image', 76 | ], 77 | 'link' => 'true', 78 | ]); 79 | ?> 80 | 82 | getDisplayName(), $space->getUrl()); ?> 83 |
84 |
87 | 96 | 99 | 100 |
101 | 110 |
111 |
114 | updated_at; ?> 115 |
122 | 123 |
124 | $pagination]); ?> 125 |
126 | 127 | user->id == $user->id)): ?> 128 |
129 | 'btn btn-primary')); ?> 130 | 131 | 132 | 133 | 134 | 135 |
136 |
137 | -------------------------------------------------------------------------------- /models/ReputationContent.php: -------------------------------------------------------------------------------- 1 | true), 50 | array('created_at, updated_at', 'safe'), 51 | ); 52 | } 53 | 54 | /** 55 | * @return array relational rules. 56 | */ 57 | public function relations() { 58 | return array( 59 | 'content' => array(self::BELONGS_TO, 'Content', 'content_id'), 60 | ); 61 | } 62 | 63 | /** 64 | * @return array customized attribute labels (name=>label) 65 | */ 66 | public function attributeLabels() { 67 | return array( 68 | 'id' => 'ID', 69 | 'score' => 'Score', 70 | 'score_short' => 'Score short', 71 | 'score_long' => 'Score long', 72 | 'content_id' => 'Content', 73 | 'created_at' => 'Created At', 74 | 'created_by' => 'Created By', 75 | 'updated_at' => 'Updated At', 76 | 'updated_by' => 'Updated By', 77 | ); 78 | } 79 | 80 | /** 81 | * Recalculate content reputation for a space 82 | * 83 | * @param $space : The space where the content should be updated 84 | * @param bool $forceUpdate : Ignore cache 85 | */ 86 | public function updateContentReputation($container, $forceUpdate = false) { 87 | $spaceSettings = ReputationBase::getSpaceSettings($container); 88 | $spaceContent = ReputationBase::getContentFromSpace($container); 89 | $lambda_short = $spaceSettings['lambda_short']; 90 | $lambda_long = $spaceSettings['lambda_long']; 91 | 92 | foreach ($spaceContent as $content) { 93 | 94 | $cacheId = 'reputation_space_content' . '_' . $container->wall_id . '_' . $content->id; 95 | $contentReputation = Yii::$app->cache->get($cacheId); 96 | 97 | if ($contentReputation === false || $forceUpdate === true) { 98 | $contentReputation = []; 99 | // get all reputation_content objects from this space 100 | $params = array('content_id' => $content->id); 101 | $contentReputation = ReputationContent::findOne($params); 102 | if ($contentReputation == null) { 103 | // Create new reputation_content entry 104 | $contentReputation = new ReputationContent(); 105 | $contentReputation->content_id = $content->id; 106 | $contentReputation->created_by = $content->created_by; 107 | } 108 | $score = ReputationContent::calculateContentReputationScore($content, $container, $forceUpdate); 109 | $contentReputation->score = $score; 110 | $timePassed = ReputationContent::getTimeInHoursSinceContentCreation($content->created_at); 111 | $contentReputation->score_long = ReputationContent::getDecayedScore($score, $timePassed, $lambda_long); 112 | $contentReputation->score_short = ReputationContent::getDecayedScore($score, $timePassed, $lambda_short); 113 | $contentReputation->updated_at = date('Y-m-d H:i:s'); 114 | $contentReputation->updated_by = $content->updated_by; 115 | $contentReputation->save(false); 116 | 117 | Yii::$app->cache->set($cacheId, $contentReputation, ReputationBase::CACHE_TIME_SECONDS); 118 | } 119 | } 120 | } 121 | 122 | /** 123 | * Returns the static model of the specified AR class. 124 | * Please note that you should have this exact method in all your CActiveRecord descendants! 125 | * @param string $className active record class name. 126 | * @return ReputationContent the static model class 127 | * 128 | public static function model($className = __CLASS__) 129 | { 130 | return parent::model($className); 131 | } 132 | * 133 | */ 134 | /* 135 | * Calculate the reputation score for all content objects inside this space 136 | * Use the count of likes, favorites and comments and the reputation settings to calculate this 137 | * 138 | * @param $content 139 | * @param $space 140 | */ 141 | public function calculateContentReputationScore($content, $container, $forceUpdate) { 142 | $spaceSettings = ReputationBase::getSpaceSettings($container); 143 | $cacheId = 'likes_earned_cache_' . $content->id; 144 | $likes = ReputationBase::getLikesFromContent($content, $content->created_by, $cacheId, $forceUpdate); 145 | if ($container->isModuleEnabled('favorite')) { 146 | $scoreCount = 1; 147 | // now count the favorites this content earned from other users 148 | $cacheId = 'favorites_earned_cache_' . $content->id; 149 | $favorites = ReputationBase::getFavoritesFromContent($content, $content->created_by, $cacheId, $forceUpdate); 150 | } else { 151 | $favorites = array(); 152 | } 153 | $cacheId = 'comments_earned_cache_' . $content->id; 154 | $comments = ReputationBase::getCommentsFromContent($content, $content->created_by, $cacheId, true, $forceUpdate); 155 | return (count($likes) * $spaceSettings['smb_likes_content'] + count($favorites) * $spaceSettings['smb_favorites_content'] + count($comments) * $spaceSettings['smb_comments_content']); 156 | } 157 | 158 | /** 159 | * Calculate time in hours since the content was created 160 | * 161 | * @param $createdAt : The creation time of the content object 162 | * @return int: Time in hours since content was created 163 | */ 164 | private function getTimeInHoursSinceContentCreation($createdAt) { 165 | $now = new DateTime(); 166 | $createdTime = new DateTime($createdAt); 167 | $timeSinceCreation = round(($now->getTimestamp() - $createdTime->getTimestamp()) / 3600, 2, PHP_ROUND_HALF_UP); 168 | 169 | // do not allow zero because this value is used as divisor later 170 | if ($timeSinceCreation == 0) { 171 | $timeSinceCreation = 1; 172 | } 173 | 174 | return $timeSinceCreation; 175 | } 176 | 177 | /** 178 | * Return the value of a decayed score, that is, 179 | * a value that decreases over time. 180 | * The formula used for the decay is exp(-lambda * t^2), 181 | * where lambda is damping factor and t is the age 182 | * of the object in seconds. 183 | * If lambda is 0, no decay takes place, whatsoever. 184 | * 185 | * @param int /float Initial score. 186 | * @param int Time in hours that has passed since the score was set. 187 | * @param float Damping factor. 188 | * @return float Decayed score. 189 | */ 190 | public function getDecayedScore($score, $age, $lambda = 0) { 191 | // Actual calculation: exp(-lambda * t^2) 192 | return ($score + 1) * exp(-$lambda * $age * $age); 193 | } 194 | 195 | } 196 | -------------------------------------------------------------------------------- /Events.php: -------------------------------------------------------------------------------- 1 | sender; 27 | $spaces = Space::find()->all(); 28 | $count_spaces = count($spaces); 29 | $processed = 0; 30 | Console::startProgress($processed, $count_spaces, '[Module] calculate REPUTATION for Spaces...', false); 31 | foreach ($spaces as $space) { 32 | if ($space->isModuleEnabled('reputation')) { 33 | $cronJobEnabled = ReputationBase::getSpaceSettings($space); 34 | if ($cronJobEnabled['cron_job'] = 1) { 35 | self::onSpaceEnabledAsDefault($space); 36 | ReputationUser::updateUserReputation($space, true); 37 | ReputationContent::updateContentReputation($space, true); 38 | Console::updateProgress(++$processed, $count_spaces); 39 | } 40 | } 41 | } 42 | Console::endProgress(true); 43 | $controller->stdout('done - ' . $processed . ' spaces checked.' . PHP_EOL, Console::FG_GREEN); 44 | 45 | $users = User::find()->all(); 46 | $count_users = count($spaces); 47 | Console::startProgress($processed, $count_users, '[Module] calculate REPUTATION for Users...', false); 48 | foreach ($users as $user) { 49 | if ($user->isModuleEnabled('reputation')) { 50 | self::onUserEnabledAsDefault($user); 51 | Console::updateProgress(++$processed, $count_users); 52 | } 53 | } 54 | Console::endProgress(true); 55 | $controller->stdout('done - ' . $processed . ' spaces checked.' . PHP_EOL, Console::FG_GREEN); 56 | } 57 | 58 | /** 59 | * Set Reputation Module when it is enabled as default on the Space 60 | * 61 | * @param $space Object 62 | */ 63 | public static function onSpaceEnabledAsDefault($space) { 64 | $moduleEnabled = \humhub\modules\space\models\Module::findOne(['space_id' => $space->id, 'module_id' => 'reputation']); 65 | $moduleAsDefaultOn = \humhub\modules\space\models\Module::find()->where(['space_id' => 0, 'module_id' => 'reputation', 'state' => 1])->orWhere(['space_id' => 0, 'module_id' => 'reputation', 'state' => 2])->one(); 66 | if ($moduleEnabled === NULL && $moduleAsDefaultOn != NULL) { 67 | $enableModule = new \humhub\modules\space\models\Module(); 68 | $enableModule->module_id = 'reputation'; 69 | $enableModule->space_id = $space->id; 70 | $enableModule->state = $moduleAsDefaultOn->state; 71 | $enableModule->save(); 72 | } 73 | } 74 | 75 | /** 76 | * Set Reputation Module when it is enabled as default on the User 77 | * 78 | * @param $user Object 79 | */ 80 | public static function onUserEnabledAsDefault($user) { 81 | $moduleEnabled = \humhub\modules\user\models\Module::findOne(['user_id' => $user->id, 'module_id' => 'reputation']); 82 | $moduleAsDefaultOn = \humhub\modules\user\models\Module::find()->where(['user_id' => 0, 'module_id' => 'reputation', 'state' => 1])->orWhere(['user_id' => 0, 'module_id' => 'reputation', 'state' => 2])->one(); 83 | if ($moduleEnabled === NULL && $moduleAsDefaultOn != NULL) { 84 | $enableModule = new \humhub\modules\user\models\Module(); 85 | $enableModule->module_id = 'reputation'; 86 | $enableModule->user_id = $user->id; 87 | $enableModule->state = $moduleAsDefaultOn->state; 88 | $enableModule->save(); 89 | } 90 | } 91 | 92 | /** 93 | * On user delete, also delete all reputation of this user 94 | * 95 | * @param type $event 96 | */ 97 | public static function onUserDelete($event) { 98 | foreach (ReputationUser::findAll(['user_id' => $event->sender->id]) as $reputationUser) { 99 | $reputationUser->delete(); 100 | } 101 | } 102 | 103 | /** 104 | * When a user leaves a space remove the user reputation for this space 105 | * 106 | * @param type $event 107 | */ 108 | public static function onSpaceMembershipDelete($event) { 109 | foreach (ReputationUser::findAll(['user_id' => $event->sender->user_id, 'space_id' => $event->sender->space_id]) as $reputationUser) { 110 | $reputationUser->delete(); 111 | } 112 | } 113 | 114 | /** 115 | * On space delete, also delete all reputation of this space 116 | * 117 | * @param type $event 118 | */ 119 | public static function onSpaceDelete($event) { 120 | foreach (ReputationUser::findAll(['wall_id' => $event->sender->contentcontainer_id]) as $reputationSpace) { 121 | $reputationSpace->delete(); 122 | } 123 | } 124 | 125 | /** 126 | * On content delete, also delete the content reputation 127 | * 128 | * @param type $event 129 | */ 130 | public static function onContentDelete($event) { 131 | foreach (ReputationContent::findAll(['content_id' => $event->sender->id]) as $reputationContent) { 132 | $reputationContent->delete(); 133 | } 134 | } 135 | 136 | /** 137 | * Show reputation menu in user profile 138 | * 139 | * @param type $event 140 | */ 141 | public static function onProfileMenuInit($event) { 142 | if ($event->sender->user !== null && $event->sender->user->isModuleEnabled('reputation')) { 143 | $event->sender->addItem(array( 144 | 'label' => Yii::t('ReputationModule.base', 'User Reputation'), 145 | 'group' => 'profile', 146 | 'url' => $event->sender->user->createUrl('/reputation/profile/config'), 147 | 'isActive' => (Yii::$app->controller->module && Yii::$app->controller->module->id == 'reputation' && Yii::$app->controller->id == 'profile' && Yii::$app->controller->action->id == 'config'), 148 | 'sortOrder' => 1000, 149 | )); 150 | } 151 | } 152 | 153 | /* 154 | * Show reputation menu in space admin menu 155 | * 156 | * @param type $event 157 | */ 158 | 159 | public static function onSpaceAdminMenuWidgetInit($event) { 160 | if ($event->sender->space !== null && $event->sender->space->isModuleEnabled('reputation') && $event->sender->space->isAdmin()) { 161 | $event->sender->addItem(['label' => Yii::t('ReputationModule.base', 'User Reputation'), 162 | 'url' => $event->sender->space->createUrl('/reputation/admin'), 163 | 'sortOrder' => 300, 164 | 'isActive' => (Yii::$app->controller->module && Yii::$app->controller->module->id === 'reputation'), 165 | ]); 166 | } 167 | } 168 | 169 | /* 170 | * Show reputation menu in space menu 171 | * 172 | * @param type $event 173 | */ 174 | 175 | public static function onSpaceMenuInit($event) { 176 | if ($event->sender->space !== null && $event->sender->space->isModuleEnabled('reputation') && $event->sender->space->isMember()) { 177 | $event->sender->addItem(array( 178 | 'label' => Yii::t('ReputationModule.base', 'Hot'), 179 | 'url' => $event->sender->space->createUrl('/reputation/space'), 180 | 'icon' => '', 181 | 'isActive' => (Yii::$app->controller->module && Yii::$app->controller->module->id == 'reputation' && Yii::$app->controller->id == 'space' && Yii::$app->controller->action->id == 'index'), 182 | 'group' => 'modules', 183 | 'sortOrder' => 200, 184 | )); 185 | } 186 | } 187 | 188 | /** 189 | * On run of integrity check command, validate all module data 190 | * 191 | * @param $event 192 | */ 193 | public static function onIntegrityCheck($event) { 194 | $integrityChecker = $event->sender; 195 | $integrityChecker->showTestHeadline("Reputation Module (Content) (" . ReputationContent::find()->count() . " entries)"); 196 | $integrityChecker->showTestHeadline("Reputation Module (User) (" . ReputationUser::find()->count() . " entries)"); 197 | } 198 | 199 | /** 200 | * Add Space Widget (TODO when Reputation on this space enabled) 201 | * 202 | * @param $event 203 | */ 204 | public static function onSpaceSidebar($event) { 205 | if ($event->sender->space->isModuleEnabled('reputation')) { 206 | $event->sender->addWidget(widgets\SpaceUserReputationWidget::className(), array('contentContainer' => $event->sender->space), array('sortOrder' => 10)); 207 | } 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /models/ReputationBase.php: -------------------------------------------------------------------------------- 1 | wall_id; 52 | $spaceContent = Yii::$app->cache->get($cacheId); 53 | if ($spaceContent === false || $forceUpdate === true) { 54 | 55 | $condition = 'contentcontainer_id=:spaceId AND object_model!=:activity'; 56 | $params = [':spaceId' => $container->wall_id, ':activity' => 'humhub\modules\activity\models\Activity']; 57 | $query = Content::find() 58 | ->where($condition, $params) 59 | ->all(); 60 | 61 | Yii::$app->cache->set($cacheId, $spaceContent = $query, ReputationContent::CACHE_TIME_SECONDS); 62 | } 63 | return $spaceContent; 64 | } 65 | 66 | /** 67 | * Count all comments a content object has received. 68 | * 69 | * @param Content $content : The content object 70 | * @param $userId : The user id 71 | * @param $cacheId : The cache id 72 | * @param bool $countOwnComments : Count comments created by same user as content 73 | * @param bool $forceUpdate : true if cache should be ignored 74 | * @return Comment[] 75 | */ 76 | public function getCommentsFromContent(Content $content, $userId, $cacheId, $countOwnComments = false, $forceUpdate = false) { 77 | $comments = Yii::$app->cache->get($cacheId); 78 | 79 | if ($comments === false || $forceUpdate === true) { 80 | $object = $content->object_model; 81 | $objectModel = $object::tableName(); 82 | $comments = array(); 83 | try { 84 | $query = Comment::find(); 85 | $query->leftJoin($objectModel . ' AS o', 'comment.object_id = o.id'); 86 | $query->leftJoin('content AS ct', 'o.id = ct.object_id'); 87 | 88 | if ($countOwnComments === true) { 89 | $condition = 'ct.id=:contentId AND ct.created_by=:userId AND comment.object_model=ct.object_model'; 90 | } else { 91 | $condition = 'ct.id=:contentId AND ct.created_by=:userId AND comment.created_by!=:userId AND comment.object_model=ct.object_model'; 92 | } 93 | $params = array(':contentId' => $content->id, ':userId' => $userId); 94 | $query->where($condition, $params); 95 | $comments = $query->all(); 96 | 97 | Yii::$app->cache->set($cacheId, $query, ReputationBase::CACHE_TIME_SECONDS); 98 | } catch (Exception $e) { 99 | Yii::trace('Couldn\'t count comments from object model: ' . $objectModel); 100 | } 101 | } 102 | 103 | return $comments; 104 | } 105 | 106 | /** 107 | * Return an array with all space settings and call setSpaceSettings when not found 108 | * @param $container Object 109 | * @return $spaceSettings array 110 | */ 111 | public function getSpaceSettings($container) { 112 | $getSettings = ContentContainerSetting::findAll(['module_id' => 'reputation', 'contentcontainer_id' => $container->wall_id]); 113 | 114 | if (count($getSettings) > 0) { 115 | foreach ($getSettings as $setting) { 116 | $spaceSettings[$setting['name']] = $setting['value']; 117 | } 118 | } else { 119 | $spaceSettings = self::setSpaceSettings($container); 120 | } 121 | return $spaceSettings; 122 | } 123 | 124 | /** 125 | * set standard Reputation settings onSpace 126 | * @param $container Object 127 | * @return $spaceSettings array 128 | */ 129 | protected function setSpaceSettings($container) { 130 | $spaceSettings = [ 131 | 'functions' => self::DEFAULT_FUNCTION, 132 | 'logarithm_base' => self::DEFAULT_LOGARITHM_BASE, 133 | 'create_content' => self::DEFAULT_CREATE_CONTENT, 134 | 'smb_likes_content' => self::DEFAULT_SMB_LIKES_CONTENT, 135 | 'smb_likes_content' => self::DEFAULT_SMB_FAVORITES_CONTENT, 136 | 'smb_favorites_content' => self::DEFAULT_SMB_COMMENTS_CONTENT, 137 | 'smb_comments_content' => self::DEFAULT_SMB_COMMENTS_CONTENT, 138 | 'daily_limit' => self::DEFAULT_DAILY_LIMIT, 139 | 'decrease_weighting' => self::DEFAULT_DECREASE_WEIGHTING, 140 | 'cron_job' => self::DEFAULT_CRON_JOB, 141 | 'lambda_long' => self::DEFAULT_LAMBDA_SHORT, 142 | 'lambda_short' => self::DEFAULT_LAMBDA_LONG, 143 | 'ranking_new_period' => self::DEFAULT_RANKING_NEW_PERIOD]; 144 | 145 | foreach ($spaceSettings as $name => $value) { 146 | Setting::Set($container->id, $name, $value, 'reputation'); 147 | } 148 | return $spaceSettings; 149 | } 150 | 151 | /** 152 | * Count all likes a content object has received. Do not count likes from user who created this post 153 | * 154 | * @param Content $content : The content object 155 | * @param $userId : The user id 156 | * @param $cacheId : The cache id 157 | * @param bool $forceUpdate : true if cache should be ignored 158 | * @return Like[] 159 | */ 160 | protected function getLikesFromContent(Content $content, $userId, $cacheId, $forceUpdate = false) { 161 | $likes = Yii::$app->cache->get($cacheId); 162 | 163 | if ($likes === false || $forceUpdate === true) { 164 | $object = $content->object_model; 165 | $objectModel = $object::tableName(); 166 | $likes = array(); 167 | try { 168 | $query = Like::find(); 169 | $query->leftJoin($objectModel . ' AS p', 'like.object_id = p.id'); 170 | $query->leftJoin('content AS ct', 'p.id = ct.object_id'); 171 | $condition = 'ct.id=:contentId AND like.created_by!=:userId AND ct.created_by=:userId AND like.object_model=:objectModel AND ct.object_model=:objectModel'; 172 | $params = array(':contentId' => $content->id, ':objectModel' => $objectModel, ':userId' => $userId); 173 | $query->where($condition, $params); 174 | $query->all(); 175 | 176 | Yii::$app->cache->set($cacheId, $query, ReputationBase::CACHE_TIME_SECONDS); 177 | } catch (Exception $e) { 178 | Yii::trace('Couldn\'t fetch likes from object model: ' . $objectModel); 179 | } 180 | } 181 | 182 | return $likes; 183 | } 184 | 185 | /** 186 | * Count all favorites a content object has received. Do not count favorites from user who created this post 187 | * 188 | * @param Content $content : The content object 189 | * @param $userId : The user id 190 | * @param $cacheId : The cache id 191 | * @param bool $forceUpdate : true if cache should be ignored 192 | * @return Favorite[] 193 | */ 194 | protected function getFavoritesFromContent(Content $content, $userId, $cacheId, $forceUpdate = false) { 195 | $favorites = Yii::$app->cache->get($cacheId); 196 | 197 | if ($favorites === false || $forceUpdate === true) { 198 | $object = $content->object_model; 199 | $objectModel = $object::tableName(); 200 | $favorites = array(); 201 | 202 | // not possible to favorite comments atm 203 | if (strcmp($objectModel, 'comment') == 0) { 204 | return array(); 205 | } 206 | 207 | try { 208 | $query = Favorite::find(); 209 | $query->leftJoin($objectModel . ' AS p', 'favorite.object_id = p.id'); 210 | $query->leftJoin('content AS ct', 'p.id = ct.object_id'); 211 | 212 | $condition = 'ct.id=:contentId AND favorite.created_by!=:userId AND ct.created_by=:userId AND favorite.object_model=:objectModel AND ct.object_model=:objectModel'; 213 | $params = array(':contentId' => $content->id, ':objectModel' => $objectModel, ':userId' => $userId); 214 | $query->where($condition, $params); 215 | $query->all(); 216 | 217 | Yii::$app->cache->set($cacheId, $query, ReputationBase::CACHE_TIME_SECONDS); 218 | } catch (Exception $e) { 219 | Yii::trace('Couldn\'t fetch favorites from object model: ' . $objectModel); 220 | } 221 | } 222 | 223 | return $favorites; 224 | } 225 | 226 | } 227 | -------------------------------------------------------------------------------- /views/space/settings.php: -------------------------------------------------------------------------------- 1 | 10 |
11 |
13 |
14 | 15 | ['settings', 'sguid' => $space->guid], 18 | 'method' => 'post', 19 | 'id' => 'configure-form', 20 | 'enableAjaxValidation' => false, 21 | ]); 22 | ?> 23 | 24 | errorSummary($model); ?> 25 |
26 | 27 |
28 | 29 |
30 | Yii::t('ReputationModule.base', 'Logarithmic'), 33 | ReputationBase::LINEAR => Yii::t('ReputationModule.base', 'Linear') 34 | ); 35 | echo $form-> 36 | field($model, 'functions')-> 37 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Function'))-> 38 | dropDownList($functions, array('class' => 'form-control', 'id' => 'dropdown_function', 'hint' => Yii::t('ReputationModule.views_adminReputation_show', 'Choose the function that should be used to show user reputation.'))); 39 | ?> 40 | 41 |
42 | 43 | functions == '1'): ?> 44 | 52 | 53 |
54 | 56 | field($model, 'logarithm_base')-> 57 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Logarithm base'))-> 58 | textInput(array('class' => 'form-control')); 59 | ?> 60 |
61 | 62 | 63 |
64 | 66 | field($model, 'daily_limit')-> 67 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Daily limit for Users'))-> 68 | textInput(array('class' => 'form-control')); 69 | ?> 70 |
71 | 72 |
73 | 74 | Yii::t('ReputationModule.base', 'Yes'), 77 | 0 => Yii::t('ReputationModule.base', 'No') 78 | ); 79 | echo $form-> 80 | field($model, 'decrease_weighting')-> 81 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Decrease weighting per post'))-> 82 | dropDownList($functions, array('class' => 'form-control', 'id' => 'join_visibility_dropdown', 'hint' => Yii::t('ReputationModule.views_adminReputation_show', 'Should the weighting of reputation decrease with the number with increasing activity?'))); 83 | ?> 84 | 85 |
86 | 87 |
88 | 89 | Yii::t('ReputationModule.base', 'Yes'), 92 | 0 => Yii::t('ReputationModule.base', 'No') 93 | ); 94 | echo $form-> 95 | field($model, 'cron_job')-> 96 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Update reputation data on hourly cron job'))-> 97 | dropDownList($functions, array('class' => 'form-control', 'id' => 'join_visibility_dropdown', 'hint' => Yii::t('ReputationModule.views_adminReputation_show', 'Should the hourly cron job update reputation data for this space?'))); 98 | ?> 99 | 100 |
101 | 102 |
103 | 105 | field($model, 'ranking_new_period')-> 106 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Time in hours for the "NEW" filter to show Content'))-> 107 | textInput(array('class' => 'form-control')); 108 | ?> 109 |
110 | 111 |

112 | 114 | 115 |

116 |
117 |
118 | 120 | field($model, 'create_content')-> 121 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Creating posts or comments'))-> 122 | textInput(array('class' => 'form-control')); 123 | ?> 124 |
125 |
126 | 128 | field($model, 'smb_likes_content')-> 129 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Somebody liked the post'))-> 130 | textInput(array('class' => 'form-control')); 131 | ?> 132 |
133 | isModuleEnabled('favorite')): ?> 134 |
135 | 137 | field($model, 'smb_favorites_content')-> 138 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Somebody marked the post as favorite'))-> 139 | textInput(array('class' => 'form-control')); 140 | ?> 141 |
142 | 143 |
144 | 146 | field($model, 'smb_comments_content')-> 147 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Somebody comments the post'))-> 148 | textInput(array('class' => 'form-control')); 149 | ?> 150 |
151 |
152 | 153 |

154 | 156 | 157 |

158 |
159 |
160 | 162 | field($model, 'lambda_long')-> 163 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Exponential decrease for Ranking Rising'))-> 164 | textInput(array('class' => 'form-control')); 165 | ?> 166 |
167 |
168 | 170 | field($model, 'lambda_short')-> 171 | label(Yii::t('ReputationModule.forms_adminController_settings', 'Exponential decrease for Ranking Hot'))-> 172 | textInput(array('class' => 'form-control')); 173 | ?> 174 |
175 |
176 | 177 |
178 | 'btn btn-primary')) ?> 179 | 180 |
181 |
182 | 183 | 219 | -------------------------------------------------------------------------------- /models/ReputationUser.php: -------------------------------------------------------------------------------- 1 | array(self::BELONGS_TO, 'User', 'user_id'), 57 | 'space' => array(self::BELONGS_TO, 'Space', 'space_id'), 58 | ); 59 | } 60 | 61 | /** 62 | * @return array customized attribute labels (name=>label) 63 | */ 64 | public function attributeLabels() { 65 | return array( 66 | 'id' => 'ID', 67 | 'value' => 'Value', 68 | 'visibility' => 'Visibility', 69 | 'user_id' => 'User', 70 | 'space_id' => 'Space ID', 71 | 'wall_id' => 'ContentContainer wall_id', 72 | 'created_at' => 'Created At', 73 | 'created_by' => 'Created By', 74 | 'updated_at' => 'Updated At', 75 | 'updated_by' => 'Updated By', 76 | ); 77 | } 78 | 79 | /** 80 | * Updates all user reputation for this space 81 | * @param $space : The space to check 82 | * @param bool $forceUpdate : Ignore cache 83 | */ 84 | public function updateUserReputation($container, $forceUpdate = false) { 85 | 86 | // get all users from this space 87 | $attributes = array('space_id' => $container->id); 88 | $spaceUsers = Membership::findAll($attributes); 89 | 90 | foreach ($spaceUsers as $user) { 91 | 92 | $cacheId = 'reputation_space_user' . '_' . $container->wall_id . '_' . $user->user_id; 93 | $userReputation = Yii::$app->cache->get($cacheId); 94 | 95 | if ($userReputation === false || $forceUpdate === true) { 96 | 97 | // get all reputation_user objects from this space 98 | $condition = array('user_id' => $user->user_id, 'space_id' => $container->id); 99 | $userReputation = ReputationUser::findOne($condition); 100 | 101 | if ($userReputation == null) { 102 | // Create new reputation_user entry 103 | $userReputation = new ReputationUser; 104 | $userReputation->user_id = $user->user_id; 105 | $userReputation->space_id = $container->id; 106 | $userReputation->wall_id = $container->wall_id; 107 | $userReputation->visibility = 0; 108 | $userReputation->created_by = $user->user_id; 109 | } 110 | $userReputation->value = ReputationUser::calculateUserReputationScore($user->user_id, $container, $forceUpdate); 111 | $userReputation->updated_at = date('Y-m-d H:i:s'); 112 | $userReputation->updated_by = $container->updated_by; 113 | $userReputation->save(); 114 | 115 | Yii::$app->cache->set($cacheId, $userReputation, ReputationBase::CACHE_TIME_SECONDS); 116 | } 117 | } 118 | 119 | ReputationUser::deleteMissingUsers($container->wall_id); 120 | } 121 | 122 | /** 123 | * Calculate the user reputation score inside a space 124 | * Use likes, favorites, comments from content that user created 125 | * Use posted comments and likes received for this comments for content the user didn't create 126 | * Include limitations in calculate like weight decrease and daily limit 127 | * 128 | * @param $userId : The userId to calculate reputation for 129 | * @param $container : The Space (contentContainer) the calculation is being based on 130 | * @return int: User reputation score inside this space 131 | */ 132 | private function calculateUserReputationScore($userId, $container, $forceUpdate = false) { 133 | $spaceSettings = ReputationBase::getSpaceSettings($container); 134 | $dailyLimit = $spaceSettings['daily_limit']; 135 | $decreaseWeighting = $spaceSettings['decrease_weighting']; 136 | 137 | $spaceContent = ReputationBase::getContentFromSpace($container, $forceUpdate); 138 | 139 | foreach ($spaceContent as $content) { 140 | /* 141 | * keep track of how many times an content object was liked, favorited etc. 142 | * this allows to decrease the value of repeating actions 143 | * e.g. the second like only gives the user half the points from the first like 144 | */ 145 | $scoreCount = 1; 146 | /* 147 | * handle content that is created by this user 148 | * use likes, favorites, comments from content that user created 149 | */ 150 | if ($content->created_by == $userId) { 151 | ReputationUser::addToDailyReputation($content, $spaceSettings['create_content'], $dailyLimit); 152 | // now count the likes this content received from other users 153 | $cacheId = 'likes_earned_cache_' . $userId . '_' . $content->id; 154 | $likes = ReputationBase::getLikesFromContent($content, $userId, $cacheId, $forceUpdate); 155 | foreach ($likes as $like) { 156 | if ($decreaseWeighting == '1') { 157 | ReputationUser::addToDailyReputation($like, $spaceSettings['smb_likes_content'] / $scoreCount, $dailyLimit); 158 | } else { 159 | ReputationUser::addToDailyReputation($like, $spaceSettings['smb_likes_content'], $dailyLimit); 160 | } 161 | $scoreCount++; 162 | } 163 | 164 | if ($container->isModuleEnabled('favorite')) { 165 | $scoreCount = 1; 166 | // now count the favorites this content received from other users 167 | $cacheId = 'favorites_earned_cache_' . $userId . '_' . $content->id; 168 | $favorites = ReputationBase::getFavoritesFromContent($content, $userId, $cacheId, $forceUpdate); 169 | foreach ($favorites as $favorite) { 170 | if ($decreaseWeighting == '1') { 171 | ReputationUser::addToDailyReputation($favorite, $spaceSettings['smb_favorites_content'] / $scoreCount, $dailyLimit); 172 | } else { 173 | ReputationUser::addToDailyReputation($favorite, $spaceSettings['smb_favorites_content'], $dailyLimit); 174 | } 175 | $scoreCount++; 176 | } 177 | } 178 | $scoreCount = 1; 179 | // now count how many comments this post has generated 180 | $cacheId = 'comments_earned_cache_' . $userId . '_' . $content->id; 181 | $comments = ReputationBase::getCommentsFromContent($content, $userId, $cacheId, false, $forceUpdate); 182 | foreach ($comments as $comment) { 183 | if ($decreaseWeighting == '1') { 184 | ReputationUser::addToDailyReputation($comment, $spaceSettings['smb_comments_content'] / $scoreCount, $dailyLimit); 185 | } else { 186 | ReputationUser::addToDailyReputation($comment, $spaceSettings['smb_comments_content'], $dailyLimit); 187 | } 188 | $scoreCount++; 189 | } 190 | $scoreCount = 1; 191 | } 192 | 193 | /** 194 | * now handle posts that were created by others users 195 | * The user gets points for comments he created and for likes the comments have received 196 | */ 197 | $commentsPosted = ReputationUser::GetCommentsGeneratedByUser($userId, $content, $forceUpdate); 198 | foreach ($commentsPosted as $commentPosted) { 199 | ReputationUser::addToDailyReputation($commentPosted, $spaceSettings['create_content'], $dailyLimit); 200 | } 201 | 202 | $commentsLiked = ReputationUser::GetCommentsGeneratedByUserLikedByOthers($userId, $content, $forceUpdate); 203 | foreach ($commentsLiked as $commentLiked) { 204 | if ($decreaseWeighting == '1') { 205 | ReputationUser::addToDailyReputation($commentLiked, $spaceSettings['smb_likes_content'] / $scoreCount, $dailyLimit); 206 | } else { 207 | ReputationUser::addToDailyReputation($commentLiked, $spaceSettings['smb_likes_content'], $dailyLimit); 208 | } 209 | $scoreCount++; 210 | } 211 | } 212 | 213 | /* 214 | * Iterate over daily_reputation structure to get final score 215 | */ 216 | $reputationScore = 0; 217 | foreach (ReputationUser::$daily_reputation as $reputation) { 218 | $reputationScore += $reputation->getScore(); 219 | } 220 | 221 | // reset this array for next user 222 | ReputationUser::$daily_reputation = array(); 223 | 224 | 225 | 226 | return ReputationUser::calculateUserScore($spaceSettings['functions'], $reputationScore, $spaceSettings['logarithm_base']); 227 | } 228 | 229 | /** 230 | * @param $content 231 | * @param $scoreToAdd 232 | * @param $daily_limit 233 | * @return array 234 | */ 235 | private function addToDailyReputation($content, $scoreToAdd, $daily_limit) { 236 | global $daily_reputation; 237 | $date = date_create($content->created_at)->format('Y-m-d'); 238 | 239 | if (array_key_exists($date, ReputationUser::$daily_reputation)) { 240 | $currentDate = ReputationUser::$daily_reputation[$date]; 241 | $currentDate->addScore($scoreToAdd); 242 | 243 | return array($date, $currentDate, ReputationUser::$daily_reputation); 244 | } else { 245 | ReputationUser::$daily_reputation[$date] = new DailyReputation($scoreToAdd, $daily_limit); 246 | } 247 | } 248 | 249 | /** 250 | * Get all comments the user created. 251 | * 252 | * @param $userId 253 | * @param Content $content 254 | * @param $forceUpdate : Ignore cache 255 | * @return Comment[] 256 | */ 257 | public function GetCommentsGeneratedByUser($userId, Content $content, $forceUpdate = false) { 258 | $cacheId = 'comments_generated_cache_' . $userId . '_' . $content->id; 259 | 260 | $commentsGenerated = Yii::$app->cache->get($cacheId); 261 | 262 | if ($commentsGenerated === false || $forceUpdate === true) { 263 | $object = $content->object_model; 264 | $objectModel = $object::tableName(); 265 | $commentsGenerated = array(); 266 | try { 267 | $query = Comment::find(); 268 | $query->leftJoin($objectModel . ' AS o', 'comment.object_id = o.id'); 269 | $query->leftJoin('content AS ct', 'o.id = ct.object_id'); 270 | $params = array(':contentId' => $content->id, ':userId' => $userId); 271 | $query->where('ct.id=:contentId AND c.created_by=:userId AND c.object_model=ct.object_model', $params); 272 | $commentsGenerated = $query->all(); 273 | 274 | Yii::$app->cache->set($cacheId, $commentsGenerated, ReputationBase::CACHE_TIME_SECONDS); 275 | } catch (Exception $e) { 276 | Yii::trace('Couldn\'t count generated comments from object model: ' . $objectModel); 277 | } 278 | } 279 | 280 | return $commentsGenerated; 281 | } 282 | 283 | /** 284 | * Get all likes that a user got for a comment he made 285 | * When a user likes his own comment it will not be counted 286 | * 287 | * @param $userId 288 | * @param Content $content 289 | * @param $forceUpdate : Ignore cache 290 | * @return int 291 | */ 292 | public function GetCommentsGeneratedByUserLikedByOthers($userId, Content $content, $forceUpdate) { 293 | $cacheId = 'comments_liked_cache_' . $userId . '_' . $content->id; 294 | $commentsLiked = Yii::$app->cache->get($cacheId); 295 | if ($commentsLiked === false || $forceUpdate === true) { 296 | $object = $content->object_model; 297 | $objectModel = $object::tableName(); 298 | $commentsLiked = array(); 299 | try { 300 | $query = Like::find(); 301 | $query->leftJoin('comment AS c', 'c.id = like.object_id'); 302 | $query->leftJoin($objectModel . ' AS o', 'c.object_id = o.id'); 303 | $query->leftJoin('content AS ct', 'o.id = ct.object_id'); 304 | $params = array(':contentId' => $content->id, ':userId' => $userId); 305 | $query->where('like.object_model=\'humhub\modules\comment\models\Comment\' AND like.created_by!=:userId AND ct.id=:contentId AND c.created_by=:userId AND c.object_model=ct.object_model', $params); 306 | $commentsLiked = $query->all(); 307 | 308 | Yii::$app->cache->set($cacheId, $commentsLiked, ReputationBase::CACHE_TIME_SECONDS); 309 | } catch (Exception $e) { 310 | Yii::trace('Couldn\'t count generated comments from object model: ' . $objectModel); 311 | } 312 | } 313 | return $commentsLiked; 314 | } 315 | 316 | /** 317 | * Calculate final user score. 318 | * 319 | * @param $function : Linear or Logarithmic 320 | * @param $reputationScore : The score the user reached 321 | * @param $logarithmBase : The logarithm base 322 | * @return int 323 | */ 324 | private function calculateUserScore($function, $reputationScore, $logarithm_base) { 325 | if ($function == ReputationBase::LINEAR) { 326 | return intval($reputationScore); 327 | } else { 328 | if ($reputationScore == 0) { 329 | return 0; 330 | } else { 331 | // increase reputation score + 1 so log is not 0 when user has 1 point 332 | $logValue = log($reputationScore + 1, $logarithm_base); 333 | return intval(round($logValue * 100)); 334 | } 335 | } 336 | } 337 | 338 | /** 339 | * Delete users that are not space members anymore 340 | * 341 | * @param $spaceId 342 | * @throws CDbException 343 | */ 344 | private function deleteMissingUsers($spaceId) { 345 | $condition = array('space_id' => $spaceId); 346 | $reputationUsers = ReputationUser::findAll($condition); 347 | foreach ($reputationUsers as $user) { 348 | if (Membership::findOne('space_id=' . $spaceId . ' AND user_id=' . $user->user_id . '')) { 349 | Membership::delete(); 350 | } 351 | } 352 | } 353 | } 354 | --------------------------------------------------------------------------------