├── .codeclimate.yml ├── .eslintignore ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── old_README.md ├── phpmd_ruleset.xml ├── src ├── Podium.php ├── PodiumCache.php ├── PodiumComponent.php ├── PodiumConfig.php ├── actions │ ├── AdminAction.php │ ├── MemberAction.php │ ├── MessageAction.php │ └── ThreadAction.php ├── assets │ ├── HighlightAsset.php │ └── PodiumAsset.php ├── console │ ├── QueueController.php │ └── UserController.php ├── controllers │ ├── AccountController.php │ ├── AdminController.php │ ├── AdminForumController.php │ ├── BaseController.php │ ├── ForumController.php │ ├── ForumPostController.php │ ├── ForumThreadController.php │ ├── InstallController.php │ ├── MembersController.php │ ├── MessagesController.php │ └── ProfileController.php ├── css │ └── podium.css ├── db │ ├── ActiveRecord.php │ ├── Query.php │ └── UserQuery.php ├── filters │ ├── AccessControl.php │ ├── InstallRule.php │ ├── LoginRequiredRule.php │ ├── PermissionDeniedRule.php │ └── PodiumRoleRule.php ├── helpers │ └── Helper.php ├── log │ ├── DbTarget.php │ └── Log.php ├── maintenance │ ├── Installation.php │ ├── Maintenance.php │ ├── SchemaOperation.php │ ├── Update.php │ └── steps │ │ ├── install.php │ │ └── update.php ├── messages │ ├── config.php │ ├── en-US │ │ └── podium │ │ │ ├── flash.php │ │ │ └── view.php │ ├── es │ │ └── podium │ │ │ ├── flash.php │ │ │ └── view.php │ ├── ja │ │ └── podium │ │ │ └── flash.php │ ├── pl │ │ └── podium │ │ │ ├── flash.php │ │ │ └── view.php │ └── ru │ │ └── podium │ │ ├── flash.php │ │ └── view.php ├── models │ ├── Activity.php │ ├── Category.php │ ├── Content.php │ ├── Email.php │ ├── Forum.php │ ├── ForumSearch.php │ ├── LogSearch.php │ ├── Message.php │ ├── MessageReceiver.php │ ├── MessageSearch.php │ ├── Meta.php │ ├── Mod.php │ ├── Poll.php │ ├── PollAnswer.php │ ├── Post.php │ ├── PostThumb.php │ ├── Subscription.php │ ├── Thread.php │ ├── ThreadView.php │ ├── User.php │ ├── UserSearch.php │ ├── Vocabulary.php │ ├── db │ │ ├── ActivityActiveRecord.php │ │ ├── CategoryActiveRecord.php │ │ ├── ContentActiveRecord.php │ │ ├── EmailActiveRecord.php │ │ ├── ForumActiveRecord.php │ │ ├── MessageActiveRecord.php │ │ ├── MessageReceiverActiveRecord.php │ │ ├── MetaActiveRecord.php │ │ ├── ModActiveRecord.php │ │ ├── PollActiveRecord.php │ │ ├── PollAnswerActiveRecord.php │ │ ├── PostActiveRecord.php │ │ ├── PostThumbActiveRecord.php │ │ ├── SubscriptionActiveRecord.php │ │ ├── ThreadActiveRecord.php │ │ ├── ThreadViewActiveRecord.php │ │ ├── UserActiveRecord.php │ │ └── VocabularyActiveRecord.php │ └── forms │ │ ├── ConfigForm.php │ │ ├── LoginForm.php │ │ ├── ReactivateForm.php │ │ ├── ResetForm.php │ │ └── SearchForm.php ├── rbac │ ├── AuthorRule.php │ ├── ModeratorRule.php │ └── Rbac.php ├── services │ ├── Sorter.php │ └── ThreadVerifier.php ├── slugs │ └── PodiumSluggableBehavior.php ├── traits │ └── FlashTrait.php ├── url-rules.php ├── views │ ├── account │ │ ├── login.php │ │ ├── password.php │ │ ├── reactivate.php │ │ ├── register.php │ │ └── reset.php │ ├── admin │ │ ├── categories.php │ │ ├── category.php │ │ ├── contents.php │ │ ├── forum.php │ │ ├── forums.php │ │ ├── index.php │ │ ├── logs.php │ │ ├── members.php │ │ ├── mods.php │ │ ├── settings.php │ │ └── view.php │ ├── elements │ │ ├── admin │ │ │ └── _navbar.php │ │ ├── forum │ │ │ ├── _forum.php │ │ │ ├── _forum_header.php │ │ │ ├── _forum_list.php │ │ │ ├── _forum_section.php │ │ │ ├── _forums.php │ │ │ ├── _icons.php │ │ │ ├── _post.php │ │ │ ├── _post_select.php │ │ │ ├── _section.php │ │ │ ├── _sections.php │ │ │ ├── _thread.php │ │ │ ├── _thread_header.php │ │ │ ├── _thread_list.php │ │ │ └── _threads.php │ │ ├── installation │ │ │ └── _navbar.php │ │ ├── main │ │ │ ├── _breadcrumbs.php │ │ │ ├── _footer.php │ │ │ ├── _members.php │ │ │ └── _navbar.php │ │ ├── members │ │ │ ├── _members_threads.php │ │ │ ├── _thread_list.php │ │ │ └── _threads.php │ │ ├── messages │ │ │ └── _navbar.php │ │ ├── profile │ │ │ └── _navbar.php │ │ └── search │ │ │ ├── _forum_search_posts.php │ │ │ ├── _forum_search_topics.php │ │ │ ├── _post.php │ │ │ ├── _search.php │ │ │ ├── _thread_header.php │ │ │ ├── _thread_list.php │ │ │ ├── _thread_posts.php │ │ │ ├── _thread_topics.php │ │ │ ├── _threads_search_posts.php │ │ │ └── _threads_search_topics.php │ ├── forum │ │ ├── ban.php │ │ ├── category.php │ │ ├── delete.php │ │ ├── deletepoll.php │ │ ├── deletepost.php │ │ ├── deleteposts.php │ │ ├── edit.php │ │ ├── editpoll.php │ │ ├── forum.php │ │ ├── index.php │ │ ├── maintenance.php │ │ ├── move.php │ │ ├── moveposts.php │ │ ├── new-thread.php │ │ ├── post.php │ │ ├── report.php │ │ ├── search.php │ │ ├── thread.php │ │ └── unread-posts.php │ ├── install │ │ ├── level-up.php │ │ └── run.php │ ├── layouts │ │ ├── installation.php │ │ ├── main.php │ │ └── maintenance.php │ ├── members │ │ ├── index.php │ │ ├── mods.php │ │ ├── posts.php │ │ ├── threads.php │ │ └── view.php │ ├── messages │ │ ├── inbox.php │ │ ├── load.php │ │ ├── new.php │ │ ├── reply.php │ │ ├── sent.php │ │ └── view.php │ └── profile │ │ ├── details.php │ │ ├── forum.php │ │ ├── profile.php │ │ └── subscriptions.php ├── web │ └── User.php └── widgets │ ├── Alert.php │ ├── Avatar.php │ ├── LatestPosts.php │ ├── Modal.php │ ├── PageSizer.php │ ├── Readers.php │ ├── codemirror │ ├── CodeMirror.php │ ├── assets │ │ ├── CodeMirrorAsset.php │ │ ├── CodeMirrorButtonsAsset.php │ │ ├── CodeMirrorConfigAsset.php │ │ ├── CodeMirrorExtraAsset.php │ │ ├── CodeMirrorLibAsset.php │ │ └── CodeMirrorModesAsset.php │ └── podium │ │ ├── podium-codemirror.css │ │ └── podium-codemirror.js │ ├── editor │ ├── EditorBasic.php │ └── EditorFull.php │ ├── gridview │ ├── ActionColumn.php │ ├── DataColumn.php │ └── GridView.php │ ├── poll │ ├── Poll.php │ └── views │ │ ├── _form.php │ │ ├── create.php │ │ ├── preview.php │ │ ├── update.php │ │ └── view.php │ └── quill │ ├── QuillBasic.php │ └── QuillFull.php └── tests ├── _bootstrap.php ├── _data └── .gitignore ├── _output └── .gitignore ├── _support ├── .gitignore ├── FunctionalTester.php └── UnitTester.php ├── functional.suite.yml ├── functional ├── InstallationCest.php └── _bootstrap.php ├── unit.suite.yml └── unit ├── InstallationTest.php └── _bootstrap.php /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | duplication: 3 | enabled: true 4 | config: 5 | languages: 6 | - javascript 7 | - php 8 | eslint: 9 | enabled: true 10 | fixme: 11 | enabled: true 12 | phpmd: 13 | enabled: true 14 | config: 15 | rulesets: "codesize,design,unusedcode,phpmd_ruleset.xml" 16 | ratings: 17 | paths: 18 | - "**.js" 19 | - "**.php" 20 | exclude_paths: 21 | - src/messages/ 22 | - src/maintenance/steps/ 23 | - tests/ 24 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*{.,-}min.js 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yii2-podium 2 | 3 | This project is abandoned in favour of new Podium that is split into [Podium API](https://github.com/bizley/yii2-podium-api) 4 | and [Podium client](https://github.com/bizley/yii2-podium-client). 5 | 6 | -- 7 | 8 | [Podium README](old_README.md) 9 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bizley/podium", 3 | "type": "yii2-extension", 4 | "description": "Yii 2 Forum Module.", 5 | "keywords": ["forum", "yii2", "extension", "podium", "module", "bootstrap", "bizley"], 6 | "license": "Apache License 2.0", 7 | "support": { 8 | "issues": "https://github.com/bizley/yii2-podium/issues", 9 | "wiki": "https://github.com/bizley/yii2-podium/wiki", 10 | "source": "https://github.com/bizley/yii2-podium" 11 | }, 12 | "authors": [ 13 | { 14 | "name": "Pawel Bizley Brzozowski", 15 | "email": "pawel@positive.codes" 16 | } 17 | ], 18 | "require": { 19 | "yiisoft/yii2": "^2.0.8", 20 | "yiisoft/yii2-bootstrap": "^2.0", 21 | "bizley/quill": "^2.0", 22 | "cebe/yii2-gravatar": "^1.0", 23 | "cebe/markdown": "^1.0", 24 | "kartik-v/yii2-widget-fileinput": "^1.0", 25 | "kartik-v/yii2-sortable": "^1.0", 26 | "kartik-v/yii2-widget-select2": "^2.0", 27 | "kartik-v/yii2-widget-datepicker": "^1.0", 28 | "himiklab/yii2-recaptcha-widget": "^1.0", 29 | "bower-asset/codemirror": "^5.0", 30 | "bower-asset/codemirror-buttons": "^1.0" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "bizley\\podium\\": "src/" 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /old_README.md: -------------------------------------------------------------------------------- 1 | Podium 2 | ====== 3 | 4 | [![Join the chat at https://gitter.im/bizley/yii2-podium](https://badges.gitter.im/bizley/yii2-podium.svg)](https://gitter.im/bizley/yii2-podium?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | [![Latest Stable Version](https://img.shields.io/packagist/v/bizley/podium.svg)](https://packagist.org/packages/bizley/podium) 6 | [![Total Downloads](https://img.shields.io/packagist/dt/bizley/podium.svg)](https://packagist.org/packages/bizley/podium) 7 | [![License](https://img.shields.io/packagist/l/bizley/podium.svg)](https://github.com/bizley/yii2-podium/blob/master/LICENSE) 8 | [![Code Climate](https://codeclimate.com/github/bizley/yii2-podium/badges/gpa.svg)](https://codeclimate.com/github/bizley/yii2-podium) 9 | [![Yii2](https://img.shields.io/badge/Powered_by-Yii_Framework-green.svg?style=flat)](http://www.yiiframework.com/) 10 | 11 | Yii 2 forum module 12 | ------------------ 13 | 14 | This extension allows you to add forum to your app. 15 | 16 | Features 17 | -------- 18 | 19 | - [Bootstrap](http://getbootstrap.com) responsive layout 20 | - [Quill](https://github.com/bizley/yii2-quill) WYSIWYG editor 21 | - [CodeMirror](https://codemirror.net/) Markdown editor 22 | - Supports Cache, Formatter and Connection components 23 | - Built-in user identity handling (+supports inherited identity) 24 | - Built-in RBAC component (+supports inherited RBAC) 25 | - Console mail queue handling 26 | - Avatars uploading (+supports Gravatars) 27 | - Built-in user messages component 28 | - English, Polish, Russian, and Spanish translation (+Japanese coming up) 29 | - Available with polls system 30 | 31 | Installation & configuration 32 | ---------------------------- 33 | 34 | Follow instructions at [Podium wiki](https://github.com/bizley/yii2-podium/wiki). 35 | 36 | > Warning: This is BETA version of Podium. 37 | > Any part of this module can change without warning. 38 | > Please report all issues and suggestions [here](https://github.com/bizley/yii2-podium/issues). 39 | 40 | Demo 41 | ---- 42 | 43 | Podium Demo is available at [http://bizley.pl/podium](http://bizley.pl/podium). 44 | 45 | Screenshots 46 | ----------- 47 | 48 | Main page 49 | ![Podium main page](https://bizley.github.io/podium/podium1.png) 50 | 51 | Member view 52 | ![Podium member view](https://bizley.github.io/podium/podium2.png) 53 | 54 | Messages inbox 55 | ![Podium messages inbox](https://bizley.github.io/podium/podium3.png) 56 | 57 | Thread view 58 | ![Podium thread view](https://bizley.github.io/podium/podium4.png) 59 | 60 | Discussion 61 | ---------- 62 | 63 | Join [Gitter](https://gitter.im/bizley/yii2-podium) channel. 64 | 65 | Tests 66 | ----- 67 | 68 | For Codeception tests run: 69 | 70 | composer exec -v -- codecept -c vendor/bizley/podium run 71 | 72 | More tests coming soon. 73 | -------------------------------------------------------------------------------- /phpmd_ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | Custom rules for checking Podium 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/actions/AdminAction.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.6 15 | */ 16 | class AdminAction extends Action 17 | { 18 | /** 19 | * @var int current role 20 | */ 21 | public $fromRole; 22 | 23 | /** 24 | * @var int target role 25 | */ 26 | public $toRole; 27 | 28 | /** 29 | * @var string method name 30 | */ 31 | public $method; 32 | 33 | /** 34 | * @var string 35 | */ 36 | public $restrictMessage; 37 | 38 | /** 39 | * @var string 40 | */ 41 | public $successMessage; 42 | 43 | /** 44 | * @var string 45 | */ 46 | public $errorMessage; 47 | 48 | /** 49 | * Runs action. 50 | * @param int $id user ID 51 | * @return Response 52 | */ 53 | public function run($id = null) 54 | { 55 | $model = User::find()->where(['id' => $id])->limit(1)->one(); 56 | if (empty($model)) { 57 | $this->controller->error(Yii::t('podium/flash', 'Sorry! We can not find User with this ID.')); 58 | return $this->controller->redirect(['admin/members']); 59 | } 60 | if ($model->role != $this->fromRole) { 61 | $this->controller->error($this->restrictMessage); 62 | return $this->controller->redirect(['admin/members']); 63 | } 64 | if (call_user_func([$model, $this->method], $this->toRole)) { 65 | $this->controller->success($this->successMessage); 66 | if ($this->method == 'promoteTo') { 67 | return $this->controller->redirect(['admin/mods', 'id' => $model->id]); 68 | } 69 | return $this->controller->redirect(['admin/members']); 70 | } 71 | $this->controller->error($this->errorMessage); 72 | return $this->controller->redirect(['admin/members']); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/actions/MemberAction.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.6 15 | */ 16 | class MemberAction extends Action 17 | { 18 | /** 19 | * @var string view name 20 | */ 21 | public $view; 22 | 23 | /** 24 | * Runs action. 25 | * @param int $id user ID 26 | * @param string $slug user slug 27 | * @return string|Response 28 | */ 29 | public function run($id = null, $slug = null) 30 | { 31 | if (!is_numeric($id) || $id < 1 || empty($slug)) { 32 | $this->controller->error(Yii::t('podium/flash', 'Sorry! We can not find the user you are looking for.')); 33 | return $this->controller->redirect(['members/index']); 34 | } 35 | 36 | $user = User::find()->where(['and', 37 | ['id' => $id], 38 | ['or', 39 | ['slug' => $slug], 40 | ['slug' => ''], 41 | ['slug' => null], 42 | ] 43 | ])->limit(1)->one(); 44 | if (empty($user)) { 45 | $this->controller->error(Yii::t('podium/flash', 'Sorry! We can not find the user you are looking for.')); 46 | return $this->controller->redirect(['members/index']); 47 | } 48 | return $this->controller->render($this->view, ['user' => $user]); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/actions/MessageAction.php: -------------------------------------------------------------------------------- 1 | 18 | * @since 0.6 19 | */ 20 | class MessageAction extends Action 21 | { 22 | /** 23 | * @var array 24 | */ 25 | public $redirectRoute; 26 | 27 | /** 28 | * @var string sender or receiver 29 | */ 30 | public $type; 31 | 32 | /** 33 | * Returns deleted status based on type. 34 | * @return int 35 | */ 36 | public function getDeletedStatus() 37 | { 38 | if ($this->type == 'sender') { 39 | return Message::STATUS_DELETED; 40 | } 41 | return MessageReceiver::STATUS_DELETED; 42 | } 43 | 44 | /** 45 | * Returns model query based on type. 46 | * @return ActiveQuery 47 | */ 48 | public function getModelQuery() 49 | { 50 | if ($this->type == 'sender') { 51 | return Message::find(); 52 | } 53 | return MessageReceiver::find(); 54 | } 55 | 56 | /** 57 | * Runs action. 58 | * @param int $id 59 | * @return Response 60 | */ 61 | public function run($id = null) 62 | { 63 | if (!is_numeric($id) || $id < 1) { 64 | $this->controller->error(Yii::t('podium/flash', 'Sorry! We can not find the message you are looking for.')); 65 | return $this->controller->redirect($this->redirectRoute); 66 | } 67 | $model = $this->modelQuery->where([ 68 | 'and', 69 | ['id' => $id, $this->type . '_id' => User::loggedId()], 70 | ['!=', $this->type . '_status', $this->deletedStatus] 71 | ])->limit(1)->one(); 72 | if (empty($model)) { 73 | $this->controller->error(Yii::t('podium/flash', 'Sorry! We can not find the message with the given ID.')); 74 | return $this->controller->redirect($this->redirectRoute); 75 | } 76 | if ($model->remove()) { 77 | $this->controller->success(Yii::t('podium/flash', 'Message has been deleted.')); 78 | } else { 79 | Log::error('Error while deleting message', $model->id, __METHOD__); 80 | $this->controller->error(Yii::t('podium/flash', 'Sorry! We can not delete this message. Contact administrator about this problem.')); 81 | } 82 | return $this->controller->redirect($this->redirectRoute); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/actions/ThreadAction.php: -------------------------------------------------------------------------------- 1 | 16 | * @since 0.6 17 | */ 18 | class ThreadAction extends Action 19 | { 20 | /** 21 | * @var string permission name 22 | */ 23 | public $permission; 24 | 25 | /** 26 | * @var string boolean attribute name to check 27 | */ 28 | public $boolAttribute; 29 | 30 | /** 31 | * @var string switcher method name 32 | */ 33 | public $switcher; 34 | 35 | /** 36 | * @var string message after on switch 37 | */ 38 | public $onMessage; 39 | 40 | /** 41 | * @var string message after off switch 42 | */ 43 | public $offMessage; 44 | 45 | private $_thread; 46 | 47 | /** 48 | * Returns thread. 49 | * @param int $cid 50 | * @param int $fid 51 | * @param int $id 52 | * @param string $slug 53 | * @return Thread 54 | */ 55 | public function getThread($cid, $fid, $id, $slug) 56 | { 57 | if ($this->_thread === null) { 58 | $this->_thread = (new ThreadVerifier([ 59 | 'categoryId' => $cid, 60 | 'forumId' => $fid, 61 | 'threadId' => $id, 62 | 'threadSlug' => $slug 63 | ]))->verify(); 64 | } 65 | return $this->_thread; 66 | } 67 | 68 | /** 69 | * Runs action. 70 | * @param int $cid 71 | * @param int $fid 72 | * @param int $id 73 | * @param string $slug 74 | * @return Response 75 | */ 76 | public function run($cid = null, $fid = null, $id = null, $slug = null) 77 | { 78 | $thread = $this->getThread($cid, $fid, $id, $slug); 79 | if (empty($thread)) { 80 | $this->controller->error(Yii::t('podium/flash', 'Sorry! We can not find the thread you are looking for.')); 81 | return $this->controller->redirect(['forum/index']); 82 | } 83 | 84 | if (!User::can($this->permission, ['item' => $thread])) { 85 | $this->controller->error(Yii::t('podium/flash', 'Sorry! You do not have the required permission to perform this action.')); 86 | return $this->controller->redirect(['forum/index']); 87 | } 88 | 89 | if (call_user_func([$thread, $this->switcher])) { 90 | $this->controller->success($thread->{$this->boolAttribute} ? $this->onMessage : $this->offMessage); 91 | } else { 92 | $this->controller->error(Yii::t('podium/flash', 'Sorry! There was an error while updating the thread.')); 93 | } 94 | return $this->controller->redirect([ 95 | 'forum/thread', 96 | 'cid' => $thread->forum->category->id, 97 | 'fid' => $thread->forum->id, 98 | 'id' => $thread->id, 99 | 'slug' => $thread->slug 100 | ]); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/assets/HighlightAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.2 12 | */ 13 | class HighlightAsset extends AssetBundle 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $css = ['https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/styles/github-gist.min.css']; 19 | } 20 | -------------------------------------------------------------------------------- /src/assets/PodiumAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.1 12 | */ 13 | class PodiumAsset extends AssetBundle 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $sourcePath = '@podium/css'; 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $css = ['podium.css']; 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public $depends = [ 29 | 'yii\web\YiiAsset', 30 | 'yii\bootstrap\BootstrapAsset', 31 | ]; 32 | } 33 | -------------------------------------------------------------------------------- /src/css/podium.css: -------------------------------------------------------------------------------- 1 | body { padding-top:20px; padding-bottom:20px } 2 | .avatar-name { padding:10px } 3 | .navbar { margin-bottom:20px } 4 | .note-editor .note-dialog .form-group.row-fluid.note-group-select-from-files { display:none } 5 | .popover.podium { display:block !important; width:auto !important; max-width:none !important; position:relative !important; z-index:auto !important } 6 | .popover.right.podium > .arrow { top:55px !important } 7 | .podium-action-bar { position:absolute; bottom:10px; right:10px; display:none } 8 | .podium-action-bar.list-inline > li { padding-right:1px; padding-left:1px } 9 | .podium-content { padding-bottom:17px } 10 | .podium-content:hover .podium-action-bar { display:block } 11 | .podium-footer { margin-top:10px; max-height:125px; overflow-y:hidden } 12 | .podium-footer hr { margin-bottom:5px } 13 | .podium-go-to-new { display:none } 14 | .podium-gradient { background-image:linear-gradient(to right bottom, white, #c5dee5) } 15 | .podium-poll { margin-bottom:20px } 16 | .podium-poll label { font-weight:normal } 17 | .podium-post { margin-bottom:5px } 18 | .podium-post .podium-content img { display:block; max-width:100%; height:auto } 19 | .podium-post blockquote { font-size:inherit !important; border-left:5px solid #337ab7; background-color:#eee; padding:3px 3px 3px 10px !important } 20 | .podium-post blockquote a.btn { background-color:#fff; text-decoration:none } 21 | .podium-thread-line:hover .podium-go-to-new { display:inline } 22 | .podium-pagination { margin-left:10px } 23 | .ql-align-center { text-align:center } 24 | .ql-align-right { text-align:right } 25 | .ql-align-justify { text-align:justify } 26 | .ql-size-small { font-size:0.75em } 27 | .ql-size-large { font-size:1.5em } 28 | .ql-size-large { font-size:1.5em } 29 | .ql-size-huge { font-size:2.5em } 30 | .ql-snow .ql-editor pre.ql-syntax { background-color:inherit; color:inherit } 31 | @media (max-width: 767px) { .podium-avatar { display:none !important }} 32 | .select2-container--krajee .select2-results__option--highlighted[aria-selected] { background-color: #d9edf7 !important } 33 | @-moz-document url-prefix() { fieldset { display: table-cell }} -------------------------------------------------------------------------------- /src/db/ActiveRecord.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.5 14 | */ 15 | class ActiveRecord extends YiiActiveRecord 16 | { 17 | /** 18 | * Returns the database connection used by this AR class. 19 | * By default, the "db" application component is used as the database connection. 20 | * You may override this method if you want to use a different database connection. 21 | * @return Connection the database connection used by this AR class. 22 | */ 23 | public static function getDb() 24 | { 25 | return Podium::getInstance()->db; 26 | } 27 | } -------------------------------------------------------------------------------- /src/db/Query.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 0.5 13 | */ 14 | class Query extends YiiQuery 15 | { 16 | /** 17 | * Creates a DB command that can be used to execute this query. 18 | * @param Connection $db the database connection used to generate the SQL statement. 19 | * If this parameter is not given, the `db` application component will be used. 20 | * @return Command the created DB command instance. 21 | */ 22 | public function createCommand($db = null) 23 | { 24 | if ($db === null) { 25 | $db = Podium::getInstance()->db; 26 | } 27 | return parent::createCommand($db); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/db/UserQuery.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 0.1 13 | */ 14 | class UserQuery extends ActiveQuery 15 | { 16 | /** 17 | * Adds proper user ID for query. 18 | * @param int $id 19 | */ 20 | public function loggedUser($id) 21 | { 22 | if (Podium::getInstance()->userComponent !== true) { 23 | return $this->andWhere(['inherited_id' => $id]); 24 | } 25 | return $this->andWhere(['id' => $id]); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/filters/AccessControl.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 0.6 13 | */ 14 | class AccessControl extends YiiAccessControl 15 | { 16 | /** 17 | * @var array the default configuration of access rules. Individual rule 18 | * configurations specified via rules will take precedence when the same 19 | * property of the rule is configured. 20 | */ 21 | public $ruleConfig = ['class' => 'bizley\podium\filters\PodiumRoleRule']; 22 | 23 | /** 24 | * Sets Podium user component. 25 | */ 26 | public function init() 27 | { 28 | $this->user = Podium::getInstance()->user; 29 | parent::init(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/filters/InstallRule.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.6 14 | */ 15 | class InstallRule extends PodiumRoleRule 16 | { 17 | /** 18 | * @var boolean whether this is an 'allow' rule or 'deny' rule. 19 | */ 20 | public $allow = false; 21 | 22 | /** 23 | * Sets match and deny callbacks. 24 | */ 25 | public function init() 26 | { 27 | parent::init(); 28 | $this->matchCallback = function () { 29 | return !Podium::getInstance()->getInstalled(); 30 | }; 31 | $this->denyCallback = function () { 32 | return Yii::$app->response->redirect([Podium::getInstance()->prepareRoute('install/run')]); 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/filters/LoginRequiredRule.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.6 14 | */ 15 | class LoginRequiredRule extends PodiumRoleRule 16 | { 17 | /** 18 | * @var boolean whether this is an 'allow' rule or 'deny' rule. 19 | */ 20 | public $allow = false; 21 | 22 | /** 23 | * @var array list of roles that this rule applies to. 24 | */ 25 | public $roles = ['?']; 26 | 27 | /** 28 | * @var string message. 29 | */ 30 | public $message; 31 | 32 | /** 33 | * @var string type of message. 34 | */ 35 | public $type = 'warning'; 36 | 37 | /** 38 | * Sets deny callback. 39 | */ 40 | public function init() 41 | { 42 | parent::init(); 43 | $this->denyCallback = function () { 44 | Yii::$app->session->addFlash($this->type, $this->message, true); 45 | return Yii::$app->response->redirect([Podium::getInstance()->prepareRoute('account/login')]); 46 | }; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/filters/PermissionDeniedRule.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.6 15 | */ 16 | class PermissionDeniedRule extends PodiumRoleRule 17 | { 18 | /** 19 | * @var boolean whether this is an 'allow' rule or 'deny' rule. 20 | */ 21 | public $allow = false; 22 | 23 | /** 24 | * @var string permission name. 25 | */ 26 | public $perm; 27 | 28 | /** 29 | * @var string redirect route. 30 | */ 31 | public $redirect; 32 | 33 | /** 34 | * Sets match and deny callbacks. 35 | */ 36 | public function init() 37 | { 38 | parent::init(); 39 | $this->matchCallback = function () { 40 | return !User::can($this->perm); 41 | }; 42 | $this->denyCallback = function () { 43 | Yii::$app->session->addFlash('danger', Yii::t('podium/flash', 'You are not allowed to perform this action.'), true); 44 | return Yii::$app->response->redirect([Podium::getInstance()->prepareRoute($this->redirect)]); 45 | }; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/filters/PodiumRoleRule.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.6 15 | */ 16 | class PodiumRoleRule extends AccessRule 17 | { 18 | /** 19 | * @param YiiUser $user the user object 20 | * @return boolean whether the rule applies to the role 21 | */ 22 | protected function matchRole($user) 23 | { 24 | if (empty($this->roles)) { 25 | return true; 26 | } 27 | foreach ($this->roles as $role) { 28 | if ($role === '?') { 29 | if ($user->getIsGuest()) { 30 | return true; 31 | } 32 | } elseif ($role === '@') { 33 | if (!$user->getIsGuest()) { 34 | return true; 35 | } 36 | } elseif (User::can($role)) { 37 | return true; 38 | } 39 | } 40 | 41 | return false; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/log/DbTarget.php: -------------------------------------------------------------------------------- 1 | 16 | * @since 0.1 17 | */ 18 | class DbTarget extends YiiDbTarget 19 | { 20 | /** 21 | * Stores log messages to DB. 22 | */ 23 | public function export() 24 | { 25 | $tableName = Podium::getInstance()->db->quoteTableName($this->logTable); 26 | $sql = "INSERT INTO $tableName ([[level]], [[category]], [[log_time]], [[ip]], [[message]], [[model]], [[user]]) 27 | VALUES (:level, :category, :log_time, :ip, :message, :model, :user)"; 28 | $command = Podium::getInstance()->db->createCommand($sql); 29 | foreach ($this->messages as $message) { 30 | list($text, $level, $category, $timestamp) = $message; 31 | $extracted = [ 32 | 'msg' => '', 33 | 'model' => null, 34 | ]; 35 | if (is_array($text) && (isset($text['msg']) || isset($text['model']))) { 36 | if (isset($text['msg'])) { 37 | if (!is_string($text['msg'])) { 38 | $extracted['msg'] = VarDumper::export($text['msg']); 39 | } else { 40 | $extracted['msg'] = $text['msg']; 41 | } 42 | } 43 | if (isset($text['model'])) { 44 | $extracted['model'] = $text['model']; 45 | } 46 | } elseif (is_string($text)) { 47 | $extracted['msg'] = $text; 48 | } else { 49 | $extracted['msg'] = VarDumper::export($text); 50 | } 51 | if (substr($category, 0, 14) == 'bizley\podium\\') { 52 | $category = substr($category, 14); 53 | } 54 | $request = Yii::$app->getRequest(); 55 | $command->bindValues([ 56 | ':level' => $level, 57 | ':category' => $category, 58 | ':log_time' => $timestamp, 59 | ':ip' => $request instanceof Request ? $request->getUserIP() : null, 60 | ':message' => $extracted['msg'], 61 | ':model' => $extracted['model'], 62 | ':user' => Log::blame(), 63 | ])->execute(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/log/Log.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.1 15 | */ 16 | class Log 17 | { 18 | /** 19 | * Returns ID of user responsible for logged action. 20 | * @return int|null 21 | */ 22 | public static function blame() 23 | { 24 | if (Yii::$app instanceof Application && !Podium::getInstance()->user->isGuest) { 25 | return User::loggedId(); 26 | } 27 | return null; 28 | } 29 | 30 | /** 31 | * Returns log types. 32 | * @return array 33 | */ 34 | public static function getTypes() 35 | { 36 | return [ 37 | 1 => Yii::t('podium/view', 'error'), 38 | 2 => Yii::t('podium/view', 'warning'), 39 | 4 => Yii::t('podium/view', 'info') 40 | ]; 41 | } 42 | 43 | /** 44 | * Calls for error log. 45 | * @param mixed $msg Message 46 | * @param string $model Model 47 | * @param string $category 48 | */ 49 | public static function error($msg, $model = null, $category = 'application') 50 | { 51 | Yii::error([ 52 | 'msg' => $msg, 53 | 'model' => $model, 54 | ], $category); 55 | } 56 | 57 | /** 58 | * Calls for info log. 59 | * @param mixed $msg Message 60 | * @param string $model Model 61 | * @param string $category 62 | */ 63 | public static function info($msg, $model = null, $category = 'application') 64 | { 65 | Yii::info([ 66 | 'msg' => $msg, 67 | 'model' => $model, 68 | ], $category); 69 | } 70 | 71 | /** 72 | * Calls for warning log. 73 | * @param mixed $msg Message 74 | * @param string $model Model 75 | * @param string $category 76 | */ 77 | public static function warning($msg, $model = null, $category = 'application') 78 | { 79 | Yii::warning([ 80 | 'msg' => $msg, 81 | 'model' => $model, 82 | ], $category); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/maintenance/Maintenance.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.1 15 | */ 16 | class Maintenance extends SchemaOperation 17 | { 18 | /** 19 | * @var DbManager authorization manager. 20 | */ 21 | public $authManager; 22 | 23 | /** 24 | * Returns percent. 25 | * Clears cache at 100. 26 | * @param int $currentStep 27 | * @param int $maxStep 28 | * @return int 29 | * @since 0.2 30 | */ 31 | public function countPercent($currentStep, $maxStep) 32 | { 33 | $percent = $maxStep ? round(100 * $currentStep / $maxStep) : 0; 34 | if ($percent > 100) { 35 | $percent = 100; 36 | } 37 | if ($percent == 100 && $currentStep != $maxStep) { 38 | $percent = 99; 39 | } 40 | if ($percent == 100) { 41 | $this->clearCache(); 42 | } 43 | return $percent; 44 | } 45 | 46 | /** 47 | * Checks if Post database table exists. 48 | * This is taken as verification of Podium installation. 49 | * @return bool whether Post database table exists. 50 | */ 51 | public static function check() 52 | { 53 | try { 54 | (new Post())->tableSchema; 55 | } catch (Exception $e) { 56 | // Prepare for installation. 57 | // No log because table might not be available. 58 | return false; 59 | } 60 | return true; 61 | } 62 | 63 | /** 64 | * Initialize component. 65 | */ 66 | public function init() 67 | { 68 | parent::init(); 69 | $this->authManager = Instance::ensure($this->module->rbac, DbManager::className()); 70 | } 71 | 72 | /** 73 | * Clears cache. 74 | * @since 0.2 75 | */ 76 | public function clearCache() 77 | { 78 | $this->module->podiumCache->flush(); 79 | } 80 | 81 | /** 82 | * Installation steps to be set. 83 | * This should be overriden. 84 | */ 85 | public function getSteps() 86 | { 87 | throw new Exception('This method must be overriden in Installation and Update class!'); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/messages/config.php: -------------------------------------------------------------------------------- 1 | __DIR__ . DIRECTORY_SEPARATOR . '..', 5 | 'languages' => ['en-US', 'ja', 'pl', 'ru', 'es'], 6 | 'sort' => true, 7 | 'except' => [ 8 | '.svn', 9 | '.git', 10 | '.gitignore', 11 | '.gitkeep', 12 | '.hgignore', 13 | '.hgkeep', 14 | '/messages', 15 | '/css', 16 | ], 17 | 'messagePath' => __DIR__, 18 | ]; 19 | -------------------------------------------------------------------------------- /src/models/Category.php: -------------------------------------------------------------------------------- 1 | 15 | * @since 0.1 16 | */ 17 | class Category extends CategoryActiveRecord 18 | { 19 | /** 20 | * Searches users. 21 | * @return ActiveDataProvider 22 | */ 23 | public function search() 24 | { 25 | $query = static::find(); 26 | if (Podium::getInstance()->user->isGuest) { 27 | $query->andWhere(['visible' => 1]); 28 | } 29 | $dataProvider = new ActiveDataProvider(['query' => $query]); 30 | $dataProvider->sort->defaultOrder = ['sort' => SORT_ASC, 'id' => SORT_ASC]; 31 | return $dataProvider; 32 | } 33 | 34 | /** 35 | * Returns categories. 36 | * @return Category[] 37 | */ 38 | public function show() 39 | { 40 | $dataProvider = new ActiveDataProvider(['query' => static::find()]); 41 | $dataProvider->sort->defaultOrder = ['sort' => SORT_ASC, 'id' => SORT_ASC]; 42 | return $dataProvider->getModels(); 43 | } 44 | 45 | /** 46 | * Sets new categories order. 47 | * @param int $order new category sorting order number 48 | * @return bool 49 | * @since 0.2 50 | */ 51 | public function newOrder($order) 52 | { 53 | $sorter = new Sorter(); 54 | $sorter->target = $this; 55 | $sorter->order = $order; 56 | $sorter->query = (new Query()) 57 | ->from(static::tableName()) 58 | ->where(['!=', 'id', $this->id]) 59 | ->orderBy(['sort' => SORT_ASC, 'id' => SORT_ASC]) 60 | ->indexBy('id'); 61 | return $sorter->run(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/models/Email.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.1 14 | */ 15 | class Email extends EmailActiveRecord 16 | { 17 | /** 18 | * Adds email to queue. 19 | * @param string $address 20 | * @param string $subject 21 | * @param string $content 22 | * @param int|null $userId 23 | * @return bool 24 | */ 25 | public static function queue($address, $subject, $content, $userId = null) 26 | { 27 | try { 28 | $email = new static; 29 | $email->user_id = $userId; 30 | $email->email = $address; 31 | $email->subject = $subject; 32 | $email->content = $content; 33 | $email->status = self::STATUS_PENDING; 34 | $email->attempt = 0; 35 | return $email->save(); 36 | } catch (Exception $e) { 37 | Log::error($e->getMessage(), null, __METHOD__); 38 | } 39 | return false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/models/ForumSearch.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.1 14 | */ 15 | class ForumSearch extends Forum 16 | { 17 | /** 18 | * @inheritdoc 19 | */ 20 | public function rules() 21 | { 22 | return [['name', 'string']]; 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function scenarios() 29 | { 30 | return Model::scenarios(); 31 | } 32 | 33 | /** 34 | * Checks if user of given ID is moderator of this forum. 35 | * @param int $userId 36 | * @return bool 37 | */ 38 | public function isMod($userId = null) 39 | { 40 | return (new Query())->from(Mod::tableName())->where(['forum_id' => $this->id, 'user_id' => $userId])->exists(); 41 | } 42 | 43 | /** 44 | * Searches for forums on admin page. 45 | * @param type $params 46 | * @return ActiveDataProvider 47 | */ 48 | public function searchForMods($params) 49 | { 50 | $query = static::find(); 51 | 52 | $dataProvider = new ActiveDataProvider([ 53 | 'query' => $query, 54 | 'pagination' => [ 55 | 'defaultPageSize' => 10, 56 | 'forcePageParam' => false 57 | ], 58 | ]); 59 | 60 | $dataProvider->sort->defaultOrder = ['name' => SORT_ASC]; 61 | 62 | if (!($this->load($params) && $this->validate())) { 63 | return $dataProvider; 64 | } 65 | 66 | $query->andFilterWhere(['name' => $this->name]); 67 | 68 | return $dataProvider; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/models/LogSearch.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.1 14 | */ 15 | class LogSearch extends ActiveRecord 16 | { 17 | /** 18 | * @inheritdoc 19 | */ 20 | public static function tableName() 21 | { 22 | return '{{%podium_log}}'; 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function rules() 29 | { 30 | return [ 31 | [['id', 'level', 'model', 'user'], 'integer'], 32 | [['category', 'ip', 'message'], 'string'], 33 | ]; 34 | } 35 | 36 | /** 37 | * Searches for logs. 38 | * @param array $params Attributes 39 | * @return ActiveDataProvider 40 | */ 41 | public function search($params) 42 | { 43 | $query = static::find(); 44 | 45 | $dataProvider = new ActiveDataProvider(['query' => $query]); 46 | $dataProvider->sort->defaultOrder = ['id' => SORT_DESC]; 47 | $dataProvider->pagination->pageSize = Yii::$app->session->get('per-page', 20); 48 | 49 | if (!($this->load($params) && $this->validate())) { 50 | return $dataProvider; 51 | } 52 | 53 | $query 54 | ->andFilterWhere(['id' => $this->id]) 55 | ->andFilterWhere(['level' => $this->level]) 56 | ->andFilterWhere(['model' => $this->model]) 57 | ->andFilterWhere(['user' => $this->user]) 58 | ->andFilterWhere(['like', 'category', $this->category]) 59 | ->andFilterWhere(['like', 'ip', $this->ip]) 60 | ->andFilterWhere(['like', 'message', $this->message]); 61 | 62 | return $dataProvider; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/models/Meta.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.1 15 | * 16 | * @property string $parsedSignature 17 | */ 18 | class Meta extends MetaActiveRecord 19 | { 20 | const DEFAULT_TIMEZONE = 'UTC'; 21 | 22 | /** 23 | * Max image sizes. 24 | */ 25 | const MAX_WIDTH = 165; 26 | const MAX_HEIGHT = 165; 27 | const MAX_SIZE = 204800; 28 | 29 | /** 30 | * @var mixed Avatar image 31 | */ 32 | public $image; 33 | 34 | /** 35 | * @inheritdoc 36 | */ 37 | public function rules() 38 | { 39 | return array_merge( 40 | parent::rules(), 41 | [['image', 'image', 42 | 'mimeTypes' => 'image/png, image/jpeg, image/gif', 43 | 'maxWidth' => self::MAX_WIDTH, 44 | 'maxHeight' => self::MAX_HEIGHT, 45 | 'maxSize' => self::MAX_SIZE], 46 | ] 47 | ); 48 | } 49 | 50 | /** 51 | * Returns signature Markdown-parsed if WYSIWYG editor is switched off. 52 | * @return string 53 | * @since 0.6 54 | */ 55 | public function getParsedSignature() 56 | { 57 | if (Podium::getInstance()->podiumConfig->get('use_wysiwyg') == '0') { 58 | $parser = new GithubMarkdown(); 59 | $parser->html5 = true; 60 | return $parser->parse($this->signature); 61 | } 62 | return $this->signature; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/models/Mod.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 0.1 13 | */ 14 | class Mod extends ModActiveRecord {} 15 | -------------------------------------------------------------------------------- /src/models/PollAnswer.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 0.5 13 | */ 14 | class PollAnswer extends PollAnswerActiveRecord {} 15 | -------------------------------------------------------------------------------- /src/models/PostThumb.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.1 12 | */ 13 | class PostThumb extends PostThumbActiveRecord {} 14 | -------------------------------------------------------------------------------- /src/models/ThreadView.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.1 15 | */ 16 | class ThreadView extends ThreadViewActiveRecord 17 | { 18 | /** 19 | * Searches for threads with unread posts. 20 | * @param array $params 21 | * @return ActiveDataProvider 22 | */ 23 | public function search() 24 | { 25 | $loggedId = User::loggedId(); 26 | $query = Thread::find()->joinWith(['threadView' => function ($q) use ($loggedId) { 27 | $q->onCondition(['user_id' => $loggedId]); 28 | $q->andWhere(['or', 29 | new Expression('new_last_seen < new_post_at'), 30 | new Expression('edited_last_seen < edited_post_at'), 31 | ['new_last_seen' => null], 32 | ['edited_last_seen' => null], 33 | ]); 34 | }], false); 35 | 36 | $dataProvider = new ActiveDataProvider([ 37 | 'query' => $query, 38 | 'pagination' => [ 39 | 'defaultPageSize' => 10, 40 | 'forcePageParam' => false 41 | ], 42 | ]); 43 | 44 | $dataProvider->sort->defaultOrder = ['edited_post_at' => SORT_ASC, 'id' => SORT_ASC]; 45 | $dataProvider->pagination->pageSize = Yii::$app->session->get('per-page', 20); 46 | 47 | return $dataProvider; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/models/UserSearch.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.1 14 | */ 15 | class UserSearch extends User 16 | { 17 | /** 18 | * @inheritdoc 19 | */ 20 | public function rules() 21 | { 22 | return [ 23 | [['id'], 'integer'], 24 | [['username', 'email'], 'safe'], 25 | [['status'], 'in', 'range' => array_keys(User::getStatuses())], 26 | [['role'], 'in', 'range' => array_keys(User::getRoles())], 27 | ]; 28 | } 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public function scenarios() 34 | { 35 | return Model::scenarios(); 36 | } 37 | 38 | /** 39 | * @inheritdoc 40 | */ 41 | public function search($params, $active = false, $mods = false) 42 | { 43 | $query = User::find(); 44 | if ($active) { 45 | $query->andWhere(['!=', 'status', User::STATUS_REGISTERED]); 46 | } 47 | if ($mods) { 48 | $query->andWhere(['role' => [User::ROLE_ADMIN, User::ROLE_MODERATOR]]); 49 | } 50 | 51 | $dataProvider = new ActiveDataProvider(['query' => $query]); 52 | $dataProvider->sort->defaultOrder = ['id' => SORT_ASC]; 53 | $dataProvider->pagination->pageSize = Yii::$app->session->get('per-page', 20); 54 | 55 | if (!($this->load($params) && $this->validate())) { 56 | return $dataProvider; 57 | } 58 | 59 | $query 60 | ->andFilterWhere(['id' => $this->id]) 61 | ->andFilterWhere(['status' => $this->status]) 62 | ->andFilterWhere(['role' => $this->role]) 63 | ->andFilterWhere(['like', 'email', $this->email]) 64 | ->andFilterWhere(['like', 'username', $this->username]); 65 | 66 | return $dataProvider; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/models/Vocabulary.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.1 15 | */ 16 | class Vocabulary extends VocabularyActiveRecord 17 | { 18 | /** 19 | * Returns data provider for simple search. 20 | * @return ActiveDataProvider 21 | */ 22 | public function search() 23 | { 24 | $query = static::find()->where(['and', 25 | ['is not', 'post_id', null], 26 | ['like', 'word', $this->query] 27 | ])->joinWith(['posts.author', 'posts.thread']); 28 | if (Podium::getInstance()->user->isGuest) { 29 | $query->joinWith(['posts.forum' => function($q) { 30 | $q->where([Forum::tableName() . '.visible' => 1]); 31 | }]); 32 | } 33 | $query->groupBy(['post_id', 'word_id']); 34 | 35 | $dataProvider = new ActiveDataProvider([ 36 | 'query' => $query, 37 | 'sort' => [ 38 | 'defaultOrder' => ['thread_id' => SORT_DESC], 39 | 'attributes' => [ 40 | 'thread_id' => [ 41 | 'asc' => ['thread_id' => SORT_ASC], 42 | 'desc' => ['thread_id' => SORT_DESC], 43 | 'default' => SORT_DESC, 44 | ], 45 | ] 46 | ], 47 | ]); 48 | 49 | return $dataProvider; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/models/db/ActivityActiveRecord.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.6 15 | * 16 | * @property integer $id 17 | * @property integer $user_id 18 | * @property string $username 19 | * @property integer $user_role 20 | * @property string $url 21 | * @property string $ip 22 | * @property integer $created_at 23 | * @property integer $updated_at 24 | * 25 | * @property User $user 26 | */ 27 | class ActivityActiveRecord extends ActiveRecord 28 | { 29 | /** 30 | * @inheritdoc 31 | */ 32 | public static function tableName() 33 | { 34 | return '{{%podium_user_activity}}'; 35 | } 36 | 37 | /** 38 | * @inheritdoc 39 | */ 40 | public function behaviors() 41 | { 42 | return [TimestampBehavior::className()]; 43 | } 44 | 45 | /** 46 | * User relation. 47 | * @return ActiveQuery 48 | */ 49 | public function getUser() 50 | { 51 | return $this->hasOne(User::className(), ['id' => 'user_id']); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/models/db/CategoryActiveRecord.php: -------------------------------------------------------------------------------- 1 | 15 | * @since 0.6 16 | * 17 | * @property integer $id 18 | * @property string $name 19 | * @property string $slug 20 | * @property string $keywords 21 | * @property string $description 22 | * @property integer $visible 23 | * @property integer $sort 24 | * @property integer $updated_at 25 | * @property integer $created_at 26 | */ 27 | class CategoryActiveRecord extends ActiveRecord 28 | { 29 | /** 30 | * @inheritdoc 31 | */ 32 | public static function tableName() 33 | { 34 | return '{{%podium_category}}'; 35 | } 36 | 37 | /** 38 | * @inheritdoc 39 | */ 40 | public function behaviors() 41 | { 42 | return [ 43 | TimestampBehavior::className(), 44 | [ 45 | 'class' => Podium::getInstance()->slugGenerator, 46 | 'attribute' => 'name', 47 | 'type' => PodiumSluggableBehavior::CATEGORY 48 | ] 49 | ]; 50 | } 51 | 52 | /** 53 | * @inheritdoc 54 | */ 55 | public function rules() 56 | { 57 | return [ 58 | [['name', 'visible'], 'required'], 59 | ['visible', 'boolean'], 60 | ['name', 'filter', 'filter' => function ($value) { 61 | return HtmlPurifier::process(trim($value)); 62 | }], 63 | [['keywords', 'description'], 'string'], 64 | ]; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/models/db/ContentActiveRecord.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.6 14 | * 15 | * @property integer $id 16 | * @property string $name 17 | * @property string $topic 18 | * @property string $content 19 | */ 20 | class ContentActiveRecord extends ActiveRecord 21 | { 22 | /** 23 | * @inheritdoc 24 | */ 25 | public static function tableName() 26 | { 27 | return '{{%podium_content}}'; 28 | } 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public function rules() 34 | { 35 | return [ 36 | [['content', 'topic'], 'required'], 37 | [['content', 'topic'], 'string', 'min' => 1], 38 | ['topic', 'filter', 'filter' => function ($value) { 39 | return HtmlPurifier::process(trim($value)); 40 | }], 41 | ['content', 'filter', 'filter' => function ($value) { 42 | return HtmlPurifier::process(trim($value), Helper::podiumPurifierConfig('full')); 43 | }], 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/models/db/EmailActiveRecord.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 0.6 13 | * 14 | * @property integer $id 15 | * @property integer $user_id 16 | * @property string $email 17 | * @property string $subject 18 | * @property string $content 19 | * @property integer $status 20 | * @property integer $attempt 21 | * @property integer $created_at 22 | * @property integer $updated_at 23 | */ 24 | class EmailActiveRecord extends ActiveRecord 25 | { 26 | /** 27 | * Statuses. 28 | */ 29 | const STATUS_PENDING = 0; 30 | const STATUS_SENT = 1; 31 | const STATUS_GAVEUP = 9; 32 | 33 | /** 34 | * @inheritdoc 35 | */ 36 | public static function tableName() 37 | { 38 | return '{{%podium_email}}'; 39 | } 40 | 41 | /** 42 | * @inheritdoc 43 | */ 44 | public function behaviors() 45 | { 46 | return [TimestampBehavior::className()]; 47 | } 48 | 49 | /** 50 | * @inheritdoc 51 | */ 52 | public function rules() 53 | { 54 | return [ 55 | [['email', 'subject', 'content'], 'required'], 56 | ['email', 'email'], 57 | [['subject', 'content'], 'string'], 58 | ['status', 'default', 'value' => self::STATUS_PENDING], 59 | ['attempt', 'default', 'value' => 0], 60 | ['status', 'in', 'range' => [self::STATUS_PENDING, self::STATUS_SENT, self::STATUS_GAVEUP]] 61 | ]; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/models/db/ForumActiveRecord.php: -------------------------------------------------------------------------------- 1 | 18 | * @since 0.6 19 | * 20 | * @property integer $id 21 | * @property integer $category_id 22 | * @property string $name 23 | * @property string $sub 24 | * @property string $slug 25 | * @property string $keywords 26 | * @property string $description 27 | * @property integer $visible 28 | * @property integer $sort 29 | * @property integer $updated_at 30 | * @property integer $created_at 31 | */ 32 | class ForumActiveRecord extends ActiveRecord 33 | { 34 | /** 35 | * @inheritdoc 36 | */ 37 | public static function tableName() 38 | { 39 | return '{{%podium_forum}}'; 40 | } 41 | 42 | /** 43 | * @inheritdoc 44 | */ 45 | public function behaviors() 46 | { 47 | return [ 48 | TimestampBehavior::className(), 49 | [ 50 | 'class' => Podium::getInstance()->slugGenerator, 51 | 'attribute' => 'name', 52 | 'type' => PodiumSluggableBehavior::FORUM 53 | ] 54 | ]; 55 | } 56 | 57 | /** 58 | * @inheritdoc 59 | */ 60 | public function rules() 61 | { 62 | return [ 63 | [['name', 'visible'], 'required'], 64 | ['visible', 'boolean'], 65 | [['name', 'sub'], 'filter', 'filter' => function ($value) { 66 | return HtmlPurifier::process(trim($value)); 67 | }], 68 | [['keywords', 'description'], 'string'], 69 | ]; 70 | } 71 | 72 | /** 73 | * Category relation. 74 | * @return ActiveQuery 75 | */ 76 | public function getCategory() 77 | { 78 | return $this->hasOne(Category::className(), ['id' => 'category_id']); 79 | } 80 | 81 | /** 82 | * Post relation. One latest post. 83 | * @return ActiveQuery 84 | */ 85 | public function getLatest() 86 | { 87 | return $this->hasOne(Post::className(), ['forum_id' => 'id'])->orderBy(['id' => SORT_DESC]); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/models/db/MessageReceiverActiveRecord.php: -------------------------------------------------------------------------------- 1 | 15 | * @since 0.6 16 | * 17 | * @property integer $id 18 | * @property integer $message_id 19 | * @property integer $receiver_id 20 | * @property integer $receiver_status 21 | * @property integer $updated_at 22 | * @property integer $created_at 23 | */ 24 | class MessageReceiverActiveRecord extends ActiveRecord 25 | { 26 | /** 27 | * Statuses. 28 | */ 29 | const STATUS_NEW = 1; 30 | const STATUS_READ = 10; 31 | const STATUS_DELETED = 20; 32 | 33 | /** 34 | * @var string Sender name 35 | */ 36 | public $senderName; 37 | 38 | /** 39 | * @var string Message topic 40 | */ 41 | public $topic; 42 | 43 | /** 44 | * @inheritdoc 45 | */ 46 | public static function tableName() 47 | { 48 | return '{{%podium_message_receiver}}'; 49 | } 50 | 51 | /** 52 | * @inheritdoc 53 | */ 54 | public function behaviors() 55 | { 56 | return [TimestampBehavior::className()]; 57 | } 58 | 59 | /** 60 | * @inheritdoc 61 | */ 62 | public function scenarios() 63 | { 64 | return array_merge( 65 | parent::scenarios(), 66 | ['remove' => ['receiver_status']] 67 | ); 68 | } 69 | 70 | /** 71 | * @inheritdoc 72 | */ 73 | public function rules() 74 | { 75 | return [ 76 | [['receiver_id', 'message_id'], 'required'], 77 | [['receiver_id', 'message_id'], 'integer', 'min' => 1], 78 | ['receiver_status', 'in', 'range' => static::getStatuses()], 79 | [['senderName', 'topic'], 'string'] 80 | ]; 81 | } 82 | 83 | /** 84 | * Returns list of statuses. 85 | * @return string[] 86 | */ 87 | public static function getStatuses() 88 | { 89 | return [self::STATUS_NEW, self::STATUS_READ, self::STATUS_DELETED]; 90 | } 91 | 92 | /** 93 | * Message relation. 94 | * @return ActiveQuery 95 | */ 96 | public function getMessage() 97 | { 98 | return $this->hasOne(Message::className(), ['id' => 'message_id']); 99 | } 100 | 101 | /** 102 | * Receiver relation. 103 | * @return ActiveQuery 104 | */ 105 | public function getReceiver() 106 | { 107 | return $this->hasOne(User::className(), ['id' => 'receiver_id']); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/models/db/MetaActiveRecord.php: -------------------------------------------------------------------------------- 1 | 15 | * @since 0.6 16 | * 17 | * @property int $id 18 | * @property int $user_id 19 | * @property string $location 20 | * @property string $signature 21 | * @property int $gravatar 22 | * @property string $avatar 23 | * @property string $timezone 24 | * @property int $anonymous 25 | * @property int $created_at 26 | * @property int $updated_at 27 | */ 28 | class MetaActiveRecord extends ActiveRecord 29 | { 30 | /** 31 | * @inheritdoc 32 | */ 33 | public static function tableName() 34 | { 35 | return '{{%podium_user_meta}}'; 36 | } 37 | 38 | /** 39 | * @inheritdoc 40 | */ 41 | public function behaviors() 42 | { 43 | return [TimestampBehavior::className()]; 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | public function rules() 50 | { 51 | return [ 52 | [['location', 'signature'], 'trim'], 53 | ['location', 'filter', 'filter' => function ($value) { 54 | return HtmlPurifier::process(trim($value)); 55 | }], 56 | ['gravatar', 'boolean'], 57 | ['signature', 'filter', 'filter' => function ($value) { 58 | if (Podium::getInstance()->podiumConfig->get('use_wysiwyg') == '0') { 59 | return HtmlPurifier::process(trim($value), Helper::podiumPurifierConfig('markdown')); 60 | } 61 | return HtmlPurifier::process(trim($value), Helper::podiumPurifierConfig()); 62 | }], 63 | ['signature', 'string', 'max' => 512], 64 | ['timezone', 'match', 'pattern' => '/[\w\-]+/'], 65 | ['anonymous', 'boolean'], 66 | ]; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/models/db/ModActiveRecord.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 0.6 13 | * 14 | * @property integer $id 15 | * @property integer $user_id 16 | * @property integer $forum_id 17 | */ 18 | class ModActiveRecord extends ActiveRecord 19 | { 20 | /** 21 | * @inheritdoc 22 | */ 23 | public static function tableName() 24 | { 25 | return '{{%podium_moderator}}'; 26 | } 27 | 28 | /** 29 | * Forum relation. 30 | * @return ActiveQuery 31 | */ 32 | public function getForum() 33 | { 34 | return $this->hasOne(static::className(), ['id' => 'forum_id']); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/models/db/PollAnswerActiveRecord.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 0.6 13 | * 14 | * @property int $id 15 | * @property string $answer 16 | * @property int $votes 17 | * @property int $poll_id 18 | */ 19 | class PollAnswerActiveRecord extends ActiveRecord 20 | { 21 | /** 22 | * @inheritdoc 23 | */ 24 | public static function tableName() 25 | { 26 | return '{{%podium_poll_answer}}'; 27 | } 28 | 29 | /** 30 | * @inheritdoc 31 | */ 32 | public function rules() 33 | { 34 | return [ 35 | [['answer', 'poll_id'], 'required'], 36 | ['answer', 'string', 'max' => 255], 37 | ['votes', 'default', 'value' => 0], 38 | ['votes', 'integer', 'min' => 0], 39 | ['poll_id', 'integer'], 40 | ]; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/models/db/PostThumbActiveRecord.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 0.6 13 | * 14 | * @property integer $id 15 | * @property integer $user_id 16 | * @property integer $post_id 17 | * @property integer $created_at 18 | * @property integer $updated_at 19 | */ 20 | class PostThumbActiveRecord extends ActiveRecord 21 | { 22 | /** 23 | * @inheritdoc 24 | */ 25 | public static function tableName() 26 | { 27 | return '{{%podium_post_thumb}}'; 28 | } 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public function behaviors() 34 | { 35 | return [TimestampBehavior::className()]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/models/db/SubscriptionActiveRecord.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.6 15 | * 16 | * @property integer $id 17 | * @property integer $user_id 18 | * @property integer $thread_id 19 | * @property integer $post_seen 20 | * 21 | * @property User $user 22 | * @property Thread $thread 23 | */ 24 | class SubscriptionActiveRecord extends ActiveRecord 25 | { 26 | /** 27 | * @inheritdoc 28 | */ 29 | public static function tableName() 30 | { 31 | return '{{%podium_subscription}}'; 32 | } 33 | 34 | /** 35 | * User relation. 36 | * @return ActiveQuery 37 | */ 38 | public function getUser() 39 | { 40 | return $this->hasOne(User::className(), ['id' => 'user_id']); 41 | } 42 | 43 | /** 44 | * Thread relation. 45 | * @return ActiveQuery 46 | */ 47 | public function getThread() 48 | { 49 | return $this->hasOne(Thread::className(), ['id' => 'thread_id']); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/models/db/ThreadViewActiveRecord.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.6 12 | * 13 | * @property integer $id 14 | * @property integer $user_id 15 | * @property integer $thread_id 16 | * @property integer $new_last_seen 17 | * @property integer $edited_last_seen 18 | */ 19 | class ThreadViewActiveRecord extends ActiveRecord 20 | { 21 | /** 22 | * @inheritdoc 23 | */ 24 | public static function tableName() 25 | { 26 | return '{{%podium_thread_view}}'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/models/db/VocabularyActiveRecord.php: -------------------------------------------------------------------------------- 1 | 15 | * @since 0.6 16 | */ 17 | class VocabularyActiveRecord extends ActiveRecord 18 | { 19 | /** 20 | * @var string Query 21 | */ 22 | public $query; 23 | 24 | /** 25 | * @var int 26 | */ 27 | public $thread_id; 28 | 29 | /** 30 | * @var int 31 | */ 32 | public $post_id; 33 | 34 | /** 35 | * @inheritdoc 36 | */ 37 | public static function tableName() 38 | { 39 | return '{{%podium_vocabulary}}'; 40 | } 41 | 42 | /** 43 | * @inheritdoc 44 | */ 45 | public function rules() 46 | { 47 | return [ 48 | ['query', 'string'], 49 | ['query', 'filter', 'filter' => function ($value) { 50 | return HtmlPurifier::process(trim($value)); 51 | }], 52 | ]; 53 | } 54 | 55 | /** 56 | * Thread relation. 57 | * @return ActiveQuery 58 | */ 59 | public function getThread() 60 | { 61 | return $this->hasOne(Thread::className(), ['id' => 'thread_id']); 62 | } 63 | 64 | /** 65 | * Post relation. 66 | * @return ActiveQuery 67 | */ 68 | public function getPostData() 69 | { 70 | return $this->hasOne(Post::className(), ['id' => 'post_id']); 71 | } 72 | 73 | /** 74 | * Posts relation via junction. 75 | * @return ActiveQuery 76 | */ 77 | public function getPosts() 78 | { 79 | return $this->hasMany(Post::className(), ['id' => 'post_id'])->viaTable('{{%podium_vocabulary_junction}}', ['word_id' => 'id']); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/models/forms/ConfigForm.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.6 15 | */ 16 | class ConfigForm extends Model 17 | { 18 | /** 19 | * @var PodiumConfig Configuration instance. 20 | */ 21 | public $config; 22 | 23 | /** 24 | * @var string[] Saved settings. 25 | */ 26 | public $settings; 27 | 28 | /** 29 | * @var string[] List of read-only settings. 30 | */ 31 | public $readonly = ['version']; 32 | 33 | /** 34 | * @inheritdoc 35 | */ 36 | public function init() 37 | { 38 | parent::init(); 39 | $this->config = Podium::getInstance()->podiumConfig; 40 | $this->settings = $this->config->all; 41 | } 42 | 43 | /** 44 | * Returns the value of saved setting. 45 | * @param string $name Name of setting. 46 | * @return string 47 | */ 48 | public function __get($name) 49 | { 50 | return isset($this->settings[$name]) ? $this->settings[$name] : ''; 51 | } 52 | 53 | /** 54 | * Updates the value of setting. 55 | * @param string[] $data 56 | * @return bool 57 | */ 58 | public function update($data) 59 | { 60 | $validator = new StringValidator(); 61 | $validator->max = 255; 62 | 63 | foreach ($data as $key => $value) { 64 | if (!in_array($key, $this->readonly) && array_key_exists($key, $this->settings)) { 65 | if (!$validator->validate($value)) { 66 | return false; 67 | } 68 | if (!$this->config->set($key, $value)) { 69 | return false; 70 | } 71 | } 72 | } 73 | return true; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/models/forms/LoginForm.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.6 14 | * 15 | * @property User $user 16 | */ 17 | class LoginForm extends Model 18 | { 19 | /** 20 | * @var string Username or email 21 | */ 22 | public $username; 23 | 24 | /** 25 | * @var string Password 26 | */ 27 | public $password; 28 | 29 | /** 30 | * @var bool Remember me flag 31 | */ 32 | public $rememberMe = false; 33 | 34 | /** 35 | * @inheritdoc 36 | */ 37 | public function rules() 38 | { 39 | return [ 40 | [['username', 'password'], 'required'], 41 | ['username', 'string', 'min' => '3'], 42 | ['rememberMe', 'boolean'], 43 | ['password', 'validatePassword'], 44 | ]; 45 | } 46 | 47 | /** 48 | * Validates password. 49 | * @param string $attribute 50 | */ 51 | public function validatePassword($attribute) 52 | { 53 | if (!$this->hasErrors()) { 54 | $user = $this->user; 55 | if (empty($user) || !$user->validatePassword($this->password)) { 56 | $this->addError($attribute, 'Incorrect username or password.'); 57 | } 58 | } 59 | } 60 | 61 | /** 62 | * Logs user in. 63 | * @return bool 64 | */ 65 | public function login() 66 | { 67 | if ($this->validate()) { 68 | return Podium::getInstance()->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0); 69 | } 70 | return false; 71 | } 72 | 73 | private $_user = false; 74 | 75 | /** 76 | * Returns user. 77 | * @return User 78 | */ 79 | public function getUser() 80 | { 81 | if ($this->_user === false) { 82 | $this->_user = User::findByKeyfield($this->username); 83 | } 84 | return $this->_user; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/rbac/AuthorRule.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.1 12 | */ 13 | class AuthorRule extends Rule 14 | { 15 | public $name = 'isPodiumAuthor'; 16 | 17 | /** 18 | * @param string|integer $user the user ID. 19 | * @param \yii\rbac\Item $item the role or permission that this rule is associated with 20 | * @param array $params parameters passed to ManagerInterface::checkAccess(). 21 | * @return bool a value indicating whether the rule permits the role or permission it is associated with. 22 | */ 23 | public function execute($user, $item, $params) 24 | { 25 | return isset($params['post']) ? $params['post']->author_id == $user : false; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/rbac/ModeratorRule.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.1 12 | */ 13 | class ModeratorRule extends Rule 14 | { 15 | public $name = 'isPodiumModerator'; 16 | 17 | /** 18 | * @param string|integer $user the user ID. 19 | * @param \yii\rbac\Item $item the role or permission that this rule is associated with 20 | * @param array $params parameters passed to ManagerInterface::checkAccess(). 21 | * @return bool a value indicating whether the rule permits the role or permission it is associated with. 22 | */ 23 | public function execute($user, $item, $params) 24 | { 25 | return isset($params['item']) ? $params['item']->isMod() : false; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/services/Sorter.php: -------------------------------------------------------------------------------- 1 | 16 | * @since 0.6 17 | */ 18 | class Sorter extends Component 19 | { 20 | /** 21 | * @var ActiveRecord 22 | */ 23 | public $target; 24 | 25 | /** 26 | * @var Query 27 | */ 28 | public $query; 29 | 30 | /** 31 | * @var int new order 32 | */ 33 | public $order; 34 | 35 | /** 36 | * Runs sorter. 37 | * @return bool 38 | */ 39 | public function run() 40 | { 41 | try { 42 | $next = 0; 43 | $newSort = -1; 44 | foreach ($this->query->each() as $id => $model) { 45 | if ($next == $this->order) { 46 | $newSort = $next++; 47 | } 48 | Podium::getInstance()->db->createCommand()->update( 49 | call_user_func([$this->target, 'tableName']), ['sort' => $next], ['id' => $id] 50 | )->execute(); 51 | $next++; 52 | } 53 | if ($newSort == -1) { 54 | $newSort = $next; 55 | } 56 | $this->target->sort = $newSort; 57 | if (!$this->target->save()) { 58 | throw new Exception('Order saving error'); 59 | } 60 | Log::info('Orded updated', $this->target->id, __METHOD__); 61 | return true; 62 | } catch (Exception $e) { 63 | Log::error($e->getMessage(), null, __METHOD__); 64 | } 65 | return false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/services/ThreadVerifier.php: -------------------------------------------------------------------------------- 1 | 15 | * @since 0.6 16 | */ 17 | class ThreadVerifier extends Component 18 | { 19 | /** 20 | * @var int category ID 21 | */ 22 | public $categoryId; 23 | 24 | /** 25 | * @var int forum ID 26 | */ 27 | public $forumId; 28 | 29 | /** 30 | * @var int thread ID 31 | */ 32 | public $threadId; 33 | 34 | /** 35 | * @var string thread slug 36 | */ 37 | public $threadSlug; 38 | 39 | /** 40 | * Validates parameters. 41 | * @return bool 42 | */ 43 | public function validate() 44 | { 45 | if (is_numeric($this->categoryId) && $this->categoryId >= 1 46 | && is_numeric($this->forumId) && $this->forumId >= 1 47 | && is_numeric($this->threadId) && $this->threadId >= 1 48 | && !empty($this->threadSlug)) { 49 | return true; 50 | } 51 | return false; 52 | } 53 | 54 | private $_query; 55 | 56 | /** 57 | * Returns verified thread. 58 | * @return Thread 59 | */ 60 | public function verify() 61 | { 62 | if (!$this->validate()) { 63 | return null; 64 | } 65 | $this->_query = Thread::find()->where([ 66 | Thread::tableName() . '.id' => $this->threadId, 67 | Thread::tableName() . '.slug' => $this->threadSlug, 68 | Thread::tableName() . '.forum_id' => $this->forumId, 69 | Thread::tableName() . '.category_id' => $this->categoryId, 70 | ]); 71 | if (Podium::getInstance()->user->isGuest) { 72 | return $this->getThreadForGuests(); 73 | } 74 | return $this->getThreadForMembers(); 75 | } 76 | 77 | /** 78 | * Returns thread for guests. 79 | * @return Thread 80 | */ 81 | protected function getThreadForGuests() 82 | { 83 | return $this->_query->joinWith([ 84 | 'forum' => function ($query) { 85 | $query->andWhere([ 86 | Forum::tableName() . '.visible' => 1 87 | ])->joinWith(['category' => function ($query) { 88 | $query->andWhere([Category::tableName() . '.visible' => 1]); 89 | }]); 90 | } 91 | ])->limit(1)->one(); 92 | } 93 | 94 | /** 95 | * Returns thread for members. 96 | * @return Thread 97 | */ 98 | protected function getThreadForMembers() 99 | { 100 | return $this->_query->joinWith('forum.category')->limit(1)->one(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/slugs/PodiumSluggableBehavior.php: -------------------------------------------------------------------------------- 1 | [ 17 | * 'podium' => [ 18 | * 'class' => 'bizley\podium\Podium', 19 | * 'slugGenerator' => MyPodiumSlugGenerator::className(), 20 | * ``` 21 | * 22 | * and then create a class: 23 | * 24 | * ``` 25 | * class MyPodiumSlugGenerator extends PodiumSluggableBehavior 26 | * { 27 | * 28 | * protected function generateSlug($slugParts) 29 | * { 30 | * if ($this->type == self::THREAD) { 31 | * $s = substr($slugParts[0], 1); 32 | * return str_replace("/", "-", $s); 33 | * } else { 34 | * return parent::generateSlug ( $slugParts ); 35 | * } 36 | * } 37 | * } 38 | * ``` 39 | * 40 | * @author David Newcomb 41 | * @since 0.8 42 | */ 43 | 44 | class PodiumSluggableBehavior extends SluggableBehavior 45 | { 46 | 47 | const CATEGORY = "category"; 48 | const FORUM = "forum"; 49 | const THREAD = "thread"; 50 | const USER = "user"; 51 | 52 | /** 53 | * @var string Use PodiumSluggableBehavior::CATEGORY,... 54 | */ 55 | public $type; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/traits/FlashTrait.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.2 14 | */ 15 | trait FlashTrait 16 | { 17 | /** 18 | * Alias for warning(). 19 | * @param string $message the flash message to be translated. 20 | * @param bool $removeAfterAccess message removal after access only. 21 | */ 22 | public function alert($message, $removeAfterAccess = true) 23 | { 24 | $this->warning($message, $removeAfterAccess); 25 | } 26 | 27 | /** 28 | * Adds flash message of 'danger' type. 29 | * @param string $message the flash message to be translated. 30 | * @param bool $removeAfterAccess message removal after access only. 31 | */ 32 | public function danger($message, $removeAfterAccess = true) 33 | { 34 | Yii::$app->session->addFlash('danger', $message, $removeAfterAccess); 35 | } 36 | 37 | /** 38 | * Alias for danger(). 39 | * @param string $message the flash message to be translated. 40 | * @param bool $removeAfterAccess message removal after access only. 41 | */ 42 | public function error($message, $removeAfterAccess = true) 43 | { 44 | $this->danger($message, $removeAfterAccess); 45 | } 46 | 47 | /** 48 | * Adds flash message of 'info' type. 49 | * @param string $message the flash message to be translated. 50 | * @param bool $removeAfterAccess message removal after access only. 51 | */ 52 | public function info($message, $removeAfterAccess = true) 53 | { 54 | Yii::$app->session->addFlash('info', $message, $removeAfterAccess); 55 | } 56 | 57 | /** 58 | * Alias for success(). 59 | * @param string $message the flash message to be translated. 60 | * @param bool $removeAfterAccess message removal after access only. 61 | */ 62 | public function ok($message, $removeAfterAccess = true) 63 | { 64 | $this->success($message, $removeAfterAccess); 65 | } 66 | 67 | /** 68 | * Adds flash message of 'success' type. 69 | * @param string $message the flash message to be translated. 70 | * @param bool $removeAfterAccess message removal after access only. 71 | */ 72 | public function success($message, $removeAfterAccess = true) 73 | { 74 | Yii::$app->session->addFlash('success', $message, $removeAfterAccess); 75 | } 76 | 77 | /** 78 | * Adds flash message of 'warning' type. 79 | * @param string $message the flash message to be translated. 80 | * @param bool $removeAfterAccess message removal after access only. 81 | */ 82 | public function warning($message, $removeAfterAccess = true) 83 | { 84 | Yii::$app->session->addFlash('warning', $message, $removeAfterAccess); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/views/account/login.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\bootstrap\ActiveForm; 11 | use yii\helpers\Html; 12 | use yii\helpers\Url; 13 | 14 | $this->title = Yii::t('podium/view', 'Sign in'); 15 | $this->params['breadcrumbs'][] = $this->title; 16 | 17 | $this->registerJs("$('[data-toggle=\"tooltip\"]').tooltip();"); 18 | 19 | ?> 20 |
21 |
22 | 'login-form']); ?> 23 |
24 | field($model, 'username')->textInput(['placeholder' => Yii::t('podium/view', 'Username or E-mail'), 'autofocus' => true])->label(false) ?> 25 |
26 |
27 | field($model, 'password')->passwordInput(['placeholder' => Yii::t('podium/view', 'Password')])->label(false) ?> 28 |
29 |
30 | field($model, 'rememberMe')->checkBox()->label(null, ['data-toggle' => 'tooltip', 'data-placement' => 'top', 'title' => Yii::t('podium/view', "Don't use this option on public computers!")]) ?> 31 |
32 |
33 | ' . Yii::t('podium/view', 'Sign in'), ['class' => 'btn btn-block btn-primary', 'name' => 'login-button']) ?> 34 |
35 |
36 | 37 | 38 |
39 | 40 |
41 |

42 | -------------------------------------------------------------------------------- /src/views/account/password.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | use yii\bootstrap\ActiveForm; 12 | 13 | $this->title = Yii::t('podium/view', 'Change password'); 14 | $this->params['breadcrumbs'][] = $this->title; 15 | 16 | ?> 17 |
18 |
19 |
20 | 6]) ?> 21 |
22 |
23 |
24 | 'password-form']); ?> 25 |
26 | field($model, 'password')->passwordInput(['placeholder' => Yii::t('podium/view', 'New password'), 'autofocus' => true])->label(false) ?> 27 |
28 |
29 | field($model, 'passwordRepeat')->passwordInput(['placeholder' => Yii::t('podium/view', 'Repeat new password')])->label(false) ?> 30 |
31 |
32 | ' . Yii::t('podium/view', 'Change password'), ['class' => 'btn btn-block btn-danger', 'name' => 'password-button']) ?> 33 |
34 | 35 |
36 |

37 | -------------------------------------------------------------------------------- /src/views/account/reactivate.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | use yii\bootstrap\ActiveForm; 12 | 13 | $this->title = Yii::t('podium/view', 'Account Activation'); 14 | $this->params['breadcrumbs'][] = $this->title; 15 | 16 | ?> 17 |
18 |
19 |
20 | 21 |
22 |
23 |
24 | 'reactivate-form']); ?> 25 |
26 | field($model, 'username')->textInput(['placeholder' => Yii::t('podium/view', 'User Name or E-mail'), 'autofocus' => true])->label(false) ?> 27 |
28 |
29 | ' . Yii::t('podium/view', 'Send me the account activation link'), ['class' => 'btn btn-block btn-success', 'name' => 'reactivate-button']) ?> 30 |
31 | 32 |
33 |

34 | -------------------------------------------------------------------------------- /src/views/account/reset.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | use yii\bootstrap\ActiveForm; 12 | 13 | $this->title = Yii::t('podium/view', 'Password Reset'); 14 | $this->params['breadcrumbs'][] = $this->title; 15 | 16 | ?> 17 |
18 |
19 |
20 | 21 |
22 |
23 |
24 | 'reset-form']); ?> 25 |
26 | field($model, 'username')->textInput(['placeholder' => Yii::t('podium/view', 'User Name or E-mail'), 'autofocus' => true])->label(false) ?> 27 |
28 |
29 | ' . Yii::t('podium/view', 'Send me the password reset link'), ['class' => 'btn btn-block btn-danger', 'name' => 'reset-button']) ?> 30 |
31 | 32 |
33 |

34 | -------------------------------------------------------------------------------- /src/views/admin/categories.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\helpers\Helper; 11 | use bizley\podium\widgets\Modal; 12 | use kartik\sortable\Sortable; 13 | use yii\helpers\Url; 14 | 15 | $this->title = Yii::t('podium/view', 'Forums'); 16 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Administration Dashboard'), 'url' => ['admin/index']]; 17 | $this->params['breadcrumbs'][] = $this->title; 18 | 19 | $items = []; 20 | foreach ($dataProvider as $category) { 21 | $items[] = ['content' => Helper::adminCategoriesPrepareContent($category)]; 22 | } 23 | 24 | if (!empty($items)) { 25 | $this->registerJs("$('#podiumModalDelete').on('show.bs.modal', function(e) { var button = $(e.relatedTarget); $('#deleteUrl').attr('href', button.data('url')); });"); 26 | $this->registerJs("$('[data-toggle=\"tooltip\"]').tooltip();"); 27 | } 28 | 29 | ?> 30 | render('/elements/admin/_navbar', ['active' => 'categories']); ?> 31 |
32 |
33 |
34 |

35 | 36 |
37 |
38 |
39 |
40 |
41 | 42 |

43 | 44 | true, 46 | 'handleLabel' => ' ', 47 | 'items' => $items, 48 | 'pluginEvents' => [ 49 | 'sortupdate' => 'function(e, ui) { $.post(\'' . Url::to(['admin/sort-category']) . '\', {id:ui.item.find(\'.podium-forum\').data(\'id\'), new:ui.item.index()}).done(function(data){ $(\'#podiumSortInfo\').html(data); }).fail(function(){ $(\'#podiumSortInfo\').html(\'' . Yii::t('podium/view', 'Sorry! There was some error while changing the order of the categories.') . '\'); }); }', 50 | ] 51 | ]); ?> 52 | 53 |
54 |

55 | 56 | 57 | 'podiumModalDelete', 59 | 'header' => Yii::t('podium/view', 'Delete Category'), 60 | 'footer' => Yii::t('podium/view', 'Delete Category'), 61 | 'footerConfirmOptions' => ['class' => 'btn btn-danger', 'id' => 'deleteUrl'] 62 | ]) ?> 63 |

64 |

65 |

66 | 67 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Url; 11 | 12 | ?> 13 | 57 | -------------------------------------------------------------------------------- /src/views/elements/forum/_forum.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\Podium; 11 | use yii\helpers\Html; 12 | use yii\helpers\Url; 13 | 14 | ?> 15 | 16 | name) ?> 17 | sub)): ?> 18 | sub) ?> 19 | 20 | 21 | threads ?> 22 | posts ?> 23 | 24 | latest) && !empty($model->latest->thread)): ?> 25 | latest->thread->name) ?> 26 | 27 | latest->author->podiumTag ?> 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/views/elements/forum/_forum_header.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | ?> 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/views/elements/forum/_forum_list.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\models\Forum; 11 | use yii\widgets\ListView; 12 | 13 | ?> 14 | (new Forum())->search($category), 16 | 'itemView' => '/elements/forum/_forum', 17 | 'summary' => '', 18 | 'emptyText' => Yii::t('podium/view', 'No forums have been added yet.'), 19 | 'emptyTextOptions' => ['tag' => 'td', 'class' => 'text-muted', 'colspan' => 4], 20 | 'options' => ['tag' => 'tbody', 'class' => null], 21 | 'itemOptions' => ['tag' => 'tr'] 22 | ]); 23 | -------------------------------------------------------------------------------- /src/views/elements/forum/_forum_section.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\widgets\Readers; 11 | use yii\helpers\Html; 12 | use yii\helpers\Url; 13 | 14 | ?> 15 |
16 | 24 |
25 | render('/elements/forum/_threads', ['forum' => $model->id, 'category' => $model->category_id, 'slug' => $model->slug, 'filters' => $filters]) ?> 26 |
27 |
28 | 29 |
30 |
31 | render('/elements/forum/_icons') ?> 32 | 'forum']) ?> 33 |
34 |
35 | -------------------------------------------------------------------------------- /src/views/elements/forum/_forums.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | ?> 11 | 12 | render('/elements/forum/_forum_header') ?> 13 | render('/elements/forum/_forum_list', ['category' => $category]) ?> 14 |
15 | -------------------------------------------------------------------------------- /src/views/elements/forum/_icons.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | ?> 11 | 18 | -------------------------------------------------------------------------------- /src/views/elements/forum/_post_select.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\Podium; 11 | use yii\helpers\Html; 12 | 13 | $this->registerJs("$('[data-toggle=\"tooltip\"]').tooltip();"); 14 | 15 | ?> 16 |
17 |
18 | $model->id, 'label' => Yii::t('podium/view', 'Select this post')]) ?> 19 |
20 |
21 |
22 |
23 |
24 | 25 | formatter->asRelativeTime($model->created_at) ?> 26 | edited && $model->edited_at): ?> 27 | ( formatter->asRelativeTime($model->edited_at) ?>) 28 | 29 | 30 | author->podiumTag ?> 31 | 32 | author->postsCount ?> 33 | 34 |
35 |
36 | parsedContent ?> 37 |
38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /src/views/elements/forum/_section.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | use yii\helpers\Url; 12 | 13 | ?> 14 |
15 | 23 |
24 | render('/elements/forum/_forums', ['category' => $model->id]) ?> 25 |
26 |
27 | -------------------------------------------------------------------------------- /src/views/elements/forum/_sections.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | ?> 11 |
12 | $dataProvider, 14 | 'itemView' => '/elements/forum/_section', 15 | 'separator' => "\n
\n", 16 | 'summary' => '', 17 | 'emptyText' => '

' . Yii::t('podium/view', 'No categories have been added yet.') . '

', 18 | ]); ?> 19 |
20 | -------------------------------------------------------------------------------- /src/views/elements/forum/_thread.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\Podium; 11 | use yii\helpers\Html; 12 | use yii\helpers\StringHelper; 13 | use yii\helpers\Url; 14 | 15 | $this->registerJs("$('[data-toggle=\"popover\"]').popover();"); 16 | $firstToSee = $model->firstToSee(); 17 | ?> 18 | 19 | parsedContent, 20, '...', true)) ?>
author->podiumName ?> formatter->asRelativeTime($firstToSee->updated_at) ?>" title=""> 20 | 21 |
22 | 25 | 28 | 29 | pinned ? '' : '' ?>name) ?>pinned ? '' : '' ?> 30 | 31 | 32 | posts > 0 ? $model->posts - 1 : 0 ?> 33 | views ?> 34 | 35 | latest) && !empty($model->latest->author)): ?> 36 | 37 | latest->author->podiumTag ?> 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/views/elements/forum/_thread_list.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\models\Thread; 11 | use yii\widgets\ListView; 12 | 13 | $filtersOn = false; 14 | if (!isset($filters)) { 15 | $filters = null; 16 | } 17 | if (!empty($filters)) { 18 | foreach ($filters as $filter) { 19 | if ($filter) { 20 | $filtersOn = true; 21 | break; 22 | } 23 | } 24 | } 25 | ?> 26 | (new Thread())->search($forum, $filters), 28 | 'itemView' => '/elements/forum/_thread', 29 | 'summary' => '', 30 | 'emptyText' => $filtersOn 31 | ? Yii::t('podium/view', 'No threads matching the filters can be found.') 32 | : Yii::t('podium/view', 'No threads have been added yet.'), 33 | 'emptyTextOptions' => ['tag' => 'td', 'class' => 'text-muted', 'colspan' => 4], 34 | 'options' => ['tag' => 'tbody'], 35 | 'itemOptions' => ['tag' => 'tr', 'class' => 'podium-thread-line'] 36 | ]); 37 | -------------------------------------------------------------------------------- /src/views/elements/forum/_threads.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\widgets\Pjax; 11 | 12 | ?> 13 | 14 | 15 | render('/elements/forum/_thread_header', ['forum' => $forum, 'category' => $category, 'slug' => $slug, 'filters' => $filters]) ?> 16 | render('/elements/forum/_thread_list', ['forum' => $forum, 'filters' => $filters]) ?> 17 |
18 | 19 | -------------------------------------------------------------------------------- /src/views/elements/installation/_navbar.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\bootstrap\NavBar; 11 | 12 | ?> 13 | 'Podium', 15 | 'brandUrl' => ['forum/index'], 16 | 'options' => ['class' => 'navbar-inverse navbar-default',], 17 | 'innerContainerOptions' => ['class' => 'container-fluid',] 18 | ]); 19 | NavBar::end(); 20 | -------------------------------------------------------------------------------- /src/views/elements/main/_breadcrumbs.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | use yii\helpers\Url; 12 | use yii\widgets\Breadcrumbs; 13 | 14 | ?> 15 |
16 | 19 | params['no-search']) || $this->params['no-search'] !== true): ?> 20 |
21 | 22 |
23 |
24 | 'form-control']); ?> 25 |
26 | 27 | 30 |
31 |
32 |
33 | 34 |
35 | 36 |
37 | -------------------------------------------------------------------------------- /src/views/elements/main/_footer.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\Podium; 11 | 12 | ?> 13 | 19 | -------------------------------------------------------------------------------- /src/views/elements/main/_members.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\helpers\Helper; 11 | use bizley\podium\models\Activity; 12 | 13 | $lastActive = Activity::lastActive(); 14 | 15 | ?> 16 |
17 |
18 |

19 | !empty($lastActive['count']) ? $lastActive['count'] : 0]) ?>
20 | !empty($lastActive['members']) ? $lastActive['members'] : 0]) ?>, 21 | !empty($lastActive['guests']) ? $lastActive['guests'] : 0]) ?>, 22 | !empty($lastActive['anonymous']) ? $lastActive['anonymous'] : 0]) ?> 23 |

24 | 25 |

26 | $name): ?> 27 | 28 | 29 |

30 | 31 |
32 | 39 |
40 | -------------------------------------------------------------------------------- /src/views/elements/members/_members_threads.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | ?> 11 |
12 | 17 |
18 | render('/elements/members/_threads', ['id' => $user->id]) ?> 19 |
20 |
21 | 22 |
23 |
24 | render('/elements/forum/_icons') ?> 25 |
26 |
27 | -------------------------------------------------------------------------------- /src/views/elements/members/_thread_list.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\models\Thread; 11 | use yii\widgets\ListView; 12 | 13 | ?> 14 | (new Thread())->searchByUser($id), 16 | 'itemView' => '/elements/forum/_thread', 17 | 'summary' => '', 18 | 'emptyText' => Yii::t('podium/view', 'No threads have been added yet.'), 19 | 'emptyTextOptions' => ['tag' => 'td', 'class' => 'text-muted', 'colspan' => 4], 20 | 'options' => ['tag' => 'tbody'], 21 | 'itemOptions' => ['tag' => 'tr', 'class' => 'podium-thread-line'] 22 | ]); 23 | -------------------------------------------------------------------------------- /src/views/elements/members/_threads.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | ?> 11 | 12 | render('/elements/forum/_thread_header') ?> 13 | render('/elements/members/_thread_list', ['id' => $id]) ?> 14 |
15 | -------------------------------------------------------------------------------- /src/views/elements/messages/_navbar.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Url; 11 | 12 | ?> 13 | 21 | -------------------------------------------------------------------------------- /src/views/elements/profile/_navbar.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\models\User; 11 | use yii\helpers\Url; 12 | 13 | $podiumUser = User::findMe(); 14 | $messageCount = $podiumUser->getNewMessagesCount(); 15 | $subscriptionCount = $podiumUser->getSubscriptionsCount(); 16 | 17 | ?> 18 | 25 | -------------------------------------------------------------------------------- /src/views/elements/search/_forum_search_posts.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | 12 | $typeName = $type == 'topics' ? Yii::t('podium/view', 'threads') : Yii::t('podium/view', 'posts'); 13 | 14 | ?> 15 |
16 |
17 |

18 | 19 | Html::encode($query), 'author' => Html::encode($author), 'type' => $typeName]) ?> 20 | 21 | Html::encode($query), 'type' => $typeName]) ?> 22 | 23 | Html::encode($author), 'type' => $typeName]) ?> 24 | 25 | $typeName]) ?> 26 | 27 |

28 |
29 |
30 | render('/elements/search/_threads_search_posts', ['dataProvider' => $dataProvider, 'type' => $type, 'query' => Html::encode($query)]) ?> 31 | -------------------------------------------------------------------------------- /src/views/elements/search/_forum_search_topics.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | 12 | $typeName = $type == 'topics' ? Yii::t('podium/view', 'threads') : Yii::t('podium/view', 'posts'); 13 | 14 | ?> 15 |
16 | 29 |
30 | render('/elements/search/_threads_search_topics', ['dataProvider' => $dataProvider, 'type' => $type]) ?> 31 |
32 |
33 | 34 |
35 |
36 | render('/elements/forum/_icons') ?> 37 |
38 |
39 | -------------------------------------------------------------------------------- /src/views/elements/search/_thread_header.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | ?> 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/views/elements/search/_thread_list.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\widgets\ListView; 11 | 12 | ?> 13 | $dataProvider, 15 | 'itemView' => '/elements/search/_thread_' . $type, 16 | 'summary' => '', 17 | 'layout' => "{summary}\n{items}\n{pager}", 18 | 'pager' => ['options' => ['class' => 'pagination podium-pagination']], 19 | 'emptyText' => $type == 'topics' ? Yii::t('podium/view', 'No matching threads can be found.') : Yii::t('podium/view', 'No matching posts can be found.'), 20 | 'emptyTextOptions' => ['tag' => 'td', 'class' => 'text-muted', 'colspan' => 4], 21 | 'options' => ['tag' => 'tbody'], 22 | 'itemOptions' => ['tag' => 'tr', 'class' => 'podium-thread-line'] 23 | ]); 24 | -------------------------------------------------------------------------------- /src/views/elements/search/_thread_posts.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\Podium; 11 | use yii\helpers\Html; 12 | use yii\helpers\Url; 13 | 14 | $this->registerJs("$('[data-toggle=\"popover\"]').popover();"); 15 | 16 | $postModel = !empty($model->posts[0]) ? $model->posts[0] : $model->postData; 17 | ?> 18 | 29 | 32 | 35 | 38 | -------------------------------------------------------------------------------- /src/views/elements/search/_thread_topics.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\Podium; 11 | use yii\helpers\Html; 12 | use yii\helpers\Url; 13 | 14 | $this->registerJs("$('[data-toggle=\"popover\"]').popover();"); 15 | 16 | $firstToSee = $model->firstToSee(); 17 | 18 | ?> 19 | 30 | 31 | 32 | 37 | -------------------------------------------------------------------------------- /src/views/elements/search/_threads_search_posts.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\widgets\ListView; 11 | use yii\widgets\Pjax; 12 | 13 | $words = []; 14 | $query = preg_replace('/\s+/', ' ', trim($query)); 15 | $tmp = explode(' ', $query); 16 | foreach ($tmp as $tmp) { 17 | if (mb_strlen($tmp, 'UTF-8') > 2) { 18 | $words[] = $tmp; 19 | } 20 | } 21 | 22 | ?> 23 |
24 | $dataProvider, 27 | 'itemView' => '/elements/search/_post', 28 | 'viewParams' => ['words' => $words, 'type' => $type], 29 | 'summary' => '', 30 | 'emptyText' => $type == 'topics' ? Yii::t('podium/view', 'No matching threads can be found.') : Yii::t('podium/view', 'No matching posts can be found.'), 31 | 'emptyTextOptions' => ['tag' => 'h3', 'class' => 'text-muted'], 32 | 'pager' => ['options' => ['class' => 'pagination pull-right']] 33 | ]); 34 | Pjax::end(); ?> 35 |
36 | -------------------------------------------------------------------------------- /src/views/elements/search/_threads_search_topics.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | ?> 11 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | thread->name) ?> 27 | 28 | 30 | thread->posts > 0 ? $postModel->thread->posts - 1 : 0 ?> 31 | 33 | thread->views ?> 34 | 36 | author->podiumTag ?>
formatter->asDatetime($postModel->created_at) ?>
37 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | name) ?> 28 | 29 | posts > 0 ? $model->posts - 1 : 0 ?>views ?> 33 | latest) && !empty($model->latest->author)): ?> 34 | latest->author->podiumTag ?>
formatter->asDatetime($model->latest->created_at) ?>
35 | 36 |
12 | render('/elements/search/_thread_header') ?> 13 | render('/elements/search/_thread_list', ['dataProvider' => $dataProvider, 'type' => $type]) ?> 14 |
15 | -------------------------------------------------------------------------------- /src/views/forum/ban.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | $this->title = Yii::t('podium/view', 'You have been banned!'); 11 | 12 | ?> 13 |
14 | 15 |

title ?>

16 |

17 |
18 | -------------------------------------------------------------------------------- /src/views/forum/category.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\Podium; 11 | use yii\helpers\Url; 12 | 13 | $this->title = $model->name; 14 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 15 | $this->params['breadcrumbs'][] = $this->title; 16 | 17 | ?> 18 | user->isGuest): ?> 19 |
20 |
21 |
22 | 23 |
24 |
25 |
26 | 27 | 28 |
29 |
30 |
31 | render('/elements/forum/_section', ['model' => $model]) ?> 32 |
33 |
34 |
35 | render('/elements/main/_members'); 36 | -------------------------------------------------------------------------------- /src/views/forum/delete.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | 12 | $this->title = Yii::t('podium/view', 'Delete Thread'); 13 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 14 | $this->params['breadcrumbs'][] = ['label' => $model->forum->category->name, 'url' => ['forum/category', 'id' => $model->forum->category->id, 'slug' => $model->forum->category->slug]]; 15 | $this->params['breadcrumbs'][] = ['label' => $model->forum->name, 'url' => ['forum/forum', 'cid' => $model->forum->category->id, 'id' => $model->forum->id, 'slug' => $model->forum->slug]]; 16 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['forum/thread', 'cid' => $model->forum->category->id, 'fid' => $model->forum->id, 'id' => $model->id, 'slug' => $model->slug]]; 17 | $this->params['breadcrumbs'][] = $this->title; 18 | 19 | ?> 20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 | id) ?> 28 |

29 |

30 |
31 |
32 |
33 | 43 | 44 |
45 |
46 |

47 | -------------------------------------------------------------------------------- /src/views/forum/deletepoll.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.5 8 | */ 9 | 10 | use bizley\podium\widgets\poll\Poll; 11 | use yii\helpers\Html; 12 | 13 | $this->title = Yii::t('podium/view', 'Delete Poll'); 14 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 15 | $this->params['breadcrumbs'][] = ['label' => $model->thread->forum->category->name, 'url' => ['forum/category', 'id' => $model->thread->forum->category->id, 'slug' => $model->thread->forum->category->slug]]; 16 | $this->params['breadcrumbs'][] = ['label' => $model->thread->forum->name, 'url' => ['forum/forum', 'cid' => $model->thread->forum->category->id, 'id' => $model->thread->forum->id, 'slug' => $model->thread->forum->slug]]; 17 | $this->params['breadcrumbs'][] = ['label' => $model->thread->name, 'url' => ['forum/thread', 'cid' => $model->thread->forum->category->id, 'fid' => $model->thread->forum->id, 'id' => $model->thread->id, 'slug' => $model->thread->slug]]; 18 | $this->params['breadcrumbs'][] = $this->title; 19 | 20 | ?> 21 |
22 |
23 |
24 | 25 |
26 |
27 |
28 | id) ?> 29 |

30 |

31 |
32 |
33 |
34 | 44 | 45 |
46 |
47 |
48 | 49 | $model, 'display' => true]) ?> 50 |
51 | -------------------------------------------------------------------------------- /src/views/forum/deletepost.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | 12 | $this->title = Yii::t('podium/view', 'Delete Post'); 13 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 14 | $this->params['breadcrumbs'][] = ['label' => $model->forum->category->name, 'url' => ['forum/category', 'id' => $model->forum->category->id, 'slug' => $model->forum->category->slug]]; 15 | $this->params['breadcrumbs'][] = ['label' => $model->forum->name, 'url' => ['forum/forum', 'cid' => $model->forum->category->id, 'id' => $model->forum->id, 'slug' => $model->forum->slug]]; 16 | $this->params['breadcrumbs'][] = ['label' => $model->thread->name, 'url' => ['forum/thread', 'cid' => $model->forum->category->id, 'fid' => $model->forum->id, 'id' => $model->thread->id, 'slug' => $model->thread->slug]]; 17 | $this->params['breadcrumbs'][] = $this->title; 18 | 19 | ?> 20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 | id) ?> 28 |

29 |

30 |
31 |
32 |
33 | 43 | 44 |
45 |
46 |

47 | render('/elements/forum/_post', ['model' => $model, 'category' => $model->forum->category->id, 'slug' => $model->thread->slug]) ?> 48 |
49 | -------------------------------------------------------------------------------- /src/views/forum/deleteposts.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | use yii\widgets\ListView; 12 | use yii\widgets\Pjax; 13 | 14 | $this->title = Yii::t('podium/view', 'Delete Posts'); 15 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 16 | $this->params['breadcrumbs'][] = ['label' => $model->forum->category->name, 'url' => ['forum/category', 'id' => $model->forum->category->id, 'slug' => $model->forum->category->slug]]; 17 | $this->params['breadcrumbs'][] = ['label' => $model->forum->name, 'url' => ['forum/forum', 'cid' => $model->forum->category->id, 'id' => $model->forum->id, 'slug' => $model->forum->slug]]; 18 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['forum/thread', 'cid' => $model->forum->category->id, 'fid' => $model->forum->id, 'id' => $model->id, 'slug' => $model->slug]]; 19 | $this->params['breadcrumbs'][] = $this->title; 20 | 21 | ?> 22 |
23 |
24 |
25 |
26 | : 27 |
28 |
29 |
30 |

31 | 32 | 33 | 34 | $dataProvider, 36 | 'itemView' => '/elements/forum/_post_select', 37 | 'summary' => '', 38 | 'emptyText' => Yii::t('podium/view', 'No posts have been added yet.'), 39 | 'emptyTextOptions' => ['tag' => 'h3', 'class' => 'text-muted'], 40 | 'pager' => ['options' => ['class' => 'pagination pull-right']] 41 | ]); ?> 42 | 43 |
44 |
45 |
46 |
47 | 54 |
55 |
56 |

57 | 7 | * @since 0.5 8 | */ 9 | 10 | use bizley\podium\widgets\poll\Poll; 11 | use yii\bootstrap\ActiveForm; 12 | use yii\helpers\Html; 13 | 14 | $this->title = Yii::t('podium/view', 'Edit Poll'); 15 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 16 | $this->params['breadcrumbs'][] = ['label' => $model->thread->forum->category->name, 'url' => ['forum/category', 'id' => $model->thread->forum->category->id, 'slug' => $model->thread->forum->category->slug]]; 17 | $this->params['breadcrumbs'][] = ['label' => $model->thread->forum->name, 'url' => ['forum/forum', 'cid' => $model->thread->forum->category->id, 'id' => $model->thread->forum->id, 'slug' => $model->thread->forum->slug]]; 18 | $this->params['breadcrumbs'][] = ['label' => $model->thread->name, 'url' => ['forum/thread', 'cid' => $model->thread->forum->category->id, 'fid' => $model->thread->forum->id, 'id' => $model->thread->id, 'slug' => $model->thread->slug]]; 19 | $this->params['breadcrumbs'][] = $this->title; 20 | 21 | ?> 22 | 23 |
24 |
25 |
26 | 'edit-poll-form']); ?> 27 |
28 | 29 |
30 | 40 | 41 |
42 |
43 |

44 | -------------------------------------------------------------------------------- /src/views/forum/forum.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\models\User; 11 | use bizley\podium\Podium; 12 | use bizley\podium\rbac\Rbac; 13 | use yii\helpers\Url; 14 | 15 | $this->title = $model->name; 16 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 17 | $this->params['breadcrumbs'][] = ['label' => $model->category->name, 'url' => ['forum/category', 'id' => $model->category->id, 'slug' => $model->category->slug]]; 18 | $this->params['breadcrumbs'][] = $this->title; 19 | 20 | ?> 21 | user->isGuest): ?> 22 |
23 |
24 |
    25 | 26 |
  • 27 | 28 |
  • 29 |
30 |
31 |
32 | 33 | 34 |
35 |
36 |
37 | render('/elements/forum/_forum_section', ['model' => $model, 'filters' => $filters]) ?> 38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /src/views/forum/index.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\Podium; 11 | use bizley\podium\widgets\LatestPosts; 12 | use yii\helpers\Url; 13 | 14 | $this->title = Yii::t('podium/view', 'Main Forum'); 15 | $this->params['breadcrumbs'][] = $this->title; 16 | 17 | ?> 18 |
19 |
20 | render('/elements/forum/_sections', ['dataProvider' => $dataProvider]) ?> 21 |
22 |
23 | user->isGuest): ?> 24 |
25 | 26 | 27 |
28 |
29 | render('/elements/main/_members'); 30 | -------------------------------------------------------------------------------- /src/views/forum/maintenance.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | $this->title = Yii::t('podium/view', 'Forum Maintenance'); 11 | 12 | $this->registerJs("var deg = 0; var rotate = function () { $('#cog1').css({'-webkit-transform':'rotate(' + deg + 'deg)', '-moz-transform':'rotate(' + deg + 'deg)', '-ms-transform':'rotate(' + deg + 'deg)', '-o-transform':'rotate(' + deg + 'deg)'}); $('#cog2').css({'-webkit-transform':'rotate(-' + deg + 'deg)', '-moz-transform':'rotate(-' + deg + 'deg)', '-ms-transform':'rotate(-' + deg + 'deg)', '-o-transform':'rotate(-' + deg + 'deg)'}); deg += 10; if (deg == 360) deg = 0;} window.setInterval(rotate, 100, deg);"); 13 | ?> 14 |
15 | 16 | 17 |

title ?>

18 |

19 |
20 | -------------------------------------------------------------------------------- /src/views/forum/move.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | 12 | $this->title = Yii::t('podium/view', 'Move Thread'); 13 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 14 | $this->params['breadcrumbs'][] = ['label' => $model->forum->category->name, 'url' => ['forum/category', 'id' => $model->forum->category->id, 'slug' => $model->forum->category->slug]]; 15 | $this->params['breadcrumbs'][] = ['label' => $model->forum->name, 'url' => ['forum/forum', 'cid' => $model->forum->category->id, 'id' => $model->forum->id, 'slug' => $model->forum->slug]]; 16 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['forum/thread', 'cid' => $model->forum->category->id, 'fid' => $model->forum->id, 'id' => $model->id, 'slug' => $model->slug]]; 17 | $this->params['breadcrumbs'][] = $this->title; 18 | 19 | ?> 20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 | 28 |

*

29 | 'forum', 'class' => 'form-control', 'options' => $options, 'encode' => false]) ?> 30 |
31 |
32 |
33 | 40 | 41 |
42 |
43 |

44 | -------------------------------------------------------------------------------- /src/views/forum/report.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\widgets\editor\EditorBasic; 11 | use yii\bootstrap\ActiveForm; 12 | use yii\helpers\Html; 13 | 14 | $this->title = Yii::t('podium/view', 'Report post'); 15 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 16 | $this->params['breadcrumbs'][] = ['label' => $post->forum->category->name, 'url' => ['forum/category', 'id' => $post->forum->category->id, 'slug' => $post->forum->category->slug]]; 17 | $this->params['breadcrumbs'][] = ['label' => $post->forum->name, 'url' => ['forum/forum', 'cid' => $post->forum->category->id, 'id' => $post->forum->id, 'slug' => $post->forum->slug]]; 18 | $this->params['breadcrumbs'][] = ['label' => $post->thread->name, 'url' => ['forum/thread', 'cid' => $post->forum->category->id, 'fid' => $post->forum->id, 'id' => $post->thread->id, 'slug' => $post->thread->slug]]; 19 | $this->params['breadcrumbs'][] = $this->title; 20 | 21 | ?> 22 | render('/elements/forum/_post', ['model' => $post, 'category' => $post->forum->category->id, 'slug' => $post->thread->slug]) ?> 23 |
24 |
25 |
26 |
27 | 'report-post-form']); ?> 28 |
29 |
30 |
31 | field($model, 'content') 32 | ->label(Yii::t('podium/view', 'Complaint')) 33 | ->widget(EditorBasic::className()) ?> 34 |
35 |
36 |
37 | 46 | 47 |
48 |
49 |

50 | -------------------------------------------------------------------------------- /src/views/forum/search.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | 12 | if (!isset($author)) { 13 | $author = ''; 14 | } 15 | if (!isset($model)) { 16 | $type = 'posts'; 17 | $display = 'topics'; 18 | } 19 | else { 20 | $type = $model->type; 21 | $display = $model->display; 22 | } 23 | ?> 24 | 25 | title = Yii::t('podium/view', 'Search for {type} with "{query}" by "{author}"', ['query' => Html::encode($query), 'author' => Html::encode($author), 'type' => $typeName]); 29 | } 30 | elseif (!empty($query) && empty($author)) { 31 | $this->title = Yii::t('podium/view', 'Search for {type} with "{query}"', ['query' => Html::encode($query), 'type' => $typeName]); 32 | } 33 | elseif (empty($query) && !empty($author)) { 34 | $this->title = Yii::t('podium/view', 'Search for {type} by "{author}"', ['author' => Html::encode($author), 'type' => $typeName]); 35 | } 36 | else { 37 | $this->title = Yii::t('podium/view', 'Search for {type}', ['type' => $typeName]); 38 | } 39 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 40 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Search Forum'), 'url' => ['forum/search']]; 41 | $this->params['breadcrumbs'][] = $this->title; 42 | ?> 43 |
44 |
45 | 46 | 47 |
48 | render('/elements/search/_forum_search_posts', ['dataProvider' => $dataProvider, 'query' => $query, 'author' => $author, 'type' => $type]) ?> 49 |
50 | 51 |
52 | render('/elements/search/_forum_search_topics', ['dataProvider' => $dataProvider, 'query' => $query, 'author' => $author, 'type' => $type]) ?> 53 |
54 | 55 |
56 |
57 | 58 | title = Yii::t('podium/view', 'Search Forum'); 60 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 61 | $this->params['breadcrumbs'][] = $this->title; 62 | ?> 63 |
64 | render('/elements/search/_search', ['model' => $model, 'list' => $list]) ?> 65 |
66 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\models\ThreadView; 11 | use bizley\podium\widgets\Readers; 12 | use yii\helpers\Url; 13 | use yii\widgets\ListView; 14 | 15 | $this->title = Yii::t('podium/view', 'Unread posts'); 16 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Main Forum'), 'url' => ['forum/index']]; 17 | $this->params['breadcrumbs'][] = $this->title; 18 | 19 | ?> 20 |
21 |
22 |
    23 |
  • 24 |
25 |
26 |
27 |
28 | 31 |
32 | 33 | render('/elements/forum/_thread_header') ?> 34 | (new ThreadView())->search(), 36 | 'itemView' => '/elements/forum/_thread', 37 | 'summary' => '', 38 | 'emptyText' => Yii::t('podium/view', 'No more unread posts at the moment.'), 39 | 'emptyTextOptions' => ['tag' => 'td', 'class' => 'text-muted', 'colspan' => 4], 40 | 'options' => ['tag' => 'tbody'], 41 | 'itemOptions' => ['tag' => 'tr', 'class' => 'podium-thread-line'] 42 | ]); ?> 43 |
44 |
45 |
46 |
47 |
48 |
    49 |
  • 50 |
  • 51 |
52 | 'unread']) ?> 53 |
54 |
-------------------------------------------------------------------------------- /src/views/layouts/installation.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\assets\PodiumAsset; 11 | use bizley\podium\helpers\Helper; 12 | use bizley\podium\widgets\Alert; 13 | use yii\helpers\Html; 14 | 15 | PodiumAsset::register($this); 16 | $this->beginPage() ?> 17 | 18 | 19 | 20 | 21 | 22 | 23 | <?= Html::encode(Helper::title($this->title)) ?> 24 | head() ?> 25 | 26 | 27 | 28 | beginBody() ?> 29 |
30 | render('/elements/installation/_navbar') ?> 31 | render('/elements/main/_breadcrumbs') ?> 32 | 33 | 34 |
35 | render('/elements/main/_footer') ?> 36 | endBody() ?> 37 | 38 | 39 | 40 | endPage() ?> 41 | -------------------------------------------------------------------------------- /src/views/layouts/main.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\assets\PodiumAsset; 11 | use bizley\podium\helpers\Helper; 12 | use bizley\podium\widgets\Alert; 13 | use yii\helpers\Html; 14 | 15 | PodiumAsset::register($this); 16 | $this->beginPage() ?> 17 | 18 | 19 | 20 | 21 | 22 | 23 | <?= Html::encode(Helper::title($this->title)) ?> 24 | head() ?> 25 | 26 | 27 | 28 | beginBody() ?> 29 |
30 | render('/elements/main/_navbar') ?> 31 | render('/elements/main/_breadcrumbs') ?> 32 | 33 | 34 |
35 | render('/elements/main/_footer') ?> 36 | endBody() ?> 37 | 38 | 39 | 40 | endPage() ?> 41 | -------------------------------------------------------------------------------- /src/views/layouts/maintenance.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Html; 11 | use bizley\podium\assets\PodiumAsset; 12 | 13 | PodiumAsset::register($this); 14 | $this->beginPage() ?> 15 | 16 | 17 | 18 | 19 | 20 | 21 | <?= Html::encode($this->title) ?> 22 | head() ?> 23 | 24 | 25 | 26 | beginBody() ?> 27 |
28 | 29 |
30 | endBody() ?> 31 | 32 | 33 | 34 | endPage() ?> 35 | -------------------------------------------------------------------------------- /src/views/members/posts.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\models\Post; 11 | use yii\helpers\Url; 12 | use yii\widgets\ListView; 13 | use yii\widgets\Pjax; 14 | 15 | $this->title = Yii::t('podium/view', 'Posts created by {name}', ['name' => $user->podiumName]); 16 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Members List'), 'url' => ['members/index']]; 17 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Member View'), 'url' => ['members/view', 'id' => $user->id, 'slug' => $user->podiumSlug]]; 18 | $this->params['breadcrumbs'][] = $this->title; 19 | 20 | ?> 21 | 27 |
28 | (new Post())->searchByUser($user->id), 31 | 'itemView' => '/elements/forum/_post', 32 | 'viewParams' => ['parent' => true], 33 | 'summary' => '', 34 | 'emptyText' => Yii::t('podium/view', 'No posts have been added yet.'), 35 | 'emptyTextOptions' => ['tag' => 'h3', 'class' => 'text-muted'], 36 | 'pager' => ['options' => ['class' => 'pagination pull-right']] 37 | ]); 38 | Pjax::end(); ?> 39 |
40 | -------------------------------------------------------------------------------- /src/views/members/threads.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use yii\helpers\Url; 11 | 12 | $this->title = Yii::t('podium/view', 'Threads started by {name}', ['name' => $user->podiumName]); 13 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Members List'), 'url' => ['members/index']]; 14 | $this->params['breadcrumbs'][] = ['label' => Yii::t('podium/view', 'Member View'), 'url' => ['members/view', 'id' => $user->id, 'slug' => $user->podiumSlug]]; 15 | $this->params['breadcrumbs'][] = $this->title; 16 | 17 | ?> 18 | 24 |
25 |
26 |
27 |
28 | render('/elements/members/_members_threads', ['user' => $user]) ?> 29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /src/views/messages/load.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\Podium; 11 | use bizley\podium\widgets\Avatar; 12 | use yii\helpers\Html; 13 | 14 | ?> 15 |
16 |
17 | $reply->reply->sender]) ?> 18 |
19 |
20 |
21 |
22 |
23 | formatter->asRelativeTime($reply->reply->created_at) ?> 24 | reply->topic) ?> 25 |
26 |
27 | reply->parsedContent ?> 28 |
29 |
30 |
31 |
-------------------------------------------------------------------------------- /src/views/profile/profile.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 0.1 8 | */ 9 | 10 | use bizley\podium\helpers\Helper; 11 | use bizley\podium\Podium; 12 | use bizley\podium\widgets\Avatar; 13 | use yii\helpers\Html; 14 | use yii\helpers\Url; 15 | 16 | $this->title = Yii::t('podium/view', 'My Profile'); 17 | $this->params['breadcrumbs'][] = $this->title; 18 | 19 | ?> 20 |
21 |
22 | render('/elements/profile/_navbar', ['active' => 'profile']) ?> 23 |
24 |
25 |
26 |
27 |

28 | podiumName) ?> 29 | 30 | email) ?> 31 | role) ?> 32 | 33 |

34 |

: meta) && !empty($model->meta->location) ? Html::encode($model->meta->location) : '-' ?>

35 |

Podium::getInstance()->formatter->asDatetime($model->created_at, 'long')]) ?> (formatter->asRelativeTime($model->created_at) ?>)

36 |

37 | 38 | 39 |

40 |
41 | 47 |
48 |
49 | 55 |

56 | -------------------------------------------------------------------------------- /src/web/User.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.5 14 | */ 15 | class User extends YiiUser 16 | { 17 | /** 18 | * Returns the access checker used for checking access. 19 | * @return CheckAccessInterface 20 | */ 21 | protected function getAccessChecker() 22 | { 23 | return Podium::getInstance()->rbac; 24 | } 25 | } -------------------------------------------------------------------------------- /src/widgets/Alert.php: -------------------------------------------------------------------------------- 1 | getSession()->setFlash('error', 'This is the message'); 22 | * \Yii::$app->getSession()->setFlash('success', 'This is the message'); 23 | * \Yii::$app->getSession()->setFlash('info', 'This is the message'); 24 | * ``` 25 | * 26 | * Multiple messages could be set as follows: 27 | * 28 | * ```php 29 | * \Yii::$app->getSession()->setFlash('error', ['Error 1', 'Error 2']); 30 | * ``` 31 | * 32 | * @author Kartik Visweswaran 33 | * @author Alexander Makarov 34 | */ 35 | class Alert extends \yii\bootstrap\Widget 36 | { 37 | /** 38 | * @var array the alert types configuration for the flash messages. 39 | * This array is setup as $key => $value, where: 40 | * - $key is the name of the session flash variable 41 | * - $value is the bootstrap alert type (i.e. danger, success, info, warning) 42 | */ 43 | public $alertTypes = [ 44 | 'error' => 'alert-danger', 45 | 'danger' => 'alert-danger', 46 | 'success' => 'alert-success', 47 | 'info' => 'alert-info', 48 | 'warning' => 'alert-warning' 49 | ]; 50 | 51 | /** 52 | * @var array the options for rendering the close button tag. 53 | */ 54 | public $closeButton = []; 55 | 56 | public function init() 57 | { 58 | parent::init(); 59 | 60 | $session = \Yii::$app->getSession(); 61 | $flashes = $session->getAllFlashes(); 62 | $appendCss = isset($this->options['class']) ? ' ' . $this->options['class'] : ''; 63 | 64 | foreach ($flashes as $type => $data) { 65 | if (isset($this->alertTypes[$type])) { 66 | $data = (array) $data; 67 | foreach ($data as $i => $message) { 68 | /* initialize css class for each alert box */ 69 | $this->options['class'] = $this->alertTypes[$type] . $appendCss; 70 | 71 | /* assign unique id to each alert box */ 72 | $this->options['id'] = $this->getId() . '-' . $type . '-' . $i; 73 | 74 | echo \yii\bootstrap\Alert::widget([ 75 | 'body' => $message, 76 | 'closeButton' => $this->closeButton, 77 | 'options' => $this->options, 78 | ]); 79 | } 80 | 81 | $session->removeFlash($type); 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/widgets/Avatar.php: -------------------------------------------------------------------------------- 1 | 17 | * @since 0.1 18 | */ 19 | class Avatar extends Widget 20 | { 21 | /** 22 | * @var User|null Avatar owner. 23 | */ 24 | public $author; 25 | 26 | /** 27 | * @var bool Whether user name should appear underneath the image. 28 | */ 29 | public $showName = true; 30 | 31 | /** 32 | * Renders the image. 33 | * Based on user settings the avatar can be uploaded image, Gravatar image or default one. 34 | * @return string 35 | */ 36 | public function run() 37 | { 38 | $avatar = Html::img(Helper::defaultAvatar(), [ 39 | 'class' => 'podium-avatar img-circle img-responsive center-block', 40 | 'alt' => Yii::t('podium/view', 'user deleted') 41 | ]); 42 | $name = Helper::deletedUserTag(true); 43 | if ($this->author instanceof User) { 44 | $avatar = Html::img(Helper::defaultAvatar(), [ 45 | 'class' => 'podium-avatar img-circle img-responsive center-block', 46 | 'alt' => Html::encode($this->author->podiumName) 47 | ]); 48 | $name = $this->author->podiumTag; 49 | $meta = $this->author->meta; 50 | if (!empty($meta)) { 51 | if (!empty($meta->gravatar)) { 52 | $avatar = Gravatar::widget([ 53 | 'email' => $this->author->email, 54 | 'defaultImage' => 'identicon', 55 | 'rating' => 'r', 56 | 'options' => [ 57 | 'alt' => Html::encode($this->author->podiumName), 58 | 'class' => 'podium-avatar img-circle img-responsive center-block', 59 | ] 60 | ]); 61 | } elseif (!empty($meta->avatar)) { 62 | $avatar = Html::img('@web/avatars/' . $meta->avatar, [ 63 | 'class' => 'podium-avatar img-circle img-responsive center-block', 64 | 'alt' => Html::encode($this->author->podiumName) 65 | ]); 66 | } 67 | } 68 | } 69 | return $avatar . ($this->showName ? Html::tag('p', $name, ['class' => 'avatar-name']) : ''); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/widgets/LatestPosts.php: -------------------------------------------------------------------------------- 1 | 16 | * @since 0.1 17 | */ 18 | class LatestPosts extends Widget 19 | { 20 | /** 21 | * @var int number of latest posts 22 | */ 23 | public $posts = 5; 24 | 25 | /** 26 | * Renders the list of posts. 27 | * @return string 28 | */ 29 | public function run() 30 | { 31 | $out = Html::beginTag('div', ['class' => 'panel panel-default']) . "\n"; 32 | $out .= Html::tag('div', Yii::t('podium/view', 'Latest posts'), ['class' => 'panel-heading']) . "\n"; 33 | 34 | $latest = Post::getLatest(is_numeric($this->posts) && $this->posts > 0 ? $this->posts : 5); 35 | 36 | if ($latest) { 37 | $out .= Html::beginTag('table', ['class' => 'table table-hover']) . "\n"; 38 | foreach ($latest as $post) { 39 | $out .= Html::beginTag('tr'); 40 | $out .= Html::beginTag('td'); 41 | $out .= Html::a($post['title'], ['forum/show', 'id' => $post['id']], ['class' => 'center-block']) . "\n"; 42 | $out .= Html::tag('small', Podium::getInstance()->formatter->asRelativeTime($post['created']) . "\n" . $post['author']) . "\n"; 43 | $out .= Html::endTag('td'); 44 | $out .= Html::endTag('tr'); 45 | } 46 | $out .= Html::endTag('table') . "\n"; 47 | } else { 48 | $out .= Html::beginTag('div', ['class' => 'panel-body']) . "\n"; 49 | $out .= Html::tag('small', Yii::t('podium/view', 'No posts have been added yet.')) . "\n"; 50 | $out .= Html::endTag('div') . "\n"; 51 | } 52 | 53 | $out .= Html::endTag('div') . "\n"; 54 | 55 | return $out; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/widgets/Modal.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.2 14 | */ 15 | class Modal extends YiiModal 16 | { 17 | /** 18 | * @var array the HTML attributes for the widget container tag. 19 | */ 20 | public $options = ['aria-hidden' => 'true']; 21 | /** 22 | * @var array footer confirmation HTML options 23 | */ 24 | public $footerConfirmOptions = []; 25 | /** 26 | * @var mixed footer confirmation URL 27 | */ 28 | public $footerConfirmUrl = '#'; 29 | 30 | 31 | /** 32 | * Initializes the widget. 33 | */ 34 | public function init() 35 | { 36 | $this->header = Html::tag('h4', $this->header, ['class' => 'modal-title', 'id' => $this->id . 'Label']); 37 | $this->options['aria-labelledby'] = $this->id . 'Label'; 38 | $this->footer = Html::button(Yii::t('podium/view', 'Cancel'), ['class' => 'btn btn-default', 'data-dismiss' => 'modal']) 39 | . "\n" . Html::a($this->footer, $this->footerConfirmUrl, $this->footerConfirmOptions); 40 | 41 | parent::init(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/widgets/PageSizer.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.1 15 | */ 16 | class PageSizer extends Widget 17 | { 18 | /** 19 | * @var array List of page sizes 20 | */ 21 | public $pageSizes = [5 => 5, 10 => 10, 20 => 20, 50 => 50]; 22 | 23 | /** 24 | * Rendering the widget dropdown. 25 | * Default page size is 20. 26 | * @return string 27 | */ 28 | public function run() 29 | { 30 | $size = 20; 31 | $saved = Yii::$app->session->get('per-page'); 32 | if (in_array($saved, $this->pageSizes)) { 33 | $size = $saved; 34 | } 35 | $selected = Yii::$app->request->get('per-page'); 36 | if (in_array($selected, $this->pageSizes)) { 37 | $size = $selected; 38 | } 39 | 40 | Yii::$app->session->set('per-page', $size); 41 | 42 | return Html::tag('div', Html::tag('div', 43 | Html::label(Yii::t('podium/view', 'Results per page'), 'per-page') 44 | . ' ' 45 | . Html::dropDownList('per-page', $size, $this->pageSizes, ['class' => 'form-control input-sm', 'id' => 'per-page']), 46 | ['class' => 'form-group'] 47 | ), ['class' => 'pull-right form-inline']) . '

'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/widgets/codemirror/CodeMirror.php: -------------------------------------------------------------------------------- 1 | 15 | * @since 0.6 16 | */ 17 | class CodeMirror extends InputWidget 18 | { 19 | /** 20 | * @var string Editor type to display 21 | */ 22 | public $type = 'basic'; 23 | 24 | /** 25 | * @inheritdoc 26 | */ 27 | public function run() 28 | { 29 | $this->registerClientScript(); 30 | if ($this->hasModel()) { 31 | if (empty($this->model->{$this->attribute})) { 32 | $this->model->{$this->attribute} = "\n\n\n\n\n\n\n\n"; 33 | } 34 | return Html::activeTextarea($this->model, $this->attribute, ['id' => 'codemirror']); 35 | } 36 | if (empty($this->value)) { 37 | $this->value = "\n\n\n\n\n\n\n\n"; 38 | } 39 | return Html::textarea($this->name, $this->value, ['id' => 'codemirror']); 40 | } 41 | 42 | /** 43 | * Registers widget assets. 44 | * Note that CodeMirror works without jQuery. 45 | */ 46 | public function registerClientScript() 47 | { 48 | $view = $this->view; 49 | CodeMirrorAsset::register($view); 50 | $js = 'var CodeMirrorLabels = { 51 | bold: "' . Yii::t('podium/view', 'Bold') . '", 52 | italic: "' . Yii::t('podium/view', 'Italic') . '", 53 | header: "' . Yii::t('podium/view', 'Header') . '", 54 | inlinecode: "' . Yii::t('podium/view', 'Inline code') . '", 55 | blockcode: "' . Yii::t('podium/view', 'Block code') . '", 56 | quote: "' . Yii::t('podium/view', 'Quote') . '", 57 | bulletedlist: "' . Yii::t('podium/view', 'Bulleted list') . '", 58 | orderedlist: "' . Yii::t('podium/view', 'Ordered list') . '", 59 | link: "' . Yii::t('podium/view', 'Link') . '", 60 | image: "' . Yii::t('podium/view', 'Image') . '", 61 | help: "' . Yii::t('podium/view', 'Help') . '", 62 | };var CodeMirrorSet = "' . $this->type . '";'; 63 | $view->registerJs($js, View::POS_BEGIN); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/widgets/codemirror/assets/CodeMirrorAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.6 12 | */ 13 | class CodeMirrorAsset extends AssetBundle 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $depends = [ 19 | 'bizley\podium\widgets\codemirror\assets\CodeMirrorLibAsset', 20 | 'bizley\podium\widgets\codemirror\assets\CodeMirrorExtraAsset', 21 | 'bizley\podium\widgets\codemirror\assets\CodeMirrorModesAsset', 22 | 'bizley\podium\widgets\codemirror\assets\CodeMirrorButtonsAsset', 23 | 'bizley\podium\widgets\codemirror\assets\CodeMirrorConfigAsset' 24 | ]; 25 | } 26 | -------------------------------------------------------------------------------- /src/widgets/codemirror/assets/CodeMirrorButtonsAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.6 12 | */ 13 | class CodeMirrorButtonsAsset extends AssetBundle 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $sourcePath = '@bower/codemirror-buttons'; 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $js = ['buttons.js']; 24 | } 25 | -------------------------------------------------------------------------------- /src/widgets/codemirror/assets/CodeMirrorConfigAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.6 12 | */ 13 | class CodeMirrorConfigAsset extends AssetBundle 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $sourcePath = '@podium/widgets/codemirror/podium'; 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $js = ['podium-codemirror.js']; 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public $css = ['podium-codemirror.css']; 29 | } 30 | -------------------------------------------------------------------------------- /src/widgets/codemirror/assets/CodeMirrorExtraAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.6 12 | */ 13 | class CodeMirrorExtraAsset extends AssetBundle 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $sourcePath = '@bower/codemirror/addon'; 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $js = [ 24 | 'mode/overlay.js', 25 | 'edit/continuelist.js', 26 | 'fold/xml-fold.js', 27 | 'edit/matchbrackets.js', 28 | 'edit/closebrackets.js', 29 | 'edit/closetag.js', 30 | 'display/panel.js', 31 | ]; 32 | } 33 | -------------------------------------------------------------------------------- /src/widgets/codemirror/assets/CodeMirrorLibAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.6 12 | */ 13 | class CodeMirrorLibAsset extends AssetBundle 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $sourcePath = '@bower/codemirror/lib'; 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $css = ['codemirror.css']; 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public $js = ['codemirror.js']; 29 | } 30 | -------------------------------------------------------------------------------- /src/widgets/codemirror/assets/CodeMirrorModesAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.6 12 | */ 13 | class CodeMirrorModesAsset extends AssetBundle 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $sourcePath = '@bower/codemirror/mode'; 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $js = [ 24 | 'xml/xml.js', 25 | 'javascript/javascript.js', 26 | 'css/css.js', 27 | 'htmlmixed/htmlmixed.js', 28 | 'clike/clike.js', 29 | 'php/php.js', 30 | 'sql/sql.js', 31 | 'meta.js', 32 | 'markdown/markdown.js', 33 | 'gfm/gfm.js', 34 | ]; 35 | } 36 | -------------------------------------------------------------------------------- /src/widgets/codemirror/podium/podium-codemirror.css: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | border:1px solid #ccc; 3 | height:auto; 4 | padding:5px; 5 | } 6 | .CodeMirror-buttonsPanel { 7 | border-top:1px solid #ccc; 8 | border-left:1px solid #ccc; 9 | border-right:1px solid #ccc; 10 | padding:10px 12px; 11 | } 12 | .CodeMirror-buttonsPanel button { 13 | background-color:#fff; 14 | border:0; 15 | margin-right:3px; 16 | border-radius:5px; 17 | } 18 | .CodeMirror-buttonsPanel button:hover { background-color:#eee } 19 | -------------------------------------------------------------------------------- /src/widgets/editor/EditorBasic.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.6 15 | */ 16 | class EditorBasic extends InputWidget 17 | { 18 | /** 19 | * @var InputWidget 20 | */ 21 | public $editor; 22 | 23 | /** 24 | * @inheritdoc 25 | */ 26 | public function init() 27 | { 28 | $config = [ 29 | 'model' => $this->model, 30 | 'attribute' => $this->attribute, 31 | 'name' => $this->name, 32 | 'value' => $this->value, 33 | 'options' => $this->options 34 | ]; 35 | if (Podium::getInstance()->podiumConfig->get('use_wysiwyg') == '0') { 36 | $this->editor = new CodeMirror($config); 37 | } else { 38 | if (empty($this->options)) { 39 | $config['options'] = ['style' => 'min-height:150px;']; 40 | } 41 | $this->editor = new QuillBasic($config); 42 | } 43 | } 44 | 45 | /** 46 | * @inheritdoc 47 | */ 48 | public function run() 49 | { 50 | return $this->editor->run(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/widgets/editor/EditorFull.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.6 15 | */ 16 | class EditorFull extends InputWidget 17 | { 18 | /** 19 | * @var InputWidget 20 | */ 21 | public $editor; 22 | 23 | /** 24 | * @inheritdoc 25 | */ 26 | public function init() 27 | { 28 | $config = [ 29 | 'model' => $this->model, 30 | 'attribute' => $this->attribute, 31 | 'name' => $this->name, 32 | 'value' => $this->value, 33 | 'options' => $this->options 34 | ]; 35 | if (Podium::getInstance()->podiumConfig->get('use_wysiwyg') == '0') { 36 | $config['type'] = 'full'; 37 | $this->editor = new CodeMirror($config); 38 | } else { 39 | if (empty($this->options)) { 40 | $config['options'] = ['style' => 'min-height:320px;']; 41 | } 42 | $this->editor = new QuillFull($config); 43 | } 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | public function run() 50 | { 51 | return $this->editor->run(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/widgets/gridview/ActionColumn.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 0.2 14 | */ 15 | class ActionColumn extends YiiActionColumn 16 | { 17 | /** 18 | * @var array the HTML attributes for the header cell tag. 19 | */ 20 | public $headerOptions = ['class' => 'text-right']; 21 | /** 22 | * @var array|\Closure the HTML attributes for the data cell tag. This can either be an array of 23 | * attributes or an anonymous function ([[Closure]]) that returns such an array. 24 | * The signature of the function should be the following: `function ($model, $key, $index, $column)`. 25 | * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered 26 | * and `$column` is a reference to the [[Column]] object. 27 | * A function may be used to assign different attributes to different rows based on the data in that row. 28 | */ 29 | public $contentOptions = ['class' => 'text-right']; 30 | /** 31 | * @var array html options to be applied to the [[initDefaultButtons()|default buttons]]. 32 | */ 33 | public $buttonOptions = [ 34 | 'class' => 'btn btn-default btn-xs', 35 | 'data-pjax' => '0', 36 | 'data-toggle' => 'tooltip', 37 | 'data-placement' => 'top', 38 | ]; 39 | 40 | /** 41 | * @inheritdoc 42 | */ 43 | public function init() 44 | { 45 | parent::init(); 46 | $this->header = Yii::t('podium/view', 'Actions'); 47 | $this->grid->view->registerJs("$('[data-toggle=\"tooltip\"]').tooltip();"); 48 | } 49 | 50 | /** 51 | * Returns button options. 52 | * @param array $options override 53 | * @return array 54 | */ 55 | public static function buttonOptions($options) 56 | { 57 | return array_merge( 58 | [ 59 | 'class' => 'btn btn-default btn-xs', 60 | 'data-pjax' => '0', 61 | 'data-toggle' => 'tooltip', 62 | 'data-placement' => 'top', 63 | ], 64 | $options 65 | ); 66 | } 67 | 68 | /** 69 | * Returns muted button HTML. 70 | * @param string $icon class 71 | * @return string 72 | */ 73 | public static function mutedButton($icon) 74 | { 75 | return Html::a(Html::tag('span', '', ['class' => $icon]), '#', ['class' => 'btn btn-xs disabled text-muted']); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/widgets/gridview/DataColumn.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 0.2 13 | */ 14 | class DataColumn extends YiiDataColumn 15 | { 16 | /** 17 | * @var boolean whether the header label should be HTML-encoded. 18 | */ 19 | public $encodeLabel = false; 20 | 21 | /** 22 | * @inheritdoc 23 | */ 24 | protected function getHeaderCellLabel() 25 | { 26 | if (!empty($this->attribute)) { 27 | return parent::getHeaderCellLabel() . Helper::sortOrder($this->attribute); 28 | } 29 | return parent::getHeaderCellLabel(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/widgets/gridview/GridView.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 0.2 15 | */ 16 | class GridView extends YiiGridView 17 | { 18 | /** 19 | * @var array the HTML attributes for the container tag of the grid view. 20 | * The "tag" element specifies the tag name of the container element and defaults to "div". 21 | */ 22 | public $options = ['class' => 'grid-view table-responsive']; 23 | /** 24 | * @var string the default data column class if the class name is not explicitly specified when configuring a data column. 25 | */ 26 | public $dataColumnClass = 'bizley\podium\widgets\gridview\DataColumn'; 27 | /** 28 | * @var array the HTML attributes for the grid table element. 29 | */ 30 | public $tableOptions = ['class' => 'table table-striped table-hover']; 31 | /** 32 | * @var string additional jQuery selector for selecting filter input fields 33 | */ 34 | public $filterSelector = 'select#per-page'; 35 | 36 | /** 37 | * Sets formatter to use Podium component. 38 | * @since 0.5 39 | */ 40 | public function init() 41 | { 42 | parent::init(); 43 | $this->formatter = Podium::getInstance()->formatter; 44 | } 45 | 46 | /** 47 | * Runs the widget. 48 | */ 49 | public function run() 50 | { 51 | Pjax::begin(); 52 | echo PageSizer::widget(); 53 | parent::run(); 54 | Pjax::end(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/widgets/poll/Poll.php: -------------------------------------------------------------------------------- 1 | 16 | * @since 0.5 17 | */ 18 | class Poll extends Widget 19 | { 20 | /** 21 | * @var PollModel 22 | */ 23 | public $model; 24 | 25 | /** 26 | * @var bool Display only 27 | */ 28 | public $display = false; 29 | 30 | 31 | /** 32 | * Rendering the poll. 33 | * @return string 34 | */ 35 | public function run() 36 | { 37 | if (!$this->model) { 38 | return null; 39 | } 40 | $hidden = $this->model->hidden; 41 | if ($hidden && !empty($this->model->end_at) && $this->model->end_at < time()) { 42 | $hidden = 0; 43 | } 44 | return $this->render('view', [ 45 | 'model' => $this->model, 46 | 'hidden' => $hidden, 47 | 'voted' => $this->display ? true : $this->model->getUserVoted(User::loggedId()), 48 | 'display' => $this->display 49 | ]); 50 | } 51 | 52 | /** 53 | * Renders poll create form. 54 | * @param ActiveForm $form 55 | * @param Thread $model 56 | * @return string 57 | */ 58 | public static function create($form, $model) 59 | { 60 | return (new static)->render('create', ['form' => $form, 'model' => $model]); 61 | } 62 | 63 | /** 64 | * Renders poll update form. 65 | * @param ActiveForm $form 66 | * @param Poll $model 67 | * @return string 68 | */ 69 | public static function update($form, $model) 70 | { 71 | return (new static)->render('update', ['form' => $form, 'model' => $model]); 72 | } 73 | 74 | /** 75 | * Returns rendered preview of the poll. 76 | * @param Thread $model 77 | * @return string 78 | */ 79 | public static function preview($model) 80 | { 81 | if (!$model->pollAdded) { 82 | return null; 83 | } 84 | return (new static)->render('preview', ['model' => $model]); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/widgets/poll/views/_form.php: -------------------------------------------------------------------------------- 1 | Yii::t('podium/view', 'Leave empty to remove')]; 9 | $opts['id'] = 'thread-poll_answers' . ($a > 1 ? '_' . $a : ''); 10 | if (!empty($model->$pollAnswers[$a - 1])) { 11 | $opts['value'] = $model->$pollAnswers[$a - 1]; 12 | $answers++; 13 | } else { 14 | $opts['value'] = null; 15 | } 16 | $options[$a] = $opts; 17 | } 18 | $answers = max([2, $answers]); 19 | $this->registerJs(<<= 10) { $(".podium-poll-plus").addClass("hide"); }}); 21 | JS 22 | ); 23 | 24 | $fieldLayoutLong = [ 25 | 'labelOptions' => ['class' => 'control-label col-sm-3'], 26 | 'template' => "{label}\n
{input}\n{hint}\n{error}
" 27 | ]; 28 | $fieldLayoutShort = [ 29 | 'labelOptions' => ['class' => 'control-label col-sm-3'], 30 | 'template' => "{label}\n
{input}\n{hint}\n{error}
" 31 | ]; 32 | 33 | ?> 34 |
35 | field($model, $pollQuestion, $fieldLayoutLong); ?> 36 |
37 |
38 | field($model, $pollVotes, $fieldLayoutShort); ?> 39 |
40 |
41 | field($model, $pollHidden, [ 42 | 'checkboxTemplate' => "
\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n{error}\n{hint}\n
" 43 | ])->checkbox(); ?> 44 |
45 |
46 | field($model, $pollEnd, $fieldLayoutShort)->widget(DatePicker::classname(), [ 47 | 'removeButton' => false, 'pluginOptions' => ['autoclose' => true, 'format' => 'yyyy-mm-dd'] 48 | ]); ?> 49 |
50 | $option): ?> 51 |
52 | field($model, $pollAnswers .'[]', $fieldLayoutLong) 53 | ->label(Yii::t('podium/view', 'Option #{n}', ['n' => $index]), ['for' => $option['id']]) 54 | ->textInput($option); ?> 55 |
56 | 57 |
58 |
59 | 60 |
61 |
-------------------------------------------------------------------------------- /src/widgets/poll/views/create.php: -------------------------------------------------------------------------------- 1 | registerJs(<< 'poll_added']); 12 | ?> 13 | 16 | 17 |
18 |
19 |
20 | 21 | 22 |
23 |
24 | render('_form', [ 25 | 'form' => $form, 26 | 'model' => $model, 27 | 'pollQuestion' => 'pollQuestion', 28 | 'pollVotes' => 'pollVotes', 29 | 'pollHidden' => 'pollHidden', 30 | 'pollEnd' => 'pollEnd', 31 | 'pollAnswers' => 'pollAnswers', 32 | ]) ?> 33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /src/widgets/poll/views/preview.php: -------------------------------------------------------------------------------- 1 | 6 |
7 |

: pollQuestion) ?>

8 |
    9 |
  • : pollVotes) ?>
  • 10 | pollHidden): ?> 11 |
  • 12 | 13 |
  • 14 | 15 |
  • 16 | 17 | : 18 | pollEnd) ? '-' : Html::encode($model->pollEnd) . ' 23:59' ?> 19 |
  • 20 |
21 |
    22 | pollAnswers as $answer): ?> 23 | 24 |
  • 25 | 26 | 27 |
-------------------------------------------------------------------------------- /src/widgets/poll/views/update.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | render('_form', [ 7 | 'form' => $form, 8 | 'model' => $model, 9 | 'pollQuestion' => 'question', 10 | 'pollVotes' => 'votes', 11 | 'pollHidden' => 'hidden', 12 | 'pollEnd' => 'end', 13 | 'pollAnswers' => 'editAnswers', 14 | ]) ?> 15 |
16 |
17 | -------------------------------------------------------------------------------- /src/widgets/quill/QuillBasic.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.2 12 | */ 13 | class QuillBasic extends Quill 14 | { 15 | /** 16 | * @var bool|string|array Toolbar buttons. 17 | */ 18 | public $toolbarOptions = [ 19 | ['bold', 'italic', 'underline', 'strike'], 20 | [['list' => 'ordered'], ['list' => 'bullet']], 21 | [['align' => []]], 22 | ['link'] 23 | ]; 24 | } 25 | -------------------------------------------------------------------------------- /src/widgets/quill/QuillFull.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 0.2 12 | */ 13 | class QuillFull extends Quill 14 | { 15 | /** 16 | * @var bool|string|array Toolbar buttons. 17 | */ 18 | public $toolbarOptions = [ 19 | [['align' => []], ['size' => ['small', false, 'large', 'huge']], 'bold', 'italic', 'underline', 'strike'], 20 | [['color' => []], ['background' => []]], 21 | [['header' => [1, 2, 3, 4, 5, 6, false]], ['script' => 'sub'], ['script' => 'super']], 22 | ['blockquote', 'code-block'], 23 | [['list' => 'ordered'], ['list' => 'bullet']], 24 | ['link', 'image', 'video'], 25 | ['clean'] 26 | ]; 27 | 28 | /** 29 | * @var array Collection of modules to include and respective options. 30 | */ 31 | public $modules = ['syntax' => true]; 32 | 33 | /** 34 | * @var string Highlight.js stylesheet to fetch from https://cdnjs.cloudflare.com 35 | */ 36 | public $highlightStyle = 'github-gist.min.css'; 37 | 38 | /** 39 | * @var string Additional JS code to be called with the editor. 40 | * @since 0.3 41 | */ 42 | public $js = "{quill}.getModule('toolbar').addHandler('image',imageHandler);function imageHandler(){var range=this.quill.getSelection();var value=prompt('URL:');this.quill.insertEmbed(range.index,'image',value,Quill.sources.USER);};"; 43 | } 44 | -------------------------------------------------------------------------------- /tests/_bootstrap.php: -------------------------------------------------------------------------------- 1 | amOnRoute('podium/install/run'); 10 | } 11 | 12 | public function openInstallPage(FunctionalTester $I) 13 | { 14 | $I->seeElement('#drop'); 15 | $I->seeElement('#installPodium'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/functional/_bootstrap.php: -------------------------------------------------------------------------------- 1 |